| 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 |
|
| Raphael | 692d2b5 | 2012-01-26 10:57:08 -0800 | [diff] [blame] | 19 | // Indicate we want at least all Windows Server 2003 (5.2) APIs.
|
| 20 | // Note: default is set by system/core/include/arch/windows/AndroidConfig.h to 0x0500
|
| 21 | // which is Win2K. However our minimum SDK tools requirement is Win XP (0x0501).
|
| 22 | // However we do need 0x0502 to get access to the WOW-64-32 constants for the
|
| 23 | // registry, except we'll need to be careful since they are not available on XP.
|
| 24 | #undef _WIN32_WINNT
|
| 25 | #define _WIN32_WINNT 0x0502
|
| 26 | // Indicate we want at least all IE 5 shell APIs
|
| 27 | #define _WIN32_IE 0x0500
|
| 28 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 29 | #include "find_java.h"
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 30 | #include <shlobj.h>
|
| Raphael | 692d2b5 | 2012-01-26 10:57:08 -0800 | [diff] [blame] | 31 | #include <ctype.h>
|
| 32 |
|
| 33 | // Define some types missing in MingW
|
| 34 | #ifndef LSTATUS
|
| 35 | typedef LONG LSTATUS;
|
| 36 | #endif
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 37 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 38 |
|
| 39 | // Extract the first thing that looks like (digit.digit+).
|
| 40 | // Note: this will break when java reports a version with major > 9.
|
| 41 | // However it will reasonably cope with "1.10", if that ever happens.
|
| 42 | static bool extractJavaVersion(const char *start,
|
| 43 | int length,
|
| 44 | CString *outVersionStr,
|
| 45 | int *outVersionInt) {
|
| 46 | const char *end = start + length;
|
| 47 | for (const char *c = start; c < end - 2; c++) {
|
| 48 | if (isdigit(c[0]) &&
|
| 49 | c[1] == '.' &&
|
| 50 | isdigit(c[2])) {
|
| 51 | const char *e = c+2;
|
| 52 | while (isdigit(e[1])) {
|
| 53 | e++;
|
| 54 | }
|
| 55 | if (outVersionStr != NULL) {
|
| 56 | outVersionStr->set(c, e - c + 1);
|
| 57 | }
|
| 58 | if (outVersionInt != NULL) {
|
| 59 | // add major * 1000, currently only 1 digit
|
| 60 | int value = (*c - '0') * 1000;
|
| 61 | // add minor
|
| 62 | for (int m = 1; *e != '.'; e--, m *= 10) {
|
| 63 | value += (*e - '0') * m;
|
| 64 | }
|
| 65 | *outVersionInt = value;
|
| 66 | }
|
| 67 | return true;
|
| 68 | }
|
| 69 | }
|
| 70 | return false;
|
| 71 | }
|
| 72 |
|
| 73 | // Check whether we can find $PATH/java.exe.
|
| 74 | // inOutPath should be the directory where we're looking at.
|
| 75 | // In output, it will be the java path we tested.
|
| 76 | // Returns the java version integer found (e.g. 1006 for 1.6).
|
| 77 | // Return 0 in case of error.
|
| 78 | static int checkPath(CPath *inOutPath) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 79 | inOutPath->addPath("java.exe");
|
| 80 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 81 | int result = 0;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 82 | PVOID oldWow64Value = disableWow64FsRedirection();
|
| 83 | if (inOutPath->fileExists()) {
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 84 | // Run java -version
|
| 85 | // Reject the version if it's not at least our current minimum.
|
| 86 | if (!getJavaVersion(*inOutPath, NULL /*versionStr*/, &result)) {
|
| 87 | result = 0;
|
| 88 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 89 | }
|
| 90 |
|
| 91 | revertWow64FsRedirection(oldWow64Value);
|
| 92 | return result;
|
| 93 | }
|
| 94 |
|
| 95 | // Check whether we can find $PATH/bin/java.exe
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 96 | // Returns the Java version found (e.g. 1006 for 1.6) or 0 in case of error.
|
| 97 | static int checkBinPath(CPath *inOutPath) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 98 | inOutPath->addPath("bin");
|
| 99 | return checkPath(inOutPath);
|
| 100 | }
|
| 101 |
|
| 102 | // Search java.exe in the environment
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 103 | int findJavaInEnvPath(CPath *outJavaPath) {
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 104 | SetLastError(0);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 105 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 106 | int currVersion = 0;
|
| 107 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 108 | const char* envPath = getenv("JAVA_HOME");
|
| 109 | if (envPath != NULL) {
|
| 110 | CPath p(envPath);
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 111 | currVersion = checkBinPath(&p);
|
| 112 | if (currVersion > 0) {
|
| 113 | if (gIsDebug) {
|
| 114 | fprintf(stderr, "Java %d found via JAVA_HOME: %s\n", currVersion, p.cstr());
|
| 115 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 116 | *outJavaPath = p;
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 117 | }
|
| 118 | if (currVersion >= MIN_JAVA_VERSION) {
|
| 119 | // As an optimization for runtime, if we find a suitable java
|
| 120 | // version in JAVA_HOME we won't waste time looking at the PATH.
|
| 121 | return currVersion;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 122 | }
|
| 123 | }
|
| 124 |
|
| 125 | envPath = getenv("PATH");
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 126 | if (!envPath) return currVersion;
|
| 127 |
|
| 128 | // Otherwise look at the entries in the current path.
|
| 129 | // If we find more than one, keep the one with the highest version.
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 130 |
|
| 131 | CArray<CString> *paths = CString(envPath).split(';');
|
| 132 | for(int i = 0; i < paths->size(); i++) {
|
| 133 | CPath p((*paths)[i].cstr());
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 134 | int v = checkPath(&p);
|
| 135 | if (v > currVersion) {
|
| 136 | if (gIsDebug) {
|
| 137 | fprintf(stderr, "Java %d found via env PATH: %s\n", v, p.cstr());
|
| 138 | }
|
| 139 | currVersion = v;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 140 | *outJavaPath = p;
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 141 | }
|
| 142 | }
|
| 143 |
|
| 144 | delete paths;
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 145 | return currVersion;
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 146 | }
|
| 147 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 148 | // --------------
|
| 149 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 150 | static bool getRegValue(const char *keyPath,
|
| 151 | const char *keyName,
|
| 152 | REGSAM access,
|
| 153 | CString *outValue) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 154 | HKEY key;
|
| 155 | LSTATUS status = RegOpenKeyExA(
|
| 156 | HKEY_LOCAL_MACHINE, // hKey
|
| 157 | keyPath, // lpSubKey
|
| 158 | 0, // ulOptions
|
| 159 | KEY_READ | access, // samDesired,
|
| 160 | &key); // phkResult
|
| 161 | if (status == ERROR_SUCCESS) {
|
| 162 |
|
| 163 | LSTATUS ret = ERROR_MORE_DATA;
|
| 164 | DWORD size = 4096; // MAX_PATH is 260, so 4 KB should be good enough
|
| 165 | char* buffer = (char*) malloc(size);
|
| 166 |
|
| 167 | while (ret == ERROR_MORE_DATA && size < (1<<16) /*64 KB*/) {
|
| 168 | ret = RegQueryValueExA(
|
| 169 | key, // hKey
|
| 170 | keyName, // lpValueName
|
| 171 | NULL, // lpReserved
|
| 172 | NULL, // lpType
|
| 173 | (LPBYTE) buffer, // lpData
|
| 174 | &size); // lpcbData
|
| 175 |
|
| 176 | if (ret == ERROR_MORE_DATA) {
|
| 177 | size *= 2;
|
| 178 | buffer = (char*) realloc(buffer, size);
|
| 179 | } else {
|
| 180 | buffer[size] = 0;
|
| 181 | }
|
| 182 | }
|
| 183 |
|
| 184 | if (ret != ERROR_MORE_DATA) outValue->set(buffer);
|
| 185 |
|
| 186 | free(buffer);
|
| 187 | RegCloseKey(key);
|
| 188 |
|
| 189 | return (ret != ERROR_MORE_DATA);
|
| 190 | }
|
| 191 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 192 | return false;
|
| 193 | }
|
| 194 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 195 | // Explore the registry to find a suitable version of Java.
|
| 196 | // Returns an int which is the version of Java found (e.g. 1006 for 1.6) and the
|
| 197 | // matching path in outJavaPath.
|
| 198 | // Returns 0 if nothing suitable was found.
|
| 199 | static int exploreJavaRegistry(const char *entry, REGSAM access, CPath *outJavaPath) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 200 |
|
| 201 | // Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion]
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 202 | CPath rootKey("SOFTWARE\\JavaSoft\\");
|
| 203 | rootKey.addPath(entry);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 204 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 205 | int versionInt = 0;
|
| 206 | CString currentVersion;
|
| 207 | CPath subKey(rootKey);
|
| 208 | if (getRegValue(subKey.cstr(), "CurrentVersion", access, ¤tVersion)) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 209 | // CurrentVersion should be something like "1.7".
|
| 210 | // We want to read HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 [JavaHome]
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 211 | subKey.addPath(currentVersion);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 212 | CPath javaHome;
|
| 213 | if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) {
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 214 | versionInt = checkBinPath(&javaHome);
|
| 215 | if (versionInt >= 0) {
|
| 216 | if (gIsDebug) {
|
| 217 | fprintf(stderr,
|
| 218 | "Java %d found via registry: %s\n",
|
| 219 | versionInt, javaHome.cstr());
|
| 220 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 221 | *outJavaPath = javaHome;
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 222 | }
|
| 223 | if (versionInt >= MIN_JAVA_VERSION) {
|
| 224 | // Heuristic: if the current version is good enough, stop here
|
| 225 | return versionInt;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 226 | }
|
| 227 | }
|
| 228 | }
|
| 229 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 230 | // Try again, but this time look at all the versions available
|
| 231 | HKEY javaHomeKey;
|
| 232 | LSTATUS status = RegOpenKeyExA(
|
| 233 | HKEY_LOCAL_MACHINE, // hKey
|
| 234 | "SOFTWARE\\JavaSoft", // lpSubKey
|
| 235 | 0, // ulOptions
|
| 236 | KEY_READ | access, // samDesired
|
| 237 | &javaHomeKey); // phkResult
|
| 238 | if (status == ERROR_SUCCESS) {
|
| 239 | char name[256];
|
| 240 | DWORD index = 0;
|
| 241 | CPath javaHome;
|
| 242 | for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; index++) {
|
| 243 | DWORD nameLen = 255;
|
| 244 | name[nameLen] = 0;
|
| 245 | result = RegEnumKeyExA(
|
| 246 | javaHomeKey, // hKey
|
| 247 | index, // dwIndex
|
| 248 | name, // lpName
|
| 249 | &nameLen, // lpcName
|
| 250 | NULL, // lpReserved
|
| 251 | NULL, // lpClass
|
| 252 | NULL, // lpcClass,
|
| 253 | NULL); // lpftLastWriteTime
|
| 254 | if (result == ERROR_SUCCESS && nameLen < 256) {
|
| 255 | name[nameLen] = 0;
|
| 256 | CPath subKey(rootKey);
|
| 257 | subKey.addPath(name);
|
| 258 |
|
| 259 | if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) {
|
| 260 | int v = checkBinPath(&javaHome);
|
| 261 | if (v > versionInt) {
|
| 262 | if (gIsDebug) {
|
| 263 | fprintf(stderr,
|
| 264 | "Java %d found via registry: %s\n",
|
| 265 | versionInt, javaHome.cstr());
|
| 266 | }
|
| 267 | *outJavaPath = javaHome;
|
| 268 | versionInt = v;
|
| 269 | }
|
| 270 | }
|
| 271 | }
|
| 272 | }
|
| 273 |
|
| 274 | RegCloseKey(javaHomeKey);
|
| 275 | }
|
| 276 |
|
| 277 | return 0;
|
| 278 | }
|
| 279 |
|
| 280 | static bool getMaxJavaInRegistry(const char *entry, REGSAM access, CPath *outJavaPath, int *inOutVersion) {
|
| 281 | CPath path;
|
| 282 | int version = exploreJavaRegistry(entry, access, &path);
|
| 283 | if (version > *inOutVersion) {
|
| 284 | *outJavaPath = path;
|
| 285 | *inOutVersion = version;
|
| 286 | return true;
|
| 287 | }
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 288 | return false;
|
| 289 | }
|
| 290 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 291 | int findJavaInRegistry(CPath *outJavaPath) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 292 | // We'll do the registry test 3 times: first using the default mode,
|
| 293 | // then forcing the use of the 32-bit registry then forcing the use of
|
| 294 | // 64-bit registry. On Windows 2k, the 2 latter will fail since the
|
| 295 | // flags are not supported. On a 32-bit OS the 64-bit is obviously
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 296 | // useless and the 2 first tests should be equivalent so we just
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 297 | // need the first case.
|
| 298 |
|
| 299 | // Check the JRE first, then the JDK.
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 300 | int version = MIN_JAVA_VERSION - 1;
|
| 301 | bool result = false;
|
| 302 | result |= getMaxJavaInRegistry("Java Runtime Environment", 0, outJavaPath, &version);
|
| 303 | result |= getMaxJavaInRegistry("Java Development Kit", 0, outJavaPath, &version);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 304 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 305 | // Get the app sysinfo state (the one hidden by WOW64)
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 306 | SYSTEM_INFO sysInfo;
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 307 | GetSystemInfo(&sysInfo);
|
| 308 | WORD programArch = sysInfo.wProcessorArchitecture;
|
| 309 | // Check the real sysinfo state (not the one hidden by WOW64) for x86
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 310 | GetNativeSystemInfo(&sysInfo);
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 311 | WORD actualArch = sysInfo.wProcessorArchitecture;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 312 |
|
| Raphael | 692d2b5 | 2012-01-26 10:57:08 -0800 | [diff] [blame] | 313 | // Only try to access the WOW64-32 redirected keys on a 64-bit system.
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 314 | // There's no point in doing this on a 32-bit system.
|
| 315 | if (actualArch == PROCESSOR_ARCHITECTURE_AMD64) {
|
| 316 | if (programArch != PROCESSOR_ARCHITECTURE_INTEL) {
|
| 317 | // If we did the 32-bit case earlier, don't do it twice.
|
| 318 | result |= getMaxJavaInRegistry(
|
| 319 | "Java Runtime Environment", KEY_WOW64_32KEY, outJavaPath, &version);
|
| 320 | result |= getMaxJavaInRegistry(
|
| 321 | "Java Development Kit", KEY_WOW64_32KEY, outJavaPath, &version);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 322 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 323 | } else if (programArch != PROCESSOR_ARCHITECTURE_AMD64) {
|
| 324 | // If we did the 64-bit case earlier, don't do it twice.
|
| 325 | result |= getMaxJavaInRegistry(
|
| 326 | "Java Runtime Environment", KEY_WOW64_64KEY, outJavaPath, &version);
|
| 327 | result |= getMaxJavaInRegistry(
|
| 328 | "Java Development Kit", KEY_WOW64_64KEY, outJavaPath, &version);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 329 | }
|
| 330 | }
|
| 331 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 332 | return result ? version : 0;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 333 | }
|
| 334 |
|
| 335 | // --------------
|
| 336 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 337 | static bool checkProgramFiles(CPath *outJavaPath, int *inOutVersion) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 338 |
|
| 339 | char programFilesPath[MAX_PATH + 1];
|
| 340 | HRESULT result = SHGetFolderPathA(
|
| 341 | NULL, // hwndOwner
|
| 342 | CSIDL_PROGRAM_FILES, // nFolder
|
| 343 | NULL, // hToken
|
| 344 | SHGFP_TYPE_CURRENT, // dwFlags
|
| 345 | programFilesPath); // pszPath
|
| 346 | if (FAILED(result)) return false;
|
| 347 |
|
| 348 | CPath path(programFilesPath);
|
| 349 | path.addPath("Java");
|
| 350 |
|
| 351 | // Do we have a C:\\Program Files\\Java directory?
|
| 352 | if (!path.dirExists()) return false;
|
| 353 |
|
| 354 | CPath glob(path);
|
| 355 | glob.addPath("j*");
|
| 356 |
|
| 357 | bool found = false;
|
| 358 | WIN32_FIND_DATAA findData;
|
| 359 | HANDLE findH = FindFirstFileA(glob.cstr(), &findData);
|
| 360 | if (findH == INVALID_HANDLE_VALUE) return false;
|
| 361 | do {
|
| 362 | if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
| 363 | CPath temp(path);
|
| 364 | temp.addPath(findData.cFileName);
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 365 | // Check C:\\Program Files[x86]\\Java\\j*\\bin\\java.exe
|
| 366 | int v = checkBinPath(&temp);
|
| 367 | if (v > *inOutVersion) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 368 | found = true;
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 369 | *inOutVersion = v;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 370 | *outJavaPath = temp;
|
| 371 | }
|
| 372 | }
|
| 373 | } while (!found && FindNextFileA(findH, &findData) != 0);
|
| 374 | FindClose(findH);
|
| 375 |
|
| 376 | return found;
|
| 377 | }
|
| 378 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 379 | int findJavaInProgramFiles(CPath *outJavaPath) {
|
| 380 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 381 | // Check the C:\\Program Files (x86) directory
|
| 382 | // With WOW64 fs redirection in place by default, we should get the x86
|
| 383 | // version on a 64-bit OS since this app is a 32-bit itself.
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 384 | bool result = false;
|
| 385 | int version = MIN_JAVA_VERSION - 1;
|
| 386 | result |= checkProgramFiles(outJavaPath, &version);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 387 |
|
| 388 | // Check the real sysinfo state (not the one hidden by WOW64) for x86
|
| 389 | SYSTEM_INFO sysInfo;
|
| 390 | GetNativeSystemInfo(&sysInfo);
|
| 391 |
|
| 392 | if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
|
| 393 | // On a 64-bit OS, try again by disabling the fs redirection so
|
| 394 | // that we can try the real C:\\Program Files directory.
|
| 395 | PVOID oldWow64Value = disableWow64FsRedirection();
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 396 | result |= checkProgramFiles(outJavaPath, &version);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 397 | revertWow64FsRedirection(oldWow64Value);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 398 | }
|
| 399 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 400 | return result ? version : 0;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 401 | }
|
| 402 |
|
| 403 | // --------------
|
| 404 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 405 |
|
| 406 | // Tries to invoke the java.exe at the given path and extract it's
|
| 407 | // version number.
|
| 408 | // - outVersionStr: if not null, will capture version as a string (e.g. "1.6")
|
| 409 | // - outVersionInt: if not null, will capture version as an int (major * 1000 + minor, e.g. 1006).
|
| 410 | bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) {
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 411 | bool result = false;
|
| 412 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 413 | // Run "java -version", which outputs something like to *STDERR*:
|
| 414 | //
|
| 415 | // java version "1.6.0_29"
|
| 416 | // Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
|
| 417 | // Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing)
|
| 418 | //
|
| 419 | // We want to capture the first line, and more exactly the "1.6" part.
|
| 420 |
|
| 421 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 422 | CString cmd;
|
| 423 | cmd.setf("\"%s\" -version", javaPath.cstr());
|
| 424 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 425 | SECURITY_ATTRIBUTES saAttr;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 426 | STARTUPINFO startup;
|
| 427 | PROCESS_INFORMATION pinfo;
|
| 428 |
|
| 429 | // Want to inherit pipe handle
|
| 430 | ZeroMemory(&saAttr, sizeof(saAttr));
|
| 431 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
| 432 | saAttr.bInheritHandle = TRUE;
|
| 433 | saAttr.lpSecurityDescriptor = NULL;
|
| 434 |
|
| 435 | // Create pipe for stdout
|
| 436 | HANDLE stdoutPipeRd, stdoutPipeWt;
|
| 437 | if (!CreatePipe(
|
| 438 | &stdoutPipeRd, // hReadPipe,
|
| 439 | &stdoutPipeWt, // hWritePipe,
|
| 440 | &saAttr, // lpPipeAttributes,
|
| 441 | 0)) { // nSize (0=default buffer size)
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 442 | if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: ");
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 443 | return false;
|
| 444 | }
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 445 | if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) {
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 446 | if (gIsConsole || gIsDebug) displayLastError("SetHandleInformation failed: ");
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 447 | return false;
|
| 448 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 449 |
|
| 450 | ZeroMemory(&pinfo, sizeof(pinfo));
|
| 451 |
|
| 452 | ZeroMemory(&startup, sizeof(startup));
|
| 453 | startup.cb = sizeof(startup);
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 454 | startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 455 | startup.wShowWindow = SW_HIDE|SW_MINIMIZE;
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 456 | // Capture both stderr and stdout
|
| 457 | startup.hStdError = stdoutPipeWt;
|
| 458 | startup.hStdOutput = stdoutPipeWt;
|
| 459 | startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 460 |
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 461 | BOOL ok = CreateProcessA(
|
| 462 | NULL, // program path
|
| 463 | (LPSTR) cmd.cstr(), // command-line
|
| 464 | NULL, // process handle is not inheritable
|
| 465 | NULL, // thread handle is not inheritable
|
| 466 | TRUE, // yes, inherit some handles
|
| 467 | 0, // process creation flags
|
| 468 | NULL, // use parent's environment block
|
| 469 | NULL, // use parent's starting directory
|
| 470 | &startup, // startup info, i.e. std handles
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 471 | &pinfo);
|
| 472 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 473 | if ((gIsConsole || gIsDebug) && !ok) displayLastError("CreateProcess failed: ");
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 474 |
|
| 475 | // Close the write-end of the output pipe (we're only reading from it)
|
| 476 | CloseHandle(stdoutPipeWt);
|
| 477 |
|
| 478 | // Read from the output pipe. We don't need to read everything,
|
| 479 | // the first line should be 'Java version "1.2.3_45"\r\n'
|
| 480 | // so reading about 32 chars is all we need.
|
| 481 | char first32[32 + 1];
|
| 482 | int index = 0;
|
| 483 | first32[0] = 0;
|
| 484 |
|
| 485 | if (ok) {
|
| 486 |
|
| 487 | #define SIZE 1024
|
| 488 | char buffer[SIZE];
|
| 489 | DWORD sizeRead = 0;
|
| 490 |
|
| 491 | while (ok) {
|
| 492 | // Keep reading in the same buffer location
|
| 493 | ok = ReadFile(stdoutPipeRd, // hFile
|
| 494 | buffer, // lpBuffer
|
| 495 | SIZE, // DWORD buffer size to read
|
| 496 | &sizeRead, // DWORD buffer size read
|
| 497 | NULL); // overlapped
|
| 498 | if (!ok || sizeRead == 0 || sizeRead > SIZE) break;
|
| 499 |
|
| 500 | // Copy up to the first 32 characters
|
| 501 | if (index < 32) {
|
| 502 | DWORD n = 32 - index;
|
| 503 | if (n > sizeRead) n = sizeRead;
|
| 504 | // copy as lowercase to simplify checks later
|
| 505 | for (char *b = buffer; n > 0; n--, b++, index++) {
|
| 506 | char c = *b;
|
| 507 | if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
|
| 508 | first32[index] = c;
|
| 509 | }
|
| 510 | first32[index] = 0;
|
| 511 | }
|
| 512 | }
|
| 513 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 514 | WaitForSingleObject(pinfo.hProcess, INFINITE);
|
| 515 |
|
| 516 | DWORD exitCode;
|
| 517 | if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
|
| 518 | // this should not return STILL_ACTIVE (259)
|
| 519 | result = exitCode == 0;
|
| 520 | }
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 521 |
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 522 | CloseHandle(pinfo.hProcess);
|
| 523 | CloseHandle(pinfo.hThread);
|
| 524 | }
|
| 525 | CloseHandle(stdoutPipeRd);
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 526 |
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 527 | if (result && index > 0) {
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 528 | // Look for a few keywords in the output however we don't
|
| 529 | // care about specific ordering or case-senstiviness.
|
| 530 | // We only captures roughtly the first line in lower case.
|
| 531 | char *j = strstr(first32, "java");
|
| 532 | char *v = strstr(first32, "version");
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 533 | if ((gIsConsole || gIsDebug) && (!j || !v)) {
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 534 | fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32);
|
| 535 | }
|
| 536 | if (j != NULL && v != NULL) {
|
| Raphael | 6123b53 | 2012-01-30 14:41:44 -0800 | [diff] [blame^] | 537 | result = extractJavaVersion(first32, index, outVersionStr, outVersionInt);
|
| Raphael | d2d6999 | 2012-01-24 15:06:56 -0800 | [diff] [blame] | 538 | }
|
| Raphael | 22fd3c1 | 2011-12-02 21:58:23 -0800 | [diff] [blame] | 539 | }
|
| 540 |
|
| 541 | return result;
|
| 542 | }
|
| 543 |
|
| 544 |
|
| Raphael | 628920b | 2011-11-22 10:40:13 -0800 | [diff] [blame] | 545 | #endif /* _WIN32 */
|