I currently come across with an issue getting my application, App, (written and built under Eclipse RCP), to work on 32bit Windows 10 environment.
Here’s the background, I recently implemented a Windows Shutdown Blocker capability using JNI by embedding that capability in a c++ native code. As a result I generated 2 versions of the dll (using Cygwin – mingw32 capabilities - https://www3.ntu.edu.sg/home/ehchua/programming/howto/Cygwin_HowTo.html) one for 64 bit (same as my dev pc) and one for 32 bit. Then bundle them up in OSGi Bundle native-code. Finally the application is then packaged up and built into a .exe that can be deployed to client machines. Some clients use 32bit Windows so I have to cater for it.
In order to test this, I setup a vm using my pc’s Hyper-V function with Windows 10 32bit OS installed (a Windows evaluation license). Deployed my app in there and tried initiate activities that kicks off the Windows Shutdown Blocker capabilities I’ve produced.
Then I get the following error in the logs (I don’t get this issue in my 64bit Windows host environment and it’s working perfectly):
!SESSION 2020-01-21 16:48:42.604 -----------------------------------------------
eclipse.buildId=unknown
java.version=1.8.0_60
java.vendor=Oracle Corporation
BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en _AU
Framework arguments: --startup plugins/org.eclipse.equinox.launcher_1.4.0.v20161219-1356.jar
Command-line arguments: -os win32 -ws win32 -arch x86 --startup plugins/org.eclipse.equinox.launcher_1.4.0.v20161219-1356.jar -user @user.home -data @user.home/App/202001201633
!ENTRY org.eclipse.ui 2 0 2020-01-21 16:50:10.579
!MESSAGE Save All Failed
!STACK 0
java.lang.UnsatisfiedLinkError: C:\Users\Dale\App\202001201633\configuration\org.eclipse.osgi\11\0\.cp\lib\shutdown\windows-i686\ShutdownBlocker.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1938)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1834)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
…
The end-to-end process on how I generated the .dll file goes like this:
- Download and install Cygwin including the g++ compilers (32bit and 64bit)
- Open Windows command prompt
- Generate .h file from JNI class:
javac -h . ShutdownBlocker.java
This is the Java file
public class ShutdownBlocker {
private static boolean nativeLibLoaded;
public static void loadLibrary() {
if(!nativeLibLoaded) {
System.loadLibrary("ShutdownBlocker");
nativeLibLoaded=true;
}
}
public static native void shutdownBlockReasonCreate(String title, String reasonText);
public static native void shutdownBlockReasonDestroy(String title);
}
This is the .h file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_app_service_util_shutdown_ShutdownBlocker */
#ifndef _Included_com_app_service_util_shutdown_ShutdownBlocker
#define _Included_com_app_service_util_shutdown_ShutdownBlocker
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_app_service_util_shutdown_ShutdownBlocker
* Method: shutdownBlockReasonCreate
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_app_service_util_shutdown_ShutdownBlocker_shutdownBlockReasonCreate
(JNIEnv *, jclass, jstring, jstring);
/*
* Class: com_app_service_util_shutdown_ShutdownBlocker
* Method: shutdownBlockReasonDestroy
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_app_service_util_shutdown_ShutdownBlocker_shutdownBlockReasonDestroy
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
- Create a .cpp file, i.e. ShutdownBlocker.cpp
#include <jni.h>
#include <iostream>
#include "com_app_service_util_shutdown_ShutdownBlocker.h"
#include <windows.h>
#include <commctrl.h>
using namespace std;
namespace {
LPCWSTR SHUTDOWN_REASON = L"Application is still saving ...";
LRESULT CALLBACK AppWndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT_PTR uIdSubclass,
_In_ DWORD_PTR dwRefData
) {
// Callback function required to process some of the key OS messages in order to stall shutdown
switch (message) {
case WM_QUERYENDSESSION:
PostMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
case WM_ENDSESSION:
PostMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, AppWndProc, uIdSubclass);
break;
}
return DefSubclassProc(hWnd, message, wParam, lParam);
}
}
JNIEXPORT void JNICALL Java_com_app_service_util_shutdown_ShutdownBlocker_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title, jstring reasonText) {
SHUTDOWN_REASON = (LPCWSTR) env->GetStringChars(reasonText, NULL);
// Parse reasonText string passed in from Java code and retrieve handle to the App window with it
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
env->ReleaseStringChars(title, str);
if (hWnd == NULL) {
return;
}
ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON);
// Obtain control of the window by subclassing its procedure
SetWindowSubclass(hWnd, &AppWndProc, 1, 0);
return;
}
JNIEXPORT void JNICALL Java_com_app_service_util_shutdown_ShutdownBlocker_shutdownBlockReasonDestroy(JNIEnv *env, jclass cls, jstring title) {
// Parse reasonText string passed in from Java code and retrieve handle to the App window with it
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
env->ReleaseStringChars(title, str);
if (hWnd == NULL) {
return;
}
// Release control of the window procedure
RemoveWindowSubclass(hWnd, &AppWndProc, 1);
ShutdownBlockReasonDestroy(hWnd);
return;
}
- Open Cygwin
- Run following command to generate ShutdownBlocker.dll file for each Windows OS architecture
For 64bit windows
x86_64-w64-mingw32-g++ -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/win32" -Wl,"C:\Windows\System32\comctl32.dll" -shared -o ShutdownBlocker.dll /cygdrive/c/dale/eclipse-rcp-workspace/App/development/shutdown/ShutdownBlocker.cpp
For 32bit windows
i686-w64-mingw32-g++ -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/win32" -Wl,--enable-stdcall-fixup "C:\Windows\SysWOW64\comctl32.dll" -shared -o ShutdownBlocker.dll /cygdrive/c/dale/eclipse-rcp-workspace/App/development/shutdown/ShutdownBlocker.cpp
- Copy the dll files to appropriate location in order for OSGi Bundle to reference
- Update OSGi reference
- Update build.properties
- Update MANIFEST.MF. Pasting relevant section here:
Bundle-NativeCode: lib/shutdown/windows-amd64/ShutdownBlocker.dll;
processor=x86_64,
lib/shutdown/windows-i686/ShutdownBlocker.dll;
processor=x86, *
- Save -> Refresh -> Clean -> commit change
- Using build tools to generate the setup.exe executable one (which bundles with Java runtime) for each OS architecture
- Deploy and run
setup32bit.exe
in my 32 bit Windows VM (as mentioned above) - Run the App to kick-off the Shutdown Blocker capability
- Then I get the above error which prevents the initiation of Shutdown Blocker capability
My approach to fix it (in vain):
- I tried to use a DLL Dependency Walker as recommended in this post JNI dependent libraries. Ran it and found it throwing a lot of error on dependencies (which I don’t find it very helpful, because I did the same on a random c:\windows\system32 dll file which gives me similar outcome)
Or maybe I’m not using this tool correctly, here’s how I run it:
C:\Users\Dale >depends.exe ShutdownBlocker.dll
- Then I found another post that suggests using a Cygwin capability called “objdump” (https://linux-tips.com/t/how-can-i-find-library-dependencies-of-a-windows-dll/328) I ran it and I got this:
$ objdump -p ShutdownBlocker.dll | grep DLL
DLL
vma: Hint Time Forward DLL First
DLL Name: KERNEL32.dll
DLL Name: msvcrt.dll
DLL Name: USER32.dll
DLL Name: libstdc++-6.dll
DLL Name: COMCTL32.dll
- Seems promising and I found “libstdc++-6.dll” is the only dll missing on the new 32bit vm. So I copied this .dll file into a directory and updated PATH variable.
- Running the App again but the change made no difference (same error)
In which case, how do I know which library it has trouble finding? Or anything I did wrong here?
Thanks in advance for anyone who can shed some light on this issue. And really appreciated your time on this!
Updated DLL Dependency Walker Image
Updated DLL Dependency Walker Image
Think I found a resolution to the issue of finding dependency dll for my ShutdownBlocker.dll. As you can see in the above image all looks fine now. It turned out by moving all the dependent libraries such as libstdc++-6.dll, libgcc_s_sjlj-1.dll and libwinpthread-1.dll to the same directory as ShutdownBlocker.dll solve the problem. Adding these dlls into PATH seemed to fix the error I had. But now I'm getting a different error:
!ENTRY org.eclipse.ui 4 0 2020-01-22 16:57:22.218
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.UnsatisfiedLinkError: com.app.service.util.shutdown.ShutdownBlocker.shutdownBlockReasonCreate(Ljava/lang/String;Ljava/lang/String;)V
(not sure if I can still discuss this issue here or I need to create a new question?)