如何使GetLastError可靠地与JNA一起使用?

时间:2011-12-07 10:38:49

标签: java winapi jna

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

大部分时间它都有效,但现在我找到了一个似乎重置上一个错误的情况。

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

  1. 我用PID 4(系统)
  2. 打开进程的句柄
  3. 我用那个句柄打电话给GetModuleFileNameEx
  4. 我用那个句柄打电话给GetProcessImageFileName
  5. 现在两个 API函数都按预期失败(它们返回零),并且在我的VB6应用程序中GetLastError在两次API调用后返回87(“无效参数”)。

    但是在我的Java应用程序中,只有在GetModuleFileNameEx之后,最后一个错误才被设置为87. GetProcessImageFileName之后它始终为零。

    我该怎么办?

    编辑:

    以下是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));
    

2 个答案:

答案 0 :(得分:5)

documentation描述了两个选项:

  

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

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

答案 1 :(得分:1)

你可以使用"抛出LastErrorException"通常是有效的,但这并不总是最好的解决方案。测试API函数的返回值更安全,并在API函数的返回值指示发生错误时调用Native.getLastError()

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

如果"抛出LastErrorException"使用时,JNA在调度函数调用之前将系统错误值设置为0。通常,在系统API函数成功后该值为零,但没有明确的保证。

Linux程序员手册(man 3 errno)声明:" 只有当调用的返回值指示错误时(例如,大多数系统为-1),其值才有意义调用;大多数库函数中的-1或NULL);成功的功能可以改变errno。"
Wikipedia:" 任何库函数都可以在返回之前更改存储的值,无论它们是否检测到错误。"

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