/*******************************************************************************
* Copyright (c) 2010-2013, Embraer S.A., Budapest University of Technology and Economics
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rodrigo Rizzi Starr, Lincoln Nascimento - initial API and implementation
*******************************************************************************/
package br.com.embraer.massif.commandevaluation.client.util;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import br.com.embraer.massif.commandevaluation.exception.MatlabError;
import br.com.embraer.massif.commandevaluation.exception.MatlabRegistryException;
import br.com.embraer.massif.commandevaluation.jna.Psapi32;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.W32APIOptions;
/**
* Class used to retrieve the current running matlabs processes with its version and file path
*
* @author ldnascim
*/
public class MatlabRunningManager {
// Required to retrieve certain information about a process
public static final int PROCESS_QUERY_INFORMATION = 0x0400;
// Required to read information about a process
public static final int PROCESS_VM_READ = 0x0010;
private static Map<String, String> matlabVersions = new HashMap<String, String>();
/**
* Initialize the map with all matlab versions
*/
static {
matlabVersions.put("5.1.1", "R9.1");
matlabVersions.put("5.2", "R10");
matlabVersions.put("5.2.1", "R10.1");
matlabVersions.put("5.3", "R11");
matlabVersions.put("5.3.1", "R11.1");
matlabVersions.put("6.0", "R12");
matlabVersions.put("6.1", "R12.1");
matlabVersions.put("6.5", "R13");
matlabVersions.put("6.5.1", "R13SP1");
matlabVersions.put("6.5.2", "R13SP2");
matlabVersions.put("7.0", "R14");
matlabVersions.put("7.0.1", "R14SP1");
matlabVersions.put("7.0.4", "R14SP2");
matlabVersions.put("7.1", "R14SP3");
matlabVersions.put("7.2", "R2006a");
matlabVersions.put("7.3", "R2006b");
matlabVersions.put("7.4", "R2007a");
matlabVersions.put("7.5", "R2007b");
matlabVersions.put("7.6", "R2008a");
matlabVersions.put("7.7", "R2008b");
matlabVersions.put("7.8", "R2009a");
matlabVersions.put("7.9", "R2009b");
matlabVersions.put("7.9.1", "R2009bSP");
matlabVersions.put("7.10", "R2010a");
matlabVersions.put("7.11", "R2010b");
matlabVersions.put("7.11.1", "R2010bSP");
matlabVersions.put("7.12", "R2011a");
matlabVersions.put("7.13", "R2011b");
matlabVersions.put("7.14", "R2012a");
matlabVersions.put("8.0", "R2012b");
matlabVersions.put("8.1", "R2013a");
matlabVersions.put("8.2", "R2013b");
matlabVersions.put("8.3", "R2014a");
matlabVersions.put("8.4", "R2014b");
matlabVersions.put("8.5", "R2015a");
matlabVersions.put("8.6", "R2015b");
matlabVersions.put("9.0", "R2016a");
matlabVersions.put("9.1", "R2016b");
matlabVersions.put("9.2", "R2017a");
matlabVersions.put("9.3", "R2017b");
}
/**
* Returns a map containing the current matlab running processes. The key is the process file path, and the value is
* the matlab version
*
* @return
* @throws MatlabRegistryException
*/
public static List<MatlabProcessInformation> getRunningMatlabs() throws MatlabRegistryException {
Kernel32 kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
Map<String, Integer> processes = findProcessPIDs(kernel32);
AvailableVersions versions = findAvailableMatlabVersions(kernel32);
List<MatlabProcessInformation> runningMatlabs = findRunningMatlabsFromPIDs(versions, processes);
return runningMatlabs;
}
private static boolean isWow64Machine(Kernel32 kernel32) {
HANDLE curProcessHandle = kernel32.GetCurrentProcess();
// create value to be passed as reference, and initialize with a value
// different from a "BOOL" one (0 or 1)
IntByReference isWow64 = new IntByReference(42);
kernel32.IsWow64Process(curProcessHandle, isWow64);
// check if it is running on a 32-bits or 64-bits machine
boolean isWow64Process = (isWow64.getValue() == 1);
boolean isWow64Pointer = (Pointer.SIZE == 8);
boolean isWow64Machine = isWow64Process || isWow64Pointer;
return isWow64Machine;
}
private static AvailableVersions findAvailableMatlabVersions(Kernel32 kernel32) throws MatlabRegistryException {
AvailableVersions versions = new AvailableVersions();
versions.isWow64Machine = isWow64Machine(kernel32);
try {
findAvailableVersions(versions);
} catch (IllegalAccessException e) {
throwMatlabRegistryException(e, versions.currentKey);
} catch (InvocationTargetException e) {
throwMatlabRegistryException(e, versions.currentKey);
}
return versions;
}
private static void findAvailableVersions(AvailableVersions versions) throws IllegalAccessException,
InvocationTargetException {
// Retrieves the current matlab 64-bits installed, if is a 64-bits machine
// if is a 32-bits machine, retrieves the current 32-bits installed
versions.currentKey = "SOFTWARE\\MathWorks\\MATLAB";
List<String> currentVersions64or32bits = WinRegistry.readStringSubKeys(WinRegistry.HKEY_LOCAL_MACHINE,
versions.currentKey, 0);
if (currentVersions64or32bits != null) {
for (String version64or32bits : currentVersions64or32bits) {
versions.currentKey = "SOFTWARE\\MathWorks\\MATLAB\\" + version64or32bits;
String folderPath = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE, versions.currentKey,
"MATLABROOT",
WinRegistry.KEY_WOW64_64KEY);
if (folderPath != null && !folderPath.isEmpty()) {
if (versions.isWow64Machine) {
versions.availableVersions64bits.put(version64or32bits, folderPath);
} else {
versions.availableVersions32bits.put(version64or32bits, folderPath);
}
}
}
}
// Retrieves the current matlab 32-bits installed
versions.currentKey = "SOFTWARE\\Wow6432Node\\MathWorks\\MATLAB";
List<String> currentVersions32bits = WinRegistry.readStringSubKeys(WinRegistry.HKEY_LOCAL_MACHINE,
versions.currentKey,
0);
if (currentVersions32bits != null) {
for (String version32bits : currentVersions32bits) {
versions.currentKey = "SOFTWARE\\MathWorks\\MATLAB\\" + version32bits;
String folderPath = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE, versions.currentKey,
"MATLABROOT",
WinRegistry.KEY_WOW64_32KEY);
if (folderPath != null && !folderPath.isEmpty()) {
versions.availableVersions32bits.put(version32bits, folderPath);
}
}
}
}
private static void throwMatlabRegistryException(Exception excp, String currentKey) throws MatlabRegistryException {
MatlabRegistryException exception = new MatlabRegistryException(MatlabError.READING_REGISTRY_ERROR, excp);
exception.set("key", currentKey);
throw exception;
}
private static Map<String, Integer> findProcessPIDs(Kernel32 kernel32) {
Map<String, Integer> processes = new HashMap<String, Integer>();
String matlabExe = "matlab.exe";
Tlhelp32.PROCESSENTRY32.ByReference processEntry = new Tlhelp32.PROCESSENTRY32.ByReference();
// gets all current running processes
WinNT.HANDLE snapshot = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0));
if (kernel32.Process32First(snapshot, processEntry)) {
while (kernel32.Process32Next(snapshot, processEntry)) {
String exePath = Native.toString(processEntry.szExeFile);
exePath = exePath.toLowerCase();
// check if its a matlab process
if (!exePath.equalsIgnoreCase(matlabExe)) {
continue;
}
WinNT.HANDLE hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false,
processEntry.th32ProcessID.intValue());
// gets process path
if (hProcess != null && hProcess.getPointer() != null) {
char[] filePath = new char[1024];
Psapi32.INSTANCE.GetModuleFileNameExW(hProcess.getPointer(), null, filePath, 256);
String processPath = Native.toString(filePath);
int pid = kernel32.GetProcessId(hProcess);
processes.put(processPath, pid);
}
}
}
return processes;
}
private static List<MatlabProcessInformation> findRunningMatlabsFromPIDs(AvailableVersions versions,
Map<String, Integer> processes) {
List<MatlabProcessInformation> runningMatlabs = new ArrayList<MatlabProcessInformation>();
// retrieves the version of the current matlab processes
for (Entry<String, Integer> processEntry : processes.entrySet()) {
String processPath = processEntry.getKey();
Integer processPID = processEntry.getValue();
MatlabProcessInformation info = findRunningMatlabFromPID(versions, processPath, processPID);
if (!info.isEmpty()) {
runningMatlabs.add(info);
}
}
return runningMatlabs;
}
private static MatlabProcessInformation findRunningMatlabFromPID(AvailableVersions versions, String processPath,
Integer processPID) {
MatlabProcessInformation info = findRunningMatlab(versions.availableVersions32bits, processPath, processPID);
if (!info.isEmpty()) {
info.is32bits = true;
} else {
// if is not a 32-bits matlab version, check if
// is a 64-bits one(it should be)
info = findRunningMatlab(versions.availableVersions64bits, processPath, processPID);
if (!info.isEmpty()) {
info.is32bits = false;
}
}
return info;
}
private static MatlabProcessInformation findRunningMatlab(Map<String, String> availableVersions,
String processPath, Integer processPID) {
for (Entry<String, String> versionEntry : availableVersions.entrySet()) {
String version = versionEntry.getKey();
String folder = versionEntry.getValue();
MatlabProcessInformation information = prepareMatlabInformation(processPath, processPID, version, folder);
if (!information.isEmpty()) {
return information;
}
}
return new MatlabProcessInformation();
}
private static MatlabProcessInformation prepareMatlabInformation(String processPath, Integer processPID,
String version, String folder) {
// if this formatting is not performed a 64-bits executable could be
// considered as a 32-bits one, since the begin of the
// folder path for both are the same:
// Example: "C:\MATLAB\R2012b" and "C:\MATLAB\R2012b-x64"
String folderEndedWithSeparator = folder;
if (!folder.endsWith(File.separator)) {
folderEndedWithSeparator = folder + File.separator;
}
MatlabProcessInformation info = new MatlabProcessInformation();
if (processPath.toUpperCase().startsWith(folderEndedWithSeparator.toUpperCase()) && matlabVersions.containsKey(version)) {
String release = matlabVersions.get(version);
info.path = processPath;
info.pid = processPID;
info.release = release;
}
return info;
}
private static class AvailableVersions {
Map<String, String> availableVersions64bits = new HashMap<String, String>();
Map<String, String> availableVersions32bits = new HashMap<String, String>();
boolean isWow64Machine;
String currentKey = "";
}
}