我有一个 C DLL 的 JNA 包装器。它工作正常,除非在 Windows 32 位系统上使用。这是一个简化的例子:
int SetData(const wchar_t* data);
int SetId(const wchar_t* id, uint32_t flags);
我按如下方式创建了 JNA 绑定:
public static native int SetData(WString data);
public static native int SetId(WString id, int flags);
第一个函数 SetData() 在 32 位和 64 位 Windows 上都可以正常工作,但第二个函数在 Windows 7 32 位上崩溃。
我尝试按照其他相关帖子中的建议使用 NativeLong,但没有帮助。
这是存储库的链接:
答案 0 :(得分:2)
您的映射是正确的:WString
是 const wchar_t*
的正确映射,而 int
(Java 中始终为 32 位)是 uint32_t
的正确映射(始终为 32位),有一个关于签名的警告,当用作标志位掩码时应该无关紧要。
我不确定您在哪里读到 NativeLong
在这里合适。这主要用于 *nix 本机代码,其中 sizeof(long)
因操作系统位数而异。实际上,它在 Windows 上实际上并不重要,因为 LONG
始终是 32 位,但它涉及不必要的对象创建而不是原始对象。
JNA 抛出的“无效内存访问”错误是对 Structured Exception Handling 捕获的本机内存错误的“优雅”处理。您真正需要知道的是您正在尝试访问不属于您的本机内存或您的本机内存分配失败。
调试这些错误总是涉及仔细跟踪内存分配。什么时候分配内存?什么时候发布?谁(Java 端或本机端)负责此分配?当您尝试将数据从用户提供的 ID 字符串复制到您的本机 DLL 正在访问的本机内存时,您的程序可能会崩溃。所以这指向了你需要调查的两个内存指针。
查看正在写入 ID 字符串的内存。找出为它分配内存的时间。确保它对于字符串足够大(对于空终止符应该是 2x 字符串长度 + 2 个字节)并正确归零(或明确附加一个空字节)。验证所有 WinAPI 调用都使用正确的(W 与 A)unicode 版本。
LA_IN_MEMORY
添加到 flags
位掩码,但收到错误消息“试用尚未开始或已被篡改!试用剩余天数:30”。这显然是由下一行 (IsLicenseGenuine()
) 产生的,这意味着 setProductId()
调用成功。LA_IN_MEMORY
标志时确定您的本机代码的不同之处可能会非常有帮助。无效的内存访问可能与识别要使用的目录或文件有关。
LA_IN_MEMORY
使问题消失,这也可能是可疑的。跟踪 Java 端定义的 ID 字符串发生的情况。鉴于字符串的常量定义,实际的内存分配可能不是问题,但请跟踪指向该字符串的本机指针以确保它不会被无意中覆盖。
正如您在评论中所指出的,减少本机内存分配可以解决此问题,这表明您已达到限制。事实证明,堆栈大小 (-Xss
) 的默认 32 位 Java 本机内存分配为 320 KB。来自Oracle docs:
在 Windows 上,默认线程堆栈大小是从二进制文件中读取的 (java.exe)。从 Java SE 6 开始,该值在 32 位 VM 中为 320k,并且 在 64 位虚拟机中为 1024k。
您可以通过运行 -Xss 选项来减少堆栈大小。为了 例子:
java -server -Xss64k
请注意,在某些版本的 Windows 上,操作系统可能会向上舍入线程 使用非常粗粒度的堆栈大小。如果请求的大小是 小于默认大小 1K 或更多,堆栈大小四舍五入 到默认值;否则,堆栈大小四舍五入为倍数 1 MB。
您可以增加此限制来解决问题,或者如您在备注中指出的那样,降低您的本机分配。您可能希望比 300K 更保守,因为它只留下少量用于堆栈的其他用途。您也可以从较小的值开始,检查 ERR_MORE_DATA
的返回值,然后使用较大的值重试。 300KB 用于注册表值似乎相当大。
另请注意,32 位 Java 具有 total process memory size limit of either 2GB or 4GB,具体取决于操作系统。如果您的 Java 堆分配增长接近该限制,则会减少您可用的本机数量。您可以使用 -Xmx
开关控制堆的大小,还可以使用 -Xss
开关确保有足够的本机内存分配。组合使用这些开关以避免达到进程大小限制。