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.
initUtils(const TCHAR * appName)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 
getAppName()83 CString getAppName() {
84     return gAppName;
85 }
86 
87 
88 // Displays a message in an ok+info dialog box.
msgBox(const TCHAR * text,...)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.
getLastWin32Error(const TCHAR * message)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
displayLastError(const TCHAR * description,...)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.
execNoWait(const TCHAR * app,const TCHAR * params,const TCHAR * workDir)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";
execWait(const TCHAR * cmd)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 
getModuleDir(CPath * outDir)220 bool getModuleDir(CPath *outDir) {
221     TCHAR programDir[MAX_PATH];
222     int ret = GetModuleFileName(NULL, programDir, sizeof(programDir) / sizeof(programDir[0]));
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().
disableWow64FsRedirection()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.
revertWow64FsRedirection(PVOID oldWow64Value)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 }
283