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 104 class DllWrapper { 105 public: 106 DllWrapper(const tstring& dllName) { 107 try { 108 // Try load DLL. 109 dll = std::unique_ptr<Dll>(new Dll(dllName)); 110 LOG_TRACE(tstrings::any() << "Load [" << dllName << "]: OK"); 111 } 112 catch (const std::exception&) { 113 // JVM DLL load failed, though it exists in file system. 114 try { 115 // Try adjust the DLL search paths with AddDllDirectory() WINAPI CALL 116 dll = loadDllWithAddDllDirectory(dllName); 117 } 118 catch (const std::exception&) { 119 // AddDllDirectory() didn't work. Try altering PATH environment 120 // variable as the last resort. 121 dll = loadDllWithAlteredPATH(dllName); 122 } 123 } 124 } 125 126 private: 127 DllWrapper(const DllWrapper&); 128 DllWrapper& operator=(const DllWrapper&); 129 130 private: 131 std::unique_ptr<Dll> dll; 132 }; 133 134 135 tstring getJvmLibPath(const Jvm& jvm) { 136 FileUtils::mkpath path; 137 138 path << FileUtils::dirname(jvm.getPath()) << _T("server") << _T("jvm.dll"); 139 140 return path; 141 } 142 143 144 void launchApp() { 145 // [RT-31061] otherwise UI can be left in back of other windows. 146 ::AllowSetForegroundWindow(ASFW_ANY); 147 148 const tstring launcherPath = SysInfo::getProcessModulePath(); 149 const tstring appImageRoot = FileUtils::dirname(launcherPath); 150 151 std::unique_ptr<Jvm> jvm(AppLauncher() 152 .setImageRoot(appImageRoot) 153 .addJvmLibName(_T("bin\\jli.dll")) 154 .setAppDir(FileUtils::mkpath() << appImageRoot << _T("app")) 155 .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot 156 << _T("runtime")) 157 .createJvmLauncher()); 158 159 const DllWrapper jliDll(jvm->getPath()); 160 std::unique_ptr<DllWrapper> splashDll; 161 if (jvm->isWithSplash()) { 162 const DllWrapper jvmDll(getJvmLibPath(*jvm)); 163 splashDll = std::unique_ptr<DllWrapper>(new DllWrapper( 164 FileUtils::mkpath() 165 << FileUtils::dirname(jvm->getPath()) 166 << _T("splashscreen.dll"))); 167 } 168 169 jvm->launch(); 170 } 171 172 } // namespace 173 174 #ifndef JP_LAUNCHERW 175 176 int __cdecl wmain() { 177 return AppLauncher::launch(std::nothrow, launchApp); 178 } 179 180 #else 181 182 namespace { 183 184 class LastErrorGuiLogAppender : public LogAppender { 185 public: 186 virtual void append(const LogEvent& v) { 187 JP_TRY; 188 189 const std::wstring msg = (tstrings::any() 190 << AppLauncher::lastErrorMsg()).wstr(); 191 MessageBox(0, msg.c_str(), 192 FileUtils::basename(SysInfo::getProcessModulePath()).c_str(), 193 MB_ICONERROR | MB_OK); 194 195 JP_CATCH_ALL; 196 } 197 }; 198 199 200 class Console { 201 public: 202 Console() { 203 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { 204 // Failed to connect to parent's console. Create our own. 205 if (!AllocConsole()) { 206 // We already have a console, no need to redirect std I/O. 207 return; 208 } 209 } 210 211 stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout)); 212 stderrChannel = std::unique_ptr<Channel>(new Channel(stderr)); 213 } 214 215 struct FileCloser { 216 typedef FILE* pointer; 217 218 void operator()(pointer h) { 219 ::fclose(h); 220 } 221 }; 222 223 typedef std::unique_ptr< 224 FileCloser::pointer, 225 FileCloser 226 > UniqueFILEHandle; 227 228 private: 229 class Channel { 230 public: 231 Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) { 232 const char* stdFileName = "CONOUT$"; 233 const char* openMode = "w"; 234 if (stdFILEHandle == stdin) { 235 stdFileName = "CONIN$"; 236 openMode = "r"; 237 } 238 239 FILE* fp = 0; 240 freopen_s(&fp, stdFileName, openMode, stdFILEHandle); 241 242 fileHandle = UniqueFILEHandle(fp); 243 244 std::ios_base::sync_with_stdio(); 245 } 246 247 virtual ~Channel() { 248 JP_TRY; 249 250 FILE* fp = 0; 251 fileHandle = UniqueFILEHandle(fp); 252 std::ios_base::sync_with_stdio(); 253 254 JP_CATCH_ALL; 255 } 256 257 private: 258 UniqueFILEHandle fileHandle; 259 FILE *stdFILEHandle; 260 }; 261 262 std::unique_ptr<Channel> stdoutChannel; 263 std::unique_ptr<Channel> stderrChannel; 264 }; 265 266 267 void launchAppW() { 268 std::unique_ptr<Console> console; 269 if (AppLauncher::isWithLogging()) { 270 console = std::unique_ptr<Console>(new Console()); 271 } 272 273 launchApp(); 274 } 275 276 } // namespace 277 278 279 int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { 280 LastErrorGuiLogAppender lastErrorLogAppender; 281 TeeLogAppender logAppender(&AppLauncher::defaultLastErrorLogAppender(), 282 &lastErrorLogAppender); 283 return AppLauncher::launch(std::nothrow, launchAppW, &logAppender); 284 } 285 286 #endif