| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 1 | /*
|
| 2 | * Copyright (C) 2011 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 | #ifdef _WIN32
|
| 18 |
|
| 19 | #include "find_java.h"
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 20 | #include <shlobj.h>
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 21 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 22 | // Check whether we can find $PATH/java.exe
|
| 23 | static bool checkPath(CPath *inOutPath) {
|
| 24 | inOutPath->addPath("java.exe");
|
| 25 |
|
| 26 | bool result = false;
|
| 27 | PVOID oldWow64Value = disableWow64FsRedirection();
|
| 28 | if (inOutPath->fileExists()) {
|
| 29 | // Make sure we can actually run "java -version".
|
| 30 | CString cmd;
|
| 31 | cmd.setf("\"%s\" -version", inOutPath->cstr());
|
| 32 | int code = execWait(cmd.cstr());
|
| 33 | result = (code == 0);
|
| 34 | }
|
| 35 |
|
| 36 | revertWow64FsRedirection(oldWow64Value);
|
| 37 | return result;
|
| 38 | }
|
| 39 |
|
| 40 | // Check whether we can find $PATH/bin/java.exe
|
| 41 | static bool checkBinPath(CPath *inOutPath) {
|
| 42 | inOutPath->addPath("bin");
|
| 43 | return checkPath(inOutPath);
|
| 44 | }
|
| 45 |
|
| 46 | // Search java.exe in the environment
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 47 | bool findJavaInEnvPath(CPath *outJavaPath) {
|
| 48 | SetLastError(0);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 49 |
|
| 50 | const char* envPath = getenv("JAVA_HOME");
|
| 51 | if (envPath != NULL) {
|
| 52 | CPath p(envPath);
|
| 53 | if (checkBinPath(&p)) {
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 54 | if (gIsDebug) msgBox("Java found via JAVA_HOME: %s", p.cstr());
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 55 | *outJavaPath = p;
|
| 56 | return true;
|
| 57 | }
|
| 58 | }
|
| 59 |
|
| 60 | envPath = getenv("PATH");
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 61 | if (!envPath) return false;
|
| 62 |
|
| 63 | CArray<CString> *paths = CString(envPath).split(';');
|
| 64 | for(int i = 0; i < paths->size(); i++) {
|
| 65 | CPath p((*paths)[i].cstr());
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 66 | if (checkPath(&p)) {
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 67 | if (gIsDebug) msgBox("Java found via env PATH: %s", p.cstr());
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 68 | *outJavaPath = p;
|
| 69 | delete paths;
|
| 70 | return true;
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 71 | }
|
| 72 | }
|
| 73 |
|
| 74 | delete paths;
|
| 75 | return false;
|
| 76 | }
|
| 77 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 78 | // --------------
|
| 79 |
|
| 80 | bool getRegValue(const char *keyPath, const char *keyName, REGSAM access, CString *outValue) {
|
| 81 | HKEY key;
|
| 82 | LSTATUS status = RegOpenKeyExA(
|
| 83 | HKEY_LOCAL_MACHINE, // hKey
|
| 84 | keyPath, // lpSubKey
|
| 85 | 0, // ulOptions
|
| 86 | KEY_READ | access, // samDesired,
|
| 87 | &key); // phkResult
|
| 88 | if (status == ERROR_SUCCESS) {
|
| 89 |
|
| 90 | LSTATUS ret = ERROR_MORE_DATA;
|
| 91 | DWORD size = 4096; // MAX_PATH is 260, so 4 KB should be good enough
|
| 92 | char* buffer = (char*) malloc(size);
|
| 93 |
|
| 94 | while (ret == ERROR_MORE_DATA && size < (1<<16) /*64 KB*/) {
|
| 95 | ret = RegQueryValueExA(
|
| 96 | key, // hKey
|
| 97 | keyName, // lpValueName
|
| 98 | NULL, // lpReserved
|
| 99 | NULL, // lpType
|
| 100 | (LPBYTE) buffer, // lpData
|
| 101 | &size); // lpcbData
|
| 102 |
|
| 103 | if (ret == ERROR_MORE_DATA) {
|
| 104 | size *= 2;
|
| 105 | buffer = (char*) realloc(buffer, size);
|
| 106 | } else {
|
| 107 | buffer[size] = 0;
|
| 108 | }
|
| 109 | }
|
| 110 |
|
| 111 | if (ret != ERROR_MORE_DATA) outValue->set(buffer);
|
| 112 |
|
| 113 | free(buffer);
|
| 114 | RegCloseKey(key);
|
| 115 |
|
| 116 | return (ret != ERROR_MORE_DATA);
|
| 117 | }
|
| 118 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 119 | return false;
|
| 120 | }
|
| 121 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 122 | bool exploreJavaRegistry(const char *entry, REGSAM access, CPath *outJavaPath) {
|
| 123 |
|
| 124 | // Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion]
|
| 125 | CPath subKey("SOFTWARE\\JavaSoft\\");
|
| 126 | subKey.addPath(entry);
|
| 127 |
|
| 128 | CString currVersion;
|
| 129 | if (getRegValue(subKey.cstr(), "CurrentVersion", access, &currVersion)) {
|
| 130 | // CurrentVersion should be something like "1.7".
|
| 131 | // We want to read HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 [JavaHome]
|
| 132 | subKey.addPath(currVersion);
|
| 133 | CPath javaHome;
|
| 134 | if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) {
|
| 135 | if (checkBinPath(&javaHome)) {
|
| 136 | *outJavaPath = javaHome;
|
| 137 | return true;
|
| 138 | }
|
| 139 | }
|
| 140 | }
|
| 141 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 142 | return false;
|
| 143 | }
|
| 144 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 145 | bool findJavaInRegistry(CPath *outJavaPath) {
|
| 146 | // We'll do the registry test 3 times: first using the default mode,
|
| 147 | // then forcing the use of the 32-bit registry then forcing the use of
|
| 148 | // 64-bit registry. On Windows 2k, the 2 latter will fail since the
|
| 149 | // flags are not supported. On a 32-bit OS the 64-bit is obviously
|
| 150 | // useless and the 2 first test should be equivalent so we just
|
| 151 | // need the first case.
|
| 152 |
|
| 153 | // Check the JRE first, then the JDK.
|
| 154 | if (exploreJavaRegistry("Java Runtime Environment", 0, outJavaPath) ||
|
| 155 | exploreJavaRegistry("Java Development Kit", 0, outJavaPath)) {
|
| 156 | return true;
|
| 157 | }
|
| 158 |
|
| 159 | // Check the real sysinfo state (not the one hidden by WOW64) for x86
|
| 160 | SYSTEM_INFO sysInfo;
|
| 161 | GetNativeSystemInfo(&sysInfo);
|
| 162 |
|
| 163 | if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
|
| 164 | if (exploreJavaRegistry("Java Runtime Environment", KEY_WOW64_32KEY, outJavaPath) ||
|
| 165 | exploreJavaRegistry("Java Development Kit", KEY_WOW64_32KEY, outJavaPath)) {
|
| 166 | return true;
|
| 167 | }
|
| 168 |
|
| 169 | if (exploreJavaRegistry("Java Runtime Environment", KEY_WOW64_64KEY, outJavaPath) ||
|
| 170 | exploreJavaRegistry("Java Development Kit", KEY_WOW64_64KEY, outJavaPath)) {
|
| 171 | return true;
|
| 172 | }
|
| 173 | }
|
| 174 |
|
| 175 | return false;
|
| 176 | }
|
| 177 |
|
| 178 | // --------------
|
| 179 |
|
| 180 | static bool checkProgramFiles(CPath *outJavaPath) {
|
| 181 |
|
| 182 | char programFilesPath[MAX_PATH + 1];
|
| 183 | HRESULT result = SHGetFolderPathA(
|
| 184 | NULL, // hwndOwner
|
| 185 | CSIDL_PROGRAM_FILES, // nFolder
|
| 186 | NULL, // hToken
|
| 187 | SHGFP_TYPE_CURRENT, // dwFlags
|
| 188 | programFilesPath); // pszPath
|
| 189 | if (FAILED(result)) return false;
|
| 190 |
|
| 191 | CPath path(programFilesPath);
|
| 192 | path.addPath("Java");
|
| 193 |
|
| 194 | // Do we have a C:\\Program Files\\Java directory?
|
| 195 | if (!path.dirExists()) return false;
|
| 196 |
|
| 197 | CPath glob(path);
|
| 198 | glob.addPath("j*");
|
| 199 |
|
| 200 | bool found = false;
|
| 201 | WIN32_FIND_DATAA findData;
|
| 202 | HANDLE findH = FindFirstFileA(glob.cstr(), &findData);
|
| 203 | if (findH == INVALID_HANDLE_VALUE) return false;
|
| 204 | do {
|
| 205 | if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
| 206 | CPath temp(path);
|
| 207 | temp.addPath(findData.cFileName);
|
| 208 | // Check C:\\Program Files[x86]\\Java\\{jdk,jre}*\\bin\\java.exe
|
| 209 | if (checkBinPath(&temp)) {
|
| 210 | found = true;
|
| 211 | *outJavaPath = temp;
|
| 212 | }
|
| 213 | }
|
| 214 | } while (!found && FindNextFileA(findH, &findData) != 0);
|
| 215 | FindClose(findH);
|
| 216 |
|
| 217 | return found;
|
| 218 | }
|
| 219 |
|
| 220 | bool findJavaInProgramFiles(CPath *outJavaPath) {
|
| 221 | // Check the C:\\Program Files (x86) directory
|
| 222 | // With WOW64 fs redirection in place by default, we should get the x86
|
| 223 | // version on a 64-bit OS since this app is a 32-bit itself.
|
| 224 | if (checkProgramFiles(outJavaPath)) return true;
|
| 225 |
|
| 226 | // Check the real sysinfo state (not the one hidden by WOW64) for x86
|
| 227 | SYSTEM_INFO sysInfo;
|
| 228 | GetNativeSystemInfo(&sysInfo);
|
| 229 |
|
| 230 | if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
|
| 231 | // On a 64-bit OS, try again by disabling the fs redirection so
|
| 232 | // that we can try the real C:\\Program Files directory.
|
| 233 | PVOID oldWow64Value = disableWow64FsRedirection();
|
| 234 | bool found = checkProgramFiles(outJavaPath);
|
| 235 | revertWow64FsRedirection(oldWow64Value);
|
| 236 | return found;
|
| 237 | }
|
| 238 |
|
| 239 | return false;
|
| 240 | }
|
| 241 |
|
| 242 | // --------------
|
| 243 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 244 | bool getJavaVersion(CPath &javaPath, CString *version) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 245 | bool result = false;
|
| 246 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 247 | // Run "java -version", which outputs something like to *STDERR*:
|
| 248 | //
|
| 249 | // java version "1.6.0_29"
|
| 250 | // Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
|
| 251 | // Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing)
|
| 252 | //
|
| 253 | // We want to capture the first line, and more exactly the "1.6" part.
|
| 254 |
|
| 255 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 256 | CString cmd;
|
| 257 | cmd.setf("\"%s\" -version", javaPath.cstr());
|
| 258 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 259 | SECURITY_ATTRIBUTES saAttr;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 260 | STARTUPINFO startup;
|
| 261 | PROCESS_INFORMATION pinfo;
|
| 262 |
|
| 263 | // Want to inherit pipe handle
|
| 264 | ZeroMemory(&saAttr, sizeof(saAttr));
|
| 265 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
| 266 | saAttr.bInheritHandle = TRUE;
|
| 267 | saAttr.lpSecurityDescriptor = NULL;
|
| 268 |
|
| 269 | // Create pipe for stdout
|
| 270 | HANDLE stdoutPipeRd, stdoutPipeWt;
|
| 271 | if (!CreatePipe(
|
| 272 | &stdoutPipeRd, // hReadPipe,
|
| 273 | &stdoutPipeWt, // hWritePipe,
|
| 274 | &saAttr, // lpPipeAttributes,
|
| 275 | 0)) { // nSize (0=default buffer size)
|
| 276 | displayLastError("CreatePipe failed: ");
|
| 277 | return false;
|
| 278 | }
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 279 | if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) {
|
| 280 | displayLastError("SetHandleInformation failed: ");
|
| 281 | return false;
|
| 282 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 283 |
|
| 284 | ZeroMemory(&pinfo, sizeof(pinfo));
|
| 285 |
|
| 286 | ZeroMemory(&startup, sizeof(startup));
|
| 287 | startup.cb = sizeof(startup);
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 288 | startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 289 | startup.wShowWindow = SW_HIDE|SW_MINIMIZE;
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 290 | // Capture both stderr and stdout
|
| 291 | startup.hStdError = stdoutPipeWt;
|
| 292 | startup.hStdOutput = stdoutPipeWt;
|
| 293 | startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 294 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 295 | BOOL ok = CreateProcessA(
|
| 296 | NULL, // program path
|
| 297 | (LPSTR) cmd.cstr(), // command-line
|
| 298 | NULL, // process handle is not inheritable
|
| 299 | NULL, // thread handle is not inheritable
|
| 300 | TRUE, // yes, inherit some handles
|
| 301 | 0, // process creation flags
|
| 302 | NULL, // use parent's environment block
|
| 303 | NULL, // use parent's starting directory
|
| 304 | &startup, // startup info, i.e. std handles
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 305 | &pinfo);
|
| 306 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 307 | if (gIsConsole && !ok) displayLastError("CreateProcess failed: ");
|
| 308 |
|
| 309 | // Close the write-end of the output pipe (we're only reading from it)
|
| 310 | CloseHandle(stdoutPipeWt);
|
| 311 |
|
| 312 | // Read from the output pipe. We don't need to read everything,
|
| 313 | // the first line should be 'Java version "1.2.3_45"\r\n'
|
| 314 | // so reading about 32 chars is all we need.
|
| 315 | char first32[32 + 1];
|
| 316 | int index = 0;
|
| 317 | first32[0] = 0;
|
| 318 |
|
| 319 | if (ok) {
|
| 320 |
|
| 321 | #define SIZE 1024
|
| 322 | char buffer[SIZE];
|
| 323 | DWORD sizeRead = 0;
|
| 324 |
|
| 325 | while (ok) {
|
| 326 | // Keep reading in the same buffer location
|
| 327 | ok = ReadFile(stdoutPipeRd, // hFile
|
| 328 | buffer, // lpBuffer
|
| 329 | SIZE, // DWORD buffer size to read
|
| 330 | &sizeRead, // DWORD buffer size read
|
| 331 | NULL); // overlapped
|
| 332 | if (!ok || sizeRead == 0 || sizeRead > SIZE) break;
|
| 333 |
|
| 334 | // Copy up to the first 32 characters
|
| 335 | if (index < 32) {
|
| 336 | DWORD n = 32 - index;
|
| 337 | if (n > sizeRead) n = sizeRead;
|
| 338 | // copy as lowercase to simplify checks later
|
| 339 | for (char *b = buffer; n > 0; n--, b++, index++) {
|
| 340 | char c = *b;
|
| 341 | if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
|
| 342 | first32[index] = c;
|
| 343 | }
|
| 344 | first32[index] = 0;
|
| 345 | }
|
| 346 | }
|
| 347 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 348 | WaitForSingleObject(pinfo.hProcess, INFINITE);
|
| 349 |
|
| 350 | DWORD exitCode;
|
| 351 | if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
|
| 352 | // this should not return STILL_ACTIVE (259)
|
| 353 | result = exitCode == 0;
|
| 354 | }
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 355 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 356 | CloseHandle(pinfo.hProcess);
|
| 357 | CloseHandle(pinfo.hThread);
|
| 358 | }
|
| 359 | CloseHandle(stdoutPipeRd);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 360 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 361 | if (index > 0) {
|
| 362 | // Look for a few keywords in the output however we don't
|
| 363 | // care about specific ordering or case-senstiviness.
|
| 364 | // We only captures roughtly the first line in lower case.
|
| 365 | char *j = strstr(first32, "java");
|
| 366 | char *v = strstr(first32, "version");
|
| 367 | if (gIsDebug && gIsConsole && (!j || !v)) {
|
| 368 | fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32);
|
| 369 | }
|
| 370 | if (j != NULL && v != NULL) {
|
| 371 | // Now extract the first thing that looks like digit.digit
|
| 372 | for (int i = 0; i < index - 2; i++) {
|
| 373 | if (isdigit(first32[i]) &&
|
| 374 | first32[i+1] == '.' &&
|
| 375 | isdigit(first32[i+2])) {
|
| 376 | version->set(first32 + i, 3);
|
| 377 | result = true;
|
| 378 | break;
|
| 379 | }
|
| 380 | }
|
| 381 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 382 | }
|
| 383 |
|
| 384 | return result;
|
| 385 | }
|
| 386 |
|
| 387 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 388 | #endif /* _WIN32 */
|