Implemented single instance handling.
This commit is contained in:
parent
b9bf24c87a
commit
6858ffe791
15
README.md
15
README.md
@ -58,7 +58,7 @@ All of the above Launch5j variants are available as `i586` (32-Bit) and `x86-64`
|
|||||||
|
|
||||||
Launch5j comes with a *default* executable icon and a *default* splash screen bitmap. These just server as an example and you probably want to replace them with your own *application-specific* graphics.
|
Launch5j comes with a *default* executable icon and a *default* splash screen bitmap. These just server as an example and you probably want to replace them with your own *application-specific* graphics.
|
||||||
|
|
||||||
It is ***not*** necessary to re-build the executable files for that purpose. Instead, you can simply use a resource editor, such as [**XN Resource Editor**](https://stefansundin.github.io/xn_resource_editor/) or [**Resource Hacker™**](http://www.angusj.com/resourcehacker/), to *modify* the pre-compiled executable files as needed:
|
It is ***not*** necessary to re-build the executable files for that purpose. Instead, you can simply use a resource editor, such as [**XN Resource Editor**](https://web.archive.org/web/20100419201225/http://www.wilsonc.demon.co.uk/d10resourceeditor.htm) ([mirror](https://stefansundin.github.io/xn_resource_editor/)) or [**Resource Hacker™**](http://www.angusj.com/resourcehacker/), to *modify* the pre-compiled executable files as needed:
|
||||||
|
|
||||||
![reshack](etc/reshacker-example.png)
|
![reshack](etc/reshacker-example.png)
|
||||||
|
|
||||||
@ -101,6 +101,19 @@ Some options can be configured via the launcher executable's [STRINGTABLE](https
|
|||||||
|
|
||||||
(This option only applies to the `registry` variant of Launch5j)
|
(This option only applies to the `registry` variant of Launch5j)
|
||||||
|
|
||||||
|
* **`ID_STR_BITNESS` (#6)**
|
||||||
|
Specifies the required ***bitness*** of the JRE. This can be either **`32`** (x86, aka i586) or **`64`** (x86-64).
|
||||||
|
If not specified, 32-Bit *and* 64-Bit JREs are accepted, with a preference to 64-Bit.
|
||||||
|
|
||||||
|
(This option only applies to the `registry` variant of Launch5j)
|
||||||
|
|
||||||
|
* **`ID_STR_MUTEXID` (#7)**
|
||||||
|
Specifies the application ID to be used when creating the [*single-instance*](http://www.bcbjournal.org/articles/vol3/9911/Single-instance_applications.htm) mutex.
|
||||||
|
The ID **must** be at least 5 characters in length and **should** be a *unique* string for each application!
|
||||||
|
If not specified, then **no** mutex will be created and thus *multiple* instances will be allowed.
|
||||||
|
|
||||||
|
*Hint:* If the specified application ID *starts* with an **`@`** character, then Launch5j will **not** show a message box when the application is already running; the **`@`** character is *not* considered a part of the actual ID.
|
||||||
|
|
||||||
*Note:* We use the convention that the default resource string value `"?"` is used to represent an “undefined” value, because resource strings cannot be empty. You can replace the default value as needed!
|
*Note:* We use the convention that the default resource string value `"?"` is used to represent an “undefined” value, because resource strings cannot be empty. You can replace the default value as needed!
|
||||||
|
|
||||||
# Build instructions
|
# Build instructions
|
||||||
|
@ -27,7 +27,7 @@ for %%m in (32,64) do (
|
|||||||
echo.
|
echo.
|
||||||
)
|
)
|
||||||
|
|
||||||
echo ALL IS COMPLETED.
|
echo ALL IS DONE.
|
||||||
|
|
||||||
:build_completed
|
:build_completed
|
||||||
pause
|
pause
|
||||||
|
@ -40,6 +40,7 @@ BEGIN
|
|||||||
ID_STR_HEADING L"?" /*custom application title*/
|
ID_STR_HEADING L"?" /*custom application title*/
|
||||||
ID_STR_JVMARGS L"?" /*additional JVM args*/
|
ID_STR_JVMARGS L"?" /*additional JVM args*/
|
||||||
ID_STR_CMDARGS L"?" /*additional command-line args*/
|
ID_STR_CMDARGS L"?" /*additional command-line args*/
|
||||||
|
ID_STR_MUTEXID L"?" /*single instance mutex ID*/
|
||||||
END
|
END
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
62
src/head.c
62
src/head.c
@ -48,6 +48,7 @@
|
|||||||
// Const
|
// Const
|
||||||
static const wchar_t *const JRE_RELATIVE_PATH = L"runtime\\bin\\javaw.exe";
|
static const wchar_t *const JRE_RELATIVE_PATH = L"runtime\\bin\\javaw.exe";
|
||||||
static const wchar_t *const JRE_DOWNLOAD_LINK = L"https://adoptopenjdk.net/";
|
static const wchar_t *const JRE_DOWNLOAD_LINK = L"https://adoptopenjdk.net/";
|
||||||
|
static const size_t MIN_MUTEXID_LENGTH = 5U;
|
||||||
static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U;
|
static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U;
|
||||||
|
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
@ -918,6 +919,47 @@ static void show_jre_download_notice(const HWND hwnd, const wchar_t *const title
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======================================================================== */
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
const wchar_t *const mutex_uuid = awprintf(L"l5j.%016llX%016llX", hashcode_0, hashcode_1);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
@ -959,7 +1001,8 @@ static wchar_t *const DEFAULT_HEADING = L"Launch5j";
|
|||||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
int result = -1;
|
int result = -1;
|
||||||
const wchar_t *app_heading = NULL, *executable_path = NULL, *executable_directory = NULL, *jarfile_path = NULL, *java_runtime_path = NULL, *jvm_extra_args = NULL, *cmd_extra_args = NULL, *command_line = NULL;
|
const wchar_t *app_heading = NULL, *mutex_name = NULL, *executable_path = NULL, *executable_directory = NULL, *jarfile_path = NULL, *java_runtime_path = NULL, *jvm_extra_args = NULL, *cmd_extra_args = NULL, *command_line = NULL;
|
||||||
|
HANDLE mutex_handle = NULL;
|
||||||
DWORD java_required_bitness = 0U;
|
DWORD java_required_bitness = 0U;
|
||||||
ULONGLONG java_required_ver_min = 0ULL, java_required_ver_max = 0ULL;
|
ULONGLONG java_required_ver_min = 0ULL, java_required_ver_max = 0ULL;
|
||||||
HGDIOBJ splash_image = NULL;
|
HGDIOBJ splash_image = NULL;
|
||||||
@ -976,6 +1019,21 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
// Create the window
|
// Create the window
|
||||||
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);
|
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);
|
||||||
|
|
||||||
|
// Single instance
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show the splash screen
|
// Show the splash screen
|
||||||
#if ENABLE_SPLASH
|
#if ENABLE_SPLASH
|
||||||
if (splash_image = LoadImage(hInstance, MAKEINTRESOURCE(ID_BITMAP_SPLASH), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE))
|
if (splash_image = LoadImage(hInstance, MAKEINTRESOURCE(ID_BITMAP_SPLASH), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE))
|
||||||
@ -1113,6 +1171,7 @@ cleanup:
|
|||||||
|
|
||||||
close_handle(&process_info.hThread);
|
close_handle(&process_info.hThread);
|
||||||
close_handle(&process_info.hProcess);
|
close_handle(&process_info.hProcess);
|
||||||
|
close_handle(&mutex_handle);
|
||||||
|
|
||||||
destroy_window(&hwnd);
|
destroy_window(&hwnd);
|
||||||
delete_object(&splash_image);
|
delete_object(&splash_image);
|
||||||
@ -1124,6 +1183,7 @@ cleanup:
|
|||||||
free((void*)jarfile_path);
|
free((void*)jarfile_path);
|
||||||
free((void*)executable_directory);
|
free((void*)executable_directory);
|
||||||
free((void*)executable_path);
|
free((void*)executable_path);
|
||||||
|
free((void*)mutex_name);
|
||||||
free((void*)app_heading);
|
free((void*)app_heading);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -24,3 +24,4 @@
|
|||||||
#define ID_STR_JAVAMIN 4
|
#define ID_STR_JAVAMIN 4
|
||||||
#define ID_STR_JAVAMAX 5
|
#define ID_STR_JAVAMAX 5
|
||||||
#define ID_STR_BITNESS 6
|
#define ID_STR_BITNESS 6
|
||||||
|
#define ID_STR_MUTEXID 7
|
||||||
|
Loading…
Reference in New Issue
Block a user