int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { gDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL); PVOID oldWow64Value = disableWow64FsRedirection(); CPath javaPath; if (!findJavaInEnvPath(&javaPath) && !findJavaInRegistry(&javaPath) && !findJavaInProgramFiles(&javaPath)) { msgBox("Failed to find Java on your system. Please reinstall it."); return 2; } _ASSERT(!javaPath.isEmpty()); revertWow64FsRedirection(oldWow64Value); // For debugging it's convenient to override the tools directory location CPath toolsDir(getenv("ANDROID_SDKMAN_TOOLS_DIR")); if (toolsDir.isEmpty()) { if (!getModuleDir(&toolsDir)) { displayLastError("Failed to get program's filename: "); return 1; } } _ASSERT(!toolsDir.isEmpty()); CPath tmpDir; if (!mkTempDir("temp-android-tool", &tmpDir)) { return 1; } _ASSERT(!tmpDir.isEmpty()); if (!mkDirs(tmpDir.cstr(), sMkDirList)) { return 1; } if (!copyFiles(toolsDir.cstr(), tmpDir.cstr(), sFilesToCopy)) { return 1; } if (!execSdkManager(javaPath.cstr(), toolsDir.cstr(), tmpDir.cstr(), lpCmdLine)) { displayLastError("Failed to start SDK Manager: "); return 1; } return 0; }
// Tries to invoke the java.exe at the given path and extract it's // version number. // - outVersionStr: if not null, will capture version as a string (e.g. "1.6") // - outVersionInt: if not null, will capture version as an int (major * 1000 + minor, e.g. 1006). bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) { bool result = false; // Run "java -version", which outputs something like to *STDERR*: // // java version "1.6.0_29" // Java(TM) SE Runtime Environment (build 1.6.0_29-b11) // Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing) // // We want to capture the first line, and more exactly the "1.6" part. CString cmd; cmd.setf("\"%s\" -version", javaPath.cstr()); SECURITY_ATTRIBUTES saAttr; STARTUPINFO startup; PROCESS_INFORMATION pinfo; // Want to inherit pipe handle ZeroMemory(&saAttr, sizeof(saAttr)); saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create pipe for stdout HANDLE stdoutPipeRd, stdoutPipeWt; if (!CreatePipe( &stdoutPipeRd, // hReadPipe, &stdoutPipeWt, // hWritePipe, &saAttr, // lpPipeAttributes, 0)) { // nSize (0=default buffer size) if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: "); return false; } if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) { if (gIsConsole || gIsDebug) displayLastError("SetHandleInformation failed: "); return false; } ZeroMemory(&pinfo, sizeof(pinfo)); ZeroMemory(&startup, sizeof(startup)); startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; startup.wShowWindow = SW_HIDE|SW_MINIMIZE; // Capture both stderr and stdout startup.hStdError = stdoutPipeWt; startup.hStdOutput = stdoutPipeWt; startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); BOOL ok = CreateProcessA( NULL, // program path (LPSTR) cmd.cstr(), // command-line NULL, // process handle is not inheritable NULL, // thread handle is not inheritable TRUE, // yes, inherit some handles 0, // process creation flags NULL, // use parent's environment block NULL, // use parent's starting directory &startup, // startup info, i.e. std handles &pinfo); if ((gIsConsole || gIsDebug) && !ok) displayLastError("CreateProcess failed: "); // Close the write-end of the output pipe (we're only reading from it) CloseHandle(stdoutPipeWt); // Read from the output pipe. We don't need to read everything, // the first line should be 'Java version "1.2.3_45"\r\n' // so reading about 32 chars is all we need. char first32[32 + 1]; int index = 0; first32[0] = 0; if (ok) { #define SIZE 1024 char buffer[SIZE]; DWORD sizeRead = 0; while (ok) { // Keep reading in the same buffer location ok = ReadFile(stdoutPipeRd, // hFile buffer, // lpBuffer SIZE, // DWORD buffer size to read &sizeRead, // DWORD buffer size read NULL); // overlapped if (!ok || sizeRead == 0 || sizeRead > SIZE) break; // Copy up to the first 32 characters if (index < 32) { DWORD n = 32 - index; if (n > sizeRead) n = sizeRead; // copy as lowercase to simplify checks later for (char *b = buffer; n > 0; n--, b++, index++) { char c = *b; if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; first32[index] = c; } first32[index] = 0; } } WaitForSingleObject(pinfo.hProcess, INFINITE); DWORD exitCode; if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) { // this should not return STILL_ACTIVE (259) result = exitCode == 0; } CloseHandle(pinfo.hProcess); CloseHandle(pinfo.hThread); } CloseHandle(stdoutPipeRd); if (result && index > 0) { // Look for a few keywords in the output however we don't // care about specific ordering or case-senstiviness. // We only captures roughtly the first line in lower case. char *j = strstr(first32, "java"); char *v = strstr(first32, "version"); if ((gIsConsole || gIsDebug) && (!j || !v)) { fprintf(stderr, "Error: keywords 'java version' not found in '%s'\n", first32); } if (j != NULL && v != NULL) { result = extractJavaVersion(first32, index, outVersionStr, outVersionInt); } } return result; }
// Explore the registry to find a suitable version of Java. // Returns an int which is the version of Java found (e.g. 1006 for 1.6) and the // matching path in outJavaPath. // Returns 0 if nothing suitable was found. static int exploreJavaRegistry(const char *entry, REGSAM access, CPath *outJavaPath) { // Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion] CPath rootKey("SOFTWARE\\JavaSoft\\"); rootKey.addPath(entry); int versionInt = 0; CString currentVersion; CPath subKey(rootKey); if (getRegValue(subKey.cstr(), "CurrentVersion", access, ¤tVersion)) { // CurrentVersion should be something like "1.7". // We want to read HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 [JavaHome] subKey.addPath(currentVersion); CPath javaHome; if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) { versionInt = checkBinPath(&javaHome); if (versionInt >= 0) { if (gIsDebug) { fprintf(stderr, "Java %d found via registry: %s\n", versionInt, javaHome.cstr()); } *outJavaPath = javaHome; } if (versionInt >= MIN_JAVA_VERSION) { // Heuristic: if the current version is good enough, stop here return versionInt; } } } // Try again, but this time look at all the versions available HKEY javaHomeKey; LSTATUS status = RegOpenKeyExA( HKEY_LOCAL_MACHINE, // hKey "SOFTWARE\\JavaSoft", // lpSubKey 0, // ulOptions KEY_READ | access, // samDesired &javaHomeKey); // phkResult if (status == ERROR_SUCCESS) { char name[256]; DWORD index = 0; CPath javaHome; for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; index++) { DWORD nameLen = 255; name[nameLen] = 0; result = RegEnumKeyExA( javaHomeKey, // hKey index, // dwIndex name, // lpName &nameLen, // lpcName NULL, // lpReserved NULL, // lpClass NULL, // lpcClass, NULL); // lpftLastWriteTime if (result == ERROR_SUCCESS && nameLen < 256) { name[nameLen] = 0; CPath subKey(rootKey); subKey.addPath(name); if (getRegValue(subKey.cstr(), "JavaHome", access, &javaHome)) { int v = checkBinPath(&javaHome); if (v > versionInt) { if (gIsDebug) { fprintf(stderr, "Java %d found via registry: %s\n", versionInt, javaHome.cstr()); } *outJavaPath = javaHome; versionInt = v; } } } } RegCloseKey(javaHomeKey); } return 0; }