分析
JDK中依赖于COM初始化的部分(D3DPipeline、声音和Windows shellfolder访问)都遵循相同的模式:
- 在单独的线程中运行代码
- 对于这个线程,调用
CoInitialize
- 在用户级别代码结束时,调用
CoUnitialize
这与MSDN为COM提供的文档一致,分析是正确的,错误表明COM子系统已经初始化为MTA线程。
因此,我修改了Java启动器(jvm.dll),并在os_windows.cpp的一些本地方法中插入了调试语句。我专注于线程方法。我发现了这个:
create_main_thread
、create_os_thread
、pd_start_thread
都在COM尚未初始化时运行
- 本地线程初始化程序(
thread_native_entry
)已经在运行时初始化了COM
我更深入地研究了_beginthreadex
,最终在stackoverflow上找到了一个线索。那指向了Visual Studio 2013 Express安装的一部分——threadex.c的源代码。
在那里,你会发现_beginthreadex
并不直接启动提供的线程函数,而是首先运行一个库初始化程序(_threadstartex
)。这个初始化程序的一部分如下:
_ptd->_initapartment = __crtIsPackagedApp();
if (_ptd->_initapartment)
{
_ptd->_initapartment = _initMTAoncurrentthread();
}
_callthreadstartex();
_crtIsPackagedApp
函数通过内核函数检测应用程序是否以“PackagedApp”(即AppX包)运行。如果是,则调用RoInitialize
函数,根据我的理解,它的作用类似于CoInitialize。
长话短说:如果您的应用程序使用Visual Studio 2013构建并作为打包应用程序运行,则会出现环境错误。
已确认,工作版本的Oracle JDK是使用VS2010 SP1构建的,而损坏的版本是使用VS2013SP4构建的。
解决方法
有了上述信息后,谷歌终于找到了一篇支持分析的参考文章:
https://blogs.msdn.microsoft.com/vcblog/2016/07/07/using-visual-c-runtime-in-centennial-project/
该文章中的引用如下:
请注意,在Windows 8时期创建的现有VC++ 12.0库具有运行时检查,以确定应用程序是否在应用程序容器下运行。当将桌面应用程序作为打包应用程序运行时,这些检查可能会限制桌面应用程序的功能或导致其表现为UWA(通用Windows应用程序)(有限的文件系统访问或创建线程初始化MTA等)。我们已经在这些框架包中包含的VC++库中修复了此行为,从而消除了桌面应用程序的现代应用程序限制。
因此,我认为修复应作为AppX包分发的应用程序有两个选项:
- 强制用户安装更新的VC++ 12.0二进制文件(通过引入博客文章中引用的依赖项),或者
- 使用来自更新包的修复版本替换Java 9(我认为也适用于Java 10)捆绑的
msvcr120.dll
我会选择第二种方法,并进行测试。从一个干净的最新的Windows 10系统开始,我安装了JDK 9.0.4,克隆了提供的测试用例,修改它以使用本地安装的JRE(而不是JDK!)并构建了appx包。运行此应用程序,我重现了问题。然后,我用来自以下APPX容器中的msvcr120.dll
替换了我的系统安装的JRE文件夹中的文件:
https://www.microsoft.com/en-us/download/details.aspx?id=53176
提示:*.appx只是带有附加签名的ZIP文件,因此您可以将它们重命名并提取内容。
我重新构建了测试用例,现在它可以正常工作(不再出现COM初始化错误)。
jar -jar main.jar
可以正常工作。 - rednoahJDK9 b57
版本的发布文件? - rednoah