| Raphael Moll | 6b94c86 | 2014-09-05 15:54:04 -0700 | [diff] [blame^] | 1 | /*
|
| 2 | * Copyright (C) 2014 The Android Open Source Project
|
| 3 | *
|
| 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 | * you may not use this file except in compliance with the License.
|
| 6 | * You may obtain a copy of the License at
|
| 7 | *
|
| 8 | * http://www.apache.org/licenses/LICENSE-2.0
|
| 9 | *
|
| 10 | * Unless required by applicable law or agreed to in writing, software
|
| 11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 | * See the License for the specific language governing permissions and
|
| 14 | * limitations under the License.
|
| 15 | */
|
| 16 |
|
| 17 | #include "stdafx.h"
|
| 18 | #include "utils.h"
|
| 19 |
|
| 20 | // Set to true to get some extra debug information
|
| 21 | bool gIsDebug = false;
|
| 22 | // Set to true to output errors to stderr (for a Console app)
|
| 23 | // or to false to output using msg box (for a Windows UI app)
|
| 24 | bool gIsConsole = false;
|
| 25 |
|
| 26 | // Application name used in error dialog. Defined using initUtils()
|
| 27 | static CString gAppName("Find Java 2");
|
| 28 |
|
| 29 | // Called by the application to initialize the app name used in error dialog boxes.
|
| 30 | void initUtils(const TCHAR *appName) {
|
| 31 | if (appName != NULL) {
|
| 32 | gAppName = CString(appName);
|
| 33 | return;
|
| 34 | }
|
| 35 |
|
| 36 | // Try to get the VERSIONINFO.FileDescription and use as app name
|
| 37 | // Errors are ignored, in which case the default app name is used.
|
| 38 |
|
| 39 | // First get the module (aka app instance) filename.
|
| 40 | TCHAR moduleName[MAX_PATH + 1];
|
| 41 | DWORD sz = ::GetModuleFileName(NULL /*AfxGetInstanceHandle()*/, moduleName, MAX_PATH);
|
| 42 | if (sz == 0) {
|
| 43 | // GetModuleFileName failed. Do nothing.
|
| 44 | return;
|
| 45 | }
|
| 46 | moduleName[sz] = '\0'; // make sure string is properly terminated.
|
| 47 |
|
| 48 | // Get the size of the FileVersionInfo buffer
|
| 49 | DWORD obsoleteHandle; // see http://blogs.msdn.com/b/oldnewthing/archive/2007/07/31/4138786.aspx
|
| 50 | DWORD fviSize = ::GetFileVersionInfoSize(moduleName, &obsoleteHandle);
|
| 51 | if (fviSize == 0) {
|
| 52 | return; // do nothing on error
|
| 53 | }
|
| 54 |
|
| 55 | char *fviBuffer = new char[fviSize];
|
| 56 | if (::GetFileVersionInfo(moduleName, 0, fviSize, fviBuffer) != 0) {
|
| 57 | VOID *vBuffer;
|
| 58 | UINT vLen;
|
| 59 |
|
| 60 | struct LANGUAGE_CODEPAGE {
|
| 61 | WORD mLanguage;
|
| 62 | WORD mCodePage;
|
| 63 | } *lgcpBuffer;
|
| 64 |
|
| 65 | UINT lgcpSize;
|
| 66 |
|
| 67 | // Read the list of languages and code pages (c.f. MSDN for VerQueryValue)
|
| 68 | if (::VerQueryValue(fviBuffer, _T("\\VarFileInfo\\Translation"), (LPVOID*)&lgcpBuffer, &lgcpSize) != 0 &&
|
| 69 | lgcpSize >= sizeof(LANGUAGE_CODEPAGE)) {
|
| 70 | // Use the first available language and code page
|
| 71 | CString subBlock;
|
| 72 | subBlock.Format(_T("\\StringFileInfo\\%04x%04x\\FileDescription"),
|
| 73 | lgcpBuffer[0].mLanguage,
|
| 74 | lgcpBuffer[0].mCodePage);
|
| 75 | if (::VerQueryValue(fviBuffer, subBlock, &vBuffer, &vLen) != 0) {
|
| 76 | gAppName.SetString((LPCTSTR)vBuffer, vLen);
|
| 77 | }
|
| 78 | }
|
| 79 | }
|
| 80 | delete fviBuffer;
|
| 81 | }
|
| 82 |
|
| 83 | CString getAppName() {
|
| 84 | return gAppName;
|
| 85 | }
|
| 86 |
|
| 87 |
|
| 88 | // Displays a message in an ok+info dialog box.
|
| 89 | void msgBox(const TCHAR* text, ...) {
|
| 90 | CString formatted;
|
| 91 | va_list ap;
|
| 92 | va_start(ap, text);
|
| 93 | formatted.FormatV(text, ap);
|
| 94 | va_end(ap);
|
| 95 |
|
| 96 | // TODO global CString to get app name
|
| 97 | MessageBox(NULL, formatted, gAppName, MB_OK | MB_ICONINFORMATION);
|
| 98 | }
|
| 99 |
|
| 100 | // Sets the string to the message matching Win32 GetLastError.
|
| 101 | // If message is non-null, it is prepended to the last error string.
|
| 102 | CString getLastWin32Error(const TCHAR* message) {
|
| 103 | DWORD err = GetLastError();
|
| 104 | CString result;
|
| 105 | LPTSTR errStr;
|
| 106 | if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
|
| 107 | FORMAT_MESSAGE_FROM_SYSTEM,
|
| 108 | NULL, /* lpSource */
|
| 109 | err, /* dwMessageId */
|
| 110 | 0, /* dwLanguageId */
|
| 111 | (LPTSTR) &errStr, /* out lpBuffer */
|
| 112 | 0, /* nSize */
|
| 113 | NULL) != 0) { /* va_list args */
|
| 114 | if (message == NULL) {
|
| 115 | result.Format(_T("[%d] %s"), err, errStr);
|
| 116 | } else {
|
| 117 | result.Format(_T("%s[%d] %s"), message, err, errStr);
|
| 118 | }
|
| 119 | LocalFree(errStr);
|
| 120 | }
|
| 121 | return result;
|
| 122 | }
|
| 123 |
|
| 124 | // Displays GetLastError prefixed with a description in an error dialog box
|
| 125 | void displayLastError(const TCHAR *description, ...) {
|
| 126 | CString formatted;
|
| 127 | va_list ap;
|
| 128 | va_start(ap, description);
|
| 129 | formatted.FormatV(description, ap);
|
| 130 | va_end(ap);
|
| 131 |
|
| 132 | CString error = getLastWin32Error(NULL);
|
| 133 | formatted.Append(_T("\r\n"));
|
| 134 | formatted.Append(error);
|
| 135 |
|
| 136 | if (gIsConsole) {
|
| 137 | _ftprintf(stderr, _T("%s\n"), (LPCTSTR) formatted);
|
| 138 | } else {
|
| 139 | CString name(gAppName);
|
| 140 | name.Append(_T(" - Error"));
|
| 141 | MessageBox(NULL, formatted, name, MB_OK | MB_ICONERROR);
|
| 142 | }
|
| 143 | }
|
| 144 |
|
| 145 | // Executes the command line. Does not wait for the program to finish.
|
| 146 | // The return code is from CreateProcess (0 means failure), not the running app.
|
| 147 | int execNoWait(const TCHAR *app, const TCHAR *params, const TCHAR *workDir) {
|
| 148 | STARTUPINFO startup;
|
| 149 | PROCESS_INFORMATION pinfo;
|
| 150 |
|
| 151 | ZeroMemory(&pinfo, sizeof(pinfo));
|
| 152 |
|
| 153 | ZeroMemory(&startup, sizeof(startup));
|
| 154 | startup.cb = sizeof(startup);
|
| 155 | startup.dwFlags = STARTF_USESHOWWINDOW;
|
| 156 | startup.wShowWindow = SW_SHOWDEFAULT;
|
| 157 |
|
| 158 | int ret = CreateProcess(
|
| 159 | app, /* program path */
|
| 160 | (TCHAR *)params, /* command-line */
|
| 161 | NULL, /* process handle is not inheritable */
|
| 162 | NULL, /* thread handle is not inheritable */
|
| 163 | TRUE, /* yes, inherit some handles */
|
| 164 | 0, /* create flags */
|
| 165 | NULL, /* use parent's environment block */
|
| 166 | workDir, /* use parent's starting directory */
|
| 167 | &startup, /* startup info, i.e. std handles */
|
| 168 | &pinfo);
|
| 169 |
|
| 170 | if (ret) {
|
| 171 | CloseHandle(pinfo.hProcess);
|
| 172 | CloseHandle(pinfo.hThread);
|
| 173 | }
|
| 174 |
|
| 175 | return ret;
|
| 176 | }
|
| 177 |
|
| 178 | // Executes command, waits for completion and returns exit code.
|
| 179 | // As indicated in MSDN for CreateProcess, callers should double-quote the program name
|
| 180 | // e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
|
| 181 | int execWait(const TCHAR *cmd) {
|
| 182 | STARTUPINFO startup;
|
| 183 | PROCESS_INFORMATION pinfo;
|
| 184 |
|
| 185 | ZeroMemory(&pinfo, sizeof(pinfo));
|
| 186 |
|
| 187 | ZeroMemory(&startup, sizeof(startup));
|
| 188 | startup.cb = sizeof(startup);
|
| 189 | startup.dwFlags = STARTF_USESHOWWINDOW;
|
| 190 | startup.wShowWindow = SW_HIDE | SW_MINIMIZE;
|
| 191 |
|
| 192 | int ret = CreateProcess(
|
| 193 | NULL, /* program path */
|
| 194 | (LPTSTR)cmd, /* command-line */
|
| 195 | NULL, /* process handle is not inheritable */
|
| 196 | NULL, /* thread handle is not inheritable */
|
| 197 | TRUE, /* yes, inherit some handles */
|
| 198 | CREATE_NO_WINDOW, /* we don't want a console */
|
| 199 | NULL, /* use parent's environment block */
|
| 200 | NULL, /* use parent's starting directory */
|
| 201 | &startup, /* startup info, i.e. std handles */
|
| 202 | &pinfo);
|
| 203 |
|
| 204 | int result = -1;
|
| 205 | if (ret) {
|
| 206 | WaitForSingleObject(pinfo.hProcess, INFINITE);
|
| 207 |
|
| 208 | DWORD exitCode;
|
| 209 | if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
|
| 210 | // this should not return STILL_ACTIVE (259)
|
| 211 | result = exitCode;
|
| 212 | }
|
| 213 | CloseHandle(pinfo.hProcess);
|
| 214 | CloseHandle(pinfo.hThread);
|
| 215 | }
|
| 216 |
|
| 217 | return result;
|
| 218 | }
|
| 219 |
|
| 220 | bool getModuleDir(CPath *outDir) {
|
| 221 | TCHAR programDir[MAX_PATH];
|
| 222 | int ret = GetModuleFileName(NULL, programDir, sizeof(programDir) * sizeof(TCHAR));
|
| 223 | if (ret != 0) {
|
| 224 | CPath dir(programDir);
|
| 225 | dir.RemoveFileSpec();
|
| 226 | *outDir = dir;
|
| 227 | return true;
|
| 228 | }
|
| 229 | return false;
|
| 230 | }
|
| 231 |
|
| 232 | // Disables the FS redirection done by WOW64.
|
| 233 | // Because this runs as a 32-bit app, Windows automagically remaps some
|
| 234 | // folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files").
|
| 235 | // This prevents the app from correctly searching for java.exe in these folders.
|
| 236 | // The registry is also remapped. This method disables this redirection.
|
| 237 | // Caller should restore the redirection later by using revertWow64FsRedirection().
|
| 238 | PVOID disableWow64FsRedirection() {
|
| 239 |
|
| 240 | // The call we want to make is the following:
|
| 241 | // PVOID oldWow64Value;
|
| 242 | // Wow64DisableWow64FsRedirection(&oldWow64Value);
|
| 243 | // However that method may not exist (e.g. on XP non-64 systems) so
|
| 244 | // we must not call it directly.
|
| 245 |
|
| 246 | PVOID oldWow64Value = 0;
|
| 247 |
|
| 248 | HMODULE hmod = LoadLibrary(_T("kernel32.dll"));
|
| 249 | if (hmod != NULL) {
|
| 250 | FARPROC proc = GetProcAddress(hmod, "Wow64DisableWow64FsRedirection");
|
| 251 | if (proc != NULL) {
|
| 252 | typedef BOOL(WINAPI *disableWow64FuncType)(PVOID *);
|
| 253 | disableWow64FuncType funcPtr = (disableWow64FuncType)proc;
|
| 254 | funcPtr(&oldWow64Value);
|
| 255 | }
|
| 256 |
|
| 257 | FreeLibrary(hmod);
|
| 258 | }
|
| 259 |
|
| 260 | return oldWow64Value;
|
| 261 | }
|
| 262 |
|
| 263 | // Reverts the redirection disabled in disableWow64FsRedirection.
|
| 264 | void revertWow64FsRedirection(PVOID oldWow64Value) {
|
| 265 |
|
| 266 | // The call we want to make is the following:
|
| 267 | // Wow64RevertWow64FsRedirection(oldWow64Value);
|
| 268 | // However that method may not exist (e.g. on XP non-64 systems) so
|
| 269 | // we must not call it directly.
|
| 270 |
|
| 271 | HMODULE hmod = LoadLibrary(_T("kernel32.dll"));
|
| 272 | if (hmod != NULL) {
|
| 273 | FARPROC proc = GetProcAddress(hmod, "Wow64RevertWow64FsRedirection");
|
| 274 | if (proc != NULL) {
|
| 275 | typedef BOOL(WINAPI *revertWow64FuncType)(PVOID);
|
| 276 | revertWow64FuncType funcPtr = (revertWow64FuncType)proc;
|
| 277 | funcPtr(oldWow64Value);
|
| 278 | }
|
| 279 |
|
| 280 | FreeLibrary(hmod);
|
| 281 | }
|
| 282 | }
|