/* * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include <io.h> #include <fcntl.h> #include <stdlib.h> #include <windows.h> #include "AppLauncher.h" #include "JvmLauncher.h" #include "Log.h" #include "Dll.h" #include "WinApp.h" #include "Toolbox.h" #include "Executor.h" #include "FileUtils.h" #include "UniqueHandle.h" #include "ErrorHandling.h" #include "WinSysInfo.h" #include "WinErrorHandling.h" // AllowSetForegroundWindow #pragma comment(lib, "user32") namespace { std::unique_ptr<Dll> loadDllWithAlteredPATH(const tstring& dllFullPath) { LOG_TRACE_FUNCTION(); const tstring vanillaPathEnvVariable = SysInfo::getEnvVariable(_T("PATH")); tstring pathEnvVariable = vanillaPathEnvVariable + _T(";") + FileUtils::dirname(dllFullPath); SysInfo::setEnvVariable(_T("PATH"), pathEnvVariable); LOG_TRACE(tstrings::any() << "New value of PATH: " << pathEnvVariable); // Schedule restore of PATH after attempt to load the given dll const auto resetPATH = runAtEndOfScope([&vanillaPathEnvVariable]() -> void { SysInfo::setEnvVariable(_T("PATH"), vanillaPathEnvVariable); }); return std::unique_ptr<Dll>(new Dll(dllFullPath)); } std::unique_ptr<Dll> loadDllWithAddDllDirectory(const tstring& dllFullPath) { LOG_TRACE_FUNCTION(); const tstring dirPath = FileUtils::dirname(dllFullPath); typedef DLL_DIRECTORY_COOKIE(WINAPI *AddDllDirectoryFunc)(PCWSTR); DllFunction<AddDllDirectoryFunc> _AddDllDirectory( Dll("kernel32.dll", Dll::System()), "AddDllDirectory"); AddDllDirectoryFunc func = _AddDllDirectory; DLL_DIRECTORY_COOKIE res = func(dirPath.c_str()); if (!res) { JP_THROW(SysError(tstrings::any() << "AddDllDirectory(" << dirPath << ") failed", func)); } LOG_TRACE(tstrings::any() << "AddDllDirectory(" << dirPath << "): OK"); // Important: use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS flag, // but not LOAD_LIBRARY_SEARCH_USER_DIRS! HMODULE dllHandle = LoadLibraryEx(dllFullPath.c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); LOG_TRACE(tstrings::any() << "LoadLibraryEx(" << dllFullPath << ", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS): " << dllHandle); const auto freeDll = runAtEndOfScope([&dllHandle]() -> void { Dll::freeLibrary(dllHandle); }); return std::unique_ptr<Dll>(new Dll(dllFullPath)); } class DllWrapper { public: DllWrapper(const tstring& dllName) { try { // Adjust the DLL search paths with AddDllDirectory() WINAPI CALL dll = loadDllWithAddDllDirectory(dllName); } catch (const std::exception&) { // Alter PATH environment variable as the last resort. dll = loadDllWithAlteredPATH(dllName); } } private: DllWrapper(const DllWrapper&); DllWrapper& operator=(const DllWrapper&); private: std::unique_ptr<Dll> dll; }; tstring getJvmLibPath(const Jvm& jvm) { FileUtils::mkpath path; path << FileUtils::dirname(jvm.getPath()) << _T("server") << _T("jvm.dll"); return path; } class RunExecutorWithMsgLoop { public: static DWORD apply(const Executor& exec) { RunExecutorWithMsgLoop instance(exec); UniqueHandle threadHandle = UniqueHandle(CreateThread(NULL, 0, worker, static_cast<LPVOID>(&instance), 0, NULL)); if (threadHandle.get() == NULL) { JP_THROW(SysError("CreateThread() failed", CreateThread)); } MSG msg; BOOL bRet; while((bRet = GetMessage(&msg, instance.hwnd, 0, 0 )) != 0) { if (bRet == -1) { JP_THROW(SysError("GetMessage() failed", GetMessage)); } else { TranslateMessage(&msg); DispatchMessage(&msg); } } // Wait for worker thread to terminate to guarantee it will not linger // around after the thread running a message loop terminates. const DWORD res = ::WaitForSingleObject(threadHandle.get(), INFINITE); if (WAIT_FAILED == res) { JP_THROW(SysError("WaitForSingleObject() failed", WaitForSingleObject)); } LOG_TRACE(tstrings::any() << "Executor worker thread terminated. Exit code=" << instance.exitCode); return instance.exitCode; } private: RunExecutorWithMsgLoop(const Executor& v): exec(v) { exitCode = 1; // Message-only window. hwnd = CreateWindowEx(0, _T("STATIC"), _T(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL); if (!hwnd) { JP_THROW(SysError("CreateWindowEx() failed", CreateWindowEx)); } } static DWORD WINAPI worker(LPVOID param) { static_cast<RunExecutorWithMsgLoop*>(param)->run(); return 0; } void run() { JP_TRY; exitCode = static_cast<DWORD>(exec.execAndWaitForExit()); JP_CATCH_ALL; JP_TRY; if (!PostMessage(hwnd, WM_QUIT, 0, 0)) { JP_THROW(SysError("PostMessage(WM_QUIT) failed", PostMessage)); } return; JP_CATCH_ALL; // All went wrong, PostMessage() failed. Just terminate with error code. exit(1); } private: const Executor& exec; DWORD exitCode; HWND hwnd; }; void launchApp() { // [RT-31061] otherwise UI can be left in back of other windows. ::AllowSetForegroundWindow(ASFW_ANY); const tstring launcherPath = SysInfo::getProcessModulePath(); const tstring appImageRoot = FileUtils::dirname(launcherPath); const tstring appDirPath = FileUtils::mkpath() << appImageRoot << _T("app"); const AppLauncher appLauncher = AppLauncher().setImageRoot(appImageRoot) .addJvmLibName(_T("bin\\jli.dll")) .setAppDir(appDirPath) .setLibEnvVariableName(_T("PATH")) .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot << _T("runtime")); const bool restart = !appLauncher.libEnvVariableContainsAppDir(); std::unique_ptr<Jvm> jvm(appLauncher.createJvmLauncher()); if (restart) { jvm->setEnvVariables(); jvm = std::unique_ptr<Jvm>(); UniqueHandle jobHandle(CreateJobObject(NULL, NULL)); if (jobHandle.get() == NULL) { JP_THROW(SysError(tstrings::any() << "CreateJobObject() failed", CreateJobObject)); } JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { }; jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; if (!SetInformationJobObject(jobHandle.get(), JobObjectExtendedLimitInformation, &jobInfo, sizeof(jobInfo))) { JP_THROW(SysError(tstrings::any() << "SetInformationJobObject() failed", SetInformationJobObject)); } Executor exec(launcherPath); exec.visible(true).withJobObject(jobHandle.get()).suspended(true).inherit(true); const auto args = SysInfo::getCommandArgs(); std::for_each(args.begin(), args.end(), [&exec] (const tstring& arg) { exec.arg(arg); }); DWORD exitCode = RunExecutorWithMsgLoop::apply(exec); exit(exitCode); return; } // zip.dll (and others) may be loaded by java without full path // make sure it will look in runtime/bin const tstring runtimeBinPath = FileUtils::dirname(jvm->getPath()); SetDllDirectory(runtimeBinPath.c_str()); LOG_TRACE(tstrings::any() << "SetDllDirectory to: " << runtimeBinPath); const DllWrapper jliDll(jvm->getPath()); std::unique_ptr<DllWrapper> splashDll; if (jvm->isWithSplash()) { const DllWrapper jvmDll(getJvmLibPath(*jvm)); splashDll = std::unique_ptr<DllWrapper>(new DllWrapper( FileUtils::mkpath() << FileUtils::dirname(jvm->getPath()) << _T("splashscreen.dll"))); } jvm->launch(); } } // namespace #ifndef JP_LAUNCHERW int __cdecl wmain() { return app::launch(std::nothrow, launchApp); } #else int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { return app::wlaunch(std::nothrow, launchApp); } #endif