tl;dr:C++ 插件需要调用 Java .jar 库。如何毫不费力地将它部署给用户?
我正在为 Qt 应用程序编写 Qt 插件。该插件需要调用现有的 Java 库。这需要跨平台(Win、Mac、Linux)和架构(32 位和 64 位 Intel,无 PPC)工作。
我得到了一个简单的“hello world”JNI 示例编译和运行。我将 CMake 脚本更新为“find_package(JNI REQUIRED)”等,以便它针对 jni.h header 进行编译并动态链接到 JVM 库。
至少在 Windows 上,CMake 可以很好地找到在编译时使用的正确 JVM。我关心的是在运行时找到正确的 JRE(jvm.dll 等),因为我对用户计算机的控制较少。
当我将插件发送给我的用户时,它将如何工作?他们需要为适当的体系结构安装 JRE。但这并不意味着 JRE lib 目录将在它们的路径中。如果不是,插件将退出并且不会加载。
在 Windows 上,64 位 JDK 安装 jvm.dll 似乎也很麻烦:
C:\Program Files\Java\jre7\bin\server\jvm.dll
但 32 位 JDK 将其安装到:
C:\Program Files (x86)\Java\jre7\bin\client\jvm.dll
我了解 PF 与 PFx86 的区别,但我不了解服务器/客户端。这些实际上是不同的 JRE 吗?
如果我针对一个 JRE 版本编译/链接并且用户有不同的版本,它会工作吗?
我想这在 Linux/Mac 上会更容易,但我还没有走到那一步。
感谢任何帮助。我不依赖于使用 JNI,但买不起 2000 美元的编译器来将 Java 转换为 native 代码库(无论如何我没有源代码),而且我听说 gcj 可能无法胜任这项任务(并且可能在 Windows 上不会有太大帮助)。
最佳答案
要添加到@hmjd 的回答中的一些更详细的注释,其中很多细节让我感到困惑。
这是我用来编译和链接测试程序的命令行:
cl
-I"C:\Program Files (x86)\Java\jdk1.7.0_02\include"
-I"C:\Program Files (x86)\Java\jdk1.7.0_02\include\win32"
/EHsc
-MD
Test.cpp
jvm.lib
delayimp.lib
/link
/LIBPATH:"C:\Program Files (x86)\Java\jdk1.7.0_02\lib"
/DELAYLOAD:jvm.dll
一些注意事项:
- 包括头文件的路径(
jni.h
等) - 添加
/EHsc
以避免此警告:“c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323):警告 C4530:使用了 C++ 异常处理程序,但未启用展开语义。指定/EHsc" - 仍然需要包含
jvm.lib
,即使它应该被延迟加载。 delayimp.lib
必须包含在内。/LIBPATH
需要跟在/link
选项之后,以便链接器获取它。这是 .lib 文件的路径。/DELAYLOAD
获取 .dll 文件,而不是 .lib 文件!如果您不小心将 .lib 文件提供给它,则不会收到有用的错误消息,它只会显示“LINK:警告 LNK4199:/DELAYLOAD:jvm.lib 已忽略;未从 jvm.lib 中找到任何导入”。
在 .cpp 文件中,#include "windows.h"
,找出哪个目录有 jvm.dll
,并进行这样的调用:
std::string temp = "C:\\Program Files (x86)\\Java\\jdk1.7.0_02\\jre\\bin\\client";
SetDllDirectory(temp.c_str());
在从库中调用任何函数之前执行此操作,以便在调用 LoadLibrary() 时它知道在哪里可以找到 DLL。另一种方法是使用 SetEnvironmentVariable()
修改 PATH
变量,但 SetDllDirectory()
似乎是更好的选择。
在启动代码的某处,添加如下代码:
__try
{
// call a function from the DLL to make sure it can be loaded
::JNI_CreateJavaVM(...);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// If not, fail
}
最好把它放在它自己的函数中的某个地方,因为 __try
/__except
结构化异常处理 (SEH) 的东西不允许与代码共享一个函数进行对象展开。
如果没有 SEH 的东西,如果找不到 DLL,程序就会崩溃。
一些有用的链接:
关于java - 如何部署混合 C++/Java (JNI) 应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9120694/