Implemented single instance handling.
This commit is contained in:
parent
b9bf24c87a
commit
6858ffe791
23
README.md
23
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.
|
||||
|
||||
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)
|
||||
|
||||
@ -90,17 +90,30 @@ Some options can be configured via the launcher executable's [STRINGTABLE](https
|
||||
Specifies the ***maximum*** supported JRE version, in the **`w.x.y.z`** format (e.g. `12.0.0.0`).
|
||||
This values is *exclusive*, i.e. only JRE versions *older* than the specified JRE version will be accepted.
|
||||
If not specified, then there is **no** upper limit on the supported JRE version.
|
||||
|
||||
*Hint:* Old-style `1.x.y.z` Java versions are automatically translated to the `x.y.z.0` format!
|
||||
|
||||
|
||||
*Hint:* Old-style `1.x.y.z` Java versions are automatically translated to the `x.y.z.0` format!
|
||||
|
||||
(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_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!
|
||||
|
||||
# Build instructions
|
||||
|
@ -27,7 +27,7 @@ for %%m in (32,64) do (
|
||||
echo.
|
||||
)
|
||||
|
||||
echo ALL IS COMPLETED.
|
||||
echo ALL IS DONE.
|
||||
|
||||
:build_completed
|
||||
pause
|
||||
|
@ -40,6 +40,7 @@ BEGIN
|
||||
ID_STR_HEADING L"?" /*custom application title*/
|
||||
ID_STR_JVMARGS L"?" /*additional JVM args*/
|
||||
ID_STR_CMDARGS L"?" /*additional command-line args*/
|
||||
ID_STR_MUTEXID L"?" /*single instance mutex ID*/
|
||||
END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
62
src/head.c
62
src/head.c
@ -48,6 +48,7 @@
|
||||
// Const
|
||||
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 size_t MIN_MUTEXID_LENGTH = 5U;
|
||||
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 */
|
||||
/* ======================================================================== */
|
||||
@ -959,7 +1001,8 @@ static wchar_t *const DEFAULT_HEADING = L"Launch5j";
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||
{
|
||||
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;
|
||||
ULONGLONG java_required_ver_min = 0ULL, java_required_ver_max = 0ULL;
|
||||
HGDIOBJ splash_image = NULL;
|
||||
@ -976,6 +1019,21 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
#if ENABLE_SPLASH
|
||||
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.hProcess);
|
||||
close_handle(&mutex_handle);
|
||||
|
||||
destroy_window(&hwnd);
|
||||
delete_object(&splash_image);
|
||||
@ -1124,6 +1183,7 @@ cleanup:
|
||||
free((void*)jarfile_path);
|
||||
free((void*)executable_directory);
|
||||
free((void*)executable_path);
|
||||
free((void*)mutex_name);
|
||||
free((void*)app_heading);
|
||||
|
||||
return result;
|
||||
|
@ -24,3 +24,4 @@
|
||||
#define ID_STR_JAVAMIN 4
|
||||
#define ID_STR_JAVAMAX 5
|
||||
#define ID_STR_BITNESS 6
|
||||
#define ID_STR_MUTEXID 7
|
||||
|
Loading…
Reference in New Issue
Block a user