Hi all,

last month I saw the message from Reinhard Pointer on this list:

http://mail.openjdk.java.net/pipermail/swing-dev/2018-January/008132.html

referring to this bug:

https://bugs.openjdk.java.net/browse/JDK-8189938

With the instructions given in:

https://bugs.openjdk.java.net/browse/JDK-8193928 (closed as duplicate of 
JDK-8189938)

I was able to reproduce the problem on a clean Windows 10 Installation,
as described in the initial report.

The resulting exception leads to my proposed fix:

        Could not initialize COM: HRESULT=0x80010106  

That HRESULT translates to: RPC_E_CHANGED_MODE

This means, that the COM subsystem is already initialized, but with a
different mode. The code uses CoInitialize and this initializes the
threading module to be apartment threaded. The above HRESULT means the
thread was already initialized to be multi-threaded.

The mode can't be changed after is was set once, so instead of bailing
out, I suggest to accept the situation and initialize to be multi-
threaded.

The consequence is that calls from COM to Java could end up on the
wrong thread. A quick look through the code suggest, that COM advises
are not used, so this is a risk that should be taken.

The fix in this case works like this:

 * try to initialize COM as it was
 * check the return
 * if it is RPC_E_CHANGED_MODE, retry initialization als multi-threaded
 * only if that fails raise an exception

One could argue, that if COM is already initialized, you don't need to
do it yourself, but documentation states, that every successful call to
CoInitialize/CoInitializeEx must be paired with CoUninitialize.

D3DPipelineManager tracks this in bComInitialized, the other two
callers in the JDK are:

- PLATFORM_API_WinOS_DirectSound.cpp
- ShellFolder2.cpp

Both are covered in the patches. For ShellFolder2 there is an
initialization check, this is modified as described above. And if both
initialization variant (apartment threaded and multi threaded fail)
this raises an exception.

PLATFORM_API_WinOS_DirectSound is slightly different. The fallback in
the initialization is still done, but no exception is raised in the
error case. This follows the code flow before this change.

I attached the diffs against JDK10 and JDK9 to this email. If they are
stripped, they can be found here:

http://www.doppel-helix.eu/JDK8189938-openjdk9.diff
http://www.doppel-helix.eu/JDK8189938-openjdk10.diff

I hope this helps with a fix.

Greetings

Matthias
diff -r 2c580c0bc54b src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
--- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp	Tue Oct 03 13:45:11 2017 -0700
+++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp	Wed Feb 07 19:27:10 2018 +0100
@@ -257,11 +257,22 @@
 JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolderManager2_initializeCom
         (JNIEnv* env, jclass cls)
 {
+    // Try to initialize the COM subsystem appartment threaded
     HRESULT hr = ::CoInitialize(NULL);
     if (FAILED(hr)) {
-        char c[64];
-        sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr);
-        JNU_ThrowInternalError(env, c);
+        // If the COM subsystem was already initialized to be multithreaded
+        // run CoInitializeEx again with the right threading mode.
+        //
+        // Ensure one successful CoInitializeEx call has happened, as they
+        // need to be balanced with CoUnitialize.
+        if(hr == RPC_E_CHANGED_MODE) {
+            hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+        }
+        if (FAILED(hr)) {
+            char c[64];
+            sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr);
+            JNU_ThrowInternalError(env, c);
+        }
     }
 }
 
diff -r 2c580c0bc54b src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
--- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp	Tue Oct 03 13:45:11 2017 -0700
+++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp	Wed Feb 07 19:27:10 2018 +0100
@@ -476,7 +476,18 @@
 
 DWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)
 {
-    ::CoInitialize(NULL);
+    // Try to initialize the COM subsystem appartment threaded
+    HRESULT hr = ::CoInitialize(NULL);
+    if (FAILED(hr)) {
+        // If the COM subsystem was already initialized to be multithreaded
+        // run CoInitializeEx again with the right threading mode.
+        //
+        // Ensure one successful CoInitializeEx call has happened, as they
+        // need to be balanced with CoUnitialize.
+        if(hr == RPC_E_CHANGED_MODE) {
+            hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+        }
+    }
     while (1) {
         // wait for something to do
         ::WaitForSingleObject(data.startEvent, INFINITE);
diff --git a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
index 523305a..24695a5 100644
--- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
+++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
@@ -297,11 +297,22 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolder2_initIDs
 JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolderManager2_initializeCom
         (JNIEnv* env, jclass cls)
 {
+    // Try to initialize the COM subsystem appartment threaded
     HRESULT hr = ::CoInitialize(NULL);
     if (FAILED(hr)) {
-        char c[64];
-        sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr);
-        JNU_ThrowInternalError(env, c);
+        // If the COM subsystem was already initialized to be multithreaded
+        // run CoInitializeEx again with the right threading mode.
+        //
+        // Ensure one successful CoInitializeEx call has happened, as they
+        // need to be balanced with CoUnitialize.
+        if(hr == RPC_E_CHANGED_MODE) {
+            hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+        }
+        if (FAILED(hr)) {
+            char c[64];
+            sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr);
+            JNU_ThrowInternalError(env, c);
+        }
     }
 }
 
diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
index 3e67738..21755f6 100644
--- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
+++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
@@ -479,7 +479,18 @@ DS_StartBufferHelper::Data::~Data() {
 
 DWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)
 {
-    ::CoInitialize(NULL);
+    // Try to initialize the COM subsystem appartment threaded
+    HRESULT hr = ::CoInitialize(NULL);
+    if (FAILED(hr)) {
+        // If the COM subsystem was already initialized to be multithreaded
+        // run CoInitializeEx again with the right threading mode.
+        //
+        // Ensure one successful CoInitializeEx call has happened, as they
+        // need to be balanced with CoUnitialize.
+        if(hr == RPC_E_CHANGED_MODE) {
+            hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+        }
+    }
     while (1) {
         // wait for something to do
         ::WaitForSingleObject(data.startEvent, INFINITE);

Reply via email to