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