xref: /aosp_15_r20/sdk/find_java2/src/JavaFinder.cpp (revision 1789df15502f1991eff51ff970dce5df8404dd56)
1*1789df15SXin Li /*
2*1789df15SXin Li * Copyright (C) 2014 The Android Open Source Project
3*1789df15SXin Li *
4*1789df15SXin Li * Licensed under the Apache License, Version 2.0 (the "License");
5*1789df15SXin Li * you may not use this file except in compliance with the License.
6*1789df15SXin Li * You may obtain a copy of the License at
7*1789df15SXin Li *
8*1789df15SXin Li *      http://www.apache.org/licenses/LICENSE-2.0
9*1789df15SXin Li *
10*1789df15SXin Li * Unless required by applicable law or agreed to in writing, software
11*1789df15SXin Li * distributed under the License is distributed on an "AS IS" BASIS,
12*1789df15SXin Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*1789df15SXin Li * See the License for the specific language governing permissions and
14*1789df15SXin Li * limitations under the License.
15*1789df15SXin Li */
16*1789df15SXin Li 
17*1789df15SXin Li #include "stdafx.h"
18*1789df15SXin Li #include "JavaFinder.h"
19*1789df15SXin Li #include "utils.h"
20*1789df15SXin Li 
21*1789df15SXin Li #include <algorithm>        // std::sort and std::unique
22*1789df15SXin Li 
23*1789df15SXin Li #define  _CRT_SECURE_NO_WARNINGS
24*1789df15SXin Li 
25*1789df15SXin Li // --------------
26*1789df15SXin Li 
27*1789df15SXin Li #define JF_REGISTRY_KEY         _T("Software\\Android\\FindJava2")
28*1789df15SXin Li #define JF_REGISTRY_VALUE_PATH  _T("JavaPath")
29*1789df15SXin Li #define JF_REGISTRY_VALUE_VERS  _T("JavaVers")
30*1789df15SXin Li 
31*1789df15SXin Li // --------------
32*1789df15SXin Li 
33*1789df15SXin Li 
34*1789df15SXin Li // Extract the first thing that looks like (digit.digit+).
35*1789df15SXin Li // Note: this will break when java reports a version with major > 9.
36*1789df15SXin Li // However it will reasonably cope with "1.10", if that ever happens.
extractJavaVersion(const TCHAR * start,int length,CString * outVersionStr,int * outVersionInt)37*1789df15SXin Li static bool extractJavaVersion(const TCHAR *start,
38*1789df15SXin Li                                int length,
39*1789df15SXin Li                                CString *outVersionStr,
40*1789df15SXin Li                                int *outVersionInt) {
41*1789df15SXin Li     const TCHAR *end = start + length;
42*1789df15SXin Li     for (const TCHAR *c = start; c < end - 2; c++) {
43*1789df15SXin Li         if (isdigit(c[0]) &&
44*1789df15SXin Li             c[1] == '.' &&
45*1789df15SXin Li             isdigit(c[2])) {
46*1789df15SXin Li             const TCHAR *e = c + 2;
47*1789df15SXin Li             while (isdigit(e[1])) {
48*1789df15SXin Li                 e++;
49*1789df15SXin Li             }
50*1789df15SXin Li             outVersionStr->SetString(c, e - c + 1);
51*1789df15SXin Li 
52*1789df15SXin Li             // major is currently only 1 digit
53*1789df15SXin Li             int major = (*c - '0');
54*1789df15SXin Li             // add minor
55*1789df15SXin Li             int minor = 0;
56*1789df15SXin Li             for (int m = 1; *e != '.'; e--, m *= 10) {
57*1789df15SXin Li                 minor += (*e - '0') * m;
58*1789df15SXin Li             }
59*1789df15SXin Li             *outVersionInt = JAVA_VERS_TO_INT(major, minor);
60*1789df15SXin Li             return true;
61*1789df15SXin Li         }
62*1789df15SXin Li     }
63*1789df15SXin Li     return false;
64*1789df15SXin Li }
65*1789df15SXin Li 
66*1789df15SXin Li // Tries to invoke the java.exe at the given path and extract it's
67*1789df15SXin Li // version number.
68*1789df15SXin Li // - outVersionStr: not null, will capture version as a string (e.g. "1.6")
69*1789df15SXin Li // - outVersionInt: not null, will capture version as an int (see JavaPath.h).
getJavaVersion(CPath & javaPath,CString * outVersionStr,int * outVersionInt)70*1789df15SXin Li bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) {
71*1789df15SXin Li     bool result = false;
72*1789df15SXin Li 
73*1789df15SXin Li     // Run "java -version", which outputs something to *STDERR* like this:
74*1789df15SXin Li     //
75*1789df15SXin Li     // java version "1.6.0_29"
76*1789df15SXin Li     // Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
77*1789df15SXin Li     // Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing)
78*1789df15SXin Li     //
79*1789df15SXin Li     // We want to capture the first line, and more exactly the "1.6" part.
80*1789df15SXin Li 
81*1789df15SXin Li 
82*1789df15SXin Li     CString cmd;
83*1789df15SXin Li     cmd.Format(_T("\"%s\" -version"), (LPCTSTR) javaPath);
84*1789df15SXin Li 
85*1789df15SXin Li     SECURITY_ATTRIBUTES   saAttr;
86*1789df15SXin Li     STARTUPINFO           startup;
87*1789df15SXin Li     PROCESS_INFORMATION   pinfo;
88*1789df15SXin Li 
89*1789df15SXin Li     // Want to inherit pipe handle
90*1789df15SXin Li     ZeroMemory(&saAttr, sizeof(saAttr));
91*1789df15SXin Li     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
92*1789df15SXin Li     saAttr.bInheritHandle = TRUE;
93*1789df15SXin Li     saAttr.lpSecurityDescriptor = NULL;
94*1789df15SXin Li 
95*1789df15SXin Li     // Create pipe for stdout
96*1789df15SXin Li     HANDLE stdoutPipeRd, stdoutPipeWt;
97*1789df15SXin Li     if (!CreatePipe(
98*1789df15SXin Li             &stdoutPipeRd,      // hReadPipe,
99*1789df15SXin Li             &stdoutPipeWt,      // hWritePipe,
100*1789df15SXin Li             &saAttr,            // lpPipeAttributes,
101*1789df15SXin Li             0)) {               // nSize (0=default buffer size)
102*1789df15SXin Li         // In FindJava2, we do not report these errors. Leave commented for reference.
103*1789df15SXin Li         // // if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: ");
104*1789df15SXin Li         return false;
105*1789df15SXin Li     }
106*1789df15SXin Li     if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) {
107*1789df15SXin Li         // In FindJava2, we do not report these errors. Leave commented for reference.
108*1789df15SXin Li         // // if (gIsConsole || gIsDebug) displayLastError("SetHandleInformation failed: ");
109*1789df15SXin Li         return false;
110*1789df15SXin Li     }
111*1789df15SXin Li 
112*1789df15SXin Li     ZeroMemory(&pinfo, sizeof(pinfo));
113*1789df15SXin Li 
114*1789df15SXin Li     ZeroMemory(&startup, sizeof(startup));
115*1789df15SXin Li     startup.cb = sizeof(startup);
116*1789df15SXin Li     startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
117*1789df15SXin Li     startup.wShowWindow = SW_HIDE | SW_MINIMIZE;
118*1789df15SXin Li     // Capture both stderr and stdout
119*1789df15SXin Li     startup.hStdError = stdoutPipeWt;
120*1789df15SXin Li     startup.hStdOutput = stdoutPipeWt;
121*1789df15SXin Li     startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
122*1789df15SXin Li 
123*1789df15SXin Li     BOOL ok = CreateProcess(
124*1789df15SXin Li         NULL,                   // program path
125*1789df15SXin Li         (LPTSTR)((LPCTSTR) cmd),// command-line
126*1789df15SXin Li         NULL,                   // process handle is not inheritable
127*1789df15SXin Li         NULL,                   // thread handle is not inheritable
128*1789df15SXin Li         TRUE,                   // yes, inherit some handles
129*1789df15SXin Li         0,                      // process creation flags
130*1789df15SXin Li         NULL,                   // use parent's environment block
131*1789df15SXin Li         NULL,                   // use parent's starting directory
132*1789df15SXin Li         &startup,               // startup info, i.e. std handles
133*1789df15SXin Li         &pinfo);
134*1789df15SXin Li 
135*1789df15SXin Li     // In FindJava2, we do not report these errors. Leave commented for reference.
136*1789df15SXin Li     // // if ((gIsConsole || gIsDebug) && !ok) displayLastError("CreateProcess failed: ");
137*1789df15SXin Li 
138*1789df15SXin Li     // Close the write-end of the output pipe (we're only reading from it)
139*1789df15SXin Li     CloseHandle(stdoutPipeWt);
140*1789df15SXin Li 
141*1789df15SXin Li     // Read from the output pipe. We don't need to read everything,
142*1789df15SXin Li     // the first line should be 'Java version "1.2.3_45"\r\n'
143*1789df15SXin Li     // so reading about 32 chars is all we need.
144*1789df15SXin Li     TCHAR first32[32 + 1];
145*1789df15SXin Li     int index = 0;
146*1789df15SXin Li     first32[0] = 0;
147*1789df15SXin Li 
148*1789df15SXin Li     if (ok) {
149*1789df15SXin Li         #define SIZE 1024
150*1789df15SXin Li         char buffer[SIZE];
151*1789df15SXin Li         DWORD sizeRead = 0;
152*1789df15SXin Li 
153*1789df15SXin Li         while (ok) {
154*1789df15SXin Li             // Keep reading in the same buffer location
155*1789df15SXin Li             // Note: ReadFile uses a char buffer, not a TCHAR one.
156*1789df15SXin Li             ok = ReadFile(stdoutPipeRd,     // hFile
157*1789df15SXin Li                           buffer,           // lpBuffer
158*1789df15SXin Li                           SIZE,             // DWORD buffer size to read
159*1789df15SXin Li                           &sizeRead,        // DWORD buffer size read
160*1789df15SXin Li                           NULL);            // overlapped
161*1789df15SXin Li             if (!ok || sizeRead == 0 || sizeRead > SIZE) break;
162*1789df15SXin Li 
163*1789df15SXin Li             // Copy up to the first 32 characters
164*1789df15SXin Li             if (index < 32) {
165*1789df15SXin Li                 DWORD n = 32 - index;
166*1789df15SXin Li                 if (n > sizeRead) n = sizeRead;
167*1789df15SXin Li                 // copy as lowercase to simplify checks later
168*1789df15SXin Li                 for (char *b = buffer; n > 0; n--, b++, index++) {
169*1789df15SXin Li                     char c = *b;
170*1789df15SXin Li                     if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
171*1789df15SXin Li                     first32[index] = c;
172*1789df15SXin Li                 }
173*1789df15SXin Li                 first32[index] = 0;
174*1789df15SXin Li             }
175*1789df15SXin Li         }
176*1789df15SXin Li 
177*1789df15SXin Li         WaitForSingleObject(pinfo.hProcess, INFINITE);
178*1789df15SXin Li 
179*1789df15SXin Li         DWORD exitCode;
180*1789df15SXin Li         if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
181*1789df15SXin Li             // this should not return STILL_ACTIVE (259)
182*1789df15SXin Li             result = exitCode == 0;
183*1789df15SXin Li         }
184*1789df15SXin Li 
185*1789df15SXin Li         CloseHandle(pinfo.hProcess);
186*1789df15SXin Li         CloseHandle(pinfo.hThread);
187*1789df15SXin Li     }
188*1789df15SXin Li     CloseHandle(stdoutPipeRd);
189*1789df15SXin Li 
190*1789df15SXin Li     if (result && index > 0) {
191*1789df15SXin Li         // Look for a few keywords in the output however we don't
192*1789df15SXin Li         // care about specific ordering or case-senstiviness.
193*1789df15SXin Li         // We only capture roughtly the first line in lower case.
194*1789df15SXin Li         TCHAR *j = _tcsstr(first32, _T("java"));
195*1789df15SXin Li         TCHAR *v = _tcsstr(first32, _T("version"));
196*1789df15SXin Li         // In FindJava2, we do not report these errors. Leave commented for reference.
197*1789df15SXin Li         // // if ((gIsConsole || gIsDebug) && (!j || !v)) {
198*1789df15SXin Li         // //     fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32);
199*1789df15SXin Li         // // }
200*1789df15SXin Li         if (j != NULL && v != NULL) {
201*1789df15SXin Li             result = extractJavaVersion(first32, index, outVersionStr, outVersionInt);
202*1789df15SXin Li         }
203*1789df15SXin Li     }
204*1789df15SXin Li 
205*1789df15SXin Li     return result;
206*1789df15SXin Li }
207*1789df15SXin Li 
208*1789df15SXin Li // --------------
209*1789df15SXin Li 
210*1789df15SXin Li // Checks whether we can find $PATH/java.exe.
211*1789df15SXin Li // inOutPath should be the directory where we're looking at.
212*1789df15SXin Li // In output, it will be the java path we tested.
213*1789df15SXin Li // Returns the java version integer found (e.g. 1006 for 1.6).
214*1789df15SXin Li // Return 0 in case of error.
checkPath(CPath * inOutPath)215*1789df15SXin Li static int checkPath(CPath *inOutPath) {
216*1789df15SXin Li 
217*1789df15SXin Li     // Append java.exe to path if not already present
218*1789df15SXin Li     CString &p = (CString&)*inOutPath;
219*1789df15SXin Li     int n = p.GetLength();
220*1789df15SXin Li     if (n < 9 || p.Right(9).CompareNoCase(_T("\\java.exe")) != 0) {
221*1789df15SXin Li         inOutPath->Append(_T("java.exe"));
222*1789df15SXin Li     }
223*1789df15SXin Li 
224*1789df15SXin Li     int result = 0;
225*1789df15SXin Li     PVOID oldWow64Value = disableWow64FsRedirection();
226*1789df15SXin Li     if (inOutPath->FileExists()) {
227*1789df15SXin Li         // Run java -version
228*1789df15SXin Li         // Reject the version if it's not at least our current minimum.
229*1789df15SXin Li         CString versionStr;
230*1789df15SXin Li         if (!getJavaVersion(*inOutPath, &versionStr, &result)) {
231*1789df15SXin Li             result = 0;
232*1789df15SXin Li         }
233*1789df15SXin Li     }
234*1789df15SXin Li 
235*1789df15SXin Li     revertWow64FsRedirection(oldWow64Value);
236*1789df15SXin Li     return result;
237*1789df15SXin Li }
238*1789df15SXin Li 
239*1789df15SXin Li // Check whether we can find $PATH/bin/java.exe
240*1789df15SXin Li // Returns the Java version found (e.g. 1006 for 1.6) or 0 in case of error.
checkBinPath(CPath * inOutPath)241*1789df15SXin Li static int checkBinPath(CPath *inOutPath) {
242*1789df15SXin Li 
243*1789df15SXin Li     // Append bin to path if not already present
244*1789df15SXin Li     CString &p = (CString&)*inOutPath;
245*1789df15SXin Li     int n = p.GetLength();
246*1789df15SXin Li     if (n < 4 || p.Right(4).CompareNoCase(_T("\\bin")) != 0) {
247*1789df15SXin Li         inOutPath->Append(_T("bin"));
248*1789df15SXin Li     }
249*1789df15SXin Li 
250*1789df15SXin Li     return checkPath(inOutPath);
251*1789df15SXin Li }
252*1789df15SXin Li 
253*1789df15SXin Li // Search java.exe in the environment
findJavaInEnvPath(std::set<CJavaPath> * outPaths)254*1789df15SXin Li static void findJavaInEnvPath(std::set<CJavaPath> *outPaths) {
255*1789df15SXin Li     ::SetLastError(0);
256*1789df15SXin Li 
257*1789df15SXin Li     const TCHAR* envPath = _tgetenv(_T("JAVA_HOME"));
258*1789df15SXin Li     if (envPath != NULL) {
259*1789df15SXin Li         CPath p(envPath);
260*1789df15SXin Li         int v = checkBinPath(&p);
261*1789df15SXin Li         if (v > 0) {
262*1789df15SXin Li             outPaths->insert(CJavaPath(v, p));
263*1789df15SXin Li         }
264*1789df15SXin Li     }
265*1789df15SXin Li 
266*1789df15SXin Li     envPath = _tgetenv(_T("PATH"));
267*1789df15SXin Li     if (envPath != NULL) {
268*1789df15SXin Li         // Otherwise look at the entries in the current path.
269*1789df15SXin Li         // If we find more than one, keep the one with the highest version.
270*1789df15SXin Li         CString pathTokens(envPath);
271*1789df15SXin Li         int curPos = 0;
272*1789df15SXin Li         CString tok;
273*1789df15SXin Li         do {
274*1789df15SXin Li             tok = pathTokens.Tokenize(_T(";"), curPos);
275*1789df15SXin Li             if (!tok.IsEmpty()) {
276*1789df15SXin Li                 CPath p(tok);
277*1789df15SXin Li                 int v = checkPath(&p);
278*1789df15SXin Li                 if (v > 0) {
279*1789df15SXin Li                     outPaths->insert(CJavaPath(v, p));
280*1789df15SXin Li                 }
281*1789df15SXin Li             }
282*1789df15SXin Li         } while (!tok.IsEmpty());
283*1789df15SXin Li     }
284*1789df15SXin Li }
285*1789df15SXin Li 
286*1789df15SXin Li 
287*1789df15SXin Li // --------------
288*1789df15SXin Li 
getRegValue(const TCHAR * keyPath,const TCHAR * keyName,REGSAM access,CString * outValue)289*1789df15SXin Li static bool getRegValue(const TCHAR *keyPath,
290*1789df15SXin Li                         const TCHAR *keyName,
291*1789df15SXin Li                         REGSAM access,
292*1789df15SXin Li                         CString *outValue) {
293*1789df15SXin Li     HKEY key;
294*1789df15SXin Li     LSTATUS status = RegOpenKeyEx(
295*1789df15SXin Li         HKEY_LOCAL_MACHINE,         // hKey
296*1789df15SXin Li         keyPath,                    // lpSubKey
297*1789df15SXin Li         0,                          // ulOptions
298*1789df15SXin Li         KEY_READ | access,          // samDesired,
299*1789df15SXin Li         &key);                      // phkResult
300*1789df15SXin Li     if (status == ERROR_SUCCESS) {
301*1789df15SXin Li         LSTATUS ret = ERROR_MORE_DATA;
302*1789df15SXin Li         DWORD size = 4096; // MAX_PATH is 260, so 4 KB should be good enough
303*1789df15SXin Li         TCHAR* buffer = (TCHAR*)malloc(size);
304*1789df15SXin Li 
305*1789df15SXin Li         while (ret == ERROR_MORE_DATA && size < (1 << 16) /*64 KB*/) {
306*1789df15SXin Li             ret = RegQueryValueEx(
307*1789df15SXin Li                 key,                // hKey
308*1789df15SXin Li                 keyName,            // lpValueName
309*1789df15SXin Li                 NULL,               // lpReserved
310*1789df15SXin Li                 NULL,               // lpType
311*1789df15SXin Li                 (LPBYTE)buffer,     // lpData
312*1789df15SXin Li                 &size);             // lpcbData
313*1789df15SXin Li 
314*1789df15SXin Li             if (ret == ERROR_MORE_DATA) {
315*1789df15SXin Li                 size *= 2;
316*1789df15SXin Li                 buffer = (TCHAR*)realloc(buffer, size);
317*1789df15SXin Li             } else {
318*1789df15SXin Li                 buffer[size] = 0;
319*1789df15SXin Li             }
320*1789df15SXin Li         }
321*1789df15SXin Li 
322*1789df15SXin Li         if (ret != ERROR_MORE_DATA) {
323*1789df15SXin Li             outValue->SetString(buffer);
324*1789df15SXin Li         }
325*1789df15SXin Li 
326*1789df15SXin Li         free(buffer);
327*1789df15SXin Li         RegCloseKey(key);
328*1789df15SXin Li 
329*1789df15SXin Li         return (ret != ERROR_MORE_DATA);
330*1789df15SXin Li     }
331*1789df15SXin Li 
332*1789df15SXin Li     return false;
333*1789df15SXin Li }
334*1789df15SXin Li 
335*1789df15SXin Li // Explore the registry to find a suitable version of Java.
336*1789df15SXin Li // Returns an int which is the version of Java found (e.g. 1006 for 1.6) and the
337*1789df15SXin Li // matching path in outJavaPath.
338*1789df15SXin Li // Returns 0 if nothing suitable was found.
exploreJavaRegistry(const TCHAR * entry,REGSAM access,std::set<CJavaPath> * outPaths)339*1789df15SXin Li static int exploreJavaRegistry(const TCHAR *entry, REGSAM access, std::set<CJavaPath> *outPaths) {
340*1789df15SXin Li 
341*1789df15SXin Li     // Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion]
342*1789df15SXin Li     CPath rootKey(_T("SOFTWARE\\JavaSoft\\"));
343*1789df15SXin Li     rootKey.Append(entry);
344*1789df15SXin Li 
345*1789df15SXin Li     CString currentVersion;
346*1789df15SXin Li     CPath subKey(rootKey);
347*1789df15SXin Li     if (getRegValue(subKey, _T("CurrentVersion"), access, &currentVersion)) {
348*1789df15SXin Li         // CurrentVersion should be something like "1.7".
349*1789df15SXin Li         // We want to read HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 [JavaHome]
350*1789df15SXin Li         subKey.Append(currentVersion);
351*1789df15SXin Li         CString value;
352*1789df15SXin Li         if (getRegValue(subKey, _T("JavaHome"), access, &value)) {
353*1789df15SXin Li             CPath javaHome(value);
354*1789df15SXin Li             int v = checkBinPath(&javaHome);
355*1789df15SXin Li             if (v > 0) {
356*1789df15SXin Li                 outPaths->insert(CJavaPath(v, javaHome));
357*1789df15SXin Li             }
358*1789df15SXin Li         }
359*1789df15SXin Li     }
360*1789df15SXin Li 
361*1789df15SXin Li     // Try again, but this time look at all the versions available
362*1789df15SXin Li     HKEY javaHomeKey;
363*1789df15SXin Li     LSTATUS status = RegOpenKeyEx(
364*1789df15SXin Li         HKEY_LOCAL_MACHINE,         // hKey
365*1789df15SXin Li         _T("SOFTWARE\\JavaSoft"),   // lpSubKey
366*1789df15SXin Li         0,                          // ulOptions
367*1789df15SXin Li         KEY_READ | access,          // samDesired
368*1789df15SXin Li         &javaHomeKey);              // phkResult
369*1789df15SXin Li     if (status == ERROR_SUCCESS) {
370*1789df15SXin Li         TCHAR name[MAX_PATH + 1];
371*1789df15SXin Li         DWORD index = 0;
372*1789df15SXin Li         CPath javaHome;
373*1789df15SXin Li         for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; index++) {
374*1789df15SXin Li             DWORD nameLen = MAX_PATH;
375*1789df15SXin Li             name[nameLen] = 0;
376*1789df15SXin Li             result = RegEnumKeyEx(
377*1789df15SXin Li                 javaHomeKey,  // hKey
378*1789df15SXin Li                 index,        // dwIndex
379*1789df15SXin Li                 name,         // lpName
380*1789df15SXin Li                 &nameLen,     // lpcName
381*1789df15SXin Li                 NULL,         // lpReserved
382*1789df15SXin Li                 NULL,         // lpClass
383*1789df15SXin Li                 NULL,         // lpcClass,
384*1789df15SXin Li                 NULL);        // lpftLastWriteTime
385*1789df15SXin Li             if (result == ERROR_SUCCESS && nameLen < MAX_PATH) {
386*1789df15SXin Li                 name[nameLen] = 0;
387*1789df15SXin Li                 CPath subKey(rootKey);
388*1789df15SXin Li                 subKey.Append(name);
389*1789df15SXin Li 
390*1789df15SXin Li                 CString value;
391*1789df15SXin Li                 if (getRegValue(subKey, _T("JavaHome"), access, &value)) {
392*1789df15SXin Li                     CPath javaHome(value);
393*1789df15SXin Li                     int v = checkBinPath(&javaHome);
394*1789df15SXin Li                     if (v > 0) {
395*1789df15SXin Li                         outPaths->insert(CJavaPath(v, javaHome));
396*1789df15SXin Li                     }
397*1789df15SXin Li                 }
398*1789df15SXin Li             }
399*1789df15SXin Li         }
400*1789df15SXin Li 
401*1789df15SXin Li         RegCloseKey(javaHomeKey);
402*1789df15SXin Li     }
403*1789df15SXin Li 
404*1789df15SXin Li     return 0;
405*1789df15SXin Li }
406*1789df15SXin Li 
findJavaInRegistry(std::set<CJavaPath> * outPaths)407*1789df15SXin Li static void findJavaInRegistry(std::set<CJavaPath> *outPaths) {
408*1789df15SXin Li     // We'll do the registry test 3 times: first using the default mode,
409*1789df15SXin Li     // then forcing the use of the 32-bit registry then forcing the use of
410*1789df15SXin Li     // 64-bit registry. On Windows 2k, the 2 latter will fail since the
411*1789df15SXin Li     // flags are not supported. On a 32-bit OS the 64-bit is obviously
412*1789df15SXin Li     // useless and the 2 first tests should be equivalent so we just
413*1789df15SXin Li     // need the first case.
414*1789df15SXin Li 
415*1789df15SXin Li     // Check the JRE first, then the JDK.
416*1789df15SXin Li     exploreJavaRegistry(_T("Java Runtime Environment"), 0, outPaths);
417*1789df15SXin Li     exploreJavaRegistry(_T("Java Development Kit"), 0, outPaths);
418*1789df15SXin Li 
419*1789df15SXin Li     // Get the app sysinfo state (the one hidden by WOW64)
420*1789df15SXin Li     SYSTEM_INFO sysInfo;
421*1789df15SXin Li     GetSystemInfo(&sysInfo);
422*1789df15SXin Li     WORD programArch = sysInfo.wProcessorArchitecture;
423*1789df15SXin Li     // Check the real sysinfo state (not the one hidden by WOW64) for x86
424*1789df15SXin Li     GetNativeSystemInfo(&sysInfo);
425*1789df15SXin Li     WORD actualArch = sysInfo.wProcessorArchitecture;
426*1789df15SXin Li 
427*1789df15SXin Li     // Only try to access the WOW64-32 redirected keys on a 64-bit system.
428*1789df15SXin Li     // There's no point in doing this on a 32-bit system.
429*1789df15SXin Li     if (actualArch == PROCESSOR_ARCHITECTURE_AMD64) {
430*1789df15SXin Li         if (programArch != PROCESSOR_ARCHITECTURE_INTEL) {
431*1789df15SXin Li             // If we did the 32-bit case earlier, don't do it twice.
432*1789df15SXin Li             exploreJavaRegistry(_T("Java Runtime Environment"), KEY_WOW64_32KEY, outPaths);
433*1789df15SXin Li             exploreJavaRegistry(_T("Java Development Kit"),     KEY_WOW64_32KEY, outPaths);
434*1789df15SXin Li 
435*1789df15SXin Li         } else if (programArch != PROCESSOR_ARCHITECTURE_AMD64) {
436*1789df15SXin Li             // If we did the 64-bit case earlier, don't do it twice.
437*1789df15SXin Li             exploreJavaRegistry(_T("Java Runtime Environment"), KEY_WOW64_64KEY, outPaths);
438*1789df15SXin Li             exploreJavaRegistry(_T("Java Development Kit"),     KEY_WOW64_64KEY, outPaths);
439*1789df15SXin Li         }
440*1789df15SXin Li     }
441*1789df15SXin Li }
442*1789df15SXin Li 
443*1789df15SXin Li // --------------
444*1789df15SXin Li 
checkProgramFiles(std::set<CJavaPath> * outPaths)445*1789df15SXin Li static void checkProgramFiles(std::set<CJavaPath> *outPaths) {
446*1789df15SXin Li 
447*1789df15SXin Li     TCHAR programFilesPath[MAX_PATH + 1];
448*1789df15SXin Li     HRESULT result = SHGetFolderPath(
449*1789df15SXin Li         NULL,                       // hwndOwner
450*1789df15SXin Li         CSIDL_PROGRAM_FILES,        // nFolder
451*1789df15SXin Li         NULL,                       // hToken
452*1789df15SXin Li         SHGFP_TYPE_CURRENT,         // dwFlags
453*1789df15SXin Li         programFilesPath);          // pszPath
454*1789df15SXin Li 
455*1789df15SXin Li     CPath path(programFilesPath);
456*1789df15SXin Li     path.Append(_T("Java"));
457*1789df15SXin Li 
458*1789df15SXin Li     // Do we have a C:\\Program Files\\Java directory?
459*1789df15SXin Li     if (!path.IsDirectory()) {
460*1789df15SXin Li         return;
461*1789df15SXin Li     }
462*1789df15SXin Li 
463*1789df15SXin Li     CPath glob(path);
464*1789df15SXin Li     glob.Append(_T("j*"));
465*1789df15SXin Li 
466*1789df15SXin Li     WIN32_FIND_DATA findData;
467*1789df15SXin Li     HANDLE findH = FindFirstFile(glob, &findData);
468*1789df15SXin Li     if (findH == INVALID_HANDLE_VALUE) {
469*1789df15SXin Li         return;
470*1789df15SXin Li     }
471*1789df15SXin Li     do {
472*1789df15SXin Li         if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
473*1789df15SXin Li             CPath temp(path);
474*1789df15SXin Li             temp.Append(findData.cFileName);
475*1789df15SXin Li             // Check C:\\Program Files[x86]\\Java\\j*\\bin\\java.exe
476*1789df15SXin Li             int v = checkBinPath(&temp);
477*1789df15SXin Li             if (v > 0) {
478*1789df15SXin Li                 outPaths->insert(CJavaPath(v, temp));
479*1789df15SXin Li             }
480*1789df15SXin Li         }
481*1789df15SXin Li     } while (FindNextFile(findH, &findData) != 0);
482*1789df15SXin Li     FindClose(findH);
483*1789df15SXin Li }
484*1789df15SXin Li 
findJavaInProgramFiles(std::set<CJavaPath> * outPaths)485*1789df15SXin Li static void findJavaInProgramFiles(std::set<CJavaPath> *outPaths) {
486*1789df15SXin Li     // Check the C:\\Program Files (x86) directory
487*1789df15SXin Li     // With WOW64 fs redirection in place by default, we should get the x86
488*1789df15SXin Li     // version on a 64-bit OS since this app is a 32-bit itself.
489*1789df15SXin Li     checkProgramFiles(outPaths);
490*1789df15SXin Li 
491*1789df15SXin Li     // Check the real sysinfo state (not the one hidden by WOW64) for x86
492*1789df15SXin Li     SYSTEM_INFO sysInfo;
493*1789df15SXin Li     GetNativeSystemInfo(&sysInfo);
494*1789df15SXin Li 
495*1789df15SXin Li     if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
496*1789df15SXin Li         // On a 64-bit OS, try again by disabling the fs redirection so
497*1789df15SXin Li         // that we can try the real C:\\Program Files directory.
498*1789df15SXin Li         PVOID oldWow64Value = disableWow64FsRedirection();
499*1789df15SXin Li         checkProgramFiles(outPaths);
500*1789df15SXin Li         revertWow64FsRedirection(oldWow64Value);
501*1789df15SXin Li     }
502*1789df15SXin Li }
503*1789df15SXin Li 
504*1789df15SXin Li //------
505*1789df15SXin Li 
506*1789df15SXin Li 
CJavaFinder(int minVersion)507*1789df15SXin Li CJavaFinder::CJavaFinder(int minVersion) : mMinVersion(minVersion) {
508*1789df15SXin Li }
509*1789df15SXin Li 
510*1789df15SXin Li 
~CJavaFinder()511*1789df15SXin Li CJavaFinder::~CJavaFinder() {
512*1789df15SXin Li }
513*1789df15SXin Li 
514*1789df15SXin Li /*
515*1789df15SXin Li  * Checks whether there's a recorded path in the registry and whether
516*1789df15SXin Li  * this path still points to a valid Java executable.
517*1789df15SXin Li  * Returns false if any of these do not match,
518*1789df15SXin Li  * Returns true if both condition match,
519*1789df15SXin Li  * outPath contains the result path when returning true.
520*1789df15SXin Li */
getRegistryPath()521*1789df15SXin Li CJavaPath CJavaFinder::getRegistryPath() {
522*1789df15SXin Li     CString existing;
523*1789df15SXin Li     CRegKey rk;
524*1789df15SXin Li 
525*1789df15SXin Li     if (rk.Open(HKEY_CURRENT_USER, JF_REGISTRY_KEY, KEY_READ) == ERROR_SUCCESS) {
526*1789df15SXin Li         ULONG sLen = MAX_PATH;
527*1789df15SXin Li         TCHAR s[MAX_PATH + 1];
528*1789df15SXin Li         if (rk.QueryStringValue(JF_REGISTRY_VALUE_PATH, s, &sLen) == ERROR_SUCCESS) {
529*1789df15SXin Li             existing.SetString(s);
530*1789df15SXin Li         }
531*1789df15SXin Li         rk.Close();
532*1789df15SXin Li     }
533*1789df15SXin Li 
534*1789df15SXin Li     if (!existing.IsEmpty()) {
535*1789df15SXin Li         CJavaPath javaPath;
536*1789df15SXin Li         if (checkJavaPath(existing, &javaPath)) {
537*1789df15SXin Li             return javaPath;
538*1789df15SXin Li         }
539*1789df15SXin Li     }
540*1789df15SXin Li 
541*1789df15SXin Li     return CJavaPath::sEmpty;
542*1789df15SXin Li }
543*1789df15SXin Li 
setRegistryPath(const CJavaPath & javaPath)544*1789df15SXin Li bool CJavaFinder::setRegistryPath(const CJavaPath &javaPath) {
545*1789df15SXin Li     CRegKey rk;
546*1789df15SXin Li 
547*1789df15SXin Li     if (rk.Create(HKEY_CURRENT_USER, JF_REGISTRY_KEY) == ERROR_SUCCESS) {
548*1789df15SXin Li         bool ok = rk.SetStringValue(JF_REGISTRY_VALUE_PATH, javaPath.mPath, REG_SZ) == ERROR_SUCCESS &&
549*1789df15SXin Li                   rk.SetStringValue(JF_REGISTRY_VALUE_VERS, javaPath.getVersion(), REG_SZ) == ERROR_SUCCESS;
550*1789df15SXin Li         rk.Close();
551*1789df15SXin Li         return ok;
552*1789df15SXin Li     }
553*1789df15SXin Li 
554*1789df15SXin Li     return false;
555*1789df15SXin Li }
556*1789df15SXin Li 
findJavaPaths(std::set<CJavaPath> * paths)557*1789df15SXin Li void CJavaFinder::findJavaPaths(std::set<CJavaPath> *paths) {
558*1789df15SXin Li     findJavaInEnvPath(paths);
559*1789df15SXin Li     findJavaInProgramFiles(paths);
560*1789df15SXin Li     findJavaInRegistry(paths);
561*1789df15SXin Li 
562*1789df15SXin Li     // Exclude any entries that do not match the minimum version.
563*1789df15SXin Li     // The set is going to be fairly small so it's easier to do it here
564*1789df15SXin Li     // than add the filter logic in all the static methods above.
565*1789df15SXin Li     if (mMinVersion > 0) {
566*1789df15SXin Li         for (auto it = paths->begin(); it != paths->end(); ) {
567*1789df15SXin Li             if (it->mVersion < mMinVersion) {
568*1789df15SXin Li                 it = paths->erase(it);  // C++11 set.erase returns an iterator to the *next* element
569*1789df15SXin Li             } else {
570*1789df15SXin Li                 ++it;
571*1789df15SXin Li             }
572*1789df15SXin Li         }
573*1789df15SXin Li     }
574*1789df15SXin Li }
575*1789df15SXin Li 
checkJavaPath(const CString & path,CJavaPath * outPath)576*1789df15SXin Li bool CJavaFinder::checkJavaPath(const CString &path, CJavaPath *outPath) {
577*1789df15SXin Li     CPath p(path);
578*1789df15SXin Li 
579*1789df15SXin Li     // try this path (if it ends with java.exe) or path\\java.exe
580*1789df15SXin Li     int v = checkPath(&p);
581*1789df15SXin Li     if (v == 0) {
582*1789df15SXin Li         // reset path and try path\\bin\\java.exe
583*1789df15SXin Li         p = CPath(path);
584*1789df15SXin Li         v = checkBinPath(&p);
585*1789df15SXin Li     }
586*1789df15SXin Li 
587*1789df15SXin Li     if (v > 0) {
588*1789df15SXin Li         outPath->set(v, p);
589*1789df15SXin Li         return v >= mMinVersion;
590*1789df15SXin Li     }
591*1789df15SXin Li 
592*1789df15SXin Li     return false;
593*1789df15SXin Li }
594*1789df15SXin Li 
595