【问题标题】:How do i deal with opaque pointers in JNA?如何处理 JNA 中的不透明指针?
【发布时间】:2021-08-08 08:24:06
【问题描述】:

我的 Java 应用程序使用 JNA 通过制造商提供的 API 与设备驱动程序进行通信。 API 使用 Windows 消息进行通信。在执行期间的某个时间点,设备有一组作为 LPARAM 传递的数据。 LPARAM 是指向包含我需要的数据的结构向量的指针。

在 C++ 中,我可以像这样访问这些数据:

ImagesStruct *Images = { 0 };
...
Images = (ImagesStruct *)lParam;

原生的 ImagesStruct 如下所示:

typedef struct _CompressedImage
{
  BYTE *pBuffer;
  int BufferLen;
}
CompressedImage;

typedef struct _ImagesStruct
{
  DWORD DocNumber;
  CompressedImage *Images;
  BOOL SnippetFront;
}
ImagesStruct;

在 Java 中,我的结构映射如下:

// ImagesStruct
public class IMAGES extends Structure {
    public int DocNumber;
    public COMPRESSED_IMAGE.ByReference Images; // pointer
    public boolean SnippetFront;

    public IMAGES() { super(); }
    public IMAGES(Pointer p) { super(p); }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList("DocNumber", "Images", "SnippetFront");
    }
}

// CompressedImage
public class COMPRESSED_IMAGE extends Structure {
    public Pointer pBuffer; // pointer
    public int BufferLen;

    public COMPRESSED_IMAGE() { super(); }
    public COMPRESSED_IMAGE(Pointer p) { super(p); }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList("pBuffer", "BufferLen");
    }

    public static class ByReference extends COMPRESSED_IMAGE implements Structure.ByReference {
        public ByReference() { super(); }
        public ByReference(Pointer p) { super(p); read(); }
    }
}

这是我尝试在 Java 中访问该数据的众多方法之一

Pointer p = lparam.toPointer(); // this works
IMAGES pImages = new IMAGES();
IMAGES[] pa = (IMAGES[]) pImages.toArray(pImages.size()); // error here

我尝试的一切,包括上面的,似乎都会导致相同的错误消息:

JNA:回调 desktop.IDeal@1175f37 抛出以下异常: java.lang.UnsupportedOperationException: 这个指针是不透明的: 常量@0x155f0000 在 com.sun.jna.Pointer$Opaque.read(Pointer.java:1320) 在 com.sun.jna.Pointer.getByteArray(Pointer.java:726) 在 desktop.IDeal.callback(IDeal.java:199) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:497) 在 com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback(CallbackReference.java:470) 在 com.sun.jna.CallbackReference$DefaultCallbackProxy.callback(CallbackReference.java:500) 在 com.sun.jna.Native.invokeInt(Native Method) 在 com.sun.jna.Function.invoke(Function.java:384) 在 com.sun.jna.Function.invoke(Function.java:316) 在 com.sun.jna.Library$Handler.invoke(Library.java:232) 在 com.sun.proxy.$Proxy2.GetMessage(Unknown Source) at win32.Window.run(Window.java:59) 在 java.lang.Thread.run(Thread.java:745)

我的映射是否正确?如果我无法将指针另一端的数据映射到一组 Java 对象,我该如何使用 Pointer 对象来访问该数据?

【问题讨论】:

    标签: java c++ jna


    【解决方案1】:

    将回调的签名更改为接受IMAGES 而不是LPARAM。 JNA 将为您处理转换。

    您还应该考虑将您的 JNA Structure 命名为与您的本地 struct 完全相同以避免混淆(使用“ImagesStruct”而不是“IMAGES”)。

    在使用基于Pointer 的构造函数初始化JNA Structure 时调用Structure.read() 也是一个好习惯。这可确保 Java 字段在构造函数返回之前与本机内存同步。

    【讨论】:

    • 感谢您的建议。我添加了对 read() 的调用,我之前错过了,但仍然收到 Pointer is opaque 消息。将回调修改为采用 IMAGES 而不是 LPARAM 会做与new IMAGES(lParam.toPointer()) 不同的事情吗?
    • 是的,改变回调签名做一些不同的事情,这就是为什么我建议的第一件事。
    • 我问的原因是因为设备向这个回调管理的窗口句柄发送了很多消息,并且LPARAM经常用于不同类型的数据(主要是ints),不会映射正确地添加到 IMAGES 对象。因此,如果我无法映射 LPARAM -> IMAGES,如果 API 开发人员确实使用了不透明指针,是否有办法手动从指针读取数据?我已经尝试过 Pointer 对象上的各种方法,例如 read()、getByte() 等……它们都失败了,但出现了相同的异常。
    • 使用Pointer 作为类型而不是LPARAM 并从中分配/派生您的Structure
    • 如果将签名方式改为Pointer或不同于LPARAM的其他方式,其他窗口消息是如何处理的?您不能使用指针调用默认的 User32.INSTANCE.DefWindowProc。如果我使用指针回调,窗口不会正确实例化。如果我同时定义了 Pointer 和 LPARAM 回调,则消息似乎在两种方法之间混合在一起。如果我只定义 LPARAM 回调,则窗口会正确实例化......但是我回到方块 1,无法处理指针。
    【解决方案2】:

    我在 JNA Win32WindowDemo 中找到了答案:只需使用 LPARAM 回调并使用 YourStructureHere(new Pointer(lParam.longValue())) 构造函数创建您的结构:

    public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) {
        if (commandCompletionMessages.contains(uMsg) || eventMessages.contains(uMsg)){
                WFSRESULT wfsresult = new WFSRESULT(new Pointer(lParam.longValue()));
        (...)
        }       
        return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-30
      • 1970-01-01
      • 2017-02-12
      • 2013-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多