如何防止JNI覆盖GetLastError()

时间:2013-01-18 10:26:41

标签: java c winapi java-native-interface

我们正在为xidobi串口项目实现一些winapi方法的一对一映射。 C方法到java的映射按预期工作,但由于未知原因GetLastError()被清除。

这是 C-Code

// CreateFile ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_CreateFile(JNIEnv *env, jobject this,
    jstring lpFileName,
    jint dwDesiredAccess,
    jint dwShareMode,
    jint lpSecurityAttributes,
    jint dwCreationDisposition,
    jint dwFlagsAndAttributes,
    jint hTemplateFile) {

const char* fileName = (*env)->GetStringUTFChars(env, lpFileName, NULL);

HANDLE handle = CreateFile(fileName,
                            dwDesiredAccess,
                            dwShareMode,
                            (LPSECURITY_ATTRIBUTES) lpSecurityAttributes,
                            dwCreationDisposition,
                            dwFlagsAndAttributes,
                            (HANDLE) hTemplateFile);

(*env)->ReleaseStringUTFChars(env, lpFileName, fileName);

return (jint) handle;
}

// GetLastError ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_GetLastError(JNIEnv *env, jobject this) {
    return (jint)  GetLastError();
}

Java 中,我们像这样调用映射的本机方法:

int handle = os.CreateFile("\\\\.\\" + portName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

if (handle != INVALID_HANDLE_VALUE)
    return handle;
int lastError= os.GetLastError(); //-> sometimes 0 (ERROR_SUCCESS)

我们发现,如果我们在GetLastError()之后立即在C中调用CreateFile(..),则会返回正确的错误代码。由于一对一映射很简单,我们假设JNI或VM调用SetLastError()本身并清除我们的上一个错误。

我们不想放弃一对一的地图设计,那么我们可以做些什么来解决这个难题呢?

这是一个类似的问题,在这种情况下无效:CreateFile() returns INVALID_HANDLE_VALUE but GetLastError() is ERROR_SUCCESS

1 个答案:

答案 0 :(得分:4)

无法保证GetLastError()无论JVM在您对CreateFile的本机通话与您对GetLastError的本机通话之间所做的事情都能生存。因此,您应该在GetLastError之后立即调用CreateFile并将值保存在您自己的线程本地插槽中。

然后,GetLastError的实施将从您存储它的任何地方检索它。

您可能希望将其重命名为LastXidobiError或其中某些内容只会检索由您的库中的调用设置的错误。

// Space to store last error ////////////////////////////////////////////
static DWORD dwTlsIndexLastError = 0;

BOOL WINAPI DllMain(
  _In_  HINSTANCE hinstDLL,
  _In_  DWORD fdwReason,
  _In_  LPVOID lpvReserved
){


    switch(fdwReason){
    case DLL_PROCESS_ATTACH:
        dwTlsIndexLastError = TlsAlloc();
        break;
    case DLL_PROCESS_DETACH:
        TlsFree(dwTlsIndexLastError);
        dwTlsIndexLastError = 0;
        break;
     }
     return TRUE;
}

///// Save the last error. 
///// Call this function after the "real" function whose error you want to report.
void SaveLastError()
{
    TlsSetValue(dsTlsIndexLastError, (LPVOID)(DWORD_PTR)GetLastError());
} 
// GetLastError ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_GetLastError(JNIEnv *env, jobject this) {
    return (jint)  TlsGetValue(dsTlsIndexLastError);
}

// CreateFile ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_CreateFile(JNIEnv *env, jobject this,
    jstring lpFileName,
    jint dwDesiredAccess,
    jint dwShareMode,
    jint lpSecurityAttributes,
    jint dwCreationDisposition,
    jint dwFlagsAndAttributes,
    jint hTemplateFile) {

const char* fileName = (*env)->GetStringUTFChars(env, lpFileName, NULL);

HANDLE handle = CreateFile(fileName,
                            dwDesiredAccess,
                            dwShareMode,
                            (LPSECURITY_ATTRIBUTES) lpSecurityAttributes,
                            dwCreationDisposition,
                            dwFlagsAndAttributes,
                            (HANDLE) hTemplateFile);

// Save the value of GetLastError for the relevant function
SaveLastError();

(*env)->ReleaseStringUTFChars(env, lpFileName, fileName);

return (jint) handle;
}