diff --git a/HMCLauncher/HMCL/HMCL.cpp b/HMCLauncher/HMCL/HMCL.cpp deleted file mode 100644 index 94c42fbbb..000000000 Binary files a/HMCLauncher/HMCL/HMCL.cpp and /dev/null differ diff --git a/HMCLauncher/HMCL/HMCL.h b/HMCLauncher/HMCL/HMCL.h deleted file mode 100644 index c4aafa38e..000000000 Binary files a/HMCLauncher/HMCL/HMCL.h and /dev/null differ diff --git a/HMCLauncher/HMCL/HMCL.vcxproj b/HMCLauncher/HMCL/HMCL.vcxproj index 9b6e9113d..03e8ac938 100644 --- a/HMCLauncher/HMCL/HMCL.vcxproj +++ b/HMCLauncher/HMCL/HMCL.vcxproj @@ -23,7 +23,7 @@ {672B1019-E741-4C0D-A986-627E2ACE157B} Win32Proj HMCL - 8.1 + 7.0 @@ -152,19 +152,25 @@ - + + + + - + + + Create Create Create Create + diff --git a/HMCLauncher/HMCL/HMCL.vcxproj.filters b/HMCLauncher/HMCL/HMCL.vcxproj.filters index 04ebb26dd..e4fb0ec74 100644 --- a/HMCLauncher/HMCL/HMCL.vcxproj.filters +++ b/HMCLauncher/HMCL/HMCL.vcxproj.filters @@ -24,7 +24,16 @@ 头文件 - + + 头文件 + + + 头文件 + + + 头文件 + + 头文件 @@ -32,7 +41,16 @@ 源文件 - + + 源文件 + + + 源文件 + + + 源文件 + + 源文件 diff --git a/HMCLauncher/HMCL/Version.cpp b/HMCLauncher/HMCL/Version.cpp new file mode 100644 index 000000000..a8b0279a6 --- /dev/null +++ b/HMCLauncher/HMCL/Version.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "Version.h" + +Version::Version(const std::wstring & rawString) +{ + int idx = 0; + ver[0] = ver[1] = ver[2] = ver[3] = 0; + for (auto &i : rawString) + { + if (idx >= 4) break; + if (i == '.') ++idx; + else if (i == '_') ++idx; + else if (isdigit(i)) ver[idx] = ver[idx] * 10 + (i - L'0'); + } +} diff --git a/HMCLauncher/HMCL/Version.h b/HMCLauncher/HMCL/Version.h new file mode 100644 index 000000000..2e0784d16 --- /dev/null +++ b/HMCLauncher/HMCL/Version.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +class Version +{ +public: + int ver[4]; + + Version(const std::wstring &rawString); + + bool operator<(const Version &other) const + { + for (int i = 0; i < 4; ++i) + if (ver[i] != other.ver[i]) + return ver[i] < other.ver[i]; + return false; + } +}; + diff --git a/HMCLauncher/HMCL/java.cpp b/HMCLauncher/HMCL/java.cpp new file mode 100644 index 000000000..93e806e66 --- /dev/null +++ b/HMCLauncher/HMCL/java.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "java.h" +#include "os.h" + +const Version JAVA_8(L"1.8"), JAVA_11(L"11"); + +const LPCWSTR JDK_OLD = L"SOFTWARE\\JavaSoft\\Java Development Kit"; +const LPCWSTR JRE_OLD = L"SOFTWARE\\JavaSoft\\Java Runtime Environment"; +const LPCWSTR JDK_NEW = L"SOFTWARE\\JavaSoft\\JDK"; +const LPCWSTR JRE_NEW = L"SOFTWARE\\JavaSoft\\JRE"; + +bool oldJavaFound = false, newJavaFound = false; + +bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring & path) +{ + WCHAR javaVer[MAX_KEY_LENGTH]; // buffer for subkey name, special for JavaVersion + DWORD cbName; // size of name string + DWORD cSubKeys = 0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + LSTATUS result; + + HKEY hKey; + if (ERROR_SUCCESS != (result = RegOpenKeyEx(rootKey, subKey, 0, KEY_WOW64_64KEY | KEY_READ, &hKey))) + return false; + + RegQueryInfoKey( + hKey, // key handle + NULL, // buffer for class name + NULL, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + NULL, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + NULL, // security descriptor + NULL); // last write time + + if (!cSubKeys) + return false; + + bool flag = false; + for (DWORD i = 0; i < cSubKeys; ++i) + { + cbName = MAX_KEY_LENGTH; + if (ERROR_SUCCESS != (result = RegEnumKeyEx(hKey, i, javaVer, &cbName, NULL, NULL, NULL, NULL))) + continue; + + HKEY javaKey; + if (ERROR_SUCCESS != RegOpenKeyEx(hKey, javaVer, 0, KEY_READ, &javaKey)) + continue; + + if (ERROR_SUCCESS == MyRegQueryValue(javaKey, L"JavaHome", REG_SZ, path)) + { + if (Version(javaVer) < JAVA_8) + oldJavaFound = true; + else if (!(Version(javaVer) < JAVA_11)) + newJavaFound = true; + else + flag = true; + } + + if (flag) + break; + } + + RegCloseKey(hKey); + + return flag; +} + +bool FindJavaInRegistry(std::wstring & path) +{ + return FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_OLD, path) || + FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_OLD, path) || + FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_NEW, path) || + FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_NEW, path); +} + +bool FindJava(std::wstring & path) +{ + return FindJavaInRegistry(path) || + ERROR_SUCCESS == MyGetEnvironmentVariable(L"JAVA_HOME", path); +} diff --git a/HMCLauncher/HMCL/java.h b/HMCLauncher/HMCL/java.h new file mode 100644 index 000000000..86e96ecd3 --- /dev/null +++ b/HMCLauncher/HMCL/java.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include "Version.h" + +// Find Java installation in system registry +bool FindJavaInRegistry(std::wstring &path); + +// Find Java Installation in registry and environment variable +bool FindJava(std::wstring &path); diff --git a/HMCLauncher/HMCL/main.cpp b/HMCLauncher/HMCL/main.cpp new file mode 100644 index 000000000..61fd58e7a --- /dev/null +++ b/HMCLauncher/HMCL/main.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "main.h" +#include "os.h" +#include "java.h" + +using namespace std; + + +void LaunchJVM(const wstring &javaPath, const wstring &jarPath) +{ + if (MyCreateProcess(L"\"" + javaPath + L"\" -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=15 -jar \"" + jarPath + L"\"")) + exit(EXIT_SUCCESS); +} + +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) +{ + wstring path, exeName; + + // Since Jar file is appended to this executable, we should first get the location of JAR file. + if (ERROR_SUCCESS != MyGetModuleFileName(NULL, exeName)) + return 1; + + + // First try the Java packaged together. + bool is64Bit = false; + GetArch(is64Bit); // if failed to determine architecture of operating system, consider 32-bit. + + if (is64Bit) + { + LaunchJVM(L"jre-x64\\bin\\javaw.exe", exeName); + } + else + { + LaunchJVM(L"jre-x86\\bin\\javaw.exe", exeName); + } + + if (FindJava(path)) + LaunchJVM(path + L"\\bin\\javaw.exe", exeName); + + // Try java in PATH + LaunchJVM(L"javaw", exeName); + + // Or we try to search Java in C:\Program Files. + { + WIN32_FIND_DATA data; + HANDLE hFind = FindFirstFile(L"C:\\Program Files\\Java\\*", &data); // Search all subdirectory + + if (hFind != INVALID_HANDLE_VALUE) { + do { + wstring javaw = wstring(L"C:\\Program Files\\Java\\") + data.cFileName + wstring(L"\\bin\\javaw.exe"); + if (FindFirstFileExists(javaw.c_str(), 0)) { + LaunchJVM(javaw, exeName); + } + } while (FindNextFile(hFind, &data)); + FindClose(hFind); + } + } + + // Consider C:\Program Files (x86)\Java + { + WIN32_FIND_DATA data; + HANDLE hFind = FindFirstFile(L"C:\\Program Files (x86)\\Java\\*", &data); // Search all subdirectory + + if (hFind != INVALID_HANDLE_VALUE) { + do { + wstring javaw = wstring(L"C:\\Program Files (x86)\\Java\\") + data.cFileName + L"\\bin\\javaw.exe"; + if (FindFirstFileExists(javaw.c_str(), 0)) { + LaunchJVM(javaw, exeName); + } + } while (FindNextFile(hFind, &data)); + FindClose(hFind); + } + } + + MessageBox(NULL, L"Java installation cannot be found in this computer, please download it from https://java.com \n" + L"未能在这台电脑上找到Java 8~Java 10,请从 https://java.com 下载安装Java", L"Error", MB_ICONERROR | MB_OK); + ShellExecute(0, 0, L"https://java.com/", 0, 0, SW_SHOW); + return 1; +} diff --git a/HMCLauncher/HMCL/main.h b/HMCLauncher/HMCL/main.h new file mode 100644 index 000000000..e60f2eb7e --- /dev/null +++ b/HMCLauncher/HMCL/main.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/HMCLauncher/HMCL/os.cpp b/HMCLauncher/HMCL/os.cpp new file mode 100644 index 000000000..820e53819 --- /dev/null +++ b/HMCLauncher/HMCL/os.cpp @@ -0,0 +1,108 @@ +#include "stdafx.h" +#include "os.h" + +using namespace std; + +LSTATUS MyRegQueryValue(HKEY hKey, LPCWSTR subKey, DWORD dwType, wstring &out) +{ + DWORD dwSize = 0; + LSTATUS ret = RegQueryValueEx(hKey, subKey, 0, &dwType, NULL, &dwSize); + if (ret != ERROR_SUCCESS) return ret; + WCHAR *buffer = new WCHAR[dwSize]; + ret = RegQueryValueEx(hKey, subKey, 0, &dwType, (LPBYTE)buffer, &dwSize); + if (ret != ERROR_SUCCESS) return ret; + out = buffer; + delete[] buffer; + return ERROR_SUCCESS; +} + +LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out) +{ + DWORD res, size = MAX_PATH; + out = wstring(); + out.resize(size); + while ((res = GetModuleFileName(hModule, &out[0], size)) == size) + { + out.resize(size += MAX_PATH); + } + if (res == 0) + return GetLastError(); + else + { + out.resize(size - MAX_PATH + res); + return ERROR_SUCCESS; + } +} + +LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring & out) +{ + DWORD res, size = MAX_PATH; + out = wstring(); + out.resize(size); + while ((res = GetEnvironmentVariable(name, &out[0], size)) == size) + { + out.resize(size += MAX_PATH); + } + if (res == 0) + return GetLastError(); + else + { + out.resize(size - MAX_PATH + res); + return ERROR_SUCCESS; + } +} + +bool MyCreateProcess(const std::wstring &command) +{ + wstring writable_command = command; + STARTUPINFO si; + PROCESS_INFORMATION pi; + si.cb = sizeof(si); + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + + return (CreateProcess(NULL, &writable_command[0], NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)); +} + +bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter) +{ + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFile(lpPath, &fd); + bool bFilter = (false == dwFilter) ? true : fd.dwFileAttributes & dwFilter; + bool ret = ((hFind != INVALID_HANDLE_VALUE) && bFilter) ? true : false; + FindClose(hFind); + return ret; +} + +bool GetArch(bool & is64Bit) +{ +#if _WIN64 + isWindows64bit = true; + return true; +#elif _WIN32 + typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + + BOOL isWow64 = FALSE; + + // IsWow64Process is not available on all supported versions of Windows. + // Use GetModuleHandle to get a handle to the DLL that contains the function + // and GetProcAddress to get a pointer to the function if available. + + LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) + GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(), &isWow64)) + return false; + + is64Bit = isWow64; + return true; + } + else // IsWow64Process is not supported, fail to detect. + return false; + +#else +#error _WIN64 and _WIN32 are both undefined. +#endif +} diff --git a/HMCLauncher/HMCL/os.h b/HMCLauncher/HMCL/os.h new file mode 100644 index 000000000..406e661b4 --- /dev/null +++ b/HMCLauncher/HMCL/os.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +const int MAX_KEY_LENGTH = 255; +const int MAX_VALUE_NAME = 16383; + +// Query registry value of class root hKey, key path subKey, stores result in parameter out. +LSTATUS MyRegQueryValue(HKEY hKey, LPCWSTR subKey, DWORD dwType, std::wstring &out); + +// Get module file name, stores result in parameter out. +LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out); + +// Get environment variable by name, C++ style, stores the value in parameter out. +LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring &out); + +// Create process by invoking CreateProcess, only pass command. +bool MyCreateProcess(const std::wstring &command); + +// Check if file lpPath exists. +bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter); + +bool GetArch(bool &is64Bit); \ No newline at end of file