aboutsummaryrefslogtreecommitdiff
path: root/find_java2/src
diff options
context:
space:
mode:
Diffstat (limited to 'find_java2/src')
-rwxr-xr-xfind_java2/src/FindJava2Dlg.cpp268
-rwxr-xr-xfind_java2/src/FindJava2Dlg.h64
-rwxr-xr-xfind_java2/src/JavaFinder.cpp594
-rwxr-xr-xfind_java2/src/JavaFinder.h52
-rwxr-xr-xfind_java2/src/JavaPath.cpp97
-rwxr-xr-xfind_java2/src/JavaPath.h57
-rwxr-xr-xfind_java2/src/WinLauncher2App.cpp154
-rwxr-xr-xfind_java2/src/WinLauncher2App.h43
-rwxr-xr-xfind_java2/src/utils.cpp282
-rwxr-xr-xfind_java2/src/utils.h59
10 files changed, 1670 insertions, 0 deletions
diff --git a/find_java2/src/FindJava2Dlg.cpp b/find_java2/src/FindJava2Dlg.cpp
new file mode 100755
index 000000000..fbdd899e0
--- /dev/null
+++ b/find_java2/src/FindJava2Dlg.cpp
@@ -0,0 +1,268 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+
+#include "stdafx.h"
+#include "utils.h"
+#include "FindJava2Dlg.h"
+#include "afxdialogex.h"
+#include <atlpath.h> // ATL CPath
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+#define COL_PATH 1
+
+
+CFindJava2Dlg::CFindJava2Dlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CFindJava2Dlg::IDD, pParent), mSelectedIndex(-1) {
+ m_hIcon = AfxGetApp()->LoadIcon(IDI_ANDROID_ICON);
+}
+
+void CFindJava2Dlg::DoDataExchange(CDataExchange* pDX) {
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_PATH_LIST, mPathsListCtrl);
+ DDX_Control(pDX, IDOK, mOkButton);
+}
+
+BEGIN_MESSAGE_MAP(CFindJava2Dlg, CDialog)
+ ON_WM_PAINT()
+ ON_WM_QUERYDRAGICON()
+ ON_BN_CLICKED(IDC_BUTTON_ADD, &CFindJava2Dlg::OnBnClickedButtonAdd)
+ ON_NOTIFY(NM_CLICK, IDC_PATH_LIST, &CFindJava2Dlg::OnNMClickPathList)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_PATH_LIST, &CFindJava2Dlg::OnLvnItemchangedPathList)
+END_MESSAGE_MAP()
+
+
+// -----
+// CFindJava2Dlg message handlers
+
+BOOL CFindJava2Dlg::OnInitDialog() {
+ CDialog::OnInitDialog();
+
+ SetWindowText(getAppName());
+
+ // Set the icon for this dialog. The framework does this automatically
+ // when the application's main window is not a dialog
+ SetIcon(m_hIcon, TRUE); // Set big icon
+ SetIcon(m_hIcon, FALSE); // Set small icon
+
+ // Initialize list controls
+ mPathsListCtrl.SetExtendedStyle(
+ mPathsListCtrl.GetExtendedStyle() |
+ LVS_EX_CHECKBOXES |
+ LVS_EX_FULLROWSELECT |
+ LVS_EX_GRIDLINES);
+
+ // We want 2 columns: Java version and path
+ mPathsListCtrl.InsertColumn(0, _T("Version"), LVCFMT_RIGHT, 60, 0);
+ mPathsListCtrl.InsertColumn(1, _T("Path"), LVCFMT_LEFT, 386, 0);
+
+ mJavaFinder->findJavaPaths(&mPaths);
+ fillPathsList();
+ adjustButtons();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+}
+
+// If you add a minimize button to your dialog, you will need the code below
+// to draw the icon. For MFC applications using the document/view model,
+// this is automatically done for you by the framework.
+// [Note: MFC boilerplate, keep as-is]
+void CFindJava2Dlg::OnPaint() {
+ if (IsIconic()) {
+ CPaintDC dc(this); // device context for painting
+
+ SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
+
+ // Center icon in client rectangle
+ int cxIcon = GetSystemMetrics(SM_CXICON);
+ int cyIcon = GetSystemMetrics(SM_CYICON);
+ CRect rect;
+ GetClientRect(&rect);
+ int x = (rect.Width() - cxIcon + 1) / 2;
+ int y = (rect.Height() - cyIcon + 1) / 2;
+
+ // Draw the icon
+ dc.DrawIcon(x, y, m_hIcon);
+ } else {
+ CDialog::OnPaint();
+ }
+}
+
+// The system calls this function to obtain the cursor to display while the user drags
+// the minimized window. [Note: MFC boilerplate, keep as-is]
+HCURSOR CFindJava2Dlg::OnQueryDragIcon() {
+ return static_cast<HCURSOR>(m_hIcon);
+}
+
+// Button add has been pressed; use file dialog and add path if it's a valid java.exe
+void CFindJava2Dlg::OnBnClickedButtonAdd() {
+ CFileDialog fileDlg(
+ TRUE, // true=open dialog, false=save-as dialog
+ _T("exe"), // lpszDefExt
+ _T("java.exe"), // lpszFileName
+ OFN_FILEMUSTEXIST || OFN_PATHMUSTEXIST,
+ NULL, // lpszFilter
+ this); // pParentWnd
+
+ if (fileDlg.DoModal() == IDOK) {
+ CString path = fileDlg.GetPathName();
+
+ CJavaPath javaPath;
+ if (!mJavaFinder->checkJavaPath(path, &javaPath)) {
+ CString msg;
+ if (javaPath.mVersion > 0) {
+ msg.Format(_T("Insufficient Java Version found: expected %s, got %s"),
+ CJavaPath(mJavaFinder->getMinVersion(), CPath()).getVersion(),
+ javaPath.getVersion());
+ } else {
+ msg.Format(_T("No valid Java Version found for %s"), path);
+ }
+ AfxMessageBox(msg, MB_OK);
+
+ } else {
+ if (mPaths.find(javaPath) == mPaths.end()) {
+ // Path isn't known yet so add it and refresh the list.
+ mPaths.insert(javaPath);
+ fillPathsList();
+ }
+
+ // Select item in list and set mSelectedIndex
+ selectPath(-1 /*index*/, &javaPath);
+ }
+ }
+}
+
+// An item in the list has been selected, select checkmark and set mSelectedIndex.
+void CFindJava2Dlg::OnNMClickPathList(NMHDR *pNMHDR, LRESULT *pResult) {
+ LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
+ int index = pNMItemActivate->iItem;
+ selectPath(index, nullptr);
+ *pResult = TRUE;
+}
+
+// An item in the list has changed, toggle checkmark as needed.
+void CFindJava2Dlg::OnLvnItemchangedPathList(NMHDR *pNMHDR, LRESULT *pResult) {
+ *pResult = FALSE;
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+
+ if ((pNMLV->uChanged & LVIF_STATE) != 0) {
+ // Item's state has changed. Check the selection to see if it needs to be adjusted.
+ int index = pNMLV->iItem;
+
+ UINT oldState = pNMLV->uOldState;
+ UINT newState = pNMLV->uNewState;
+
+ if ((oldState & LVIS_STATEIMAGEMASK) != 0 || (newState & LVIS_STATEIMAGEMASK) != 0) {
+ // Checkbox uses the STATEIMAGE: 1 for unchecked, 2 for checked.
+ // Checkbox is checked when (old/new-state & state-image-mask) == INDEXTOSTATEIMAGEMASK(2).
+
+ bool oldChecked = (oldState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2);
+ bool newChecked = (newState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2);
+
+ if (oldChecked && !newChecked && index == mSelectedIndex) {
+ mSelectedIndex = -1;
+ adjustButtons();
+ } else if (!oldChecked && newChecked && index != mSelectedIndex) {
+ // Uncheck any checked rows if any
+ for (int n = mPathsListCtrl.GetItemCount() - 1; n >= 0; --n) {
+ if (n != index && mPathsListCtrl.GetCheck(n)) {
+ mPathsListCtrl.SetCheck(n, FALSE);
+ }
+ }
+
+ mSelectedIndex = index;
+ adjustButtons();
+ }
+ // We handled this case, don't dispatch it further
+ *pResult = TRUE;
+ }
+ }
+}
+
+// -----
+
+const CJavaPath& CFindJava2Dlg::getSelectedPath() {
+ int i = 0;
+ for (const CJavaPath &p : mPaths) {
+ if (i == mSelectedIndex) {
+ return p;
+ }
+ ++i;
+ }
+
+ return CJavaPath::sEmpty;
+}
+
+
+void CFindJava2Dlg::fillPathsList() {
+ mPathsListCtrl.DeleteAllItems();
+ int index = 0;
+
+ for (const CJavaPath& pv : mPaths) {
+ mPathsListCtrl.InsertItem(index, pv.getVersion()); // column 0 = version
+ mPathsListCtrl.SetItemText(index, COL_PATH, pv.mPath); // column 1 = path
+ mPathsListCtrl.SetCheck(index, mSelectedIndex == index);
+ ++index;
+ }
+}
+
+// Checks the given index if valid. Unchecks all other items.
+//
+// If index >= 0, it is used to select that item from the ListControl.
+// Otherwise if path != nullptr, it is used to find the item and select it.
+//
+// Side effect: in both cases, mSelectedIndex is set to the matching index or -1.
+//
+// If index is invalid and path isn't in the mPaths list, all items are unselected
+// so calling this with (0, nullptr) will clear the current selection.
+void CFindJava2Dlg::selectPath(int index, const CJavaPath *path) {
+
+ const CJavaPath *foundPath;
+ // If index is not defined, find the given path in the internal list.
+ // If path is not defined, find its index in the internal list.
+ int i = 0;
+ int n = mPathsListCtrl.GetItemCount();
+ for (const CJavaPath &p : mPaths) {
+ if (index < 0 && path != nullptr && p == *path) {
+ index = i;
+ foundPath = path;
+ } else if (index == i) {
+ foundPath = &p;
+ }
+
+ // uncheck any marked path
+ if (i != index && i < n && mPathsListCtrl.GetCheck(i)) {
+ mPathsListCtrl.SetCheck(i, FALSE);
+ }
+
+ ++i;
+ }
+
+ mSelectedIndex = index;
+ if (index >= 0 && index <= n) {
+ mPathsListCtrl.SetCheck(index, TRUE);
+ }
+
+ adjustButtons();
+}
+
+void CFindJava2Dlg::adjustButtons() {
+ int n = mPathsListCtrl.GetItemCount();
+ mOkButton.EnableWindow(mSelectedIndex >= 0 && mSelectedIndex < n);
+}
diff --git a/find_java2/src/FindJava2Dlg.h b/find_java2/src/FindJava2Dlg.h
new file mode 100755
index 000000000..7e0943c30
--- /dev/null
+++ b/find_java2/src/FindJava2Dlg.h
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#pragma once
+#include "afxwin.h"
+#include "JavaFinder.h"
+
+#include "resource.h" // main symbols
+
+
+// CFindJava2Dlg dialog
+class CFindJava2Dlg : public CDialog {
+ // Construction
+public:
+ CFindJava2Dlg(CWnd* pParent = NULL); // standard constructor
+
+ void setJavaFinder(CJavaFinder *javaFinder) { mJavaFinder = javaFinder; }
+ const CJavaPath& getSelectedPath();
+
+ // Dialog Data
+ enum { IDD = IDD_FINDJAVA2_DIALOG };
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+
+
+ // Implementation
+protected:
+ HICON m_hIcon;
+
+ // Generated message map functions
+ virtual BOOL OnInitDialog();
+ afx_msg void OnPaint();
+ afx_msg HCURSOR OnQueryDragIcon();
+ DECLARE_MESSAGE_MAP()
+
+ afx_msg void OnBnClickedButtonAdd();
+ afx_msg void OnNMClickPathList(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnLvnItemchangedPathList(NMHDR *pNMHDR, LRESULT *pResult);
+
+private:
+ std::set<CJavaPath> mPaths;
+ int mSelectedIndex;
+ CJavaFinder *mJavaFinder;
+ CListCtrl mPathsListCtrl;
+ CButton mOkButton;
+
+ void fillPathsList();
+ void adjustButtons();
+ void selectPath(int index = -1, const CJavaPath *path = nullptr);
+};
diff --git a/find_java2/src/JavaFinder.cpp b/find_java2/src/JavaFinder.cpp
new file mode 100755
index 000000000..60a2e230e
--- /dev/null
+++ b/find_java2/src/JavaFinder.cpp
@@ -0,0 +1,594 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#include "stdafx.h"
+#include "JavaFinder.h"
+#include "utils.h"
+
+#include <algorithm> // std::sort and std::unique
+
+#define _CRT_SECURE_NO_WARNINGS
+
+// --------------
+
+#define JF_REGISTRY_KEY _T("Software\\Android\\FindJava2")
+#define JF_REGISTRY_VALUE_PATH _T("JavaPath")
+#define JF_REGISTRY_VALUE_VERS _T("JavaVers")
+
+// --------------
+
+
+// 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 TCHAR *start,
+ int length,
+ CString *outVersionStr,
+ int *outVersionInt) {
+ const TCHAR *end = start + length;
+ for (const TCHAR *c = start; c < end - 2; c++) {
+ if (isdigit(c[0]) &&
+ c[1] == '.' &&
+ isdigit(c[2])) {
+ const TCHAR *e = c + 2;
+ while (isdigit(e[1])) {
+ e++;
+ }
+ outVersionStr->SetString(c, e - c + 1);
+
+ // major is currently only 1 digit
+ int major = (*c - '0');
+ // add minor
+ int minor = 0;
+ for (int m = 1; *e != '.'; e--, m *= 10) {
+ minor += (*e - '0') * m;
+ }
+ *outVersionInt = JAVA_VERS_TO_INT(major, minor);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Tries to invoke the java.exe at the given path and extract it's
+// version number.
+// - outVersionStr: not null, will capture version as a string (e.g. "1.6")
+// - outVersionInt: not null, will capture version as an int (see JavaPath.h).
+bool getJavaVersion(CPath &javaPath, CString *outVersionStr, int *outVersionInt) {
+ bool result = false;
+
+ // Run "java -version", which outputs something to *STDERR* like this:
+ //
+ // 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.Format(_T("\"%s\" -version"), (LPCTSTR) javaPath);
+
+ 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)
+ // In FindJava2, we do not report these errors. Leave commented for reference.
+ // // if (gIsConsole || gIsDebug) displayLastError("CreatePipe failed: ");
+ return false;
+ }
+ if (!SetHandleInformation(stdoutPipeRd, HANDLE_FLAG_INHERIT, 0)) {
+ // In FindJava2, we do not report these errors. Leave commented for reference.
+ // // 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 = CreateProcess(
+ NULL, // program path
+ (LPTSTR)((LPCTSTR) cmd),// 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);
+
+ // In FindJava2, we do not report these errors. Leave commented for reference.
+ // // 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.
+ TCHAR 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
+ // Note: ReadFile uses a char buffer, not a TCHAR one.
+ 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 capture roughtly the first line in lower case.
+ TCHAR *j = _tcsstr(first32, _T("java"));
+ TCHAR *v = _tcsstr(first32, _T("version"));
+ // In FindJava2, we do not report these errors. Leave commented for reference.
+ // // 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;
+}
+
+// --------------
+
+// Checks 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) {
+
+ // Append java.exe to path if not already present
+ CString &p = (CString&)*inOutPath;
+ int n = p.GetLength();
+ if (n < 9 || p.Right(9).CompareNoCase(_T("\\java.exe")) != 0) {
+ inOutPath->Append(_T("java.exe"));
+ }
+
+ int result = 0;
+ PVOID oldWow64Value = disableWow64FsRedirection();
+ if (inOutPath->FileExists()) {
+ // Run java -version
+ // Reject the version if it's not at least our current minimum.
+ CString versionStr;
+ if (!getJavaVersion(*inOutPath, &versionStr, &result)) {
+ result = 0;
+ }
+ }
+
+ revertWow64FsRedirection(oldWow64Value);
+ 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) {
+
+ // Append bin to path if not already present
+ CString &p = (CString&)*inOutPath;
+ int n = p.GetLength();
+ if (n < 4 || p.Right(4).CompareNoCase(_T("\\bin")) != 0) {
+ inOutPath->Append(_T("bin"));
+ }
+
+ return checkPath(inOutPath);
+}
+
+// Search java.exe in the environment
+static void findJavaInEnvPath(std::set<CJavaPath> *outPaths) {
+ ::SetLastError(0);
+
+ const TCHAR* envPath = _tgetenv(_T("JAVA_HOME"));
+ if (envPath != NULL) {
+ CPath p(envPath);
+ int v = checkBinPath(&p);
+ if (v > 0) {
+ outPaths->insert(CJavaPath(v, p));
+ }
+ }
+
+ envPath = _tgetenv(_T("PATH"));
+ if (envPath != NULL) {
+ // Otherwise look at the entries in the current path.
+ // If we find more than one, keep the one with the highest version.
+ CString pathTokens(envPath);
+ int curPos = 0;
+ CString tok;
+ do {
+ tok = pathTokens.Tokenize(_T(";"), curPos);
+ if (!tok.IsEmpty()) {
+ CPath p(tok);
+ int v = checkPath(&p);
+ if (v > 0) {
+ outPaths->insert(CJavaPath(v, p));
+ }
+ }
+ } while (!tok.IsEmpty());
+ }
+}
+
+
+// --------------
+
+static bool getRegValue(const TCHAR *keyPath,
+ const TCHAR *keyName,
+ REGSAM access,
+ CString *outValue) {
+ HKEY key;
+ LSTATUS status = RegOpenKeyEx(
+ 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
+ TCHAR* buffer = (TCHAR*)malloc(size);
+
+ while (ret == ERROR_MORE_DATA && size < (1 << 16) /*64 KB*/) {
+ ret = RegQueryValueEx(
+ key, // hKey
+ keyName, // lpValueName
+ NULL, // lpReserved
+ NULL, // lpType
+ (LPBYTE)buffer, // lpData
+ &size); // lpcbData
+
+ if (ret == ERROR_MORE_DATA) {
+ size *= 2;
+ buffer = (TCHAR*)realloc(buffer, size);
+ } else {
+ buffer[size] = 0;
+ }
+ }
+
+ if (ret != ERROR_MORE_DATA) {
+ outValue->SetString(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 TCHAR *entry, REGSAM access, std::set<CJavaPath> *outPaths) {
+
+ // Let's visit HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment [CurrentVersion]
+ CPath rootKey(_T("SOFTWARE\\JavaSoft\\"));
+ rootKey.Append(entry);
+
+ CString currentVersion;
+ CPath subKey(rootKey);
+ if (getRegValue(subKey, _T("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.Append(currentVersion);
+ CString value;
+ if (getRegValue(subKey, _T("JavaHome"), access, &value)) {
+ CPath javaHome(value);
+ int v = checkBinPath(&javaHome);
+ if (v > 0) {
+ outPaths->insert(CJavaPath(v, javaHome));
+ }
+ }
+ }
+
+ // Try again, but this time look at all the versions available
+ HKEY javaHomeKey;
+ LSTATUS status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, // hKey
+ _T("SOFTWARE\\JavaSoft"), // lpSubKey
+ 0, // ulOptions
+ KEY_READ | access, // samDesired
+ &javaHomeKey); // phkResult
+ if (status == ERROR_SUCCESS) {
+ TCHAR name[MAX_PATH + 1];
+ DWORD index = 0;
+ CPath javaHome;
+ for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; index++) {
+ DWORD nameLen = MAX_PATH;
+ name[nameLen] = 0;
+ result = RegEnumKeyEx(
+ javaHomeKey, // hKey
+ index, // dwIndex
+ name, // lpName
+ &nameLen, // lpcName
+ NULL, // lpReserved
+ NULL, // lpClass
+ NULL, // lpcClass,
+ NULL); // lpftLastWriteTime
+ if (result == ERROR_SUCCESS && nameLen < MAX_PATH) {
+ name[nameLen] = 0;
+ CPath subKey(rootKey);
+ subKey.Append(name);
+
+ CString value;
+ if (getRegValue(subKey, _T("JavaHome"), access, &value)) {
+ CPath javaHome(value);
+ int v = checkBinPath(&javaHome);
+ if (v > 0) {
+ outPaths->insert(CJavaPath(v, javaHome));
+ }
+ }
+ }
+ }
+
+ RegCloseKey(javaHomeKey);
+ }
+
+ return 0;
+}
+
+static void findJavaInRegistry(std::set<CJavaPath> *outPaths) {
+ // We'll do the registry test 3 times: first using the default mode,
+ // then forcing the use of the 32-bit registry then forcing the use of
+ // 64-bit registry. On Windows 2k, the 2 latter will fail since the
+ // flags are not supported. On a 32-bit OS the 64-bit is obviously
+ // useless and the 2 first tests should be equivalent so we just
+ // need the first case.
+
+ // Check the JRE first, then the JDK.
+ exploreJavaRegistry(_T("Java Runtime Environment"), 0, outPaths);
+ exploreJavaRegistry(_T("Java Development Kit"), 0, outPaths);
+
+ // Get the app sysinfo state (the one hidden by WOW64)
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ WORD programArch = sysInfo.wProcessorArchitecture;
+ // Check the real sysinfo state (not the one hidden by WOW64) for x86
+ GetNativeSystemInfo(&sysInfo);
+ WORD actualArch = sysInfo.wProcessorArchitecture;
+
+ // Only try to access the WOW64-32 redirected keys on a 64-bit system.
+ // There's no point in doing this on a 32-bit system.
+ if (actualArch == PROCESSOR_ARCHITECTURE_AMD64) {
+ if (programArch != PROCESSOR_ARCHITECTURE_INTEL) {
+ // If we did the 32-bit case earlier, don't do it twice.
+ exploreJavaRegistry(_T("Java Runtime Environment"), KEY_WOW64_32KEY, outPaths);
+ exploreJavaRegistry(_T("Java Development Kit"), KEY_WOW64_32KEY, outPaths);
+
+ } else if (programArch != PROCESSOR_ARCHITECTURE_AMD64) {
+ // If we did the 64-bit case earlier, don't do it twice.
+ exploreJavaRegistry(_T("Java Runtime Environment"), KEY_WOW64_64KEY, outPaths);
+ exploreJavaRegistry(_T("Java Development Kit"), KEY_WOW64_64KEY, outPaths);
+ }
+ }
+}
+
+// --------------
+
+static void checkProgramFiles(std::set<CJavaPath> *outPaths) {
+
+ TCHAR programFilesPath[MAX_PATH + 1];
+ HRESULT result = SHGetFolderPath(
+ NULL, // hwndOwner
+ CSIDL_PROGRAM_FILES, // nFolder
+ NULL, // hToken
+ SHGFP_TYPE_CURRENT, // dwFlags
+ programFilesPath); // pszPath
+
+ CPath path(programFilesPath);
+ path.Append(_T("Java"));
+
+ // Do we have a C:\\Program Files\\Java directory?
+ if (!path.IsDirectory()) {
+ return;
+ }
+
+ CPath glob(path);
+ glob.Append(_T("j*"));
+
+ WIN32_FIND_DATA findData;
+ HANDLE findH = FindFirstFile(glob, &findData);
+ if (findH == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ do {
+ if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ CPath temp(path);
+ temp.Append(findData.cFileName);
+ // Check C:\\Program Files[x86]\\Java\\j*\\bin\\java.exe
+ int v = checkBinPath(&temp);
+ if (v > 0) {
+ outPaths->insert(CJavaPath(v, temp));
+ }
+ }
+ } while (FindNextFile(findH, &findData) != 0);
+ FindClose(findH);
+}
+
+static void findJavaInProgramFiles(std::set<CJavaPath> *outPaths) {
+ // Check the C:\\Program Files (x86) directory
+ // With WOW64 fs redirection in place by default, we should get the x86
+ // version on a 64-bit OS since this app is a 32-bit itself.
+ checkProgramFiles(outPaths);
+
+ // Check the real sysinfo state (not the one hidden by WOW64) for x86
+ SYSTEM_INFO sysInfo;
+ GetNativeSystemInfo(&sysInfo);
+
+ if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ // On a 64-bit OS, try again by disabling the fs redirection so
+ // that we can try the real C:\\Program Files directory.
+ PVOID oldWow64Value = disableWow64FsRedirection();
+ checkProgramFiles(outPaths);
+ revertWow64FsRedirection(oldWow64Value);
+ }
+}
+
+//------
+
+
+CJavaFinder::CJavaFinder(int minVersion) : mMinVersion(minVersion) {
+}
+
+
+CJavaFinder::~CJavaFinder() {
+}
+
+/*
+ * Checks whether there's a recorded path in the registry and whether
+ * this path still points to a valid Java executable.
+ * Returns false if any of these do not match,
+ * Returns true if both condition match,
+ * outPath contains the result path when returning true.
+*/
+CJavaPath CJavaFinder::getRegistryPath() {
+ CString existing;
+ CRegKey rk;
+
+ if (rk.Open(HKEY_CURRENT_USER, JF_REGISTRY_KEY, KEY_READ) == ERROR_SUCCESS) {
+ ULONG sLen = MAX_PATH;
+ TCHAR s[MAX_PATH + 1];
+ if (rk.QueryStringValue(JF_REGISTRY_VALUE_PATH, s, &sLen) == ERROR_SUCCESS) {
+ existing.SetString(s);
+ }
+ rk.Close();
+ }
+
+ if (!existing.IsEmpty()) {
+ CJavaPath javaPath;
+ if (checkJavaPath(existing, &javaPath)) {
+ return javaPath;
+ }
+ }
+
+ return CJavaPath::sEmpty;
+}
+
+bool CJavaFinder::setRegistryPath(const CJavaPath &javaPath) {
+ CRegKey rk;
+
+ if (rk.Create(HKEY_CURRENT_USER, JF_REGISTRY_KEY) == ERROR_SUCCESS) {
+ bool ok = rk.SetStringValue(JF_REGISTRY_VALUE_PATH, javaPath.mPath, REG_SZ) == ERROR_SUCCESS &&
+ rk.SetStringValue(JF_REGISTRY_VALUE_VERS, javaPath.getVersion(), REG_SZ) == ERROR_SUCCESS;
+ rk.Close();
+ return ok;
+ }
+
+ return false;
+}
+
+void CJavaFinder::findJavaPaths(std::set<CJavaPath> *paths) {
+ findJavaInEnvPath(paths);
+ findJavaInProgramFiles(paths);
+ findJavaInRegistry(paths);
+
+ // Exclude any entries that do not match the minimum version.
+ // The set is going to be fairly small so it's easier to do it here
+ // than add the filter logic in all the static methods above.
+ if (mMinVersion > 0) {
+ for (auto it = paths->begin(); it != paths->end(); ) {
+ if (it->mVersion < mMinVersion) {
+ it = paths->erase(it); // C++11 set.erase returns an iterator to the *next* element
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+bool CJavaFinder::checkJavaPath(const CString &path, CJavaPath *outPath) {
+ CPath p(path);
+
+ // try this path (if it ends with java.exe) or path\\java.exe
+ int v = checkPath(&p);
+ if (v == 0) {
+ // reset path and try path\\bin\\java.exe
+ p = CPath(path);
+ v = checkBinPath(&p);
+ }
+
+ if (v > 0) {
+ outPath->set(v, p);
+ return v >= mMinVersion;
+ }
+
+ return false;
+}
+
diff --git a/find_java2/src/JavaFinder.h b/find_java2/src/JavaFinder.h
new file mode 100755
index 000000000..c22b0086d
--- /dev/null
+++ b/find_java2/src/JavaFinder.h
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#pragma once
+
+
+#include <set> // STL std::set
+#include "JavaPath.h"
+
+class CJavaFinder {
+public:
+ // Creates a new JavaFinder.
+ // minVersion to accept, using JAVA_VERS_TO_INT macro. 0 to accept everything.
+ CJavaFinder(int minVersion = 0);
+ ~CJavaFinder();
+
+ int getMinVersion() const { return mMinVersion; }
+
+ // Returns the path recorded in the registry.
+ // If there is no path or it is no longer valid, returns an empty string.
+ CJavaPath getRegistryPath();
+
+ // Sets the given path as the default to use in the registry.
+ // Returns true on success.
+ bool setRegistryPath(const CJavaPath &javaPath);
+
+ // Scans the registry, the environment and program files for potential Java.exe locations.
+ // Fills the given set with the tuples (version, path) found, guaranteed sorted and unique.
+ void findJavaPaths(std::set<CJavaPath> *paths);
+
+ // Checks the given path for a given java.exe.
+ // Input path variation tried are: path as-is, path/java.exe or path/bin/java.exe.
+ // Places the java path and version in outPath;
+ // Returns true if a java path was found *and* its version is at least mMinVersion.
+ bool checkJavaPath(const CString &path, CJavaPath *outPath);
+
+private:
+ int mMinVersion;
+};
diff --git a/find_java2/src/JavaPath.cpp b/find_java2/src/JavaPath.cpp
new file mode 100755
index 000000000..ebe30c576
--- /dev/null
+++ b/find_java2/src/JavaPath.cpp
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#include "stdafx.h"
+#include "JavaPath.h"
+#include "utils.h"
+
+#define _CRT_SECURE_NO_WARNINGS
+
+// --------------
+
+const CJavaPath CJavaPath::sEmpty = CJavaPath();
+
+CJavaPath::CJavaPath(int version, CPath path) : mVersion(version), mPath(path) {
+ mPath.Canonicalize();
+}
+
+bool CJavaPath::isEmpty() const {
+ return mVersion <= 0;
+}
+
+void CJavaPath::clear() {
+ mVersion = 0;
+ mPath = CPath();
+}
+
+void CJavaPath::set(int version, CPath path) {
+ mVersion = version;
+ mPath = path;
+ mPath.Canonicalize();
+}
+
+CString CJavaPath::getVersion() const {
+ CString s;
+ s.Format(_T("%d.%d"), JAVA_MAJOR(mVersion), JAVA_MINOR(mVersion));
+ return s;
+}
+
+
+bool CJavaPath::toShortPath() {
+ const TCHAR *longPath = mPath;
+ if (longPath == nullptr) {
+ return false;
+ }
+
+ DWORD lenShort = _tcslen(longPath) + 1;
+ TCHAR *shortPath = (TCHAR *)malloc(lenShort * sizeof(TCHAR));
+
+ 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 = (TCHAR *)malloc(length);
+ length = GetShortPathName(longPath, shortPath, lenShort);
+ }
+
+ if (length != 0) {
+ mPath = CPath(shortPath);
+ }
+
+ free(shortPath);
+ return length != 0;
+}
+
+bool CJavaPath::operator< (const CJavaPath& rhs) const {
+ if (mVersion != rhs.mVersion) {
+ // sort in reverse order on the version
+ return rhs.mVersion > mVersion;
+ }
+ // sort in normal order on the path
+ const CString &pl = mPath;
+ const CString &pr = rhs.mPath;
+ return pl.Compare(pr) < 0;
+}
+
+bool CJavaPath::operator== (const CJavaPath& rhs) const {
+ if (mVersion == rhs.mVersion) {
+ const CString &pl = mPath;
+ const CString &pr = rhs.mPath;
+ return pl.Compare(pr) == 0;
+ }
+ return false;
+}
diff --git a/find_java2/src/JavaPath.h b/find_java2/src/JavaPath.h
new file mode 100755
index 000000000..b7f40a5f7
--- /dev/null
+++ b/find_java2/src/JavaPath.h
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#pragma once
+
+#include <atlpath.h> // ATL CPath
+
+
+// Transforms a Java major.minor number (e.g. "1.7") to internal int value (1007)
+#define JAVA_VERS_TO_INT(major, minor) ((major) * 1000 + (minor))
+// Extracts the major part from the internal int major.minor number
+#define JAVA_MAJOR(majorMinor) ((majorMinor) / 1000)
+// Extracts the minor part from the internal int major.minor number
+#define JAVA_MINOR(majorMinor) ((majorMinor) % 1000)
+
+
+struct CJavaPath {
+ int mVersion;
+ CPath mPath;
+
+ // Static empty path that can be returned as a reference.
+ static const CJavaPath sEmpty;
+
+ CJavaPath() : mVersion(0) {}
+ CJavaPath(int version, CPath path);
+ void set(int version, CPath path);
+
+ // Returns true if path/version is empty/0
+ bool isEmpty() const;
+
+ // Clears path and version to 0
+ void clear();
+
+ // Converts the internal path into a short DOS path.
+ // Returns true if this was possible and false if the conversion failed.
+ bool toShortPath();
+
+ // Returns the version formatted as a string (e.g. "1.7" instead of 1007.)
+ CString getVersion() const;
+
+ // Operators < and == for this to be suitable in an ordered std::set
+ bool operator< (const CJavaPath& rhs) const;
+ bool operator== (const CJavaPath& rhs) const;
+};
diff --git a/find_java2/src/WinLauncher2App.cpp b/find_java2/src/WinLauncher2App.cpp
new file mode 100755
index 000000000..392ad9e62
--- /dev/null
+++ b/find_java2/src/WinLauncher2App.cpp
@@ -0,0 +1,154 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+
+#include "stdafx.h"
+#include "WinLauncher2App.h"
+
+#include "utils.h"
+#include "JavaFinder.h"
+#include "FindJava2Dlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+
+// CWinLauncher2App
+
+BEGIN_MESSAGE_MAP(CWinLauncher2App, CWinApp)
+ ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+
+// The one and only CWinLauncher2App object
+CWinLauncher2App theApp;
+
+class CLauncherCmdLineInfo : public CCommandLineInfo {
+public:
+ bool mDoHelp;
+ bool mDoForceUi;
+ bool mDoJava1_7;
+ CString mFilename;
+
+ CLauncherCmdLineInfo() : mDoHelp(false), mDoForceUi(false), mDoJava1_7(false) {}
+
+ virtual void ParseParam(const TCHAR* pszParam, BOOL bFlag, BOOL bLast) {
+ // Expected command line:
+ // /h | help : msg box with command line arguments
+ // /f | force : force UI selection
+ // /7 : require java 1.7
+ // path-to-launch
+
+ if (!bFlag) {
+ mFilename = pszParam;
+ } else if (_tcsnccmp(pszParam, _T("h"), 2) == 0) {
+ mDoHelp = true;
+ } else if (_tcsnccmp(pszParam, _T("f"), 2) == 0) {
+ mDoForceUi = true;
+ } else if (_tcsnccmp(pszParam, _T("7"), 2) == 0) {
+ mDoJava1_7 = true;
+ }
+ }
+};
+
+
+CWinLauncher2App::CWinLauncher2App() {
+ // support Restart Manager
+ m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
+
+ // TODO: add construction code here,
+ // Place all significant initialization in InitInstance
+}
+
+BOOL CWinLauncher2App::InitInstance() {
+ // InitCommonControlsEx() is required on Windows XP if an application
+ // manifest specifies use of ComCtl32.dll version 6 or later to enable
+ // visual styles. Otherwise, any window creation will fail.
+ INITCOMMONCONTROLSEX InitCtrls;
+ InitCtrls.dwSize = sizeof(InitCtrls);
+ // Set this to include all the common control classes you want to use
+ // in your application.
+ InitCtrls.dwICC = ICC_WIN95_CLASSES;
+ InitCommonControlsEx(&InitCtrls);
+
+ CWinApp::InitInstance();
+ AfxEnableControlContainer();
+
+ // Create the shell manager, in case the dialog contains
+ // any shell tree view or shell list view controls.
+ CShellManager *pShellManager = new CShellManager;
+
+ // Activate "Windows Native" visual manager for enabling themes in MFC controls
+ CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
+
+ // Set CWinApp default registry key. Must be consistent with all apps using findjava2.
+ SetRegistryKey(_T("Android-FindJava2"));
+
+ // Use VERSIONINFO.FileDescription as the canonical app name
+ initUtils(NULL);
+
+ CLauncherCmdLineInfo cmdLine;
+ ParseCommandLine(cmdLine);
+
+ if (cmdLine.mDoHelp) {
+ const TCHAR *msg =
+ _T("WinLauncher2 [/7|/f|/h]\r\n")
+ _T("/7 : Requires Java 1.7 instead of 1.6\r\n")
+ _T("/f : Force UI\r\n")
+ _T("/h : Help\r\n");
+ AfxMessageBox(msg);
+ return FALSE; // quit without starting MFC app msg loop
+ }
+
+ CJavaFinder javaFinder(JAVA_VERS_TO_INT(1, cmdLine.mDoJava1_7 ? 7 : 6));
+ CJavaPath javaPath = javaFinder.getRegistryPath();
+ if (cmdLine.mDoForceUi || javaPath.isEmpty()) {
+ javaPath.clear();
+
+ CFindJava2Dlg dlg;
+ dlg.setJavaFinder(&javaFinder);
+ m_pMainWnd = &dlg;
+ INT_PTR nResponse = dlg.DoModal();
+
+ if (nResponse == IDOK) {
+ // Use choice selected by user and save in registry.
+ javaPath = dlg.getSelectedPath();
+ javaFinder.setRegistryPath(javaPath);
+ } else if (nResponse == IDCANCEL) {
+ // Canceled by user, exit silently.
+ } else if (nResponse == -1) {
+ TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
+ }
+ }
+
+ if (!javaPath.isEmpty()) {
+ // TODO actually launch configured app instead of just printing path.
+ CString msg(_T("PLACEHOLDER TODO run app using "));
+ msg.Append(javaPath.mPath);
+ AfxMessageBox(msg);
+ }
+
+ // Delete the shell manager created above.
+ if (pShellManager != NULL) {
+ delete pShellManager;
+ }
+
+ // Since the dialog has been closed, return FALSE so that we exit the
+ // application, rather than start the application's message pump.
+ return FALSE;
+}
+
diff --git a/find_java2/src/WinLauncher2App.h b/find_java2/src/WinLauncher2App.h
new file mode 100755
index 000000000..11aca9706
--- /dev/null
+++ b/find_java2/src/WinLauncher2App.h
@@ -0,0 +1,43 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#pragma once
+
+#ifndef __AFXWIN_H__
+ #error "include 'stdafx.h' before including this file for PCH"
+#endif
+
+#include "resource.h" // main symbols
+
+
+// CWinLauncher2App:
+// See WinLauncher2.cpp for the implementation of this class
+//
+
+class CWinLauncher2App : public CWinApp {
+public:
+ CWinLauncher2App();
+
+// Overrides
+public:
+ virtual BOOL InitInstance();
+
+// Implementation
+
+ DECLARE_MESSAGE_MAP()
+};
+
+extern CWinLauncher2App theApp; \ No newline at end of file
diff --git a/find_java2/src/utils.cpp b/find_java2/src/utils.cpp
new file mode 100755
index 000000000..7c8c66b67
--- /dev/null
+++ b/find_java2/src/utils.cpp
@@ -0,0 +1,282 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#include "stdafx.h"
+#include "utils.h"
+
+// 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;
+
+// Application name used in error dialog. Defined using initUtils()
+static CString gAppName("Find Java 2");
+
+// Called by the application to initialize the app name used in error dialog boxes.
+void initUtils(const TCHAR *appName) {
+ if (appName != NULL) {
+ gAppName = CString(appName);
+ return;
+ }
+
+ // Try to get the VERSIONINFO.FileDescription and use as app name
+ // Errors are ignored, in which case the default app name is used.
+
+ // First get the module (aka app instance) filename.
+ TCHAR moduleName[MAX_PATH + 1];
+ DWORD sz = ::GetModuleFileName(NULL /*AfxGetInstanceHandle()*/, moduleName, MAX_PATH);
+ if (sz == 0) {
+ // GetModuleFileName failed. Do nothing.
+ return;
+ }
+ moduleName[sz] = '\0'; // make sure string is properly terminated.
+
+ // Get the size of the FileVersionInfo buffer
+ DWORD obsoleteHandle; // see http://blogs.msdn.com/b/oldnewthing/archive/2007/07/31/4138786.aspx
+ DWORD fviSize = ::GetFileVersionInfoSize(moduleName, &obsoleteHandle);
+ if (fviSize == 0) {
+ return; // do nothing on error
+ }
+
+ char *fviBuffer = new char[fviSize];
+ if (::GetFileVersionInfo(moduleName, 0, fviSize, fviBuffer) != 0) {
+ VOID *vBuffer;
+ UINT vLen;
+
+ struct LANGUAGE_CODEPAGE {
+ WORD mLanguage;
+ WORD mCodePage;
+ } *lgcpBuffer;
+
+ UINT lgcpSize;
+
+ // Read the list of languages and code pages (c.f. MSDN for VerQueryValue)
+ if (::VerQueryValue(fviBuffer, _T("\\VarFileInfo\\Translation"), (LPVOID*)&lgcpBuffer, &lgcpSize) != 0 &&
+ lgcpSize >= sizeof(LANGUAGE_CODEPAGE)) {
+ // Use the first available language and code page
+ CString subBlock;
+ subBlock.Format(_T("\\StringFileInfo\\%04x%04x\\FileDescription"),
+ lgcpBuffer[0].mLanguage,
+ lgcpBuffer[0].mCodePage);
+ if (::VerQueryValue(fviBuffer, subBlock, &vBuffer, &vLen) != 0) {
+ gAppName.SetString((LPCTSTR)vBuffer, vLen);
+ }
+ }
+ }
+ delete[] fviBuffer;
+}
+
+CString getAppName() {
+ return gAppName;
+}
+
+
+// Displays a message in an ok+info dialog box.
+void msgBox(const TCHAR* text, ...) {
+ CString formatted;
+ va_list ap;
+ va_start(ap, text);
+ formatted.FormatV(text, ap);
+ va_end(ap);
+
+ // TODO global CString to get app name
+ MessageBox(NULL, formatted, gAppName, MB_OK | MB_ICONINFORMATION);
+}
+
+// Sets the string to the message matching Win32 GetLastError.
+// If message is non-null, it is prepended to the last error string.
+CString getLastWin32Error(const TCHAR* message) {
+ DWORD err = GetLastError();
+ CString result;
+ LPTSTR errStr;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, /* lpSource */
+ err, /* dwMessageId */
+ 0, /* dwLanguageId */
+ (LPTSTR) &errStr, /* out lpBuffer */
+ 0, /* nSize */
+ NULL) != 0) { /* va_list args */
+ if (message == NULL) {
+ result.Format(_T("[%d] %s"), err, errStr);
+ } else {
+ result.Format(_T("%s[%d] %s"), message, err, errStr);
+ }
+ LocalFree(errStr);
+ }
+ return result;
+}
+
+// Displays GetLastError prefixed with a description in an error dialog box
+void displayLastError(const TCHAR *description, ...) {
+ CString formatted;
+ va_list ap;
+ va_start(ap, description);
+ formatted.FormatV(description, ap);
+ va_end(ap);
+
+ CString error = getLastWin32Error(NULL);
+ formatted.Append(_T("\r\n"));
+ formatted.Append(error);
+
+ if (gIsConsole) {
+ _ftprintf(stderr, _T("%s\n"), (LPCTSTR) formatted);
+ } else {
+ CString name(gAppName);
+ name.Append(_T(" - Error"));
+ MessageBox(NULL, formatted, name, 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 TCHAR *app, const TCHAR *params, const TCHAR *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 = CreateProcess(
+ app, /* program path */
+ (TCHAR *)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 TCHAR *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 = CreateProcess(
+ NULL, /* program path */
+ (LPTSTR)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) {
+ TCHAR programDir[MAX_PATH];
+ int ret = GetModuleFileName(NULL, programDir, sizeof(programDir) / sizeof(programDir[0]));
+ if (ret != 0) {
+ CPath dir(programDir);
+ dir.RemoveFileSpec();
+ *outDir = dir;
+ 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(_T("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(_T("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);
+ }
+}
diff --git a/find_java2/src/utils.h b/find_java2/src/utils.h
new file mode 100755
index 000000000..3557f5806
--- /dev/null
+++ b/find_java2/src/utils.h
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2014 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.
+*/
+
+#pragma once
+
+#include <atlpath.h> // ATL CPath
+
+// Global flag indicating whether this is running in debug mode (more printfs)
+extern bool gIsDebug;
+// Global flag indicating whether this is running in console mode or GUI.
+// In console mode, errors are written on the console; in GUI they use a MsgBox.
+extern bool gIsConsole;
+
+// Must be called by the application to initialize the app name used in error dialog boxes.
+// If NULL is used, fetches VERSIONINFO.FileDescription from resources if available.
+void initUtils(const TCHAR *appName);
+
+// Returns the app name set in initUtils
+CString getAppName();
+
+// Displays a message in an ok+info dialog box. Useful in console mode.
+void msgBox(const TCHAR* text, ...);
+
+// Displays GetLastError prefixed with a description in an error dialog box. Useful in console mode.
+void displayLastError(const TCHAR *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 TCHAR *app, const TCHAR *params, const TCHAR *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 TCHAR *cmd);
+
+bool getModuleDir(CPath *outDir);
+
+// 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.
+PVOID disableWow64FsRedirection();
+
+// Reverts the redirection disabled in disableWow64FsRedirection.
+void revertWow64FsRedirection(PVOID oldWow64Value);