815 lines
22 KiB
C
815 lines
22 KiB
C
|
/*
|
||
|
Launch4j (http://launch4j.sourceforge.net/)
|
||
|
Cross-platform Java application wrapper for creating Windows native executables.
|
||
|
|
||
|
Copyright (c) 2004, 2008 Grzegorz Kowal,
|
||
|
Ian Roberts (jdk preference patch)
|
||
|
Sylvain Mina (single instance patch)
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||
|
shall not be used in advertising or otherwise to promote the sale, use or other
|
||
|
dealings in this Software without prior written authorization.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#include "resource.h"
|
||
|
#include "head.h"
|
||
|
|
||
|
HMODULE hModule;
|
||
|
FILE* hLog;
|
||
|
BOOL console = FALSE;
|
||
|
BOOL wow64 = FALSE;
|
||
|
int foundJava = NO_JAVA_FOUND;
|
||
|
|
||
|
struct _stat statBuf;
|
||
|
PROCESS_INFORMATION pi;
|
||
|
DWORD priority;
|
||
|
|
||
|
char mutexName[STR] = {0};
|
||
|
|
||
|
char errUrl[256] = {0};
|
||
|
char errTitle[STR] = "Launch4j";
|
||
|
char errMsg[BIG_STR] = {0};
|
||
|
|
||
|
char javaMinVer[STR] = {0};
|
||
|
char javaMaxVer[STR] = {0};
|
||
|
char foundJavaVer[STR] = {0};
|
||
|
char foundJavaKey[_MAX_PATH] = {0};
|
||
|
|
||
|
char oldPwd[_MAX_PATH] = {0};
|
||
|
char workingDir[_MAX_PATH] = {0};
|
||
|
char cmd[_MAX_PATH] = {0};
|
||
|
char args[MAX_ARGS] = {0};
|
||
|
|
||
|
FILE* openLogFile(const char* exePath, const int pathLen) {
|
||
|
char path[_MAX_PATH] = {0};
|
||
|
strncpy(path, exePath, pathLen);
|
||
|
strcat(path, "\\launch4j.log");
|
||
|
return fopen(path, "a");
|
||
|
}
|
||
|
|
||
|
void closeLogFile() {
|
||
|
if (hLog != NULL) {
|
||
|
fclose(hLog);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void setWow64Flag() {
|
||
|
LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
|
||
|
GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
|
||
|
|
||
|
if (fnIsWow64Process != NULL) {
|
||
|
fnIsWow64Process(GetCurrentProcess(), &wow64);
|
||
|
}
|
||
|
debug("WOW64:\t\t%s\n", wow64 ? "yes" : "no");
|
||
|
}
|
||
|
|
||
|
void setConsoleFlag() {
|
||
|
console = TRUE;
|
||
|
}
|
||
|
|
||
|
void msgBox(const char* text) {
|
||
|
if (console) {
|
||
|
printf("%s: %s\n", errTitle, text);
|
||
|
} else {
|
||
|
MessageBox(NULL, text, errTitle, MB_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void signalError() {
|
||
|
DWORD err = GetLastError();
|
||
|
if (err) {
|
||
|
LPVOID lpMsgBuf;
|
||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||
|
| FORMAT_MESSAGE_FROM_SYSTEM
|
||
|
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
|
NULL,
|
||
|
err,
|
||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||
|
(LPTSTR) &lpMsgBuf,
|
||
|
0,
|
||
|
NULL);
|
||
|
debug("Error:\t\t%s\n", (LPCTSTR) lpMsgBuf);
|
||
|
strcat(errMsg, "\n\n");
|
||
|
strcat(errMsg, (LPCTSTR) lpMsgBuf);
|
||
|
msgBox(errMsg);
|
||
|
LocalFree(lpMsgBuf);
|
||
|
} else {
|
||
|
msgBox(errMsg);
|
||
|
}
|
||
|
if (*errUrl) {
|
||
|
debug("Open URL:\t%s\n", errUrl);
|
||
|
ShellExecute(NULL, "open", errUrl, NULL, NULL, SW_SHOWNORMAL);
|
||
|
}
|
||
|
closeLogFile();
|
||
|
}
|
||
|
|
||
|
BOOL loadString(const int resID, char* buffer) {
|
||
|
HRSRC hResource;
|
||
|
HGLOBAL hResourceLoaded;
|
||
|
LPBYTE lpBuffer;
|
||
|
|
||
|
hResource = FindResourceEx(hModule, RT_RCDATA, MAKEINTRESOURCE(resID),
|
||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
|
||
|
if (NULL != hResource) {
|
||
|
hResourceLoaded = LoadResource(hModule, hResource);
|
||
|
if (NULL != hResourceLoaded) {
|
||
|
lpBuffer = (LPBYTE) LockResource(hResourceLoaded);
|
||
|
if (NULL != lpBuffer) {
|
||
|
int x = 0;
|
||
|
do {
|
||
|
buffer[x] = (char) lpBuffer[x];
|
||
|
} while (buffer[x++] != 0);
|
||
|
// debug("Resource %d:\t%s\n", resID, buffer);
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
SetLastError(0);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL loadBool(const int resID) {
|
||
|
char boolStr[20] = {0};
|
||
|
loadString(resID, boolStr);
|
||
|
return strcmp(boolStr, TRUE_STR) == 0;
|
||
|
}
|
||
|
|
||
|
int loadInt(const int resID) {
|
||
|
char intStr[20] = {0};
|
||
|
loadString(resID, intStr);
|
||
|
return atoi(intStr);
|
||
|
}
|
||
|
|
||
|
BOOL regQueryValue(const char* regPath, unsigned char* buffer,
|
||
|
unsigned long bufferLength) {
|
||
|
HKEY hRootKey;
|
||
|
char* key;
|
||
|
char* value;
|
||
|
if (strstr(regPath, HKEY_CLASSES_ROOT_STR) == regPath) {
|
||
|
hRootKey = HKEY_CLASSES_ROOT;
|
||
|
} else if (strstr(regPath, HKEY_CURRENT_USER_STR) == regPath) {
|
||
|
hRootKey = HKEY_CURRENT_USER;
|
||
|
} else if (strstr(regPath, HKEY_LOCAL_MACHINE_STR) == regPath) {
|
||
|
hRootKey = HKEY_LOCAL_MACHINE;
|
||
|
} else if (strstr(regPath, HKEY_USERS_STR) == regPath) {
|
||
|
hRootKey = HKEY_USERS;
|
||
|
} else if (strstr(regPath, HKEY_CURRENT_CONFIG_STR) == regPath) {
|
||
|
hRootKey = HKEY_CURRENT_CONFIG;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
key = strchr(regPath, '\\') + 1;
|
||
|
value = strrchr(regPath, '\\') + 1;
|
||
|
*(value - 1) = 0;
|
||
|
|
||
|
HKEY hKey;
|
||
|
unsigned long datatype;
|
||
|
BOOL result = FALSE;
|
||
|
if ((wow64 && RegOpenKeyEx(hRootKey,
|
||
|
key,
|
||
|
0,
|
||
|
KEY_READ | KEY_WOW64_64KEY,
|
||
|
&hKey) == ERROR_SUCCESS)
|
||
|
|| RegOpenKeyEx(hRootKey,
|
||
|
key,
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hKey) == ERROR_SUCCESS) {
|
||
|
result = RegQueryValueEx(hKey, value, NULL, &datatype, buffer, &bufferLength)
|
||
|
== ERROR_SUCCESS;
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
*(value - 1) = '\\';
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void regSearch(const HKEY hKey, const char* keyName, const int searchType) {
|
||
|
DWORD x = 0;
|
||
|
unsigned long size = BIG_STR;
|
||
|
FILETIME time;
|
||
|
char buffer[BIG_STR] = {0};
|
||
|
while (RegEnumKeyEx(
|
||
|
hKey, // handle to key to enumerate
|
||
|
x++, // index of subkey to enumerate
|
||
|
buffer, // address of buffer for subkey name
|
||
|
&size, // address for size of subkey buffer
|
||
|
NULL, // reserved
|
||
|
NULL, // address of buffer for class string
|
||
|
NULL, // address for size of class buffer
|
||
|
&time) == ERROR_SUCCESS) {
|
||
|
|
||
|
if (strcmp(buffer, javaMinVer) >= 0
|
||
|
&& (!*javaMaxVer || strcmp(buffer, javaMaxVer) <= 0)
|
||
|
&& strcmp(buffer, foundJavaVer) > 0) {
|
||
|
strcpy(foundJavaVer, buffer);
|
||
|
strcpy(foundJavaKey, keyName);
|
||
|
appendPath(foundJavaKey, buffer);
|
||
|
foundJava = searchType;
|
||
|
debug("Match:\t\t%s\\%s\n", keyName, buffer);
|
||
|
} else {
|
||
|
debug("Ignore:\t\t%s\\%s\n", keyName, buffer);
|
||
|
}
|
||
|
size = BIG_STR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void regSearchWow(const char* keyName, const int searchType) {
|
||
|
HKEY hKey;
|
||
|
debug("64-bit search:\t%s...\n", keyName);
|
||
|
if (wow64 && RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
keyName,
|
||
|
0,
|
||
|
KEY_READ | KEY_WOW64_64KEY,
|
||
|
&hKey) == ERROR_SUCCESS) {
|
||
|
|
||
|
regSearch(hKey, keyName, searchType | KEY_WOW64_64KEY);
|
||
|
RegCloseKey(hKey);
|
||
|
if ((foundJava & KEY_WOW64_64KEY) != NO_JAVA_FOUND)
|
||
|
{
|
||
|
debug("Using 64-bit runtime.\n");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
debug("32-bit search:\t%s...\n", keyName);
|
||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
keyName,
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hKey) == ERROR_SUCCESS) {
|
||
|
regSearch(hKey, keyName, searchType);
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void regSearchJreSdk(const char* jreKeyName, const char* sdkKeyName,
|
||
|
const int jdkPreference) {
|
||
|
if (jdkPreference == JDK_ONLY || jdkPreference == PREFER_JDK) {
|
||
|
regSearchWow(sdkKeyName, FOUND_SDK);
|
||
|
if (jdkPreference != JDK_ONLY) {
|
||
|
regSearchWow(jreKeyName, FOUND_JRE);
|
||
|
}
|
||
|
} else { // jdkPreference == JRE_ONLY or PREFER_JRE
|
||
|
regSearchWow(jreKeyName, FOUND_JRE);
|
||
|
if (jdkPreference != JRE_ONLY) {
|
||
|
regSearchWow(sdkKeyName, FOUND_SDK);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL findJavaHome(char* path, const int jdkPreference) {
|
||
|
regSearchJreSdk("SOFTWARE\\JavaSoft\\Java Runtime Environment",
|
||
|
"SOFTWARE\\JavaSoft\\Java Development Kit",
|
||
|
jdkPreference);
|
||
|
if (foundJava == NO_JAVA_FOUND) {
|
||
|
regSearchJreSdk("SOFTWARE\\IBM\\Java2 Runtime Environment",
|
||
|
"SOFTWARE\\IBM\\Java Development Kit",
|
||
|
jdkPreference);
|
||
|
}
|
||
|
if (foundJava != NO_JAVA_FOUND) {
|
||
|
HKEY hKey;
|
||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
foundJavaKey,
|
||
|
0,
|
||
|
KEY_READ | (foundJava & KEY_WOW64_64KEY),
|
||
|
&hKey) == ERROR_SUCCESS) {
|
||
|
unsigned char buffer[BIG_STR] = {0};
|
||
|
unsigned long bufferlength = BIG_STR;
|
||
|
unsigned long datatype;
|
||
|
if (RegQueryValueEx(hKey, "JavaHome", NULL, &datatype, buffer,
|
||
|
&bufferlength) == ERROR_SUCCESS) {
|
||
|
int i = 0;
|
||
|
do {
|
||
|
path[i] = buffer[i];
|
||
|
} while (path[i++] != 0);
|
||
|
if (foundJava & FOUND_SDK) {
|
||
|
appendPath(path, "jre");
|
||
|
}
|
||
|
RegCloseKey(hKey);
|
||
|
return TRUE;
|
||
|
}
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Extract the executable name, returns path length.
|
||
|
*/
|
||
|
int getExePath(char* exePath) {
|
||
|
if (GetModuleFileName(hModule, exePath, _MAX_PATH) == 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
return strrchr(exePath, '\\') - exePath;
|
||
|
}
|
||
|
|
||
|
void appendPath(char* basepath, const char* path) {
|
||
|
if (basepath[strlen(basepath) - 1] != '\\') {
|
||
|
strcat(basepath, "\\");
|
||
|
}
|
||
|
strcat(basepath, path);
|
||
|
}
|
||
|
|
||
|
void appendJavaw(char* jrePath) {
|
||
|
if (console) {
|
||
|
appendPath(jrePath, "bin\\java.exe");
|
||
|
} else {
|
||
|
appendPath(jrePath, "bin\\javaw.exe");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void appendLauncher(const BOOL setProcName, char* exePath,
|
||
|
const int pathLen, char* cmd) {
|
||
|
if (setProcName) {
|
||
|
char tmpspec[_MAX_PATH];
|
||
|
char tmpfile[_MAX_PATH];
|
||
|
strcpy(tmpspec, cmd);
|
||
|
strcat(tmpspec, LAUNCH4J_TMP_DIR);
|
||
|
tmpspec[strlen(tmpspec) - 1] = 0;
|
||
|
if (_stat(tmpspec, &statBuf) == 0) {
|
||
|
// Remove temp launchers and manifests
|
||
|
struct _finddata_t c_file;
|
||
|
long hFile;
|
||
|
appendPath(tmpspec, "*.exe");
|
||
|
strcpy(tmpfile, cmd);
|
||
|
strcat(tmpfile, LAUNCH4J_TMP_DIR);
|
||
|
char* filename = tmpfile + strlen(tmpfile);
|
||
|
if ((hFile = _findfirst(tmpspec, &c_file)) != -1L) {
|
||
|
do {
|
||
|
strcpy(filename, c_file.name);
|
||
|
debug("Unlink:\t\t%s\n", tmpfile);
|
||
|
_unlink(tmpfile);
|
||
|
strcat(tmpfile, MANIFEST);
|
||
|
debug("Unlink:\t\t%s\n", tmpfile);
|
||
|
_unlink(tmpfile);
|
||
|
} while (_findnext(hFile, &c_file) == 0);
|
||
|
}
|
||
|
_findclose(hFile);
|
||
|
} else {
|
||
|
if (_mkdir(tmpspec) != 0) {
|
||
|
debug("Mkdir failed:\t%s\n", tmpspec);
|
||
|
appendJavaw(cmd);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
char javaw[_MAX_PATH];
|
||
|
strcpy(javaw, cmd);
|
||
|
appendJavaw(javaw);
|
||
|
strcpy(tmpfile, cmd);
|
||
|
strcat(tmpfile, LAUNCH4J_TMP_DIR);
|
||
|
char* tmpfilename = tmpfile + strlen(tmpfile);
|
||
|
char* exeFilePart = exePath + pathLen + 1;
|
||
|
|
||
|
// Copy manifest
|
||
|
char manifest[_MAX_PATH] = {0};
|
||
|
strcpy(manifest, exePath);
|
||
|
strcat(manifest, MANIFEST);
|
||
|
if (_stat(manifest, &statBuf) == 0) {
|
||
|
strcat(tmpfile, exeFilePart);
|
||
|
strcat(tmpfile, MANIFEST);
|
||
|
debug("Copy:\t\t%s -> %s\n", manifest, tmpfile);
|
||
|
CopyFile(manifest, tmpfile, FALSE);
|
||
|
}
|
||
|
|
||
|
// Copy launcher
|
||
|
strcpy(tmpfilename, exeFilePart);
|
||
|
debug("Copy:\t\t%s -> %s\n", javaw, tmpfile);
|
||
|
if (CopyFile(javaw, tmpfile, FALSE)) {
|
||
|
strcpy(cmd, tmpfile);
|
||
|
return;
|
||
|
} else if (_stat(javaw, &statBuf) == 0) {
|
||
|
long fs = statBuf.st_size;
|
||
|
if (_stat(tmpfile, &statBuf) == 0 && fs == statBuf.st_size) {
|
||
|
debug("Reusing:\t\t%s\n", tmpfile);
|
||
|
strcpy(cmd, tmpfile);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
appendJavaw(cmd);
|
||
|
}
|
||
|
|
||
|
void appendAppClasspath(char* dst, const char* src, const char* classpath) {
|
||
|
strcat(dst, src);
|
||
|
if (*classpath) {
|
||
|
strcat(dst, ";");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL isJrePathOk(const char* path) {
|
||
|
char javaw[_MAX_PATH];
|
||
|
BOOL result = FALSE;
|
||
|
if (*path) {
|
||
|
strcpy(javaw, path);
|
||
|
appendJavaw(javaw);
|
||
|
result = _stat(javaw, &statBuf) == 0;
|
||
|
}
|
||
|
debug("Check launcher:\t%s %s\n", javaw, result ? "(OK)" : "(n/a)");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Expand environment %variables%
|
||
|
*/
|
||
|
BOOL expandVars(char *dst, const char *src, const char *exePath, const int pathLen) {
|
||
|
char varName[STR];
|
||
|
char varValue[MAX_VAR_SIZE];
|
||
|
while (strlen(src) > 0) {
|
||
|
char *start = strchr(src, '%');
|
||
|
if (start != NULL) {
|
||
|
char *end = strchr(start + 1, '%');
|
||
|
if (end == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
// Copy content up to %VAR%
|
||
|
strncat(dst, src, start - src);
|
||
|
// Insert value of %VAR%
|
||
|
*varName = 0;
|
||
|
strncat(varName, start + 1, end - start - 1);
|
||
|
// Remember value start for logging
|
||
|
char *currentVarValue = dst + strlen(dst);
|
||
|
if (strcmp(varName, "EXEDIR") == 0) {
|
||
|
strncat(dst, exePath, pathLen);
|
||
|
} else if (strcmp(varName, "EXEFILE") == 0) {
|
||
|
strcat(dst, exePath);
|
||
|
} else if (strcmp(varName, "PWD") == 0) {
|
||
|
GetCurrentDirectory(_MAX_PATH, dst + strlen(dst));
|
||
|
} else if (strcmp(varName, "OLDPWD") == 0) {
|
||
|
strcat(dst, oldPwd);
|
||
|
} else if (strstr(varName, HKEY_STR) == varName) {
|
||
|
regQueryValue(varName, dst + strlen(dst), BIG_STR);
|
||
|
} else if (GetEnvironmentVariable(varName, varValue, MAX_VAR_SIZE) > 0) {
|
||
|
strcat(dst, varValue);
|
||
|
}
|
||
|
debug("Substitute:\t%s = %s\n", varName, currentVarValue);
|
||
|
src = end + 1;
|
||
|
} else {
|
||
|
// Copy remaining content
|
||
|
strcat(dst, src);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void appendHeapSizes(char *dst) {
|
||
|
MEMORYSTATUS m;
|
||
|
memset(&m, 0, sizeof(m));
|
||
|
GlobalMemoryStatus(&m);
|
||
|
|
||
|
appendHeapSize(dst, INITIAL_HEAP_SIZE, INITIAL_HEAP_PERCENT,
|
||
|
m.dwAvailPhys, "-Xms");
|
||
|
appendHeapSize(dst, MAX_HEAP_SIZE, MAX_HEAP_PERCENT,
|
||
|
m.dwAvailPhys, "-Xmx");
|
||
|
}
|
||
|
|
||
|
void appendHeapSize(char *dst, const int absID, const int percentID,
|
||
|
const DWORD freeMemory, const char *option) {
|
||
|
|
||
|
const int mb = 1048576; // 1 MB
|
||
|
const int mbLimit32 = 1500; // Max heap size in MB on 32-bit JREs
|
||
|
int abs = loadInt(absID);
|
||
|
int percent = loadInt(percentID);
|
||
|
int free = (long long) freeMemory * percent / (100 * mb); // 100% * 1 MB
|
||
|
int size = free > abs ? free : abs;
|
||
|
if (size > 0) {
|
||
|
if (!(foundJava & KEY_WOW64_64KEY) && size > mbLimit32) {
|
||
|
debug("Heap limit:\tReduced %d MB heap size to 32-bit maximum %d MB\n",
|
||
|
size, mbLimit32);
|
||
|
size = mbLimit32;
|
||
|
}
|
||
|
|
||
|
debug("Heap %s:\t%d MB / %d%%, Free: %d MB, Heap size: %d MB\n",
|
||
|
option, abs, percent, freeMemory / mb, size);
|
||
|
strcat(dst, option);
|
||
|
_itoa(size, dst + strlen(dst), 10); // 10 -- radix
|
||
|
strcat(dst, "m ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int prepare(const char *lpCmdLine) {
|
||
|
char tmp[MAX_ARGS] = {0};
|
||
|
hModule = GetModuleHandle(NULL);
|
||
|
if (hModule == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get executable path
|
||
|
char exePath[_MAX_PATH] = {0};
|
||
|
int pathLen = getExePath(exePath);
|
||
|
if (pathLen == -1) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Initialize logging
|
||
|
if (strstr(lpCmdLine, "--l4j-debug") != NULL) {
|
||
|
hLog = openLogFile(exePath, pathLen);
|
||
|
if (hLog == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
debug("\n\nCmdLine:\t%s %s\n", exePath, lpCmdLine);
|
||
|
}
|
||
|
|
||
|
setWow64Flag();
|
||
|
|
||
|
// Set default error message, title and optional support web site url.
|
||
|
loadString(SUPPORT_URL, errUrl);
|
||
|
loadString(ERR_TITLE, errTitle);
|
||
|
if (!loadString(STARTUP_ERR, errMsg)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Single instance
|
||
|
loadString(MUTEX_NAME, mutexName);
|
||
|
if (*mutexName) {
|
||
|
SECURITY_ATTRIBUTES security;
|
||
|
security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||
|
security.bInheritHandle = TRUE;
|
||
|
security.lpSecurityDescriptor = NULL;
|
||
|
CreateMutexA(&security, FALSE, mutexName);
|
||
|
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||
|
debug("Instance already exists.");
|
||
|
return ERROR_ALREADY_EXISTS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Working dir
|
||
|
char tmp_path[_MAX_PATH] = {0};
|
||
|
GetCurrentDirectory(_MAX_PATH, oldPwd);
|
||
|
if (loadString(CHDIR, tmp_path)) {
|
||
|
strncpy(workingDir, exePath, pathLen);
|
||
|
appendPath(workingDir, tmp_path);
|
||
|
_chdir(workingDir);
|
||
|
debug("Working dir:\t%s\n", workingDir);
|
||
|
}
|
||
|
|
||
|
// Use bundled jre or find java
|
||
|
if (loadString(JRE_PATH, tmp_path)) {
|
||
|
char jrePath[MAX_ARGS] = {0};
|
||
|
expandVars(jrePath, tmp_path, exePath, pathLen);
|
||
|
debug("Bundled JRE:\t%s\n", jrePath);
|
||
|
if (jrePath[0] == '\\' || jrePath[1] == ':') {
|
||
|
// Absolute
|
||
|
strcpy(cmd, jrePath);
|
||
|
} else {
|
||
|
// Relative
|
||
|
strncpy(cmd, exePath, pathLen);
|
||
|
appendPath(cmd, jrePath);
|
||
|
}
|
||
|
}
|
||
|
if (!isJrePathOk(cmd)) {
|
||
|
if (!loadString(JAVA_MIN_VER, javaMinVer)) {
|
||
|
loadString(BUNDLED_JRE_ERR, errMsg);
|
||
|
return FALSE;
|
||
|
}
|
||
|
loadString(JAVA_MAX_VER, javaMaxVer);
|
||
|
if (!findJavaHome(cmd, loadInt(JDK_PREFERENCE))) {
|
||
|
loadString(JRE_VERSION_ERR, errMsg);
|
||
|
strcat(errMsg, " ");
|
||
|
strcat(errMsg, javaMinVer);
|
||
|
if (*javaMaxVer) {
|
||
|
strcat(errMsg, " - ");
|
||
|
strcat(errMsg, javaMaxVer);
|
||
|
}
|
||
|
loadString(DOWNLOAD_URL, errUrl);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (!isJrePathOk(cmd)) {
|
||
|
loadString(LAUNCHER_ERR, errMsg);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append a path to the Path environment variable
|
||
|
char jreBinPath[_MAX_PATH];
|
||
|
strcpy(jreBinPath, cmd);
|
||
|
strcat(jreBinPath, "\\bin");
|
||
|
if (!appendToPathVar(jreBinPath)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Set environment variables
|
||
|
char envVars[MAX_VAR_SIZE] = {0};
|
||
|
loadString(ENV_VARIABLES, envVars);
|
||
|
char *var = strtok(envVars, "\t");
|
||
|
while (var != NULL) {
|
||
|
char *varValue = strchr(var, '=');
|
||
|
*varValue++ = 0;
|
||
|
*tmp = 0;
|
||
|
expandVars(tmp, varValue, exePath, pathLen);
|
||
|
debug("Set var:\t%s = %s\n", var, tmp);
|
||
|
SetEnvironmentVariable(var, tmp);
|
||
|
var = strtok(NULL, "\t");
|
||
|
}
|
||
|
*tmp = 0;
|
||
|
|
||
|
// Process priority
|
||
|
priority = loadInt(PRIORITY_CLASS);
|
||
|
|
||
|
// Custom process name
|
||
|
const BOOL setProcName = loadBool(SET_PROC_NAME)
|
||
|
&& strstr(lpCmdLine, "--l4j-default-proc") == NULL;
|
||
|
const BOOL wrapper = loadBool(WRAPPER);
|
||
|
|
||
|
appendLauncher(setProcName, exePath, pathLen, cmd);
|
||
|
|
||
|
// Heap sizes
|
||
|
appendHeapSizes(args);
|
||
|
|
||
|
// JVM options
|
||
|
if (loadString(JVM_OPTIONS, tmp)) {
|
||
|
strcat(tmp, " ");
|
||
|
} else {
|
||
|
*tmp = 0;
|
||
|
}
|
||
|
/*
|
||
|
* Load additional JVM options from .l4j.ini file
|
||
|
* Options are separated by spaces or CRLF
|
||
|
* # starts an inline comment
|
||
|
*/
|
||
|
strncpy(tmp_path, exePath, strlen(exePath) - 3);
|
||
|
strcat(tmp_path, "l4j.ini");
|
||
|
long hFile;
|
||
|
if ((hFile = _open(tmp_path, _O_RDONLY)) != -1) {
|
||
|
const int jvmOptLen = strlen(tmp);
|
||
|
char* src = tmp + jvmOptLen;
|
||
|
char* dst = src;
|
||
|
const int len = _read(hFile, src, MAX_ARGS - jvmOptLen - BIG_STR);
|
||
|
BOOL copy = TRUE;
|
||
|
int i;
|
||
|
for (i = 0; i < len; i++, src++) {
|
||
|
if (*src == '#') {
|
||
|
copy = FALSE;
|
||
|
} else if (*src == 13 || *src == 10) {
|
||
|
copy = TRUE;
|
||
|
if (dst > tmp && *(dst - 1) != ' ') {
|
||
|
*dst++ = ' ';
|
||
|
}
|
||
|
} else if (copy) {
|
||
|
*dst++ = *src;
|
||
|
}
|
||
|
}
|
||
|
*dst = 0;
|
||
|
if (len > 0 && *(dst - 1) != ' ') {
|
||
|
strcat(tmp, " ");
|
||
|
}
|
||
|
_close(hFile);
|
||
|
}
|
||
|
|
||
|
// Expand environment %variables%
|
||
|
expandVars(args, tmp, exePath, pathLen);
|
||
|
|
||
|
// MainClass + Classpath or Jar
|
||
|
char mainClass[STR] = {0};
|
||
|
char jar[_MAX_PATH] = {0};
|
||
|
loadString(JAR, jar);
|
||
|
if (loadString(MAIN_CLASS, mainClass)) {
|
||
|
if (!loadString(CLASSPATH, tmp)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
char exp[MAX_ARGS] = {0};
|
||
|
expandVars(exp, tmp, exePath, pathLen);
|
||
|
strcat(args, "-classpath \"");
|
||
|
if (wrapper) {
|
||
|
appendAppClasspath(args, exePath, exp);
|
||
|
} else if (*jar) {
|
||
|
appendAppClasspath(args, jar, exp);
|
||
|
}
|
||
|
|
||
|
// Deal with wildcards or >> strcat(args, exp); <<
|
||
|
char* cp = strtok(exp, ";");
|
||
|
while(cp != NULL) {
|
||
|
debug("Add classpath:\t%s\n", cp);
|
||
|
if (strpbrk(cp, "*?") != NULL) {
|
||
|
int len = strrchr(cp, '\\') - cp + 1;
|
||
|
strncpy(tmp_path, cp, len);
|
||
|
char* filename = tmp_path + len;
|
||
|
*filename = 0;
|
||
|
struct _finddata_t c_file;
|
||
|
long hFile;
|
||
|
if ((hFile = _findfirst(cp, &c_file)) != -1L) {
|
||
|
do {
|
||
|
strcpy(filename, c_file.name);
|
||
|
strcat(args, tmp_path);
|
||
|
strcat(args, ";");
|
||
|
debug(" \" :\t%s\n", tmp_path);
|
||
|
} while (_findnext(hFile, &c_file) == 0);
|
||
|
}
|
||
|
_findclose(hFile);
|
||
|
} else {
|
||
|
strcat(args, cp);
|
||
|
strcat(args, ";");
|
||
|
}
|
||
|
cp = strtok(NULL, ";");
|
||
|
}
|
||
|
*(args + strlen(args) - 1) = 0;
|
||
|
|
||
|
strcat(args, "\" ");
|
||
|
strcat(args, mainClass);
|
||
|
} else if (wrapper) {
|
||
|
strcat(args, "-jar \"");
|
||
|
strcat(args, exePath);
|
||
|
strcat(args, "\"");
|
||
|
} else {
|
||
|
strcat(args, "-jar \"");
|
||
|
strncat(args, exePath, pathLen);
|
||
|
appendPath(args, jar);
|
||
|
strcat(args, "\"");
|
||
|
}
|
||
|
|
||
|
// Constant command line args
|
||
|
if (loadString(CMD_LINE, tmp)) {
|
||
|
strcat(args, " ");
|
||
|
strcat(args, tmp);
|
||
|
}
|
||
|
|
||
|
// Command line args
|
||
|
if (*lpCmdLine) {
|
||
|
strcpy(tmp, lpCmdLine);
|
||
|
char* dst;
|
||
|
while ((dst = strstr(tmp, "--l4j-")) != NULL) {
|
||
|
char* src = strchr(dst, ' ');
|
||
|
if (src == NULL || *(src + 1) == 0) {
|
||
|
*dst = 0;
|
||
|
} else {
|
||
|
strcpy(dst, src + 1);
|
||
|
}
|
||
|
}
|
||
|
if (*tmp) {
|
||
|
strcat(args, " ");
|
||
|
strcat(args, tmp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
debug("Launcher:\t%s\n", cmd);
|
||
|
debug("Launcher args:\t%s\n", args);
|
||
|
debug("Args length:\t%d/32768 chars\n", strlen(args));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void closeHandles() {
|
||
|
CloseHandle(pi.hThread);
|
||
|
CloseHandle(pi.hProcess);
|
||
|
closeLogFile();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Append a path to the Path environment variable
|
||
|
*/
|
||
|
BOOL appendToPathVar(const char* path) {
|
||
|
char chBuf[MAX_VAR_SIZE] = {0};
|
||
|
|
||
|
const int pathSize = GetEnvironmentVariable("Path", chBuf, MAX_VAR_SIZE);
|
||
|
if (MAX_VAR_SIZE - pathSize - 1 < strlen(path)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
strcat(chBuf, ";");
|
||
|
strcat(chBuf, path);
|
||
|
return SetEnvironmentVariable("Path", chBuf);
|
||
|
}
|
||
|
|
||
|
DWORD execute(const BOOL wait) {
|
||
|
STARTUPINFO si;
|
||
|
memset(&pi, 0, sizeof(pi));
|
||
|
memset(&si, 0, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
|
||
|
DWORD dwExitCode = -1;
|
||
|
char cmdline[MAX_ARGS];
|
||
|
strcpy(cmdline, "\"");
|
||
|
strcat(cmdline, cmd);
|
||
|
strcat(cmdline, "\" ");
|
||
|
strcat(cmdline, args);
|
||
|
if (CreateProcess(NULL, cmdline, NULL, NULL,
|
||
|
TRUE, priority, NULL, NULL, &si, &pi)) {
|
||
|
if (wait) {
|
||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||
|
GetExitCodeProcess(pi.hProcess, &dwExitCode);
|
||
|
debug("Exit code:\t%d\n", dwExitCode);
|
||
|
closeHandles();
|
||
|
} else {
|
||
|
dwExitCode = 0;
|
||
|
}
|
||
|
}
|
||
|
return dwExitCode;
|
||
|
}
|