"Swing-Shell" java.lang.InternalError: 无法初始化COM:HRESULT=0x80010106

15

我有一个Java 9应用程序,我正在尝试将其打包到Windows商店。奇怪的是,当我直接运行exe启动器时,它按预期工作,但是当我通过APPX包运行启动器时,出现以下奇怪的错误:

Exception in thread "Swing-Shell" java.lang.InternalError: Could not 
initialize COM: HRESULT=0x80010106 
at java.desktop/sun.awt.shell.Win32ShellFolderManager2.initializeCom(Native Method) 
at java.desktop/sun.awt.shell.Win32ShellFolderManager2$ComInvoker$1.run(Unknown Source at java.base/java.lang.Thread.run(Unknown Source)

HRESULT=0x80010106表示RPC_E_CHANGED_MODE,我猜这意味着COM以某种方式已在MTA模式下初始化。但为什么这只是Windows Bridge沙箱中的问题?Windows Bridge是否以某种方式预先初始化了COM?

我不确定这是Java 9问题还是Desktop Bridge问题,或者两者都有关。是否有人对如何识别问题的原因或解决方法有任何想法?

我制作了一个最小化的示例项目来重现此问题。

该应用程序在直接执行时可以工作,但在通过APPX启动器执行时无法工作。为什么呢?


{btsdaf} - Naman
{btsdaf} - Alan Bateman
@AlanBateman 这个问题在 APPX 容器之外不会重复。使用 Java 9 运行 jar -jar main.jar 可以正常工作。 - rednoah
如果我能够访问所有Java 9 EA版本构建,那么我就可以缩小引入问题的构建范围,但我无法在任何地方找到它们进行下载,也无法弄清如何从源代码构建它们。 - rednoah
我在哪里可以下载JDK9 b57版本的发布文件? - rednoah
显示剩余11条评论
1个回答

2

分析

JDK中依赖于COM初始化的部分(D3DPipeline、声音和Windows shellfolder访问)都遵循相同的模式:

  • 在单独的线程中运行代码
  • 对于这个线程,调用CoInitialize
  • 在用户级别代码结束时,调用CoUnitialize

这与MSDN为COM提供的文档一致,分析是正确的,错误表明COM子系统已经初始化为MTA线程。

因此,我修改了Java启动器(jvm.dll),并在os_windows.cpp的一些本地方法中插入了调试语句。我专注于线程方法。我发现了这个:

  • create_main_threadcreate_os_threadpd_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初始化错误)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接