diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c7473d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin +/obj diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d75d50 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +############################################################ +# Launch5j, by LoRd_MuldeR # +# 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/ # +############################################################ + +MARCH ?= i586 +MTUNE ?= intel + +CFLAGS = -O3 -municode -mwindows -march=$(MARCH) -mtune=$(MTUNE) + +.PHONY: all init headers resources clean + +all: headers + +init: + mkdir -p bin + mkdir -p obj + +resources: init + windres -o obj/icon.o res/icon.rc + windres -o obj/splash_screen.o res/splash_screen.rc + +headers: init resources + $(CC) $(CFLAGS) -o bin/head.exe -DJAR_FILE_WRAPPED=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 src/head.c obj/icon.o obj/splash_screen.o + $(CC) $(CFLAGS) -o bin/head_nosplash.exe -DJAR_FILE_WRAPPED=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 src/head.c obj/icon.o + $(CC) $(CFLAGS) -o bin/head_nowait.exe -DJAR_FILE_WRAPPED=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 src/head.c obj/icon.o obj/splash_screen.o + $(CC) $(CFLAGS) -o bin/head_nowait_nosplash.exe -DJAR_FILE_WRAPPED=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 src/head.c obj/icon.o + $(CC) $(CFLAGS) -o bin/head_wrapped.exe -DJAR_FILE_WRAPPED=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 src/head.c obj/icon.o obj/splash_screen.o + $(CC) $(CFLAGS) -o bin/head_wrapped_nosplash.exe -DJAR_FILE_WRAPPED=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 src/head.c obj/icon.o + $(CC) $(CFLAGS) -o bin/head_wrapped_nowait.exe -DJAR_FILE_WRAPPED=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 src/head.c obj/icon.o obj/splash_screen.o + $(CC) $(CFLAGS) -o bin/head_wrapped_nowait_nosplash.exe -DJAR_FILE_WRAPPED=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 src/head.c obj/icon.o + strip bin/head.exe + strip bin/head_nosplash.exe + strip bin/head_nowait.exe + strip bin/head_nowait_nosplash.exe + strip bin/head_wrapped.exe + strip bin/head_wrapped_nosplash.exe + strip bin/head_wrapped_nowait.exe + strip bin/head_wrapped_nowait_nosplash.exe + +clean: init + rm -f bin/*.exe + rm -f obj/*.o diff --git a/res/launcher.ico b/res/icon.ico similarity index 100% rename from res/launcher.ico rename to res/icon.ico diff --git a/res/icon.rc b/res/icon.rc new file mode 100644 index 0000000..79c0386 --- /dev/null +++ b/res/icon.rc @@ -0,0 +1,17 @@ +/************************************************************/ +/* Launch5j, by LoRd_MuldeR */ +/* 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/ */ +/************************************************************/ + +#include "../src/resource.h" + +// ICON +ID_APP_ICON ICON "icon.ico" diff --git a/res/resources.rc b/res/splash_screen.rc similarity index 94% rename from res/resources.rc rename to res/splash_screen.rc index c55d1d3..13a6527 100644 --- a/res/resources.rc +++ b/res/splash_screen.rc @@ -13,8 +13,5 @@ #include "../src/resource.h" -// ICON -ID_APP_ICON ICON "launcher.ico" - // BITMAP ID_SPLASH_BITMAP BITMAP "splash_screen.bmp" diff --git a/src/main.c b/src/head.c similarity index 83% rename from src/main.c rename to src/head.c index f61d04a..85194f9 100644 --- a/src/main.c +++ b/src/head.c @@ -23,13 +23,22 @@ #include "resource.h" // Const -static const DWORD SPLASH_SCREEN_TIMEOUT = 30U; +static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U; static const wchar_t *const JRE_RELATIVE_PATH = L"runtime\\bin\\javaw.exe"; // Options -#define ENABLE_SPLASH 1 -#define WAIT_FOR_INPUT_IDLE 1 -//#define JAR_FILE_WRAPPED 1 +#ifndef ENABLE_SPLASH +#define ENABLE_SPLASH 1 +#endif +#ifndef JAR_FILE_WRAPPED +#define JAR_FILE_WRAPPED 0 +#endif +#ifndef WAIT_FOR_WINDOW +#define WAIT_FOR_WINDOW 1 +#endif +#ifndef STAY_ALIVE +#define STAY_ALIVE 1 +#endif /* ======================================================================== */ /* String routines */ @@ -177,7 +186,9 @@ static const wchar_t *get_executable_directory(const wchar_t *const executable_p static const wchar_t *get_jarfile_path(const wchar_t *const executable_path, const wchar_t *const executable_directory) { -#ifndef JAR_FILE_WRAPPED +#if JAR_FILE_WRAPPED + return wcsdup(executable_path); /*JAR file is wrapped*/ +#else const wchar_t *jarfile_path = NULL; const wchar_t *const path_prefix = remove_file_extension(executable_path); @@ -197,8 +208,6 @@ static const wchar_t *get_jarfile_path(const wchar_t *const executable_path, con free((void*)path_prefix); return jarfile_path; -#else - return wcsdup(executable_path); /*JAR file is wrapped*/ #endif } @@ -269,6 +278,77 @@ static BOOL process_window_messages(const HWND hwnd) return result; } +/* ======================================================================== */ +/* Find window functions */ +/* ======================================================================== */ + +typedef struct +{ + DWORD process_id; + HWND hwnd; +} +find_window_t; + +static BOOL CALLBACK enum_windows_callback(const HWND hwnd, const LPARAM lparam) +{ + DWORD process_id = MAXDWORD; + find_window_t *const ptr = (find_window_t*) lparam; + if(IsWindowVisible(hwnd)) + { + GetWindowThreadProcessId(hwnd, &process_id); + if(process_id == ptr->process_id) + { + ptr->hwnd = hwnd; + return FALSE; + } + } + return TRUE; +} + +static HWND find_window_by_process_id(const DWORD process_id) +{ + find_window_t find_window_data; + find_window_data.process_id = process_id; + find_window_data.hwnd = NULL; + EnumWindows(enum_windows_callback, (LONG_PTR)&find_window_data); + return find_window_data.hwnd; +} + +/* ======================================================================== */ +/* 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) +{ + const DWORD ticks_start = GetTickCount(); + for (;;) + { + const HWND child_hwnd = find_window_by_process_id(process_id); + if (child_hwnd) + { + SwitchToThisWindow(child_hwnd, TRUE); + return TRUE; + } + if (signaled_or_failed(WaitForSingleObject(process_handle, 13U))) + { + break; + } + const DWORD ticks_delta = GetTickCount() - ticks_start; + if(ticks_delta > SPLASH_SCREEN_TIMEOUT) + { + break; + } + process_window_messages(hwnd); + } + + return FALSE; +} + /* ======================================================================== */ /* Message box */ /* ======================================================================== */ @@ -344,7 +424,6 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine int result = -1; const wchar_t *executable_path = NULL, *executable_directory = NULL, *jarfile_path = NULL, *java_runtime_path = NULL, *command_line = NULL; HGDIOBJ splash_image = NULL; - DWORD exit_code = 0U; PROCESS_INFORMATION process_info; STARTUPINFOW startup_info; @@ -356,7 +435,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, L"STATIC", L"", WS_POPUP | SS_BITMAP, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); // Show the splash screen -#ifdef ENABLE_SPLASH +#if ENABLE_SPLASH if(splash_image = LoadImage(hInstance, MAKEINTRESOURCE(ID_SPLASH_BITMAP), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE)) { if(create_splash_screen(hwnd, splash_image)) @@ -401,7 +480,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine } // Does the JAR file exist? -#ifndef JAR_FILE_WRAPPED +#if !JAR_FILE_WRAPPED if(!file_exists(jarfile_path)) { show_message_format(hwnd, MB_ICONERROR | MB_TOPMOST, L"JAR not found", L"The required JAR file could not be found:\n\n%ls\n\n\nRe-installing the application may fix the problem!", jarfile_path); @@ -425,7 +504,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine } // Process pending window messages -#ifdef ENABLE_SPLASH +#if ENABLE_SPLASH process_window_messages(hwnd); #endif @@ -446,33 +525,27 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine } // Process pending window messages -#ifdef ENABLE_SPLASH +#if ENABLE_SPLASH process_window_messages(hwnd); -#ifdef WAIT_FOR_INPUT_IDLE - for (DWORD t = 0U; t < SPLASH_SCREEN_TIMEOUT; ++t) - { - if (WaitForInputIdle(process_info.hProcess, 1000U) != WAIT_TIMEOUT) - { - break; /*child-process is ready!*/ - } - if (WaitForSingleObject(process_info.hProcess, 1U) != WAIT_TIMEOUT) - { - break; /*child process terminated!*/ - } - process_window_messages(hwnd); - } +#if WAIT_FOR_WINDOW + wait_for_process_ready(hwnd, process_info.hProcess, process_info.dwProcessId); #endif destroy_window(&hwnd); #endif - // Wait for process to exit - WaitForSingleObject(process_info.hProcess, INFINITE); - - // Get the exit code - if(GetExitCodeProcess(process_info.hProcess, &exit_code)) + // Wait for process to exit, then get exit code +#if STAY_ALIVE + if(WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_OBJECT_0) { - result = (int) exit_code; + DWORD exit_code = 0U; + if(GetExitCodeProcess(process_info.hProcess, &exit_code)) + { + result = (int) exit_code; + } } +#else + result = 0; +#endif cleanup: