aboutsummaryrefslogtreecommitdiff
path: root/find_java/src/source
diff options
context:
space:
mode:
Diffstat (limited to 'find_java/src/source')
-rwxr-xr-xfind_java/src/source/find_java.h45
-rw-r--r--find_java/src/source/find_java_exe.cpp237
-rwxr-xr-xfind_java/src/source/find_java_lib.cpp573
-rwxr-xr-xfind_java/src/source/utils.cpp202
-rwxr-xr-xfind_java/src/source/utils.h370
5 files changed, 1427 insertions, 0 deletions
diff --git a/find_java/src/source/find_java.h b/find_java/src/source/find_java.h
new file mode 100755
index 000000000..49b5fd9f4
--- /dev/null
+++ b/find_java/src/source/find_java.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _H_FIND_JAVA
+#define _H_FIND_JAVA
+
+#ifdef _WIN32
+
+#include "utils.h"
+
+#define TO_JAVA_VERSION(major, minor) ((major) * 1000 + (minor))
+#define JAVA_MAJOR(version) ((version) / 1000)
+#define JAVA_MINOR(version) ((version) % 1000)
+
+// We currently search for a Java version for at least 1.6 by default
+#define MIN_JAVA_VERSION_MAJOR 1
+#define MIN_JAVA_VERSION_MINOR 6
+#define MIN_JAVA_VERSION TO_JAVA_VERSION(MIN_JAVA_VERSION_MAJOR, MIN_JAVA_VERSION_MINOR)
+
+int findJavaInPath(const CPath &path, CPath *outJavaPath, bool isJdk = false,
+ int minVersion = MIN_JAVA_VERSION);
+int findJavaInEnvPath(CPath *outJavaPath, bool isJdk = false,
+ int minVersion = MIN_JAVA_VERSION);
+int findJavaInRegistry(CPath *outJavaPath, bool isJdk = false,
+ int minVersion = MIN_JAVA_VERSION);
+int findJavaInProgramFiles(CPath *outJavaPath, bool isJdk = false,
+ int minVersion = MIN_JAVA_VERSION);
+
+bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt);
+
+#endif /* _WIN32 */
+#endif /* _H_FIND_JAVA */
diff --git a/find_java/src/source/find_java_exe.cpp b/find_java/src/source/find_java_exe.cpp
new file mode 100644
index 000000000..a8be377ef
--- /dev/null
+++ b/find_java/src/source/find_java_exe.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * "find_java.exe", for Windows only.
+ * Tries to find a Java binary in a variety of places and prints the
+ * first one found on STDOUT and returns 0.
+ *
+ * If not found, returns error 1 with no message
+ * (unless ANDROID_SDKMAN_DEBUG or -d if set, in which case there's a message on STDERR).
+ *
+ * Implementation details:
+ * - We don't have access to ATL or MFC.
+ * - We don't want to pull in things like STL.
+ * - No Unicode/MBCS support for now.
+ *
+ * TODO for later version:
+ * - provide an env variable to let users override which version is being used.
+ * - if there's more than one java.exe found, enumerate them all.
+ * - and in that case take the one with the highest Java version number.
+ * - since that operation is expensive, do it only once and cache the result
+ * in a temp file. If the temp file is not found or the java binary no
+ * longer exists, re-run the enumaration.
+ */
+
+#ifdef _WIN32
+
+#include "utils.h"
+#include "find_java.h"
+#include <io.h>
+#include <fcntl.h>
+
+static int showHelpMessage() {
+ printf(
+ "Outputs the path of the first Java.exe found on the local system.\n"
+ "Returns code 0 when found, 1 when not found.\n"
+ "Options:\n"
+ "-h / -help : This help.\n"
+ "-t / -test : Internal test.\n"
+ "-e / -error : Print an error message to the console if Java.exe isn't found.\n"
+ "-j / -jdk : Only returns java.exe found in a JDK.\n"
+ "-s / -short : Print path in short DOS form.\n"
+ "-p / -path `dir` : A custom path to search first. Pass in JDK base dir if -j is set.\n"
+ "-w / -javaw : Search a matching javaw.exe; defaults to java.exe if not found.\n"
+ "-m / -minv # : Pass in a minimum version to use (default: 1.6).\n"
+ "-v / -version: Only prints the Java version found.\n"
+ );
+ return 2;
+}
+
+static void printError(const char *message) {
+
+ CString error;
+ error.setLastWin32Error(message);
+ printf(error.cstr());
+}
+
+static void testFindJava(bool isJdk, int minVersion) {
+
+ CPath javaPath("<not found>");
+ int v = findJavaInEnvPath(&javaPath, isJdk, minVersion);
+ printf(" findJavaInEnvPath: [%d] %s\n", v, javaPath.cstr());
+
+ javaPath.set("<not found>");
+ v = findJavaInRegistry(&javaPath, isJdk, minVersion);
+ printf(" findJavaInRegistry [%d] %s\n", v, javaPath.cstr());
+
+ javaPath.set("<not found>");
+ v = findJavaInProgramFiles(&javaPath, isJdk, minVersion);
+ printf(" findJavaInProgramFiles [%d] %s\n", v, javaPath.cstr());
+}
+
+static void testFindJava(int minVersion) {
+
+ printf("Searching for version %d.%d or newer...\n", JAVA_MAJOR(minVersion),
+ JAVA_MINOR(minVersion));
+
+ printf("\n");
+ printf("Searching for any java.exe:\n");
+ testFindJava(false, minVersion);
+
+ printf("\n");
+ printf("Searching for java.exe within a JDK:\n");
+ testFindJava(true, minVersion);
+}
+
+// Returns 0 on failure or a java version on success.
+int parseMinVersionArg(char* arg) {
+
+ int versionMajor = -1;
+ int versionMinor = -1;
+ if (sscanf(arg, "%d.%d", &versionMajor, &versionMinor) != 2) {
+ // -m arg is malformatted
+ return 0;
+ }
+ return TO_JAVA_VERSION(versionMajor, versionMinor);
+}
+
+int main(int argc, char* argv[]) {
+
+ gIsConsole = true; // tell utils to to print errors to stderr
+ gIsDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL);
+ bool doShortPath = false;
+ bool doVersion = false;
+ bool doJavaW = false;
+ bool isJdk = false;
+ bool shouldPrintError = false;
+ int minVersion = MIN_JAVA_VERSION;
+ const char *customPathStr = NULL;
+
+ for (int i = 1; i < argc; i++) {
+ if (strncmp(argv[i], "-t", 2) == 0) {
+ testFindJava(minVersion);
+ return 0;
+
+ } else if (strncmp(argv[i], "-j", 2) == 0) {
+ isJdk = true;
+
+ } else if (strncmp(argv[i], "-e", 2) == 0) {
+ shouldPrintError = true;
+
+ } else if (strncmp(argv[i], "-p", 2) == 0) {
+ i++;
+ if (i == argc) {
+ return showHelpMessage();
+ }
+ customPathStr = argv[i];
+ } else if (strncmp(argv[i], "-d", 2) == 0) {
+ gIsDebug = true;
+
+ } else if (strncmp(argv[i], "-s", 2) == 0) {
+ doShortPath = true;
+
+ } else if (strncmp(argv[i], "-v", 2) == 0) {
+ doVersion = true;
+
+ } else if (strncmp(argv[i], "-m", 2) == 0) {
+ i++;
+ if (i == argc ||
+ ((minVersion = parseMinVersionArg(argv[i])) == 0)) {
+ return showHelpMessage();
+ }
+ }
+ else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "-javaw") == 0) {
+ doJavaW = true;
+
+ }
+ else {
+ return showHelpMessage();
+ }
+ }
+
+ // Find the first suitable version of Java we can use.
+ CPath javaPath;
+
+ int version = 0;
+ if (customPathStr != NULL) {
+ CPath customPath(customPathStr);
+ version = findJavaInPath(customPath, &javaPath, isJdk, minVersion);
+ }
+ if (version == 0) {
+ version = findJavaInEnvPath(&javaPath, isJdk, minVersion);
+ }
+ if (version == 0) {
+ version = findJavaInRegistry(&javaPath, isJdk, minVersion);
+ }
+ if (version == 0) {
+ version = findJavaInProgramFiles(&javaPath, isJdk, minVersion);
+ }
+
+ if (version == 0) {
+ CString s;
+ s.setf("Failed to find Java %d.%d (or newer) on your system. ", JAVA_MAJOR(minVersion),
+ JAVA_MINOR(minVersion));
+
+ if (gIsDebug) {
+ fprintf(stderr, s.cstr());
+ }
+
+ if (shouldPrintError) {
+ printError(s.cstr());
+ }
+
+ return 1;
+ }
+ _ASSERT(!javaPath.isEmpty());
+
+ if (doShortPath) {
+ if (!javaPath.toShortPath(&javaPath)) {
+ CString s;
+ s.setf("Failed to convert path (%s) to a short DOS path. ", javaPath.cstr());
+ fprintf(stderr, s.cstr());
+
+ if (shouldPrintError) {
+ printError(s.cstr());
+ }
+
+ return 1;
+ }
+ }
+
+ if (doVersion) {
+ // Print version found. We already have the version as an integer
+ // so we don't need to run java -version a second time.
+ printf("%d.%d", JAVA_MAJOR(version), JAVA_MINOR(version));
+ return 0;
+ }
+
+ if (doJavaW) {
+ // Try to find a javaw.exe instead of java.exe at the same location.
+ CPath javawPath(javaPath);
+ javawPath.replaceName("java.exe", "javaw.exe");
+ // Only accept it if we can actually find the exec
+ if (javawPath.fileExists()) {
+ javaPath.set(javawPath.cstr());
+ }
+ }
+
+ // Print java.exe path found
+ printf(javaPath.cstr());
+ return 0;
+}
+
+#endif /* _WIN32 */
diff --git a/find_java/src/source/find_java_lib.cpp b/find_java/src/source/find_java_lib.cpp
new file mode 100755
index 000000000..fe50f8396
--- /dev/null
+++ b/find_java/src/source/find_java_lib.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _WIN32
+
+// Indicate we want at least all Windows Server 2003 (5.2) APIs.
+// Note: default is set by system/core/include/arch/windows/AndroidConfig.h to 0x0500
+// which is Win2K. However our minimum SDK tools requirement is Win XP (0x0501).
+// However we do need 0x0502 to get access to the WOW-64-32 constants for the
+// registry, except we'll need to be careful since they are not available on XP.
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0502
+// Indicate we want at least all IE 5 shell APIs
+#define _WIN32_IE 0x0500
+
+#include "find_java.h"
+#include <shlobj.h>
+#include <ctype.h>
+
+// Define some types missing in MingW
+#ifndef LSTATUS
+typedef LONG LSTATUS;
+#endif
+
+// Check to see if the application is running in 32-bit or 64-bit mode. In other words, this will
+// return false if you run a 32-bit build even on a 64-bit machine.
+static bool isApplication64() {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+
+ // Note: The constant name here is a bit misleading, as it actually covers all 64-bit processors
+ // and not just AMD.
+ // See also: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
+ return (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
+}
+
+// Extract the first thing that looks like (digit.digit+).
+// Note: this will break when java reports a version with major > 9.
+// However it will reasonably cope with "1.10", if that ever happens.
+static bool extractJavaVersion(const char *start,
+ int length,
+ CString *outVersionStr,
+ int *outVersionInt) {
+ const char *end = start + length;
+ for (const char *c = start; c < end - 2; c++) {
+ if (isdigit(c[0]) &&
+ c[1] == '.' &&
+ isdigit(c[2])) {
+ const char *e = c+2;
+ while (isdigit(e[1])) {
+ e++;
+ }
+ if (outVersionStr != NULL) {
+ outVersionStr->set(c, e - c + 1);
+ }
+ if (outVersionInt != NULL) {
+ // add major * 1000, currently only 1 digit
+ int value = (*c - '0') * 1000;
+ // add minor
+ for (int m = 1; *e != '.'; e--, m *= 10) {
+ value += (*e - '0') * m;
+ }
+ *outVersionInt = value;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// Check if the passed in path is a path to a JDK
+static bool isJdkPath(const CPath &path) {
+
+ // If the files "bin/java.exe" and "lib/tools.jar" exist, we're probably a JDK.
+ CPath pathBin(path);
+ pathBin.addPath("bin");
+ pathBin.addPath("java.exe");
+
+ CPath pathTools(path);
+ pathTools.addPath("lib");
+ pathTools.addPath("tools.jar");
+
+ return (pathBin.fileExists() && pathTools.fileExists());
+}
+
+// Check whether we can find $PATH/java.exe.
+// inOutPath should be the directory where we're looking at.
+// In output, it will be the java path we tested.
+// Returns the java version integer found (e.g. 1006 for 1.6).
+// Return 0 in case of error.
+static int checkPath(CPath *inOutPath) {
+ inOutPath->addPath("java.exe");
+
+ int result = 0;
+ if (inOutPath->fileExists()) {
+ // Run java -version
+ // Reject the version if it's not at least our current minimum.
+ if (!getJavaVersion(*inOutPath, NULL /*versionStr*/, &result)) {
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+// Check whether we can find $PATH/bin/java.exe
+// Returns the Java version found (e.g. 1006 for 1.6) or 0 in case of error.
+static int checkBinPath(CPath *inOutPath) {
+ inOutPath->addPath("bin");
+ return checkPath(inOutPath);
+}
+
+// Test for the existence of java.exe in a custom path
+int findJavaInPath(const CPath &path, CPath *outJavaPath, bool isJdk, int minVersion) {
+ SetLastError(0);
+
+ int version = 0;
+ CPath temp(path);
+ if (!isJdk) {
+ version = checkPath(&temp);
+ }
+ else {
+ if (isJdkPath(temp)) {
+ version = checkBinPath(&temp);
+ }
+ }
+
+ if (version >= minVersion) {
+ if (gIsDebug) {
+ fprintf(stderr, "Java %d found in path: %s\n", version, temp.cstr());
+ }
+ *outJavaPath = temp;
+ return version;
+ }
+ return 0;
+}
+
+// Search java.exe in the environment
+int findJavaInEnvPath(CPath *outJavaPath, bool isJdk, int minVersion) {
+ SetLastError(0);
+
+ const char* envPath = getenv("JAVA_HOME");
+ if (envPath != NULL) {
+ CPath p(envPath);
+
+ if (!isJdk || isJdkPath(p)) {
+ int v = checkBinPath(&p);
+ if (v >= minVersion) {
+ if (gIsDebug) {
+ fprintf(stderr, "Java %d found via JAVA_HOME: %s\n", v, p.cstr());
+ }
+ *outJavaPath = p;
+ // As an optimization for runtime, if we find a suitable java
+ // version in JAVA_HOME we won't waste time looking at the PATH.
+ return v;
+ }
+ }
+ }
+
+ int currVersion = 0;
+ envPath = getenv("PATH");
+ if (!envPath) return currVersion;
+
+ // Otherwise look at the entries in the current path.
+ // If we find more than one, keep the one with the highest version.
+
+ CArray<CString> *paths = CString(envPath).split(';');
+ for(int i = 0; i < paths->size(); i++) {
+ CPath p((*paths)[i].cstr());
+
+ if (isJdk && !isJdkPath(p)) {
+ continue;
+ }
+
+ int v = checkPath(&p);
+ if (v >= minVersion && v > currVersion) {
+ if (gIsDebug) {
+ fprintf(stderr, "Java %d found via env PATH: %s\n", v, p.cstr());
+ }
+
+ currVersion = v;
+ *outJavaPath = p;
+ }
+ }
+
+ delete paths;
+ return currVersion;
+}
+
+// --------------
+
+static bool getRegValue(const char *keyPath,
+ const char *keyName,
+ REGSAM access,
+ CString *outValue) {
+ HKEY key;
+ LSTATUS status = RegOpenKeyExA(
+ HKEY_LOCAL_MACHINE, // hKey
+ keyPath, // lpSubKey
+ 0, // ulOptions
+ KEY_READ | access, // samDesired,
+ &key); // phkResult
+ if (status == ERROR_SUCCESS) {
+
+ LSTATUS ret = ERROR_MORE_DATA;
+ DWORD size = 4096; // MAX_PATH is 260, so 4 KB should be good enough
+ char* buffer = (char*) malloc(size);
+
+ while (ret == ERROR_MORE_DATA && size < (1<<16) /*64 KB*/) {
+ ret = RegQueryValueExA(
+ key, // hKey
+ keyName, // lpValueName
+ NULL, // lpReserved
+ NULL, // lpType
+ (LPBYTE) buffer, // lpData
+ &size); // lpcbData
+
+ if (ret == ERROR_MORE_DATA) {
+ size *= 2;
+ buffer = (char*) realloc(buffer, size);
+ } else {
+ buffer[size] = 0;
+ }
+ }
+
+ if (ret != ERROR_MORE_DATA) outValue->set(buffer);
+
+ free(buffer);
+ RegCloseKey(key);
+
+ return (ret != ERROR_MORE_DATA);
+ }
+
+ return false;
+}
+
+// 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, int minVersion,
+ 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, &currentVersion)) {
+ // 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 >= minVersion) {
+ // 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;
+}
+
+static bool getMaxJavaInRegistry(const char *entry, REGSAM access, CPath *outJavaPath, int *inOutVersion) {
+ CPath path;
+ int version = exploreJavaRegistry(entry, access, *inOutVersion, &path);
+ if (version > *inOutVersion) {
+ *outJavaPath = path;
+ *inOutVersion = version;
+ return true;
+ }
+ return false;
+}
+
+int findJavaInRegistry(CPath *outJavaPath, bool isJdk, int minVersion) {
+ // Check the JRE first, then the JDK.
+ int version = minVersion - 1; // Inner methods check if they're greater than this version.
+ bool result = false;
+ result |= (!isJdk && getMaxJavaInRegistry("Java Runtime Environment", 0, outJavaPath, &version));
+ result |= getMaxJavaInRegistry("Java Development Kit", 0, outJavaPath, &version);
+
+ // Even if we're 64-bit, try again but check the 32-bit registry, looking for 32-bit java.
+ if (isApplication64()) {
+ result |= (!isJdk &&
+ getMaxJavaInRegistry("Java Runtime Environment", KEY_WOW64_32KEY, outJavaPath, &version));
+ result |= getMaxJavaInRegistry("Java Development Kit", KEY_WOW64_32KEY, outJavaPath, &version);
+ }
+
+ return result ? version : 0;
+}
+
+// --------------
+
+static bool checkProgramFiles(CPath *outJavaPath, int *inOutVersion, bool isJdk,
+ bool force32bit) {
+
+ char programFilesPath[MAX_PATH + 1];
+ int nFolder = force32bit ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
+ HRESULT result = SHGetFolderPathA(
+ NULL, // hwndOwner
+ nFolder,
+ NULL, // hToken
+ SHGFP_TYPE_CURRENT, // dwFlags
+ programFilesPath); // pszPath
+ if (FAILED(result)) return false;
+
+ CPath path(programFilesPath);
+ path.addPath("Java");
+
+ // Do we have a C:\Program Files\Java directory?
+ if (!path.dirExists()) return false;
+
+ CPath glob(path);
+ glob.addPath("j*");
+
+ bool found = false;
+ WIN32_FIND_DATAA findData;
+ HANDLE findH = FindFirstFileA(glob.cstr(), &findData);
+ if (findH == INVALID_HANDLE_VALUE) return false;
+ do {
+ if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ CPath temp(path);
+ temp.addPath(findData.cFileName);
+ // Check C:\Program Files\Java\j*\bin\java.exe
+
+ if (!isJdk || isJdkPath(temp)) {
+ int v = checkBinPath(&temp);
+ if (v > *inOutVersion) {
+ found = true;
+ *inOutVersion = v;
+ *outJavaPath = temp;
+ }
+ }
+ }
+ } while (!found && FindNextFileA(findH, &findData) != 0);
+ FindClose(findH);
+
+ return found;
+}
+
+int findJavaInProgramFiles(CPath *outJavaPath, bool isJdk, int minVersion) {
+
+ // Check the C:\Program Files directory
+ bool result = false;
+ int version = minVersion - 1; // Inner methods check if they're greater than this version.
+ result |= checkProgramFiles(outJavaPath, &version, isJdk, false);
+
+ // Even if we're 64-bit, try again but check the C:\Program Files (x86) directory, looking for
+ // 32-bit java.
+ if (isApplication64()) {
+ result |= checkProgramFiles(outJavaPath, &version, isJdk, true);
+ }
+
+ return result ? version : 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");
+ if (!j) {
+ j = strstr(first32, "openjdk");
+ }
+ char *v = strstr(first32, "version");
+ if ((gIsConsole || gIsDebug) && (!j || !v)) {
+ fprintf(stderr, "Error: keywords 'java|openjdk version' not found in '%s'\n", first32);
+ }
+ if (j != NULL && v != NULL) {
+ result = extractJavaVersion(first32, index, outVersionStr, outVersionInt);
+ }
+ }
+
+ return result;
+}
+
+
+#endif /* _WIN32 */
diff --git a/find_java/src/source/utils.cpp b/find_java/src/source/utils.cpp
new file mode 100755
index 000000000..e2ce58f16
--- /dev/null
+++ b/find_java/src/source/utils.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _WIN32
+
+#include "utils.h"
+
+#define _CRT_SECURE_NO_WARNINGS 1
+
+// Set to true to get some extra debug information
+bool gIsDebug = false;
+// Set to true to output errors to stderr (for a Console app)
+// or to false to output using msg box (for a Windows UI app)
+bool gIsConsole = false;
+
+// Displays a message in an ok+info dialog box.
+void msgBox(const char* text, ...) {
+ CString formatted;
+ va_list ap;
+ va_start(ap, text);
+ formatted.setv(text, ap);
+ va_end(ap);
+
+ MessageBoxA(NULL, formatted.cstr(), "Android SDK Manager", MB_OK | MB_ICONINFORMATION);
+}
+
+// Displays GetLastError prefixed with a description in an error dialog box
+void displayLastError(const char *description, ...) {
+ CString formatted;
+ va_list ap;
+ va_start(ap, description);
+ formatted.setv(description, ap);
+ va_end(ap);
+
+ CString error;
+ error.setLastWin32Error(NULL);
+ formatted.add("\r\n");
+ formatted.add(error.cstr());
+
+ if (gIsConsole) {
+ fprintf(stderr, "%s\n", formatted.cstr());
+ } else {
+ MessageBox(NULL, formatted.cstr(), "Android SDK Manager - Error", MB_OK | MB_ICONERROR);
+ }
+}
+
+// Executes the command line. Does not wait for the program to finish.
+// The return code is from CreateProcess (0 means failure), not the running app.
+int execNoWait(const char *app, const char *params, const char *workDir) {
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+
+ ZeroMemory(&pinfo, sizeof(pinfo));
+
+ ZeroMemory(&startup, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWDEFAULT;
+
+ int ret = CreateProcessA(
+ (LPSTR) app, /* program path */
+ (LPSTR) params, /* command-line */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ 0, /* create flags */
+ NULL, /* use parent's environment block */
+ workDir, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo);
+
+ if (ret) {
+ CloseHandle(pinfo.hProcess);
+ CloseHandle(pinfo.hThread);
+ }
+
+ return ret;
+}
+
+// Executes command, waits for completion and returns exit code.
+// As indicated in MSDN for CreateProcess, callers should double-quote the program name
+// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
+int execWait(const char *cmd) {
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+
+ ZeroMemory(&pinfo, sizeof(pinfo));
+
+ ZeroMemory(&startup, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_HIDE|SW_MINIMIZE;
+
+ int ret = CreateProcessA(
+ NULL, /* program path */
+ (LPSTR) cmd, /* command-line */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ CREATE_NO_WINDOW, /* we don't want a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo);
+
+ int result = -1;
+ if (ret) {
+ WaitForSingleObject(pinfo.hProcess, INFINITE);
+
+ DWORD exitCode;
+ if (GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
+ // this should not return STILL_ACTIVE (259)
+ result = exitCode;
+ }
+ CloseHandle(pinfo.hProcess);
+ CloseHandle(pinfo.hThread);
+ }
+
+ return result;
+}
+
+bool getModuleDir(CPath *outDir) {
+ CHAR programDir[MAX_PATH];
+ int ret = GetModuleFileName(NULL, programDir, sizeof(programDir));
+ if (ret != 0) {
+ // Remove the last segment to keep only the directory.
+ int pos = ret - 1;
+ while (pos > 0 && programDir[pos] != '\\') {
+ --pos;
+ }
+ outDir->set(programDir, pos);
+ return true;
+ }
+ return false;
+}
+
+// Disables the FS redirection done by WOW64.
+// Because this runs as a 32-bit app, Windows automagically remaps some
+// folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files").
+// This prevents the app from correctly searching for java.exe in these folders.
+// The registry is also remapped. This method disables this redirection.
+// Caller should restore the redirection later by using revertWow64FsRedirection().
+PVOID disableWow64FsRedirection() {
+
+ // The call we want to make is the following:
+ // PVOID oldWow64Value;
+ // Wow64DisableWow64FsRedirection(&oldWow64Value);
+ // However that method may not exist (e.g. on XP non-64 systems) so
+ // we must not call it directly.
+
+ PVOID oldWow64Value = 0;
+
+ HMODULE hmod = LoadLibrary("kernel32.dll");
+ if (hmod != NULL) {
+ FARPROC proc = GetProcAddress(hmod, "Wow64DisableWow64FsRedirection");
+ if (proc != NULL) {
+ typedef BOOL (WINAPI *disableWow64FuncType)(PVOID *);
+ disableWow64FuncType funcPtr = (disableWow64FuncType)proc;
+ funcPtr(&oldWow64Value);
+ }
+
+ FreeLibrary(hmod);
+ }
+
+ return oldWow64Value;
+}
+
+// Reverts the redirection disabled in disableWow64FsRedirection.
+void revertWow64FsRedirection(PVOID oldWow64Value) {
+
+ // The call we want to make is the following:
+ // Wow64RevertWow64FsRedirection(oldWow64Value);
+ // However that method may not exist (e.g. on XP non-64 systems) so
+ // we must not call it directly.
+
+ HMODULE hmod = LoadLibrary("kernel32.dll");
+ if (hmod != NULL) {
+ FARPROC proc = GetProcAddress(hmod, "Wow64RevertWow64FsRedirection");
+ if (proc != NULL) {
+ typedef BOOL (WINAPI *revertWow64FuncType)(PVOID);
+ revertWow64FuncType funcPtr = (revertWow64FuncType)proc;
+ funcPtr(oldWow64Value);
+ }
+
+ FreeLibrary(hmod);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/find_java/src/source/utils.h b/find_java/src/source/utils.h
new file mode 100755
index 000000000..a65e6fe50
--- /dev/null
+++ b/find_java/src/source/utils.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _H_UTILS
+#define _H_UTILS
+
+#ifdef _WIN32
+
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include <direct.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <windows.h>
+
+// VS vs MINGW specific includes
+#ifdef USE_VS_CRT
+ #include <crtdbg.h> // for _ASSERT
+#else
+ #define _ASSERT(x) // undef
+#endif
+
+extern bool gIsDebug;
+extern bool gIsConsole;
+
+// An array that knows its own size. Not dynamically resizable.
+template <class T> class CArray {
+ T* mPtr;
+ int mSize;
+public:
+ explicit CArray(int size) {
+ mSize = size;
+ mPtr = new T[size];
+ }
+
+ ~CArray() {
+ if (mPtr != NULL) {
+ delete[] mPtr;
+ mPtr = NULL;
+ }
+ mSize = 0;
+ }
+
+ T& operator[](int i) {
+ _ASSERT(i >= 0 && i < mSize);
+ return mPtr[i];
+ }
+
+ int size() const {
+ return mSize;
+ }
+};
+
+// A simple string class wrapper.
+class CString {
+protected:
+ char *mStr;
+public:
+ CString() { mStr = NULL; }
+ CString(const CString &str) { mStr = NULL; set(str.mStr); }
+ explicit CString(const char *str) { mStr = NULL; set(str); }
+ CString(const char *start, size_t length) { mStr = NULL; set(start, length); }
+
+ CString& operator=(const CString &str) {
+ return set(str.cstr());
+ }
+
+ CString& set(const char *str) {
+ if (str != mStr) {
+ _free();
+ if (str != NULL) {
+ mStr = _strdup(str);
+ }
+ }
+ return *this;
+ }
+
+ CString& set(const char *start, size_t length) {
+ _free();
+ if (start != NULL) {
+ mStr = (char *)malloc(length + 1);
+ strncpy(mStr, start, length);
+ mStr[length] = 0;
+ }
+ return *this;
+ }
+
+ CString& setv(const char *str, va_list ap) {
+ _free();
+ // _vscprintf(str, ap) is only available with the MSVCRT, not MinGW.
+ // Instead we'll iterate till we have enough space to generate the string.
+ size_t len = strlen(str) + 1024;
+ mStr = (char *)malloc(len);
+ strcpy(mStr, str); // provide a default in case vsnprintf totally fails
+ for (int guard = 0; guard < 10; guard++) {
+ int ret = vsnprintf(mStr, len, str, ap);
+ if (ret == -1) {
+ // Some implementations don't give the proper size needed
+ // so double the space and try again.
+ len *= 2;
+ } else if (ret >= len) {
+ len = ret + 1;
+ } else {
+ // There was enough space to write.
+ break;
+ }
+ mStr = (char *)realloc((void *)mStr, len);
+ strcpy(mStr, str); // provide a default in case vsnprintf totally fails
+ }
+ return *this;
+ }
+
+ CString& setf(const char *str, ...) {
+ _free();
+ va_list ap;
+ va_start(ap, str);
+ setv(str, ap);
+ va_end(ap);
+ return *this;
+ }
+
+ virtual ~CString() { _free(); }
+
+ // Returns the C string owned by this CString. It will be
+ // invalid as soon as this CString is deleted or out of scope.
+ const char * cstr() const {
+ return mStr;
+ }
+
+ bool isEmpty() const {
+ return mStr == NULL || *mStr == 0;
+ }
+
+ size_t length() const {
+ return mStr == NULL ? 0 : strlen(mStr);
+ }
+
+ CString& add(const char *str) {
+ if (mStr == NULL) {
+ set(str);
+ } else {
+ mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(str) + 1);
+ strcat(mStr, str);
+ }
+ return *this;
+ }
+
+ CString& add(const char *str, int length) {
+ if (mStr == NULL) {
+ set(str, length);
+ } else {
+ size_t l1 = strlen(mStr);
+ mStr = (char *)realloc((void *)mStr, l1 + length + 1);
+ strncpy(mStr + l1, str, length);
+ mStr[l1 + length] = 0;
+ }
+ return *this;
+ }
+
+ CArray<CString> * split(char sep) const {
+ if (mStr == NULL) {
+ return new CArray<CString>(0);
+ }
+ const char *last = NULL;
+ int n = 0;
+ for (const char *s = mStr; *s; s++) {
+ if (*s == sep && s != mStr && (last == NULL || s > last+1)) {
+ n++;
+ last = s;
+ }
+ }
+
+ CArray<CString> *result = new CArray<CString>(n);
+ last = NULL;
+ n = 0;
+ for (const char *s = mStr; *s; s++) {
+ if (*s == sep) {
+ if (s != mStr && (last == NULL || s > last+1)) {
+ const char *start = last ? last : mStr;
+ (*result)[n++].set(start, s-start);
+ }
+ last = s+1;
+ }
+ }
+
+ return result;
+ }
+
+ // Sets the string to the message matching Win32 GetLastError.
+ // If message is non-null, it is prepended to the last error string.
+ CString& setLastWin32Error(const char *message) {
+ DWORD err = GetLastError();
+ LPSTR errStr;
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, /* lpSource */
+ err, /* dwMessageId */
+ 0, /* dwLanguageId */
+ (LPSTR)&errStr, /* lpBuffer */
+ 0, /* nSize */
+ NULL) != 0) { /* va_list args */
+ if (message == NULL) {
+ setf("[%d] %s", err, errStr);
+ } else {
+ setf("%s[%d] %s", message, err, errStr);
+ }
+ LocalFree(errStr);
+ }
+ return *this;
+ }
+
+private:
+ void _free() {
+ if (mStr != NULL) {
+ free((void *)mStr);
+ mStr = NULL;
+ }
+ }
+
+};
+
+// A simple path class wrapper.
+class CPath : public CString {
+public:
+ CPath() : CString() { }
+ CPath(const CString &str) : CString(str) { }
+ CPath(const CPath &str) : CString(str) { }
+ explicit CPath(const char *str) : CString(str) { }
+ CPath(const char *start, int length) : CString(start, length) { }
+
+ CPath& operator=(const CPath &str) {
+ set(str.cstr());
+ return *this;
+ }
+
+ // Appends a path segment, adding a \ as necessary.
+ CPath& addPath(const CString &s) {
+ return addPath(s.cstr());
+ }
+
+ // Appends a path segment, adding a \ as necessary.
+ CPath& addPath(const char *s) {
+ _ASSERT(s != NULL);
+ if (s != NULL && s[0] != 0) {
+ size_t n = length();
+ if (n > 0 && s[0] != '\\' && mStr[n-1] != '\\') add("\\");
+ add(s);
+ }
+ return *this;
+ }
+
+ // Returns true if file exist and is not a directory.
+ // There's no garantee we have rights to access it.
+ bool fileExists() const {
+ if (mStr == NULL) return false;
+ DWORD attribs = GetFileAttributesA(mStr);
+ return attribs != INVALID_FILE_ATTRIBUTES &&
+ !(attribs & FILE_ATTRIBUTE_DIRECTORY);
+ }
+
+ // Returns true if file exist and is a directory.
+ // There's no garantee we have rights to access it.
+ bool dirExists() const {
+ if (mStr == NULL) return false;
+ DWORD attribs = GetFileAttributesA(mStr);
+ return attribs != INVALID_FILE_ATTRIBUTES &&
+ (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+ // Returns a copy of the directory portion of the path, if any
+ CPath dirName() const {
+ CPath result;
+ if (mStr != NULL) {
+ char *pos = strrchr(mStr, '\\');
+ if (pos != NULL) {
+ result.set(mStr, pos - mStr);
+ }
+ }
+ return result;
+ }
+
+ // Returns a pointer to the baseName part of the path.
+ // It becomes invalid if the path changes.
+ const char * baseName() const {
+ if (mStr != NULL) {
+ char *pos = strrchr(mStr, '\\');
+ if (pos != NULL) {
+ return pos + 1;
+ }
+ }
+ return NULL;
+ }
+
+ // If the path ends with the given searchName, replace in-place by the new name
+ void replaceName(const char *searchName, const char* newName) {
+ if (mStr == NULL) return;
+ size_t n = length();
+ size_t sn = strlen(searchName);
+ if (n < sn) return;
+ // if mStr ends with searchName
+ if (strcmp(mStr + n - sn, searchName) == 0) {
+ size_t sn2 = strlen(newName);
+ if (sn2 > sn) {
+ mStr = (char *)realloc((void *)mStr, n + sn2 - sn + 1);
+ }
+ strcpy(mStr + n - sn, newName);
+ mStr[n + sn2 - sn] = 0;
+ }
+ }
+
+ // Returns a copy of this path as a DOS short path in the destination.
+ // Returns true if the Win32 getShortPathName method worked.
+ // In case of error, returns false and does not change the destination.
+ // It's OK to invoke this->toShortPath(this).
+ bool toShortPath(CPath *dest) {
+ const char *longPath = mStr;
+ if (mStr == NULL) return false;
+
+ DWORD lenShort = (DWORD)strlen(longPath) + 1; // GetShortPathName deals with DWORDs
+ char * shortPath = (char *)malloc(lenShort);
+
+ DWORD length = GetShortPathName(longPath, shortPath, lenShort);
+ if (length > lenShort) {
+ // The buffer wasn't big enough, this is the size to use.
+ free(shortPath);
+ lenShort = length;
+ shortPath = (char *)malloc(length);
+ length = GetShortPathName(longPath, shortPath, lenShort);
+ }
+
+ if (length != 0) dest->set(shortPath);
+
+ free(shortPath);
+ return length != 0;
+ }
+};
+
+// Displays a message in an ok+info dialog box.
+void msgBox(const char* text, ...);
+
+// Displays GetLastError prefixed with a description in an error dialog box
+void displayLastError(const char *description, ...);
+
+// Executes the command line. Does not wait for the program to finish.
+// The return code is from CreateProcess (0 means failure), not the running app.
+int execNoWait(const char *app, const char *params, const char *workDir);
+
+// Executes command, waits for completion and returns exit code.
+// As indicated in MSDN for CreateProcess, callers should double-quote the program name
+// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
+int execWait(const char *cmd);
+
+bool getModuleDir(CPath *outDir);
+
+#endif /* _WIN32 */
+#endif /* _H_UTILS */