1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <io.h> 27 #include <fcntl.h> 28 #include <windows.h> 29 30 #include "AppLauncher.h" 31 #include "JvmLauncher.h" 32 #include "Log.h" 33 #include "Dll.h" 34 #include "Toolbox.h" 35 #include "FileUtils.h" 36 #include "UniqueHandle.h" 37 #include "ErrorHandling.h" 38 #include "WinSysInfo.h" 39 #include "WinErrorHandling.h" 40 41 42 // AllowSetForegroundWindow 43 #pragma comment(lib, "user32") 44 45 46 namespace { 47 48 std::unique_ptr<Dll> loadDllWithAlteredPATH(const tstring& dllFullPath) { 49 LOG_TRACE_FUNCTION(); 50 51 const tstring vanillaPathEnvVariable = SysInfo::getEnvVariable(_T("PATH")); 52 53 tstring pathEnvVariable = vanillaPathEnvVariable 54 + _T(";") 55 + FileUtils::dirname(dllFullPath); 56 57 SysInfo::setEnvVariable(_T("PATH"), pathEnvVariable); 58 59 LOG_TRACE(tstrings::any() << "New value of PATH: " << pathEnvVariable); 60 61 // Schedule restore of PATH after attempt to load the given dll 62 const auto resetPATH = runAtEndOfScope([&vanillaPathEnvVariable]() -> void { 63 SysInfo::setEnvVariable(_T("PATH"), vanillaPathEnvVariable); 64 }); 65 66 return std::unique_ptr<Dll>(new Dll(dllFullPath)); 67 } 68 69 std::unique_ptr<Dll> loadDllWithAddDllDirectory(const tstring& dllFullPath) { 70 LOG_TRACE_FUNCTION(); 71 72 const tstring dirPath = FileUtils::dirname(dllFullPath); 73 74 typedef DLL_DIRECTORY_COOKIE(WINAPI *AddDllDirectoryFunc)(PCWSTR); 75 76 DllFunction<AddDllDirectoryFunc> _AddDllDirectory( 77 Dll("kernel32.dll", Dll::System()), "AddDllDirectory"); 78 79 AddDllDirectoryFunc func = _AddDllDirectory; 80 DLL_DIRECTORY_COOKIE res = func(dirPath.c_str()); 81 if (!res) { 82 JP_THROW(SysError(tstrings::any() 83 << "AddDllDirectory(" << dirPath << ") failed", func)); 84 } 85 86 LOG_TRACE(tstrings::any() << "AddDllDirectory(" << dirPath << "): OK"); 87 88 // Important: use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS flag, 89 // but not LOAD_LIBRARY_SEARCH_USER_DIRS! 90 HMODULE dllHandle = LoadLibraryEx(dllFullPath.c_str(), NULL, 91 LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); 92 93 LOG_TRACE(tstrings::any() << "LoadLibraryEx(" << dllFullPath 94 << ", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS): " << dllHandle); 95 96 const auto freeDll = runAtEndOfScope([&dllHandle]() -> void { 97 Dll::freeLibrary(dllHandle); 98 }); 99 100 return std::unique_ptr<Dll>(new Dll(dllFullPath)); 101 } 102 103 void launchApp() { 104 // [RT-31061] otherwise UI can be left in back of other windows. 105 ::AllowSetForegroundWindow(ASFW_ANY); 106 107 const tstring launcherPath = SysInfo::getProcessModulePath(); 108 const tstring appImageRoot = FileUtils::dirname(launcherPath); 109 110 std::unique_ptr<Jvm> jvm(AppLauncher() 111 .setImageRoot(appImageRoot) 112 .addJvmLibName(_T("bin\\jli.dll")) 113 .setAppDir(FileUtils::mkpath() << appImageRoot << _T("app")) 114 .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot 115 << _T("runtime")) 116 .createJvmLauncher()); 117 118 std::unique_ptr<Dll> jvmDll; 119 try { 120 // Try load JVM DLL. 121 jvmDll = std::unique_ptr<Dll>(new Dll(jvm->getPath())); 122 } catch (const std::exception&) { 123 // JVM DLL load failed, though it exists in file system. 124 try { 125 // Try adjust the DLL search paths with AddDllDirectory() WINAPI CALL 126 jvmDll = loadDllWithAddDllDirectory(jvm->getPath()); 127 } catch (const std::exception&) { 128 // AddDllDirectory() didn't work. Try altering PATH environment 129 // variable as the last resort. 130 jvmDll = loadDllWithAlteredPATH(jvm->getPath()); 131 } 132 } 133 134 jvm->launch(); 135 } 136 137 } // namespace 138 139 #ifndef JP_LAUNCHERW 140 141 int __cdecl wmain() { 142 return AppLauncher::launch(std::nothrow, launchApp); 143 } 144 145 #else 146 147 namespace { 148 149 class LastErrorGuiLogAppender : public LogAppender { 150 public: 151 virtual void append(const LogEvent& v) { 152 JP_TRY; 153 154 const std::wstring msg = (tstrings::any() 155 << AppLauncher::lastErrorMsg()).wstr(); 156 MessageBox(0, msg.c_str(), 157 FileUtils::basename(SysInfo::getProcessModulePath()).c_str(), 158 MB_ICONERROR | MB_OK); 159 160 JP_CATCH_ALL; 161 } 162 }; 163 164 165 class Console { 166 public: 167 Console() { 168 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { 169 // Failed to connect to parent's console. Create our own. 170 if (!AllocConsole()) { 171 // We already have a console, no need to redirect std I/O. 172 return; 173 } 174 } 175 176 stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout)); 177 stderrChannel = std::unique_ptr<Channel>(new Channel(stderr)); 178 } 179 180 struct FileCloser { 181 typedef FILE* pointer; 182 183 void operator()(pointer h) { 184 ::fclose(h); 185 } 186 }; 187 188 typedef std::unique_ptr< 189 FileCloser::pointer, 190 FileCloser 191 > UniqueFILEHandle; 192 193 private: 194 class Channel { 195 public: 196 Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) { 197 const char* stdFileName = "CONOUT$"; 198 const char* openMode = "w"; 199 if (stdFILEHandle == stdin) { 200 stdFileName = "CONIN$"; 201 openMode = "r"; 202 } 203 204 FILE* fp = 0; 205 freopen_s(&fp, stdFileName, openMode, stdFILEHandle); 206 207 fileHandle = UniqueFILEHandle(fp); 208 209 std::ios_base::sync_with_stdio(); 210 } 211 212 virtual ~Channel() { 213 JP_TRY; 214 215 FILE* fp = 0; 216 fileHandle = UniqueFILEHandle(fp); 217 std::ios_base::sync_with_stdio(); 218 219 JP_CATCH_ALL; 220 } 221 222 private: 223 UniqueFILEHandle fileHandle; 224 FILE *stdFILEHandle; 225 }; 226 227 std::unique_ptr<Channel> stdoutChannel; 228 std::unique_ptr<Channel> stderrChannel; 229 }; 230 231 232 void launchAppW() { 233 std::unique_ptr<Console> console; 234 if (AppLauncher::isWithLogging()) { 235 console = std::unique_ptr<Console>(new Console()); 236 } 237 238 launchApp(); 239 } 240 241 } // namespace 242 243 244 int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { 245 LastErrorGuiLogAppender lastErrorLogAppender; 246 TeeLogAppender logAppender(&AppLauncher::defaultLastErrorLogAppender(), 247 &lastErrorLogAppender); 248 return AppLauncher::launch(std::nothrow, launchAppW, &logAppender); 249 } 250 251 #endif