2020-09-26 18:51:42 +02:00
/************************************************************/
/* Launch5j, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* Java JAR wrapper for creating Windows native executables */
/* https://github.com/lordmulder/ */
/* */
/* This work has been released under the MIT license. */
/* Please see LICENSE.TXT for details! */
/* */
/* ACKNOWLEDGEMENT */
/* This project is partly inspired by the Launch4j project: */
/* https://sourceforge.net/p/launch4j/ */
/************************************************************/
# define WIN32_LEAN_AND_MEAN 1
2020-09-27 18:42:22 +02:00
// CRT
2020-09-26 16:38:28 +02:00
# include <stdlib.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <errno.h>
# include <direct.h>
2020-09-27 18:42:22 +02:00
// Win32 API
# include <Windows.h>
# include <shellapi.h>
2020-09-30 21:23:06 +02:00
# include <commctrl.h>
2020-09-27 18:42:22 +02:00
// Resources
2020-09-26 18:51:42 +02:00
# include "resource.h"
2020-09-26 16:38:28 +02:00
2020-09-26 18:51:42 +02:00
// Options
2020-09-29 21:21:59 +02:00
# ifndef L5J_JAR_FILE_WRAPPED
# error L5J_JAR_FILE_WRAPPED flag is *not* defined!
2020-09-26 22:17:23 +02:00
# endif
2020-09-29 21:21:59 +02:00
# ifndef L5J_DETECT_REGISTRY
# error L5J_DETECT_REGISTRY flag is *not* defined!
2020-09-28 13:30:57 +02:00
# endif
2020-09-29 21:21:59 +02:00
# ifndef L5J_ENABLE_SPLASH
# error L5J_ENABLE_SPLASH flag is *not* defined!
2020-09-26 22:17:23 +02:00
# endif
2020-09-29 21:21:59 +02:00
# ifndef L5J_STAY_ALIVE
# error L5J_STAY_ALIVE flag is *not* defined!
2020-09-26 22:17:23 +02:00
# endif
2020-10-02 16:00:22 +02:00
# ifndef L5J_ENCODE_ARGS
# error L5J_ENCODE_ARGS flag is *not* defined!
# endif
2020-09-29 21:21:59 +02:00
# ifndef L5J_WAIT_FOR_WINDOW
# define L5J_WAIT_FOR_WINDOW 1
2020-09-27 18:42:22 +02:00
# endif
2020-09-28 13:30:57 +02:00
2020-09-27 20:33:49 +02:00
// Const
2020-09-29 21:21:59 +02:00
static const wchar_t * const JRE_DOWNLOAD_LINK_DEFAULT = L " https://adoptopenjdk.net/ " ;
static const wchar_t * const JRE_RELATIVE_PATH_DEFAULT = L " runtime \\ bin \\ javaw.exe " ;
2020-09-29 19:58:33 +02:00
static const size_t MIN_MUTEXID_LENGTH = 5U ;
2020-09-27 20:33:49 +02:00
static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U ;
2020-09-27 22:34:42 +02:00
2020-09-26 16:38:28 +02:00
/* ======================================================================== */
/* String routines */
/* ======================================================================== */
2020-09-28 13:30:57 +02:00
# define XSTR(S) STR(S)
# define STR(S) #S
2020-09-26 16:38:28 +02:00
# define NOT_EMPTY(STR) ((STR) && ((STR)[0U]))
2020-09-28 18:27:01 +02:00
# define AVAILABLE(OPT) (NOT_EMPTY(OPT) && (wcscmp((OPT), L"?") != 0))
2020-09-28 13:30:57 +02:00
2020-09-27 18:42:22 +02:00
# define SET_STRING(DST,SRC) do \
{ \
if ( ( DST ) ) { free ( ( void * ) ( DST ) ) ; } \
( DST ) = ( SRC ) ; \
} \
while ( 0 )
2020-09-26 16:38:28 +02:00
2020-10-01 22:30:24 +02:00
static wchar_t * vaswprintf ( const wchar_t * const fmt , va_list ap )
2020-09-26 16:38:28 +02:00
{
const int str_len = _vscwprintf ( fmt , ap ) ;
if ( str_len < 1 )
{
return NULL ;
}
wchar_t * buffer = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * ( ( ( size_t ) str_len ) + 1U ) ) ;
if ( ! buffer )
{
return NULL ;
}
2020-10-01 15:45:56 +02:00
const int result = _vsnwprintf ( buffer , ( ( size_t ) str_len ) + 1U , fmt , ap ) ;
2020-09-26 16:38:28 +02:00
if ( result < 1 )
{
free ( buffer ) ;
buffer = NULL ;
}
2020-09-27 18:42:22 +02:00
return buffer ;
}
2020-10-01 22:30:24 +02:00
static wchar_t * aswprintf ( const wchar_t * const fmt , . . . )
2020-09-27 18:42:22 +02:00
{
va_list ap ;
va_start ( ap , fmt ) ;
2020-10-01 22:30:24 +02:00
wchar_t * const buffer = vaswprintf ( fmt , ap ) ;
2020-09-27 18:42:22 +02:00
2020-09-26 16:38:28 +02:00
va_end ( ap ) ;
return buffer ;
}
static wchar_t * wcsndup ( const wchar_t * const str , const size_t n )
{
size_t str_len = wcslen ( str ) ;
if ( n < str_len )
{
str_len = n ;
}
wchar_t * const result = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * ( str_len + 1U ) ) ;
if ( ! result )
{
return NULL ;
}
wcsncpy ( result , str , str_len ) ;
result [ str_len ] = ' \0 ' ;
return result ;
}
2020-09-28 17:09:31 +02:00
static wchar_t * wcstrim ( wchar_t * const str )
{
if ( NOT_EMPTY ( str ) )
{
size_t pos = 0U , out = 0U ;
while ( str [ pos ] & & iswspace ( str [ pos ] ) )
{
+ + pos ;
}
if ( pos > 0U )
{
while ( str [ pos ] )
{
str [ out + + ] = str [ pos + + ] ;
}
str [ out ] = L ' \0 ' ;
}
else
{
for ( ; str [ out ] ; + + out ) ;
}
while ( ( out > 0U ) & & ( iswspace ( str [ out - 1U ] ) ) )
{
str [ - - out ] = L ' \0 ' ;
}
}
return str ;
}
2020-10-01 15:45:56 +02:00
/* ======================================================================== */
/* Character encoding */
/* ======================================================================== */
static const char * const HEX_CHARS = " 0123456789ABCDEF " ;
static CHAR * utf16_to_bytes ( const wchar_t * const input , const UINT code_page )
{
CHAR * buffer ;
DWORD buffer_size = 0U , result = 0U ;
buffer_size = WideCharToMultiByte ( code_page , 0 , input , - 1 , NULL , 0 , NULL , NULL ) ;
if ( buffer_size < 1U )
{
return NULL ;
}
buffer = ( CHAR * ) malloc ( sizeof ( CHAR ) * buffer_size ) ;
if ( ! buffer )
{
return NULL ;
}
result = WideCharToMultiByte ( code_page , 0 , input , - 1 , ( LPSTR ) buffer , buffer_size , NULL , NULL ) ;
if ( ( result > 0U ) & & ( result < = buffer_size ) )
{
return buffer ;
}
free ( buffer ) ;
return NULL ;
}
static BOOL char_needs_encoding ( const CHAR c )
{
if ( ( ( c > = ' 0 ' ) & & ( c < = ' 9 ' ) ) | | ( ( c > = ' A ' ) & & ( c < = ' Z ' ) ) | | ( ( c > = ' a ' ) & & ( c < = ' z ' ) ) )
{
return FALSE ;
}
if ( ( c = = ' - ' ) | | ( c = = ' _ ' ) | | ( c = = ' . ' ) | | ( c = = ' * ' ) | | ( c = = ' ' ) )
{
return FALSE ;
}
return TRUE ;
}
static size_t url_encoded_length ( const CHAR * const input )
{
if ( ( input ) & & input [ 0U ] )
{
size_t length = strlen ( input ) ;
for ( size_t i = 0U ; input [ i ] ; + + i )
{
if ( char_needs_encoding ( input [ i ] ) )
{
length + = 2U ;
}
}
return length + 1U ;
}
return 0U ;
}
static const wchar_t * url_encode_str ( const CHAR * const input )
{
const size_t buffer_size = url_encoded_length ( input ) ;
if ( buffer_size < 1U )
{
return NULL ;
}
wchar_t * buffer = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * buffer_size ) ;
if ( ! buffer )
{
return NULL ;
}
size_t j = 0U ;
for ( size_t i = 0U ; input [ i ] ; + + i )
{
if ( char_needs_encoding ( input [ i ] ) )
{
buffer [ j + + ] = L ' % ' ;
buffer [ j + + ] = ( wchar_t ) HEX_CHARS [ ( ( ( BYTE ) input [ i ] ) > > 4 ) & 0xF ] ;
buffer [ j + + ] = ( wchar_t ) HEX_CHARS [ ( ( BYTE ) input [ i ] ) & 0xF ] ;
}
else
{
buffer [ j + + ] = ( wchar_t ) ( ( input [ i ] ! = ' ' ) ? input [ i ] : ' + ' ) ;
}
}
buffer [ j ] = ' \0 ' ;
return buffer ;
}
static const wchar_t * url_encode_wcs ( const wchar_t * const input , const UINT code_page )
{
const CHAR * byte_string = utf16_to_bytes ( input , code_page ) ;
if ( ! byte_string )
{
return NULL ;
}
const wchar_t * encoded = url_encode_str ( byte_string ) ;
free ( ( void * ) byte_string ) ;
return encoded ;
}
2020-09-27 18:42:22 +02:00
/* ======================================================================== */
/* System information */
/* ======================================================================== */
typedef BOOL ( WINAPI * LPFN_ISWOW64PROCESS ) ( HANDLE , PBOOL ) ;
static BOOL running_on_64bit ( void )
{
# ifndef _M_X64
BOOL is_wow64_flag = FALSE ;
const LPFN_ISWOW64PROCESS is_wow64_process = ( LPFN_ISWOW64PROCESS ) GetProcAddress ( GetModuleHandleW ( L " kernel32 " ) , " IsWow64Process " ) ;
if ( is_wow64_process )
{
if ( ! is_wow64_process ( GetCurrentProcess ( ) , & is_wow64_flag ) )
{
is_wow64_flag = FALSE ;
}
}
return is_wow64_flag ;
# else
return TRUE ;
# endif
}
2020-09-26 16:38:28 +02:00
/* ======================================================================== */
/* File name routines */
/* ======================================================================== */
static wchar_t * get_directory_part ( const wchar_t * const path )
{
size_t lastsep = SIZE_MAX ;
if ( NOT_EMPTY ( path ) )
{
for ( size_t i = 0 ; path [ i ] ; + + i )
{
if ( ( path [ i ] = = L ' \\ ' ) | | ( path [ i ] = = L ' / ' ) )
{
lastsep = i ;
}
}
}
if ( lastsep ! = SIZE_MAX )
{
return wcsndup ( path , lastsep ) ;
}
return wcsdup ( L " . " ) ;
}
2020-09-28 17:09:31 +02:00
static wchar_t * get_path_without_suffix ( const wchar_t * const path )
2020-09-26 16:38:28 +02:00
{
size_t lastsep = SIZE_MAX ;
size_t lastdot = SIZE_MAX ;
for ( size_t i = 0 ; path [ i ] ; + + i )
{
if ( ( path [ i ] = = L ' \\ ' ) | | ( path [ i ] = = L ' / ' ) )
{
lastsep = i ;
}
else if ( path [ i ] = = L ' . ' )
{
lastdot = i ;
}
}
if ( lastdot ! = SIZE_MAX )
{
if ( ( lastsep = = SIZE_MAX ) | | ( lastdot > lastsep ) )
{
return wcsndup ( path , lastdot ) ;
}
}
return wcsdup ( path ) ;
}
2020-09-28 17:09:31 +02:00
static wchar_t * trim_trailing_separator ( wchar_t * const path )
2020-09-27 18:42:22 +02:00
{
2020-09-29 22:45:41 +02:00
if ( NOT_EMPTY ( path ) )
2020-09-27 18:42:22 +02:00
{
size_t len = wcslen ( path ) ;
while ( ( len > 0U ) & & ( ( path [ len - 1U ] = = L ' \\ ' ) | | ( path [ len - 1U ] = = L ' / ' ) ) )
{
path [ - - len ] = L ' \0 ' ;
}
}
2020-09-28 17:09:31 +02:00
return path ;
2020-09-27 18:42:22 +02:00
}
2020-09-29 22:45:41 +02:00
static const wchar_t * skip_leading_separator ( const wchar_t * path )
{
if ( NOT_EMPTY ( path ) )
{
for ( ; ( * path ) & & ( ( * path = = L ' \\ ' ) | | ( * path = = L ' / ' ) ) ; + + path ) ;
}
return path ;
}
2020-09-30 14:15:06 +02:00
static const wchar_t * get_absolute_path ( const wchar_t * const path )
{
DWORD buff_len = 0U ;
wchar_t * buffer = NULL ;
if ( NOT_EMPTY ( path ) )
{
for ( ; ; )
{
const DWORD result = GetFullPathNameW ( path , buff_len , buffer , NULL ) ;
if ( result > 0U )
{
if ( result < buff_len )
{
return buffer ;
}
else
{
if ( ! ( buffer = ( wchar_t * ) realloc ( buffer , sizeof ( wchar_t ) * ( buff_len = result ) ) ) )
{
break ;
}
}
}
else
{
break ; /*error*/
}
}
}
free ( buffer ) ;
return NULL ;
}
2020-10-01 18:30:11 +02:00
static const wchar_t * get_short_path ( const wchar_t * const path )
{
DWORD buff_len = 0U ;
wchar_t * buffer = NULL ;
if ( NOT_EMPTY ( path ) )
{
for ( ; ; )
{
const DWORD result = GetShortPathNameW ( path , buffer , buff_len ) ;
if ( result > 0U )
{
if ( result < buff_len )
{
return buffer ;
}
else
{
if ( ! ( buffer = ( wchar_t * ) realloc ( buffer , sizeof ( wchar_t ) * ( buff_len = result ) ) ) )
{
break ;
}
}
}
else
{
break ; /*error*/
}
}
}
free ( buffer ) ;
return NULL ;
}
2020-09-26 16:38:28 +02:00
static BOOL file_exists ( const wchar_t * const filename ) {
struct _stat buffer ;
if ( _wstat ( filename , & buffer ) = = 0 )
{
return S_ISDIR ( buffer . st_mode ) ? FALSE : TRUE ;
}
return FALSE ;
}
2020-09-30 18:15:45 +02:00
static DWORD file_is_executable ( const wchar_t * const file_path )
2020-09-30 14:15:06 +02:00
{
DWORD binary_type = 0U ;
if ( GetBinaryTypeW ( file_path , & binary_type ) )
{
switch ( binary_type )
{
case SCS_32BIT_BINARY :
return 32U ;
case SCS_64BIT_BINARY :
return 64U ;
}
}
return 0U ;
}
2020-09-28 17:09:31 +02:00
/* ======================================================================== */
/* Resource routines */
/* ======================================================================== */
static wchar_t * load_string ( const HINSTANCE hinstance , const UINT id )
{
wchar_t * buffer ;
const int str_len = LoadStringW ( hinstance , id , ( PWCHAR ) & buffer , 0 ) ;
if ( str_len > 0 )
{
2020-09-30 21:23:06 +02:00
if ( ( buffer = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * ( str_len + 1U ) ) ) )
2020-09-28 17:09:31 +02:00
{
2020-09-28 21:21:57 +02:00
if ( LoadStringW ( hinstance , id , buffer , str_len + 1U ) > 0 )
2020-09-28 17:09:31 +02:00
{
return wcstrim ( buffer ) ;
}
free ( buffer ) ;
}
}
return NULL ;
}
2020-09-29 12:01:43 +02:00
static DWORD load_uint32 ( const HINSTANCE hinstance , const UINT id , const DWORD fallback )
{
DWORD value = fallback ;
const wchar_t * const str = load_string ( hinstance , id ) ;
if ( NOT_EMPTY ( str ) )
{
value = wcstoul ( str , NULL , 10 ) ;
}
free ( ( void * ) str ) ;
return value ;
}
2020-09-27 18:42:22 +02:00
/* ======================================================================== */
/* Registry routines */
/* ======================================================================== */
typedef BOOL ( * reg_enum_callback_t ) ( const wchar_t * const key_name , const ULONG_PTR user_data ) ;
static DWORD get_registry_view ( const BOOL view_64bit )
{
# ifndef _M_X64
return view_64bit ? KEY_WOW64_64KEY : 0U ;
# else
return view_64bit ? 0U : KEY_WOW64_32KEY ;
# endif
}
static wchar_t * reg_read_string ( const HKEY root_key , const wchar_t * const path , const wchar_t * const name , const BOOL view_64bit )
{
HKEY key = NULL ;
if ( RegOpenKeyEx ( root_key , path , 0U , KEY_QUERY_VALUE | get_registry_view ( view_64bit ) , & key ) ! = ERROR_SUCCESS )
{
return FALSE ;
}
DWORD buffer_len = MAX_PATH ;
BYTE * buffer = ( BYTE * ) malloc ( sizeof ( wchar_t ) * buffer_len ) ;
if ( ! buffer )
{
RegCloseKey ( key ) ;
return NULL ;
}
DWORD type = 0U , size = sizeof ( wchar_t ) * buffer_len ;
LSTATUS status = RegQueryValueExW ( key , name , NULL , & type , buffer , & size ) ;
while ( status = = ERROR_MORE_DATA )
{
buffer_len = ( buffer_len < 512U ) ? 512U : ( 2U * buffer_len ) ;
BYTE * buffer = ( BYTE * ) realloc ( buffer , size = ( sizeof ( wchar_t ) * buffer_len ) ) ;
if ( ! buffer )
{
RegCloseKey ( key ) ;
return NULL ;
}
status = RegQueryValueExW ( key , name , NULL , & type , buffer , & size ) ;
}
if ( ( status ! = ERROR_SUCCESS ) | | ( size < sizeof ( wchar_t ) ) | | ( ( type ! = REG_SZ ) & & ( type ! = REG_EXPAND_SZ ) ) )
{
free ( buffer ) ;
RegCloseKey ( key ) ;
return NULL ;
}
const size_t char_count = size / sizeof ( wchar_t ) ;
if ( ( ( wchar_t * ) buffer ) [ char_count - 1U ] )
{
while ( char_count > = buffer_len )
{
BYTE * buffer = ( BYTE * ) realloc ( buffer , sizeof ( wchar_t ) * ( buffer_len = char_count + 1U ) ) ;
if ( ! buffer )
{
RegCloseKey ( key ) ;
return NULL ;
}
}
( ( wchar_t * ) buffer ) [ char_count ] = L ' \0 ' ;
}
RegCloseKey ( key ) ;
return ( wchar_t * ) buffer ;
}
2020-09-29 16:43:26 +02:00
static DWORD reg_read_string_uint32 ( const HKEY root_key , const wchar_t * const path , const wchar_t * const name , const BOOL view_64bit )
{
DWORD value = 0 ;
const wchar_t * const string = reg_read_string ( root_key , path , name , view_64bit ) ;
if ( NOT_EMPTY ( string ) )
{
value = wcstoul ( string , NULL , 10 ) ;
}
free ( ( void * ) string ) ;
return value ;
}
2020-09-27 18:42:22 +02:00
static BOOL reg_enum_subkeys ( const HKEY root_key , const wchar_t * const path , const BOOL view_64bit , const reg_enum_callback_t callback , const ULONG_PTR user_data )
{
HKEY key = NULL ;
if ( RegOpenKeyEx ( root_key , path , 0U , KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | get_registry_view ( view_64bit ) , & key ) ! = ERROR_SUCCESS )
{
return FALSE ;
}
DWORD max_len = 0U ;
if ( RegQueryInfoKeyW ( key , NULL , NULL , NULL , NULL , & max_len , NULL , NULL , NULL , NULL , NULL , NULL ) ! = ERROR_SUCCESS )
{
RegCloseKey ( key ) ;
return FALSE ;
}
wchar_t * buffer = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * ( max_len + 1U ) ) ;
if ( ! buffer )
{
RegCloseKey ( key ) ;
return FALSE ;
}
BOOL result = TRUE ;
for ( DWORD index = 0U ; index < MAXDWORD ; + + index )
{
DWORD len = max_len + 1U ;
switch ( RegEnumKeyExW ( key , index , buffer , & len , NULL , NULL , NULL , NULL ) )
{
case ERROR_SUCCESS :
if ( ! callback ( buffer , user_data ) )
{
goto exit_loop ;
}
break ;
case ERROR_MORE_DATA :
result = FALSE ;
break ;
case ERROR_NO_MORE_ITEMS :
goto exit_loop ;
default :
result = FALSE ;
goto exit_loop ;
}
}
exit_loop :
RegCloseKey ( key ) ;
free ( buffer ) ;
return result ;
}
2020-09-26 16:38:28 +02:00
/* ======================================================================== */
/* Path detection */
/* ======================================================================== */
static const wchar_t * const DEFAULT_JARFILE_NAME = L " application.jar " ;
static const wchar_t * get_executable_path ( void )
{
if ( _wpgmptr & & _wpgmptr [ 0U ] )
{
2020-09-28 17:09:31 +02:00
wchar_t * const executable_path = wcsdup ( _wpgmptr ) ;
return wcstrim ( executable_path ) ;
2020-09-26 16:38:28 +02:00
}
return NULL ;
}
static const wchar_t * get_executable_directory ( const wchar_t * const executable_path )
{
const wchar_t * const directory_part = get_directory_part ( executable_path ) ;
if ( directory_part )
{
return directory_part ;
}
free ( ( void * ) directory_part ) ;
return wcsdup ( L " . " ) ;
}
static const wchar_t * get_jarfile_path ( const wchar_t * const executable_path , const wchar_t * const executable_directory )
{
2020-09-29 21:21:59 +02:00
# if L5J_JAR_FILE_WRAPPED
2020-09-26 22:17:23 +02:00
return wcsdup ( executable_path ) ; /*JAR file is wrapped*/
# else
2020-09-26 16:38:28 +02:00
const wchar_t * jarfile_path = NULL ;
2020-09-28 17:09:31 +02:00
const wchar_t * const path_prefix = get_path_without_suffix ( executable_path ) ;
2020-09-26 16:38:28 +02:00
if ( NOT_EMPTY ( path_prefix ) )
{
const size_t len = wcslen ( path_prefix ) ;
if ( ! ( ( len > 0U ) & & ( ( path_prefix [ len - 1U ] = = L ' \\ ' ) | | ( path_prefix [ len - 1U ] = = L ' / ' ) ) ) )
{
2020-10-01 22:30:24 +02:00
jarfile_path = aswprintf ( L " %s.jar " , path_prefix ) ;
2020-09-26 16:38:28 +02:00
}
}
if ( ! jarfile_path )
{
2020-10-01 22:30:24 +02:00
jarfile_path = NOT_EMPTY ( executable_directory ) ? aswprintf ( L " %s \\ %s " , executable_directory , DEFAULT_JARFILE_NAME ) : wcsdup ( DEFAULT_JARFILE_NAME ) ;
2020-09-26 16:38:28 +02:00
}
free ( ( void * ) path_prefix ) ;
return jarfile_path ;
2020-09-26 18:51:42 +02:00
# endif
2020-09-26 16:38:28 +02:00
}
/* ======================================================================== */
/* Path manipulation */
/* ======================================================================== */
static const BOOL set_current_directory ( const wchar_t * const path )
{
if ( NOT_EMPTY ( path ) )
{
if ( iswalpha ( path [ 0U ] ) & & ( path [ 1U ] = = L ' : ' ) & & ( path [ 2U ] = = L ' \0 ' ) )
{
const wchar_t root_path [ 4U ] = { path [ 0U ] , L ' : ' , L ' \\ ' , L ' \0 ' } ;
return SetCurrentDirectoryW ( root_path ) ;
}
else
{
return SetCurrentDirectoryW ( path ) ;
}
}
else
{
return SetCurrentDirectoryW ( L " \\ " ) ;
}
}
2020-09-27 18:42:22 +02:00
/* ======================================================================== */
/* Java detection (from registry) */
/* ======================================================================== */
typedef struct
{
2020-09-30 18:15:45 +02:00
const struct
{
DWORD bitness ;
ULONGLONG ver_min ;
ULONGLONG ver_max ;
}
required ;
const struct
{
HKEY root_key ;
const wchar_t * base_path ;
BOOL view_64bit ;
}
registry ;
struct
{
DWORD bitness ;
ULONGLONG version ;
const wchar_t * runtime_path ;
}
result ;
2020-09-27 18:42:22 +02:00
}
java_home_t ;
2020-10-01 01:45:04 +02:00
static BOOL detect_update_format ( const wchar_t * const version_str )
2020-09-28 23:06:31 +02:00
{
BOOL digit_flag = FALSE ;
size_t pos = 0U ;
while ( version_str [ pos ] & & iswspace ( version_str [ pos ] ) )
{
+ + pos ;
}
while ( version_str [ pos ] & & iswdigit ( version_str [ pos ] ) )
{
digit_flag = TRUE ;
+ + pos ;
}
while ( version_str [ pos ] & & iswspace ( version_str [ pos ] ) )
{
+ + pos ;
}
2020-10-01 01:45:04 +02:00
if ( digit_flag & & version_str [ pos ] )
2020-09-28 23:06:31 +02:00
{
return ( version_str [ pos ] = = L ' u ' ) | | ( version_str [ pos ] = = L ' U ' ) ;
}
return FALSE ;
}
2020-09-27 18:42:22 +02:00
static ULONGLONG parse_java_version ( const wchar_t * const version_str )
{
ULONGLONG version = 0ULL ;
UINT level = 0U ;
2020-10-01 01:45:04 +02:00
static const wchar_t * const delimiters = L " .,_+-uUbB " ;
2020-09-27 18:42:22 +02:00
if ( NOT_EMPTY ( version_str ) )
{
wchar_t * const temp = wcsdup ( version_str ) ;
if ( temp )
{
2020-10-01 01:45:04 +02:00
const BOOL is_update_forma = detect_update_format ( temp ) ;
2020-09-27 18:42:22 +02:00
BOOL first_token = TRUE ;
2020-10-01 01:45:04 +02:00
const wchar_t * token = wcstok ( temp , delimiters ) ;
2020-09-27 18:42:22 +02:00
while ( token )
{
const DWORD component = wcstoul ( token , NULL , 10 ) ;
if ( ! ( first_token & & ( component = = 1U ) ) )
{
version = ( version < < 16 ) | ( component & 0xFFFF ) ;
+ + level ;
2020-10-01 01:45:04 +02:00
if ( is_update_forma & & ( level = = 1U ) )
2020-09-28 23:06:31 +02:00
{
version < < = 16 ;
+ + level ;
}
}
if ( level > 3U )
{
break ;
2020-09-27 18:42:22 +02:00
}
2020-10-01 01:45:04 +02:00
token = wcstok ( NULL , delimiters ) ;
2020-09-27 18:42:22 +02:00
first_token = FALSE ;
}
}
free ( temp ) ;
}
while ( level < 4U )
{
version < < = 16 ;
+ + level ;
}
return version ;
}
2020-09-30 18:15:45 +02:00
static DWORD detect_java_runtime_verify ( const wchar_t * * const executable_path_out , const HKEY root_key , const wchar_t * const full_reg_path , const BOOL reg_view_64bit )
2020-09-27 18:42:22 +02:00
{
static const wchar_t * const REL_PATHS [ ] =
{
2020-10-01 22:30:24 +02:00
L " %s \\ jre \\ bin \\ javaw.exe " , L " %s \\ bin \\ javaw.exe " , NULL
2020-09-27 18:42:22 +02:00
} ;
2020-09-30 18:15:45 +02:00
* executable_path_out = NULL ;
DWORD result = 0U ;
wchar_t * const java_home_path = reg_read_string ( root_key , full_reg_path , L " JavaHome " , reg_view_64bit ) ;
2020-09-28 17:09:31 +02:00
trim_trailing_separator ( wcstrim ( java_home_path ) ) ;
2020-09-29 16:43:26 +02:00
2020-09-27 18:42:22 +02:00
if ( NOT_EMPTY ( java_home_path ) )
{
for ( size_t i = 0U ; REL_PATHS [ i ] ; + + i )
{
2020-10-01 22:30:24 +02:00
const wchar_t * const javaw_executable_path = aswprintf ( REL_PATHS [ i ] , java_home_path ) ;
2020-10-01 01:45:04 +02:00
if ( javaw_executable_path )
2020-09-27 18:42:22 +02:00
{
2020-10-01 01:45:04 +02:00
const wchar_t * const absolute_executable_path = get_absolute_path ( javaw_executable_path ) ;
2020-09-30 18:15:45 +02:00
if ( absolute_executable_path )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
const DWORD bitness = file_is_executable ( absolute_executable_path ) ;
if ( bitness > 0U )
2020-09-30 14:15:06 +02:00
{
2020-09-30 18:15:45 +02:00
* executable_path_out = absolute_executable_path ;
result = bitness ;
}
else
{
free ( ( void * ) absolute_executable_path ) ;
2020-09-30 14:15:06 +02:00
}
2020-09-27 18:42:22 +02:00
}
2020-10-01 01:45:04 +02:00
free ( ( void * ) javaw_executable_path ) ;
2020-09-27 18:42:22 +02:00
}
2020-09-30 18:15:45 +02:00
if ( result > 0U )
{
break ; /*found executable*/
}
2020-09-27 18:42:22 +02:00
}
}
free ( java_home_path ) ;
2020-09-29 16:43:26 +02:00
return result ;
}
2020-09-27 18:42:22 +02:00
static BOOL detect_java_runtime_callback ( const wchar_t * const key_name , const ULONG_PTR user_data )
{
2020-09-29 12:01:43 +02:00
java_home_t * const context_ptr = ( java_home_t * ) user_data ;
2020-09-30 18:15:45 +02:00
ULONGLONG version = parse_java_version ( key_name ) ;
2020-10-01 01:45:04 +02:00
if ( ( version > = context_ptr - > required . ver_min ) & & ( version < context_ptr - > required . ver_max ) & & ( version > context_ptr - > result . version ) )
2020-09-30 18:15:45 +02:00
{
2020-10-01 22:30:24 +02:00
const wchar_t * const full_reg_path = aswprintf ( L " %s \\ %s " , context_ptr - > registry . base_path , key_name ) ;
2020-10-01 01:45:04 +02:00
if ( full_reg_path )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
const wchar_t * java_runtime_path ;
const DWORD bitness = detect_java_runtime_verify ( & java_runtime_path , context_ptr - > registry . root_key , full_reg_path , context_ptr - > registry . view_64bit ) ;
if ( bitness > 0U )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
if ( ( ( context_ptr - > required . bitness = = 0U ) | | ( bitness = = context_ptr - > required . bitness ) ) & & ( bitness > = context_ptr - > result . bitness ) )
2020-09-29 16:43:26 +02:00
{
2020-09-30 18:15:45 +02:00
context_ptr - > result . bitness = bitness ;
context_ptr - > result . version = version ;
SET_STRING ( context_ptr - > result . runtime_path , java_runtime_path ) ;
}
else
{
free ( ( void * ) java_runtime_path ) ;
2020-09-29 16:43:26 +02:00
}
2020-09-27 18:42:22 +02:00
}
2020-10-01 01:45:04 +02:00
free ( ( void * ) full_reg_path ) ;
2020-09-27 18:42:22 +02:00
}
}
2020-09-29 16:43:26 +02:00
2020-09-27 18:42:22 +02:00
return TRUE ;
}
2020-09-30 18:15:45 +02:00
static const wchar_t * detect_java_runtime_loop ( const BOOL reg_view_64bit , const DWORD required_bitness , const ULONGLONG required_ver_min , const ULONGLONG required_ver_max )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
static const wchar_t * const REG_KEY_PATHS [ ] =
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
L " SOFTWARE \\ JavaSoft \\ Java Runtime Environment " , L " SOFTWARE \\ JavaSoft \\ JRE " ,
L " SOFTWARE \\ JavaSoft \\ Java Development Kit " , L " SOFTWARE \\ JavaSoft \\ JDK " ,
NULL /*EOL*/
2020-09-27 18:42:22 +02:00
} ;
const wchar_t * runtime_path = NULL ;
2020-09-30 18:15:45 +02:00
DWORD bitness = 0U ;
ULONGLONG version = 0U ;
2020-09-27 18:42:22 +02:00
2020-09-30 18:15:45 +02:00
for ( size_t i = 0 ; REG_KEY_PATHS [ i ] ; + + i )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
java_home_t search_state =
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
{ required_bitness , required_ver_min , required_ver_max } ,
{ HKEY_LOCAL_MACHINE , REG_KEY_PATHS [ i ] , reg_view_64bit } ,
{ bitness , version , NULL }
} ;
reg_enum_subkeys ( HKEY_LOCAL_MACHINE , REG_KEY_PATHS [ i ] , reg_view_64bit , detect_java_runtime_callback , ( ULONG_PTR ) & search_state ) ;
if ( search_state . result . runtime_path )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
bitness = search_state . result . bitness ;
version = search_state . result . version ;
SET_STRING ( runtime_path , search_state . result . runtime_path ) ;
2020-09-27 18:42:22 +02:00
}
}
2020-09-30 18:15:45 +02:00
if ( ( ( required_bitness = = 0U ) | | ( bitness = = required_bitness ) ) & & ( version > = required_ver_min ) & & ( version < required_ver_max ) & & runtime_path )
{
return runtime_path ;
}
2020-09-29 12:01:43 +02:00
free ( ( void * ) runtime_path ) ;
2020-09-27 18:42:22 +02:00
return NULL ;
}
2020-09-29 12:01:43 +02:00
static const wchar_t * detect_java_runtime ( const DWORD required_bitness , const ULONGLONG required_ver_min , const ULONGLONG required_ver_max )
2020-09-27 18:42:22 +02:00
{
2020-09-29 12:01:43 +02:00
const wchar_t * java_runtime_path ;
2020-09-30 18:15:45 +02:00
if ( running_on_64bit ( ) )
2020-09-27 18:42:22 +02:00
{
2020-09-30 21:23:06 +02:00
if ( ( java_runtime_path = detect_java_runtime_loop ( TRUE , required_bitness , required_ver_min , required_ver_max ) ) )
2020-09-29 12:01:43 +02:00
{
return java_runtime_path ;
}
2020-09-27 18:42:22 +02:00
}
2020-09-30 21:23:06 +02:00
if ( ( java_runtime_path = detect_java_runtime_loop ( FALSE , required_bitness , required_ver_min , required_ver_max ) ) )
2020-09-27 18:42:22 +02:00
{
2020-09-30 18:15:45 +02:00
return java_runtime_path ;
2020-09-27 18:42:22 +02:00
}
return NULL ;
}
2020-09-29 12:01:43 +02:00
static const ULONGLONG load_java_version ( const HINSTANCE hinstance , const UINT id , const ULONGLONG fallback )
{
ULONGLONG value = fallback ;
const wchar_t * const str = load_string ( hinstance , id ) ;
if ( NOT_EMPTY ( str ) )
{
const ULONGLONG temp = parse_java_version ( str ) ;
if ( temp > = ( 5ull < < 48 ) )
{
value = temp ;
}
}
free ( ( void * ) str ) ;
return value ;
}
static DWORD load_java_bitness ( const HINSTANCE hinstance , const UINT id )
{
const DWORD value = load_uint32 ( hinstance , id , 0U ) ;
return ( ( value = = 32U ) | | ( value = = 64U ) ) ? value : 0U ;
}
2020-10-01 15:45:56 +02:00
/* ======================================================================== */
/* Command-line */
/* ======================================================================== */
static wchar_t * encode_commandline_args ( const int argc , const LPWSTR * const argv )
{
wchar_t * result_buffer = NULL ;
if ( argv & & ( argc > 0 ) )
{
const wchar_t * * encoded_argv = ( const wchar_t * * ) malloc ( sizeof ( wchar_t * ) * argc ) ;
if ( encoded_argv )
{
size_t total_len = 0U ;
for ( int i = 0 ; i < argc ; + + i )
{
if ( NOT_EMPTY ( encoded_argv [ i ] = url_encode_wcs ( argv [ i ] , CP_UTF8 ) ) )
{
total_len + = ( wcslen ( encoded_argv [ i ] ) + 1U ) ;
}
}
if ( total_len > 0U )
{
if ( ( result_buffer = ( wchar_t * ) calloc ( total_len , sizeof ( wchar_t ) ) ) )
{
for ( int i = 0 ; i < argc ; + + i )
{
if ( NOT_EMPTY ( encoded_argv [ i ] ) )
{
if ( result_buffer [ 0U ] )
{
wcscat ( result_buffer , L " " ) ;
}
wcscat ( result_buffer , encoded_argv [ i ] ) ;
}
}
}
}
for ( int i = 0 ; i < argc ; + + i )
{
free ( ( void * ) encoded_argv [ i ] ) ;
}
free ( encoded_argv ) ;
}
}
return result_buffer ;
}
static const wchar_t * encode_commandline ( const wchar_t * const command_line )
{
2020-10-02 16:00:22 +02:00
# if L5J_ENCODE_ARGS
2020-10-01 15:45:56 +02:00
const wchar_t * encoded = NULL ;
if ( NOT_EMPTY ( command_line ) )
{
int argc = 0 ;
const LPWSTR * const argv = CommandLineToArgvW ( command_line , & argc ) ;
if ( argv )
{
encoded = encode_commandline_args ( argc , argv ) ;
LocalFree ( ( HLOCAL ) argv ) ;
}
}
return encoded ;
2020-10-02 16:00:22 +02:00
# else
return wcsdup ( command_line ) ;
# endif
2020-10-01 15:45:56 +02:00
}
2020-09-26 18:51:42 +02:00
/* ======================================================================== */
/* Splash screen */
/* ======================================================================== */
static BOOL create_splash_screen ( const HWND hwnd , const HANDLE splash_image )
{
if ( hwnd & & splash_image )
{
RECT rect ;
SendMessageW ( hwnd , STM_SETIMAGE , IMAGE_BITMAP , ( LPARAM ) splash_image ) ;
GetWindowRect ( hwnd , & rect ) ;
const int x = ( GetSystemMetrics ( SM_CXSCREEN ) - ( rect . right - rect . left ) ) / 2 ;
const int y = ( GetSystemMetrics ( SM_CYSCREEN ) - ( rect . bottom - rect . top ) ) / 2 ;
SetWindowPos ( hwnd , HWND_TOP , x , y , 0 , 0 , SWP_NOSIZE ) ;
ShowWindow ( hwnd , SW_SHOW ) ;
return UpdateWindow ( hwnd ) ;
}
return FALSE ;
}
static BOOL process_window_messages ( const HWND hwnd )
{
BOOL result = FALSE ;
if ( hwnd ! = NULL )
{
MSG msg = { } ;
for ( DWORD k = 0U ; k < MAXWORD ; + + k )
{
if ( PeekMessageW ( & msg , hwnd , 0U , 0U , PM_REMOVE ) )
{
result = TRUE ;
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
else
{
break ; /*no more messages!*/
}
}
}
return result ;
}
2020-09-26 22:17:23 +02:00
/* ======================================================================== */
/* Find window functions */
/* ======================================================================== */
typedef struct
{
2020-09-26 22:30:12 +02:00
const DWORD process_id ;
2020-09-26 22:17:23 +02:00
HWND hwnd ;
}
find_window_t ;
static BOOL CALLBACK enum_windows_callback ( const HWND hwnd , const LPARAM lparam )
{
2020-09-29 12:01:43 +02:00
find_window_t * const context_ptr = ( find_window_t * ) lparam ;
const DWORD required_process_id = context_ptr - > process_id ;
2020-09-26 22:17:23 +02:00
if ( IsWindowVisible ( hwnd ) )
{
2020-09-29 12:01:43 +02:00
DWORD process_id = MAXDWORD ;
2020-09-26 22:17:23 +02:00
GetWindowThreadProcessId ( hwnd , & process_id ) ;
2020-09-29 12:01:43 +02:00
if ( process_id = = required_process_id )
2020-09-26 22:17:23 +02:00
{
2020-09-29 12:01:43 +02:00
context_ptr - > hwnd = hwnd ;
2020-09-26 22:17:23 +02:00
return FALSE ;
}
}
return TRUE ;
}
static HWND find_window_by_process_id ( const DWORD process_id )
{
2020-09-26 22:30:12 +02:00
find_window_t find_window = { process_id , NULL } ;
EnumWindows ( enum_windows_callback , ( LONG_PTR ) & find_window ) ;
return find_window . hwnd ;
2020-09-26 22:17:23 +02:00
}
/* ======================================================================== */
/* Wait for process */
/* ======================================================================== */
static BOOL signaled_or_failed ( const DWORD wait_result )
{
return ( wait_result = = WAIT_OBJECT_0 ) | | ( wait_result = = WAIT_FAILED ) ;
}
static BOOL wait_for_process_ready ( const HWND hwnd , const HANDLE process_handle , const DWORD process_id )
{
2020-09-27 00:53:14 +02:00
BOOL input_idle = FALSE ;
2020-09-26 22:17:23 +02:00
const DWORD ticks_start = GetTickCount ( ) ;
for ( ; ; )
{
2020-09-29 21:21:59 +02:00
if ( input_idle | | signaled_or_failed ( WaitForInputIdle ( process_handle , 25U ) ) )
2020-09-26 22:17:23 +02:00
{
2020-09-27 00:53:14 +02:00
const HWND child_hwnd = find_window_by_process_id ( process_id ) ;
if ( child_hwnd )
{
SwitchToThisWindow ( child_hwnd , TRUE ) ;
return TRUE ;
}
input_idle = TRUE ;
2020-09-26 22:17:23 +02:00
}
2020-09-27 00:53:14 +02:00
if ( signaled_or_failed ( WaitForSingleObject ( process_handle , 1U ) ) )
2020-09-26 22:17:23 +02:00
{
break ;
}
const DWORD ticks_delta = GetTickCount ( ) - ticks_start ;
if ( ticks_delta > SPLASH_SCREEN_TIMEOUT )
{
break ;
}
process_window_messages ( hwnd ) ;
}
return FALSE ;
}
2020-09-26 16:38:28 +02:00
/* ======================================================================== */
/* Message box */
/* ======================================================================== */
2020-09-29 22:07:03 +02:00
# define IS_HTTP_URL(STR) (NOT_EMPTY(STR) && ((wcsnicmp((STR), L"http: //", 7U) == 0) || (wcsnicmp((STR), L"https://", 8U) == 0)))
2020-09-26 16:38:28 +02:00
static const wchar_t * describe_system_error ( const DWORD error_code )
{
const wchar_t * error_test = NULL , * buffer = NULL ;
const DWORD len = FormatMessageW ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS , NULL , error_code , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & buffer , 0 , NULL ) ;
if ( ( len > 0U ) & & NOT_EMPTY ( buffer ) )
{
error_test = wcsdup ( buffer ) ;
LocalFree ( ( HLOCAL ) buffer ) ;
}
return error_test ;
}
2020-09-27 18:42:22 +02:00
# define show_message(HWND, FLAGS, TITLE, TEXT) MessageBoxW((HWND), (TEXT), (TITLE), (FLAGS))
2020-09-26 16:38:28 +02:00
2020-09-27 18:42:22 +02:00
static int show_message_format ( HWND hwnd , const DWORD flags , const wchar_t * const title , const wchar_t * const format , . . . )
{
int result = - 1 ;
va_list ap ;
va_start ( ap , format ) ;
2020-10-01 22:30:24 +02:00
const wchar_t * const text = vaswprintf ( format , ap ) ;
2020-09-27 18:42:22 +02:00
if ( NOT_EMPTY ( text ) )
{
result = MessageBoxW ( hwnd , text , title , flags ) ;
}
free ( ( void * ) text ) ;
return result ;
}
2020-09-26 16:38:28 +02:00
2020-09-29 21:21:59 +02:00
static void show_jre_download_notice ( const HINSTANCE hinstance , const HWND hwnd , const wchar_t * const title , const DWORD required_bitness , const ULONGLONG required_ver )
2020-09-27 22:34:42 +02:00
{
2020-09-29 12:01:43 +02:00
const DWORD req_version_comp [ ] =
2020-09-28 13:30:57 +02:00
{
2020-09-29 12:01:43 +02:00
( required_ver > > 48 ) & 0xFFFF , ( required_ver > > 32 ) & 0xFFFF , ( required_ver > > 16 ) & 0xFFFF , required_ver & 0xFFFF
2020-09-28 13:30:57 +02:00
} ;
2020-09-29 21:21:59 +02:00
wchar_t * const jre_download_link = load_string ( hinstance , ID_STR_JAVAURL ) ;
2020-09-29 12:01:43 +02:00
wchar_t * const version_str = ( req_version_comp [ 3U ] ! = 0U )
2020-10-01 22:30:24 +02:00
? aswprintf ( L " %u.%u.%u_%u " , req_version_comp [ 0U ] , req_version_comp [ 1U ] , req_version_comp [ 2U ] , req_version_comp [ 3U ] )
2020-09-29 12:01:43 +02:00
: ( ( req_version_comp [ 2U ] ! = 0U )
2020-10-01 22:30:24 +02:00
? aswprintf ( L " %u.%u.%u " , req_version_comp [ 0U ] , req_version_comp [ 1U ] , req_version_comp [ 2U ] )
: aswprintf ( L " %u.%u " , req_version_comp [ 0U ] , req_version_comp [ 1U ] ) ) ;
2020-09-27 22:34:42 +02:00
if ( version_str )
{
2020-09-29 22:07:03 +02:00
const wchar_t * const jre_download_ptr = IS_HTTP_URL ( jre_download_link ) ? jre_download_link : JRE_DOWNLOAD_LINK_DEFAULT ;
2020-09-29 12:01:43 +02:00
const int result = ( required_bitness = = 0U )
2020-10-01 22:30:24 +02:00
? show_message_format ( hwnd , MB_ICONINFORMATION | MB_OKCANCEL | MB_TOPMOST , title ,
L " This application requires the Java Runtime Environment, version %s, or a compatible newer version. \n \n "
L " We recommend downloading the OpenJDK runtime here: \n %s " ,
2020-09-29 21:21:59 +02:00
version_str , jre_download_ptr )
2020-10-01 22:30:24 +02:00
: show_message_format ( hwnd , MB_ICONINFORMATION | MB_OKCANCEL | MB_TOPMOST , title ,
L " This application requires the Java Runtime Environment, version %s, or a compatible newer version. \n \n "
L " Only the %u-Bit (%s) version of the JRE is supported! \n \n "
L " We recommend downloading the OpenJDK runtime here: \n %s " ,
2020-09-29 21:21:59 +02:00
version_str , required_bitness , ( required_bitness = = 64 ) ? L " x64 " : L " x86 " , jre_download_ptr ) ;
2020-09-29 12:01:43 +02:00
if ( result = = IDOK )
2020-09-27 22:34:42 +02:00
{
2020-09-29 21:21:59 +02:00
ShellExecuteW ( hwnd , NULL , jre_download_ptr , NULL , NULL , SW_SHOW ) ;
2020-10-01 22:30:24 +02:00
show_message ( hwnd , MB_ICONWARNING | MB_OKCANCEL | MB_TOPMOST , title , L " When installing OpenJDK on your machine, please be sure to enable \x201c JavaSoft (Oracle) registry keys \x201d , otherwise we will not be able to find your Java Runtime Environment !!! " ) ;
2020-09-27 22:34:42 +02:00
}
}
2020-09-29 21:21:59 +02:00
free ( version_str ) ;
free ( jre_download_link ) ;
2020-09-27 22:34:42 +02:00
}
2020-09-29 19:58:33 +02:00
/* ======================================================================== */
/* Single instance */
/* ======================================================================== */
static ULONGLONG hash_code ( const BYTE * const message , const size_t message_len )
{
ULONGLONG hash = 0xCBF29CE484222325ull ;
for ( size_t iter = 0U ; iter < message_len ; + + iter )
{
hash ^ = message [ iter ] ;
hash * = 0x00000100000001B3ull ;
}
return hash ;
}
static BOOL initialize_mutex ( HANDLE * const handle , const wchar_t * const mutex_name )
{
static const char * const BUILD_TIME = __DATE__ " " __TIME__ ;
const ULONGLONG hashcode_0 = hash_code ( ( const BYTE * ) BUILD_TIME , sizeof ( wchar_t ) * strlen ( BUILD_TIME ) ) ;
const ULONGLONG hashcode_1 = hash_code ( ( const BYTE * ) mutex_name , sizeof ( wchar_t ) * wcslen ( mutex_name ) ) ;
2020-10-01 22:30:24 +02:00
const wchar_t * const mutex_uuid = aswprintf ( L " l5j.%016llX%016llX " , hashcode_0 , hashcode_1 ) ;
2020-09-29 19:58:33 +02:00
if ( ! mutex_uuid )
{
return TRUE ; /*better safe than sorry*/
}
BOOL result = TRUE ;
if ( ( * handle = CreateMutexW ( NULL , TRUE , mutex_uuid ) ) ! = NULL )
{
if ( GetLastError ( ) = = ERROR_ALREADY_EXISTS )
{
result = FALSE ;
}
}
free ( ( void * ) mutex_uuid ) ;
return result ;
}
2020-09-26 18:51:42 +02:00
/* ======================================================================== */
/* Utilities */
/* ======================================================================== */
static void close_handle ( HANDLE * const handle )
{
if ( * handle )
{
CloseHandle ( * handle ) ;
* handle = NULL ;
}
}
static void delete_object ( HGDIOBJ * const handle )
{
if ( * handle )
{
DeleteObject ( * handle ) ;
* handle = NULL ;
}
}
static void destroy_window ( HWND * const hwnd )
{
if ( * hwnd )
{
DestroyWindow ( * hwnd ) ;
* hwnd = NULL ;
}
}
2020-09-26 16:38:28 +02:00
/* ======================================================================== */
/* MAIN */
/* ======================================================================== */
2020-09-28 19:57:32 +02:00
static wchar_t * const DEFAULT_HEADING = L " Launch5j " ;
# define APP_HEADING (AVAILABLE(app_heading) ? app_heading : DEFAULT_HEADING)
2020-10-01 15:45:56 +02:00
int WINAPI wWinMain ( HINSTANCE hInstance , HINSTANCE _hPrevInstance , PWSTR pCmdLine , int _nCmdShow )
2020-09-26 16:38:28 +02:00
{
2020-09-26 18:51:42 +02:00
int result = - 1 ;
2020-10-01 18:30:11 +02:00
const wchar_t * app_heading = NULL , * mutex_name = NULL , * executable_path = NULL , * executable_directory = NULL , * jarfile_path = NULL , * jarfile_short_path = NULL ,
* java_runtime_path = NULL , * jre_relative_path = NULL , * jvm_extra_args = NULL , * cmd_extra_args = NULL , * cmd_args_encoded = NULL , * ext_args_encoded = NULL , * command_line = NULL ;
2020-09-29 19:58:33 +02:00
HANDLE mutex_handle = NULL ;
2020-09-29 12:01:43 +02:00
DWORD java_required_bitness = 0U ;
ULONGLONG java_required_ver_min = 0ULL , java_required_ver_max = 0ULL ;
2020-09-26 18:51:42 +02:00
HGDIOBJ splash_image = NULL ;
2020-09-29 21:21:59 +02:00
BOOL have_screen_created = FALSE ;
2020-09-26 16:38:28 +02:00
PROCESS_INFORMATION process_info ;
2020-09-26 18:51:42 +02:00
STARTUPINFOW startup_info ;
2020-09-26 16:38:28 +02:00
2020-09-30 21:23:06 +02:00
// Ensure that the ComCtl32 DLL is loaded
InitCommonControls ( ) ;
2020-09-26 16:38:28 +02:00
// Initialize
SecureZeroMemory ( & startup_info , sizeof ( STARTUPINFOW ) ) ;
SecureZeroMemory ( & process_info , sizeof ( PROCESS_INFORMATION ) ) ;
2020-10-01 15:45:56 +02:00
// Get current process ID
const DWORD pid = GetCurrentProcessId ( ) ;
2020-09-28 19:57:32 +02:00
// Load title
app_heading = load_string ( hInstance , ID_STR_HEADING ) ;
2020-09-26 18:51:42 +02:00
// Create the window
2020-09-28 19:57:32 +02:00
HWND hwnd = CreateWindowExW ( WS_EX_TOOLWINDOW | WS_EX_TOPMOST , L " STATIC " , APP_HEADING , WS_POPUP | SS_BITMAP , 0 , 0 , CW_USEDEFAULT , CW_USEDEFAULT , NULL , NULL , hInstance , NULL ) ;
2020-09-26 18:51:42 +02:00
2020-09-29 19:58:33 +02:00
// Single instance
2020-09-29 21:21:59 +02:00
# if L5J_STAY_ALIVE
2020-09-29 19:58:33 +02:00
mutex_name = load_string ( hInstance , ID_STR_MUTEXID ) ;
if ( AVAILABLE ( mutex_name ) & & ( wcslen ( mutex_name ) > = MIN_MUTEXID_LENGTH + ( ( mutex_name [ 0U ] = = L ' @ ' ) ? 0U : 1U ) ) )
{
if ( ! initialize_mutex ( & mutex_handle , ( mutex_name [ 0U ] = = L ' @ ' ) ? mutex_name + 1U : mutex_name ) )
{
if ( mutex_name [ 0U ] ! = L ' @ ' )
{
show_message ( hwnd , MB_ICONWARNING | MB_TOPMOST , APP_HEADING , L " The application is already running. \n \n "
L " If you see this message even though the application does not appear to be running, try restarting your computer! " ) ;
}
goto cleanup ;
}
}
2020-09-29 21:21:59 +02:00
# endif
2020-09-29 19:58:33 +02:00
2020-09-26 18:51:42 +02:00
// Show the splash screen
2020-09-29 21:21:59 +02:00
# if L5J_ENABLE_SPLASH
2020-09-30 21:23:06 +02:00
if ( ( splash_image = LoadImage ( hInstance , MAKEINTRESOURCE ( ID_BITMAP_SPLASH ) , IMAGE_BITMAP , 0 , 0 , LR_DEFAULTSIZE ) ) )
2020-09-26 18:51:42 +02:00
{
2020-09-27 18:42:22 +02:00
if ( create_splash_screen ( hwnd , splash_image ) )
2020-09-26 18:51:42 +02:00
{
2020-09-29 21:21:59 +02:00
have_screen_created = TRUE ;
2020-09-26 18:51:42 +02:00
process_window_messages ( hwnd ) ;
}
}
# endif
2020-09-26 16:38:28 +02:00
// Find executable path
2020-09-27 18:42:22 +02:00
if ( ! ( executable_path = get_executable_path ( ) ) )
2020-09-26 16:38:28 +02:00
{
2020-09-28 19:57:32 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The path of the executable could not be determined! " ) ;
2020-09-26 16:38:28 +02:00
goto cleanup ;
}
// Find executable directory
2020-09-27 18:42:22 +02:00
if ( ! ( executable_directory = get_executable_directory ( executable_path ) ) )
2020-09-26 16:38:28 +02:00
{
2020-09-28 19:57:32 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The executable directory could not be determined! " ) ;
2020-09-26 16:38:28 +02:00
goto cleanup ;
}
// Set the current directory
2020-09-27 18:42:22 +02:00
if ( _wcsicmp ( executable_directory , L " . " ) ! = 0 )
2020-09-26 16:38:28 +02:00
{
set_current_directory ( executable_directory ) ;
}
// Find the JAR file path
2020-09-27 18:42:22 +02:00
if ( ! ( jarfile_path = get_jarfile_path ( executable_path , executable_directory ) ) )
2020-09-26 16:38:28 +02:00
{
2020-09-28 19:57:32 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The path of the JAR file could not be determined! " ) ;
2020-09-26 16:38:28 +02:00
goto cleanup ;
}
2020-09-26 18:51:42 +02:00
// Does the JAR file exist?
2020-09-29 21:21:59 +02:00
# if !L5J_JAR_FILE_WRAPPED
2020-09-27 18:42:22 +02:00
if ( ! file_exists ( jarfile_path ) )
2020-09-26 16:38:28 +02:00
{
2020-10-01 22:30:24 +02:00
show_message_format ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The required JAR file could not be found: \n \n %s \n \n \n Re-installing the application may fix the problem! " , jarfile_path ) ;
2020-09-26 16:38:28 +02:00
goto cleanup ;
}
2020-09-26 18:51:42 +02:00
# endif
2020-09-26 16:38:28 +02:00
2020-10-01 18:30:11 +02:00
// Convert JAR file path to short form
jarfile_short_path = get_short_path ( jarfile_path ) ;
2020-09-27 18:42:22 +02:00
// Find the Java runtime executable path (possibly from the registry)
2020-09-29 21:21:59 +02:00
# if L5J_DETECT_REGISTRY
2020-09-29 12:01:43 +02:00
java_required_ver_min = load_java_version ( hInstance , ID_STR_JAVAMIN , ( 8ull < < 48 ) ) ;
java_required_ver_max = load_java_version ( hInstance , ID_STR_JAVAMAX , MAXULONGLONG ) ;
java_required_bitness = load_java_bitness ( hInstance , ID_STR_BITNESS ) ;
if ( ! ( java_runtime_path = detect_java_runtime ( java_required_bitness , java_required_ver_min , java_required_ver_max ) ) )
2020-09-27 18:42:22 +02:00
{
2020-09-28 19:57:32 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " Java Runtime Environment (JRE) could not be found! " ) ;
2020-09-29 21:21:59 +02:00
show_jre_download_notice ( hInstance , hwnd , APP_HEADING , java_required_bitness , java_required_ver_min ) ;
2020-09-27 18:42:22 +02:00
goto cleanup ;
}
# else
2020-09-29 21:21:59 +02:00
jre_relative_path = load_string ( hInstance , ID_STR_JREPATH ) ;
2020-10-02 16:00:22 +02:00
const wchar_t * const jre_relative_path_offset = AVAILABLE ( jre_relative_path ) ? skip_leading_separator ( jre_relative_path ) : NULL ;
if ( ! ( java_runtime_path = aswprintf ( L " %s \\ %s " , executable_directory , NOT_EMPTY ( jre_relative_path_offset ) ? jre_relative_path_offset : JRE_RELATIVE_PATH_DEFAULT ) ) )
2020-09-27 18:42:22 +02:00
{
2020-10-02 16:00:22 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The path of the Java runtime could not be determined! " ) ;
goto cleanup ;
}
if ( ! file_is_executable ( java_runtime_path ) )
{
show_message_format ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The Java runtime could not be found or is invalid: \n \n %s \n \n \n Re-installing the application may fix the problem! " , java_runtime_path ) ;
goto cleanup ;
2020-09-26 16:38:28 +02:00
}
2020-09-27 18:42:22 +02:00
# endif
2020-09-26 16:38:28 +02:00
2020-09-28 18:27:01 +02:00
// Load additional options
2020-09-28 17:09:31 +02:00
jvm_extra_args = load_string ( hInstance , ID_STR_JVMARGS ) ;
2020-09-28 18:27:01 +02:00
cmd_extra_args = load_string ( hInstance , ID_STR_CMDARGS ) ;
2020-09-28 17:09:31 +02:00
2020-10-01 15:45:56 +02:00
// Get user-provided command-line args
cmd_args_encoded = encode_commandline ( pCmdLine ) ;
// Build command-line
if ( AVAILABLE ( cmd_extra_args ) & & ( ext_args_encoded = encode_commandline ( cmd_extra_args ) ) )
{
2020-10-01 18:30:11 +02:00
const wchar_t * const jarfile_ptr = NOT_EMPTY ( jarfile_short_path ) ? jarfile_short_path : jarfile_path ;
2020-10-01 15:45:56 +02:00
command_line = AVAILABLE ( jvm_extra_args )
2020-10-01 22:30:24 +02:00
? aswprintf ( NOT_EMPTY ( cmd_args_encoded ) ? L " \" %s \" %s -Dl5j.pid=%u -jar \" %s \" %s %s " : L " \" %s \" %s -Dl5j.pid=%u -jar \" %s \" %s " , java_runtime_path , jvm_extra_args , pid , jarfile_ptr , ext_args_encoded , cmd_args_encoded )
2020-10-02 16:00:22 +02:00
: aswprintf ( NOT_EMPTY ( cmd_args_encoded ) ? L " \" %s \" -Dl5j.pid=%u -jar \" %s \" %s %s " : L " \" %s \" -Dl5j.pid=%u -jar \" %s \" %s " , java_runtime_path , pid , jarfile_ptr , ext_args_encoded , cmd_args_encoded ) ;
2020-10-01 15:45:56 +02:00
}
else
{
2020-10-01 18:30:11 +02:00
const wchar_t * const jarfile_ptr = NOT_EMPTY ( jarfile_short_path ) ? jarfile_short_path : jarfile_path ;
2020-10-01 15:45:56 +02:00
command_line = AVAILABLE ( jvm_extra_args )
2020-10-01 22:30:24 +02:00
? aswprintf ( NOT_EMPTY ( cmd_args_encoded ) ? L " \" %s \" %s -Dl5j.pid=%u -jar \" %s \" %s " : L " \" %s \" %s -Dl5j.pid=%u -jar \" %s \" " , java_runtime_path , jvm_extra_args , pid , jarfile_ptr , cmd_args_encoded )
2020-10-02 16:00:22 +02:00
: aswprintf ( NOT_EMPTY ( cmd_args_encoded ) ? L " \" %s \" -Dl5j.pid=%u -jar \" %s \" %s " : L " \" %s \" -Dl5j.pid=%u -jar \" %s \" " , java_runtime_path , pid , jarfile_ptr , cmd_args_encoded ) ;
2020-10-01 15:45:56 +02:00
}
// Make sure command-line was created
2020-09-27 18:42:22 +02:00
if ( ! command_line )
2020-09-26 16:38:28 +02:00
{
2020-09-28 19:57:32 +02:00
show_message ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " The Java command-line could not be generated! " ) ;
2020-09-26 16:38:28 +02:00
goto cleanup ;
}
2020-09-26 18:51:42 +02:00
// Process pending window messages
2020-09-29 21:21:59 +02:00
# if L5J_ENABLE_SPLASH
2020-09-26 18:51:42 +02:00
process_window_messages ( hwnd ) ;
# endif
2020-09-26 16:38:28 +02:00
// Now actually start the process!
2020-09-27 18:42:22 +02:00
if ( ! CreateProcessW ( NULL , ( LPWSTR ) command_line , NULL , NULL , FALSE , 0U , NULL , executable_directory , & startup_info , & process_info ) )
2020-09-26 16:38:28 +02:00
{
const wchar_t * const error_text = describe_system_error ( GetLastError ( ) ) ;
2020-09-27 18:42:22 +02:00
if ( error_text )
2020-09-26 16:38:28 +02:00
{
2020-10-01 22:30:24 +02:00
show_message_format ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " Failed to create the Java process: \n \n %s \n \n \n %s " , command_line , error_text ) ;
2020-09-26 16:38:28 +02:00
free ( ( void * ) error_text ) ;
}
else
{
2020-10-01 22:30:24 +02:00
show_message_format ( hwnd , MB_ICONERROR | MB_TOPMOST , APP_HEADING , L " Failed to create the Java process: \n \n %s " , command_line ) ;
2020-09-26 16:38:28 +02:00
}
goto cleanup ;
}
2020-09-26 18:51:42 +02:00
// Process pending window messages
2020-09-29 21:21:59 +02:00
# if L5J_ENABLE_SPLASH
2020-09-26 18:51:42 +02:00
process_window_messages ( hwnd ) ;
2020-09-29 21:21:59 +02:00
// Wait until child-process window is showing
# if L5J_WAIT_FOR_WINDOW
2020-09-26 22:17:23 +02:00
wait_for_process_ready ( hwnd , process_info . hProcess , process_info . dwProcessId ) ;
2020-09-26 18:51:42 +02:00
# endif
# endif
2020-09-26 16:38:28 +02:00
2020-09-29 21:21:59 +02:00
// Hide the splash screen now
if ( have_screen_created )
{
ShowWindow ( hwnd , SW_HIDE ) ;
}
2020-09-26 22:30:12 +02:00
// Wait for process to exit, then get the exit code
2020-09-29 21:21:59 +02:00
# if L5J_STAY_ALIVE
2020-09-27 18:42:22 +02:00
if ( signaled_or_failed ( WaitForSingleObject ( process_info . hProcess , INFINITE ) ) )
2020-09-26 16:38:28 +02:00
{
2020-09-26 22:17:23 +02:00
DWORD exit_code = 0U ;
2020-09-27 18:42:22 +02:00
if ( GetExitCodeProcess ( process_info . hProcess , & exit_code ) )
2020-09-26 22:17:23 +02:00
{
result = ( int ) exit_code ;
}
2020-09-26 16:38:28 +02:00
}
2020-09-26 22:17:23 +02:00
# else
result = 0 ;
# endif
2020-09-26 16:38:28 +02:00
2020-09-26 18:51:42 +02:00
cleanup :
close_handle ( & process_info . hThread ) ;
close_handle ( & process_info . hProcess ) ;
2020-09-29 19:58:33 +02:00
close_handle ( & mutex_handle ) ;
2020-09-26 18:51:42 +02:00
destroy_window ( & hwnd ) ;
2020-09-27 18:42:22 +02:00
delete_object ( & splash_image ) ;
2020-09-26 18:51:42 +02:00
2020-09-28 18:27:01 +02:00
free ( ( void * ) jvm_extra_args ) ;
free ( ( void * ) cmd_extra_args ) ;
2020-10-01 15:45:56 +02:00
free ( ( void * ) ext_args_encoded ) ;
free ( ( void * ) cmd_args_encoded ) ;
2020-09-26 16:38:28 +02:00
free ( ( void * ) command_line ) ;
free ( ( void * ) java_runtime_path ) ;
free ( ( void * ) jarfile_path ) ;
2020-10-01 18:30:11 +02:00
free ( ( void * ) jarfile_short_path ) ;
2020-09-29 21:21:59 +02:00
free ( ( void * ) jre_relative_path ) ;
2020-09-26 16:38:28 +02:00
free ( ( void * ) executable_directory ) ;
free ( ( void * ) executable_path ) ;
2020-09-29 19:58:33 +02:00
free ( ( void * ) mutex_name ) ;
2020-09-28 19:57:32 +02:00
free ( ( void * ) app_heading ) ;
2020-09-26 16:38:28 +02:00
2020-09-26 18:51:42 +02:00
return result ;
2020-09-26 16:38:28 +02:00
}