如何部署混合C++/Java(JNI)应用程序?
tl;DR:C++插件需要调用java。jar库。如何将其部署到用户身上而不会让人感到太头痛强>
我正在为一个Qt应用程序编写一个Qt插件。该插件需要调用现有的Java库。这需要跨平台(Win、Mac、Linux)和体系结构(32位和64位Intel,无PPC)工作
我编译并运行了一个简单的“hello world”JNI示例。我将CMake脚本更新为“find_package(需要JNI)”等,因此它可以根据JNI进行编译。针对JVM库的h头和动态链接
至少在Windows上,CMake很好地找到了在编译时使用的正确JVM。我关心的是在运行时找到正确的JRE(jvm.dll等),因为我对用户计算机的控制较少
当我将插件发送给我的用户时,它将如何工作?他们将需要安装一个JRE以获得合适的体系结构。但这并不意味着JRE库目录将位于它们的路径中。如果没有,插件就退出,不加载
在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转换成本机代码库(反正我没有源代码),而且我听说gcj可能无法胜任这项任务(在Windows上可能不会有太大帮助)
# 1 楼答案
在@hmjd的回答中添加了一些更详细的注释,其中很多细节让我感到困惑
这是我用来编译和链接测试程序的命令行:
需要注意的几件事:
jni.h
,等等)jvm.lib
,即使它应该延迟加载李>delayimp.lib
必须包括在内李>/LIBPATH
需要跟随/link
选项,以便链接器获得它。这是通往的道路。lib文件李>/DELAYLOAD
获取。dll文件,而不是。lib文件!如果你不小心给了它。lib文件您没有收到有用的错误消息,它只是说“LINK:warning LNK4199:/DELAYLOAD:jvm.lib被忽略;没有从jvm.lib找到导入”李>在教室里。cpp文件
#include "windows.h"
,找出哪个目录有jvm.dll
,并进行如下调用:在从库中调用任何函数之前执行此操作,以便在调用LoadLibrary()时,它知道在哪里可以找到DLL。另一种方法是用
SetEnvironmentVariable()
修改PATH
变量,但SetDllDirectory()
似乎是更好的选择在启动代码的某个地方,添加如下代码:
最好把它放在自己的函数中,因为
__try
/__except
结构化异常处理(SEH)的东西不允许与可能执行对象展开的代码共享函数如果没有SEH,如果找不到DLL,程序就会崩溃
一些有用的链接:
# 2 楼答案
由于我无法在Linux上找到一个与
/DELAYLOAD
等价的方法(这里的问题是:How can I make lazy/delay loading work in Linux?),我一直在使用dlopen()
。它没有我想象的那么糟糕,我可以使用这里描述的技术:Alternatives to dlsym() and dlopen() in C++。事实上,它看起来像jni。h头就是为这种方法设计的。也许我本来应该在Windows上做的# 3 楼答案
仅适用于Windows的可能解决方案
使用延迟DLL加载功能构建QT插件
请参阅DELAYLOAD了解如何执行此操作,但它只是将
/DELAYLOAD:jvm.dll
和Delayimp.lib
添加到链接器命令中。这意味着jvm.dll
在加载QT插件时不会加载,而是在需要时加载(注意,不需要使用LoadLibrary()
和GetProcAddress()
)(我不知道Linux或Mac上是否有类似的功能)提供一种机制来通知QT插件使用什么JRE
这可以是一个注册表值、一个配置文件,也可以是专门针对插件的环境变量(绝对不是其他应用程序可能依赖的环境变量
JAVA_HOME
或JRE_HOME
)。环境变量示例:QT插件修改其路径
在QT插件调用依赖于JRE的任何函数之前,它会修改其
PATH
环境变量,例如,在PATH
的值的开头插入%DANQT_32_JRE_HOME%\bin\server;%DANQT_32_JRE_HOME%\bin\client;
。这意味着当QT插件执行第一个需要JRE的操作时,它将从插入的目录中加载。(64位的环境变量不同)。至于bin\server
和bin\client
,我的理解是,它们本质上是相同的,但是server
由于运行时性能的原因,在初始化期间执行得更多我不确定QT插件是否是针对JRE 6构建的,是否安装了JRE 7。如果存在兼容性问题,则将其作为先决条件安装要求,或者,如果允许(我不确定合法性),将
jvm.dll
与QT插件一起提供