如何使GetLastError可靠地与JNA一起工作



我在Java应用程序中使用各种Win32 API函数,我使用GetLastError来获取有关失败API调用的信息。

大多数情况下它工作,但现在我发现一个情况,某些东西似乎重置最后一个错误。

这是我在Java应用程序和VB6应用程序中所做的:

  1. 我打开PID为4 (System)的进程句柄
  2. 我用这个句柄调用GetModuleFileNameEx
  3. 我用那个句柄调用GetProcessImageFileName

现在两个 API函数都按预期失败(它们返回零),并且在我的VB6应用程序GetLastError返回87("无效参数")后,两个API调用。

但是在我的Java应用程序中,只有在GetModuleFileNameEx之后,最后一个错误被设置为87。GetProcessImageFileName之后总是0

我该怎么办?

编辑:

下面是JNA声明:

public interface PsApi extends StdCallLibrary {
    PsApi INSTANCE = (PsApi) Native.loadLibrary("psapi", PsApi.class, 
            W32APIOptions.UNICODE_OPTIONS);
    int GetModuleFileNameEx(WinNT.HANDLE hProcess, WinDef.HMODULE hModule, 
            char[] lpFilename, int nSize);
    int GetProcessImageFileName(WinNT.HANDLE hProcess, char[] lpImageFileName, 
            int nSize);
}

GetProcessImageFileName的调用代码

char[] buffer = new char[2048];
int result = PsApi.INSTANCE.GetProcessImageFileName(hProcess, buffer, 
   buffer.length);
if (result == 0) {
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new String(Arrays.copyOf(buffer, result));

对于GetModuleFileNameEx

char[] buffer = new char[2048];
int result = PsApi.INSTANCE.GetModuleFileNameEx(hProcess, hModule, buffer,
    buffer.length);
if (result == 0) {
     throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new String(Arrays.copyOf(buffer, result));

文档描述了两个选项:

如果函数设置了系统错误属性(errno或GetLastError()),如果您在JNA映射中声明了异常,则错误代码将作为lasterroreexception抛出。或者,您可以使用Native.getLastError()来检索它,前提是Native.setPreserveLastError(boolean)已经被调用并带有一个真值。抛出异常是首选,因为它有更好的性能。

调用GetLastError的问题是JNA框架和Java运行时可能会调用重置错误的Windows函数。因此,您不应该尝试直接调用GetLastError

您可以使用"throws lasterroreexception ",这通常是有效的,但这并不总是最好的解决方案。测试API函数的返回值并在API函数的返回值表明发生错误时调用Native.getLastError()会更安全。

native . getlasterror()返回系统错误值的内部副本,该副本是在JNA调度的最后一个API方法调用返回后立即存储的(这是在调用ffi_call()之后在native/dispatch.c中完成的)。错误值由JNA存储在线程本地存储中,不能由Java运行时更改。不再需要调用Native.setPreserveLastError(true),因为最后的错误值总是被保留。

如果使用了"throws lasterroreexception ", JNA在调度函数调用之前将系统错误值设置为0。正常情况下,系统API函数成功后,该值为零,但没有明确的保证。

Linux程序员手册(man 3 errno)指出:"它的值只有在调用的返回值表明有错误时才有意义(例如,大多数系统调用的-1;-1或NULL从大多数库函数);"
维基百科:"任何库函数都可以在返回之前更改存储的值,无论它们是否检测到错误。"

在Windows下,GetLastError API函数的文档不能保证一个API函数,它被记录为设置最后错误代码,在成功时将其设置为0或将其保留为0。

最新更新