java.lang.Error: Invalid memory access on 32 bit Windows

java.lang.Error: Invalid memory access on 32 bit Windows

我有一个 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,但没有帮助。

这里是存储库的link:

https://github.com/cryptlex/lexactivator-java/blob/master/src/main/java/com/cryptlex/lexactivator/LexActivatorNative.java

您的映射是正确的:WStringconst wchar_t* 的正确映射,int(Java 中始终是 32 位)是 [= 的正确映射13=](始终为 32 位),但需要注意的是,当用作标志位掩码时,签名无关紧要。

我不确定你在哪里读到 NativeLong 在这里是合适的。这主要用于 *nix 本机代码,其中 sizeof(long) 根据 OS 位数而不同。实际上,它在 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 标志时本机代码的不同之处可能会非常有帮助。无效的内存访问可能与识别要使用的目录或文件有关。
      • 3.14.9 的最新更新日志条目涉及此标志。查看该提交可能会提示问题。
      • 3.15.0 中的另一个最新更改涉及自动检测 Windows 上的文件,鉴于 LA_IN_MEMORY 使问题消失,这也可能是可疑的。
    • 给定无效密钥时,出现错误消息“43:产品 ID 不正确。”是 returned,因此本机代码中访问无主内存的点是在此错误检查之后。
  • 跟踪在 Java 端定义的 ID 字符串发生了什么。鉴于字符串的常量定义,实际的内存分配可能不是问题,但要跟踪指向该字符串的本机指针以确保它不会被无意中覆盖。

正如您在评论中指出的那样,减少本机内存分配可以解决此问题,表明您已达到极限。事实证明,堆栈大小 (-Xss) 的默认 32 位 Java 本机内存分配是 320 KB。来自 Oracle docs:

On Windows, the default thread stack size is read from the binary (java.exe). As of Java SE 6, this value is 320k in the 32-bit VM and 1024k in the 64-bit VM.

You can reduce your stack size by running with the -Xss option. For example:

java -server -Xss64k

Note that on some versions of Windows, the OS may round up thread stack sizes using very coarse granularity. If the requested size is less than the default size by 1K or more, the stack size is rounded up to the default; otherwise, the stack size is rounded up to a multiple of 1 MB.

您可以增加此限制来解决问题,或者如您在备注中指出的那样,降低您的本机分配。您可能希望比 300K 更保守,因为它只留下少量用于堆栈的其他用途。您也可以从较小的值开始,检查 ERR_MORE_DATA 的 return 值,然后使用更大的值重试。 300KB 似乎是一个相当大的数字,用于注册表值。

另请注意,32 位 Java 有一个 total process memory size limit of either 2GB or 4GB,具体取决于 OS。如果您的 Java 堆分配增长到接近该限制,它会减少您可用的本机数量。您可以使用 -Xmx 开关控制堆的大小,还可以使用 -Xss 开关确保足够的本机内存分配。组合使用这些开关以避免达到进程大小限制。