From 98f855739252857a8dfb59fcdaf1e5e35ca1b271 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 1 Jul 2023 18:16:59 +0200 Subject: [PATCH] flatlaf-natives-windows: reworked linking/loading of jawt.dll; now loading jawt.dll when first used (issue #673) --- CHANGELOG.md | 3 + .../formdev/flatlaf/ui/FlatNativeLibrary.java | 21 +++-- .../flatlaf-natives-windows/build.gradle.kts | 5 +- .../flatlaf-natives-windows/lib/README.md | 6 -- .../flatlaf-natives-windows/lib/jawt-x86.lib | Bin 1688 -> 0 bytes .../lib/jawt-x86_64.lib | Bin 1682 -> 0 bytes .../src/main/cpp/FlatWndProc.cpp | 85 ++++++++++++++++-- 7 files changed, 99 insertions(+), 21 deletions(-) delete mode 100644 flatlaf-natives/flatlaf-natives-windows/lib/README.md delete mode 100644 flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86.lib delete mode 100644 flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86_64.lib diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dea1cb7..e273e984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ FlatLaf Change Log not visible. (issue #686) - "Monocai" theme: Fixed unreadable text color of default buttons. (issue #693) +- Native Windows libraries: Fixed crash when running in Java 8 and newer Java + version is installed in `PATH` environment variable and using class + `SystemInfo` before AWT initialization. (issue #673) ## 3.1.1 diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java index 4cb8d63d..263fbdb5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java @@ -61,13 +61,20 @@ class FlatNativeLibrary classifier = SystemInfo.isX86_64 ? "windows-x86_64" : "windows-x86"; ext = "dll"; - // In Java 8, load jawt.dll (part of JRE) explicitly because it - // is not found when running application with /bin/java.exe. - // When using /jre/bin/java.exe, it is found. - // jawt.dll is located in /jre/bin/. - // Java 9 and later do not have this problem, - // but load jawt.dll anyway to be on the safe side. - loadJAWT(); + // Do not load jawt.dll (part of JRE) here explicitly because + // the FlatLaf native library flatlaf.dll may be loaded very early on Windows + // (e.g. from class com.formdev.flatlaf.util.SystemInfo) and before AWT is + // initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll. + // In Java 8, loading jawt.dll before AWT is initialized may load + // a wrong version of awt.dll if a newer Java version (e.g. 19) + // is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll + // are loaded at same time and calling JAWT_GetAWT() crashes the application. + // + // To avoid this, flatlaf.dll is not linked to jawt.dll, + // which avoids loading jawt.dll when flatlaf.dll is loaded. + // Instead flatlaf.dll dynamically loads jawt.dll when first used, + // which is guaranteed after AWT initialization. + } else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) { // Linux: requires x86_64 diff --git a/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts index 69490664..41942b9d 100644 --- a/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts +++ b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts @@ -71,12 +71,11 @@ tasks { val nativesDir = project( ":flatlaf-core" ).projectDir.resolve( "src/main/resources/com/formdev/flatlaf/natives" ) val is64Bit = name.contains( "64" ) val libraryName = if( is64Bit ) "flatlaf-windows-x86_64.dll" else "flatlaf-windows-x86.dll" - val jawt = if( is64Bit ) "lib/jawt-x86_64" else "lib/jawt-x86" linkerArgs.addAll( toolChain.map { when( it ) { - is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lGdi32", "-lshell32", "-lAdvAPI32", "-lKernel32", "-lDwmapi" ) - is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "Dwmapi.lib", "/NODEFAULTLIB" ) + is Gcc, is Clang -> listOf( "-lUser32", "-lGdi32", "-lshell32", "-lAdvAPI32", "-lKernel32", "-lDwmapi" ) + is VisualCpp -> listOf( "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "Dwmapi.lib", "/NODEFAULTLIB" ) else -> emptyList() } } ) diff --git a/flatlaf-natives/flatlaf-natives-windows/lib/README.md b/flatlaf-natives/flatlaf-natives-windows/lib/README.md deleted file mode 100644 index c1e55b11..00000000 --- a/flatlaf-natives/flatlaf-natives-windows/lib/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Contains libraries used to compile FlatLaf Windows 10 native libraries (DLLs). - -- `jawt-x86.lib` is `/lib/jawt.lib` from AdoptOpenJDK jdk8u282-b08 32-bit, - which is required to build 32-bit DLL -- `jawt-x86_64.lib` is `/lib/jawt.lib` from AdoptOpenJDK jdk8u282-b08 - 64-bit, which is required to build 64-bit DLL diff --git a/flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86.lib b/flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86.lib deleted file mode 100644 index a16a97918c3753301423d80c70c62d7ec8fa85df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1688 zcmb_dy-piJ5dQAY7{`i)MN*(hk&J`{X^hW4eu@a46HqW52AM;YYh#mO1TrFyQsxCx z@c{l6^{`GX4X5{*dT1lEA8ygH*>r5yEp#6d|cUEh)*XU4tLgdT`%ip zG7s^zVco0HfZtZ>Fe6zk?H{Zw%=>nA0Y=$Ea1!mZiq=owL^%& zf=$%vwxD0f9<}*#Z#e|1>cN3{^js#q`IaYv<>3EVtP~}0V&oMKD^=ay`SxXEv&jMr zwD~EFS$!lvq-xc2qdbuj(QDdV(Y!VQ1qVLTmqu^az~J=%qbYif`$%5ew2t5fvxtdP zlX#UFCV&s)@sj5G>F8Rma$Nsf|J+DU72hR;d|_2fI|wWDhkO9lncgLcQt=TcSJ3U1 z`r#3od4BpAx3_W`>+@i{Y>2l$pe@}KMEk|JID&YrLjdk|W?Bg2Z<(0ErvvxFp}mcO z=%9K*yzI3)GTxl&*it;OY+W4}+A1PXR7X{dTBNNyvVwAT{1V2jTpfeYdN0}J3_SU$ z#QTofTz5zPp&OdH?YpC9Burd^+)?eLt)o1_S?{+nW*uB?dx_^8x^+OWlPCWV{!ef> R1{S|iW7@As=gTUo!W}+jEr|dC diff --git a/flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86_64.lib b/flatlaf-natives/flatlaf-natives-windows/lib/jawt-x86_64.lib deleted file mode 100644 index d9a9c8f3bbd2e03048230145dfd8b3434d0728e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1682 zcmb_dPfHt75dUS9#&jt(wDwRzSV6D_N!?_VdJx%IOB;<*;!2M%Y1Y;hC88-kddsPw z!Grh}?Afy?=}Cn4lN9XC?wh!&X`4VEyf<%tGn@JUGPO`YXzVPSFRb2W7i`;ha!x+i zi_dA+&KC5oW&*ee#5zFy6i8eF$ywe?mDV=atDahZz4@wI+VIw^>Qnto8%kBS%H_LK z9I-eQdT+NXt7^IE6|qu$=czZ_Z6fq(b8lZok)b`$-FluN*E=!bG!9%5Gn))TK>{%t zbjJU%J#0L8z3m?%hn?|vcO{npW#5_4cC+ty;+TAzswF9o6ve|U<8QzJ{Be3SlHK%F8MFoqQ|%k>)asv5cioyN`jif zjHtNJAwppg;unV!#ea4x;pt=gz-r}^x|I!y2+utfij(= z>VJuH(C)}OFC*Py1l{p1@QMyv{kp>sMcp&f9XSEHJ5Gerl)Gc}RUf39T!FtnB<9dL zo0pfZX39ielZCLh{d4w@iONfwcp`bCqRWLV6Xgibdf$c7l=ViN+d=X=LDom~O4+&% YE&h+-sq{PiQauUcOZ2?0QZJal0ilX2qW}N^ diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp index 45d6b850..1c3d4cf0 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp @@ -542,10 +542,82 @@ void FlatWndProc::setMenuItemState( HMENU systemMenu, int item, bool enabled ) { ::SetMenuItemInfo( systemMenu, item, FALSE, &mii ); } +//---- window handle ---------------------------------------------------------- + +#ifdef _WIN64 +#define GETAWT_METHOD_NAME "JAWT_GetAWT" +#else +#define GETAWT_METHOD_NAME "_JAWT_GetAWT@8" +#endif + +typedef jboolean (JNICALL *JAWT_GetAWT_Type)( JNIEnv*, JAWT* ); + +static HMODULE jawtModule = NULL; +static JAWT_GetAWT_Type pJAWT_GetAWT = NULL; + + HWND getWindowHandle( JNIEnv* env, jobject window ) { + + // flatlaf.dll is not linked to jawt.dll because flatlaf.dll may be loaded + // very early on Windows (e.g. from class com.formdev.flatlaf.util.SystemInfo) and + // before AWT is initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll. + // In Java 8, loading jawt.dll before AWT is initialized may load + // a wrong version of awt.dll if a newer Java version (e.g. 19) + // is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll + // are loaded at same time and calling JAWT_GetAWT() crashes the application. + // + // To avoid this, flatlaf.dll is not linked to jawt.dll, + // which avoids loading jawt.dll when flatlaf.dll is loaded. + // Instead flatlaf.dll dynamically loads jawt.dll when first used, + // which is guaranteed after AWT initialization. + // + // Load JAWT library from ${java.home}\bin\jawt.dll and use wide chars for path + // for the case that Java path uses special characters. (this is similar to JNA) + + // load JAWT library jawt.dll + if( jawtModule == NULL ) { + // invoke: javaHome = System.getProperty( "java.home" ) + jclass cls = env->FindClass( "java/lang/System" ); + jmethodID mid = (cls != NULL) ? env->GetStaticMethodID( cls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;" ) : NULL; + jstring javaHome = (mid != NULL) ? (jstring) env->CallStaticObjectMethod( cls, mid, env->NewStringUTF( "java.home" ) ) : NULL; + if( javaHome != NULL ) { + // invoke: jawtPath = javaHome.concat( "\\bin\\jawt.dll" ) + jmethodID mid2 = env->GetMethodID( env->GetObjectClass( javaHome ), "concat", "(Ljava/lang/String;)Ljava/lang/String;" ); + jstring jawtPath = (mid2 != NULL) ? (jstring) env->CallObjectMethod( javaHome, mid2, env->NewStringUTF( "\\bin\\jawt.dll" ) ) : NULL; + if( jawtPath != NULL ) { + // convert Java UTF-8 string to Windows wide chars + const char* sjawtPath = env->GetStringUTFChars( jawtPath, NULL ); + int wstr_len = MultiByteToWideChar( CP_UTF8, 0, sjawtPath, -1, NULL, 0 ); + if( wstr_len > 0 ) { + wchar_t* wstr = new wchar_t[wstr_len]; + if( MultiByteToWideChar( CP_UTF8, 0, sjawtPath, -1, wstr, wstr_len ) == wstr_len ) { + // load jawt.dll from Java home + jawtModule = LoadLibraryExW( wstr, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); + } + delete[] wstr; + } + env->ReleaseStringUTFChars( jawtPath, sjawtPath ); + } + } + + // fallback + if( jawtModule == NULL ) + jawtModule = LoadLibraryA( "jawt.dll" ); + + if( jawtModule == NULL ) + return 0; + } + + // get address of method JAWT_GetAWT() + if( pJAWT_GetAWT == NULL ) { + pJAWT_GetAWT = (JAWT_GetAWT_Type) GetProcAddress( jawtModule, GETAWT_METHOD_NAME ); + if( pJAWT_GetAWT == NULL ) + return 0; + } + JAWT awt; awt.version = JAWT_VERSION_1_4; - if( !JAWT_GetAWT( env, &awt ) ) + if( !pJAWT_GetAWT( env, &awt ) ) return 0; jawt_DrawingSurface* ds = awt.GetDrawingSurface( env, window ); @@ -558,12 +630,15 @@ HWND getWindowHandle( JNIEnv* env, jobject window ) { return 0; } + HWND hwnd = 0; + JAWT_DrawingSurfaceInfo* dsi = ds->GetDrawingSurfaceInfo( ds ); - JAWT_Win32DrawingSurfaceInfo* wdsi = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo; + if( dsi != NULL ) { + JAWT_Win32DrawingSurfaceInfo* wdsi = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo; + hwnd = wdsi->hwnd; + ds->FreeDrawingSurfaceInfo( dsi ); + } - HWND hwnd = wdsi->hwnd; - - ds->FreeDrawingSurfaceInfo( dsi ); ds->Unlock( ds ); awt.FreeDrawingSurface( ds );