0

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:

  1. Download and install Cygwin including the g++ compilers (32bit and 64bit)
  2. Open Windows command prompt
  3. 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
  1. 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;
}
  1. Open Cygwin
  2. 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
  1. Copy the dll files to appropriate location in order for OSGi Bundle to reference
  2. 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, *
  1. Save -> Refresh -> Clean -> commit change
  2. Using build tools to generate the setup.exe executable one (which bundles with Java runtime) for each OS architecture
  3. Deploy and run setup32bit.exe in my 32 bit Windows VM (as mentioned above)
  4. Run the App to kick-off the Shutdown Blocker capability
  5. Then I get the above error which prevents the initiation of Shutdown Blocker capability

My approach to fix it (in vain):

  1. 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)

enter image description here

Or maybe I’m not using this tool correctly, here’s how I run it:

C:\Users\Dale >depends.exe ShutdownBlocker.dll
  1. 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
  1. 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.
  2. 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

enter image description here

Updated DLL Dependency Walker Image

enter image description here

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?)

dale
  • 439
  • 3
  • 11
  • 28
  • 1
    Not sure about the actual problem, putting the DLLs in PATH should work. To make sure, check if all of them are in fact 32-bit DLLs, especially msvcrt.dll. A few observations: a) You can ignore the "API-MS-WIN-CORE" error entries in Dependency Walker, they don't matter in your case. b) To make the screenshot more useful you should collapse all but the first tree level c) Once you solve this problem, you will get more UnsatisfiedLinkErrors because your exported functions are missing the leading underscore (there's a compiler option in cygin gcc to get them) – user2543253 Jan 21 '20 at 12:12
  • Thanks @user2543253, as per your comment I looked further into the Dependency Walker and found that my 32bit ShutdownBlocker.dll somehow has a dependency on a 64bit libstdc++-6.dll (kinda weird). Don't know how I get that in the first place. So I copied a 64bit libstdc++-6.dll to the PATH location again, I got a different error "Unsatisfied link error: ... ShutdownBlocker.dll: %1 is not a valid Win32 application", which is kinda expected. Looks like I need to revisit how I compiled the 32 bit dll. I've attached a screenshot of the dependency walker in the post. – dale Jan 21 '20 at 23:53
  • There's nothing wrong with how you compile your library 32/64-bit-wise. If Dependency Walker shows a 64-bit DLL, that doesn't mean that the lib requires a 64-bit DLL but that Dependency Walker *found* a 64-bit DLL, that is unusable for a 32-bit application, instead of the correct 32-bit DLL. You have to make sure that you actually copy the 32-bit DLL (and just to be safe remove all 64-bit copies of libstdc++.dll from your PATH). – user2543253 Jan 22 '20 at 13:57
  • OK, forget that, you solved it in the meantime, I didn't actually read your updated question. Your new problem is the one with the underscores that I mentioned before. – user2543253 Jan 22 '20 at 13:59
  • Ah, yes, you did mentioned it. I wasn't so sure what that meant back then now I do. Except I still don't understand about the leading underscore issue. I know you mentioned a compiler option in cygwin, here's a stackoverflow link I found (https://stackoverflow.com/questions/1034852/adding-leading-underscores-to-assembly-symbols-with-gcc-on-win32) were you referring to the option "-fleading-underscore" ? – dale Jan 22 '20 at 22:51
  • 1
    Think I solved the last issue (unsatisfied link error), basically by including "-Wl,--add-stdcall-alias" flag in my g++ compilation would remove this error. This solution is found in the JNI tutorial (https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html) Under "(Windows) 32-bit JDK [Obsolete?]" section. Thank you very much @user2543253 for your suggestions! I'm going to do a few more tests myself to make sure no more issues. – dale Jan 23 '20 at 00:08

0 Answers0