From 2710345fee830219e4d6ea7a71986cf1cae77f15 Mon Sep 17 00:00:00 2001 From: Michael Freeman Date: Fri, 4 Dec 2015 18:48:17 -0500 Subject: [PATCH] added Module32FirstW and Module32NextW to Kernel32 added MODULEENTRY32W to Tlhelp32 --- CHANGES.md | 1 + .../com/sun/jna/platform/win32/Kernel32.java | 49 ++++++-- .../sun/jna/platform/win32/Kernel32Util.java | 64 ++++++++++- .../com/sun/jna/platform/win32/Tlhelp32.java | 108 ++++++++++++++++++ .../sun/jna/platform/win32/Kernel32Test.java | 74 +++++++++++- .../jna/platform/win32/Kernel32UtilTest.java | 14 ++- 6 files changed, 297 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 44b68bc39a..2e1069ee6a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -24,6 +24,7 @@ Features * [#524](https://github.com/java-native-access/jna/pull/524): Added IShellFolder interface plus necessary utility functions to Windows platform, and a sample for enumerating objects in My Computer - [@lwahonen](https://github.com/lwahonen). * [#484](https://github.com/twall/jna/pull/484): Added `XFetchName` to `X11` interface - [@pinaf](https://github.com/pinaf). * [#554](https://github.com/java-native-access/jna/pull/554): Initial code for a few Unix 'libc' API(s) [@lgoldstein](https://github.com/lgoldstein) +* [#552](https://github.com/java-native-access/jna/pull/552): Added `Module32FirstW` and `Module32NextW` to `com.sun.jna.platform.win32.Kernel32` (and helper to `com.sun.jna.platform.win32.Kernel32Util`) and `MODULEENTRY32W` structure to `com.sun.jna.platform.win32.Tlhelp32` - [@mlfreeman2](https://github.com/mlfreeman2). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index 8fe90c9a79..c5feac7c85 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -1269,18 +1269,18 @@ boolean CreateProcessW(String lpApplicationName, char[] lpCommandLine, * This function retrieves the full path of the executable file of a given process. * * @param hProcess - * Handle for the running process + * Handle for the running process * @param dwFlags - * 0 - The name should use the Win32 path format. - * 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format. + * 0 - The name should use the Win32 path format. + * 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format. * @param lpExeName - * pre-allocated character buffer for the returned path + * pre-allocated character buffer for the returned path * @param lpdwSize - * input: the size of the allocated buffer - * output: the length of the returned path in characters + * input: the size of the allocated buffer + * output: the length of the returned path in characters * * @return true if successful false if not. To get extended error information, - * call GetLastError. + * call GetLastError. */ boolean QueryFullProcessImageName(HANDLE hProcess, int dwFlags, char[] lpExeName, IntByReference lpdwSize); @@ -3289,4 +3289,39 @@ boolean GetVolumePathNamesForVolumeName(String lpszVolumeName, * information, call GetLastError. */ boolean EnumResourceNames(HMODULE hModule, Pointer type, WinBase.EnumResNameProc proc, Pointer lParam); + + /** + * Retrieves information about the first module associated with a process. + * + * @see MSDN + * @param hSnapshot + * A handle to the snapshot returned from a previous call to the + * CreateToolhelp32Snapshot function. + * @param lpme + * A pointer to a MODULEENTRY32 structure. + * @return Returns TRUE if the first entry of the module list has been + * copied to the buffer or FALSE otherwise.
+ * The ERROR_NO_MORE_FILES error value is returned by the + * GetLastError function if no modules exist or the snapshot does + * not contain module information. + */ + boolean Module32FirstW(HANDLE hSnapshot, Tlhelp32.MODULEENTRY32W lpme); + + /** + * Retrieves information about the next module associated with a process or + * thread. + * + * @see MSDN + * @param hSnapshot + * A handle to the snapshot returned from a previous call to the + * CreateToolhelp32Snapshot function. + * @param lpme + * A pointer to a MODULEENTRY32 structure. + * @return Returns TRUE if the first entry of the module list has been + * copied to the buffer or FALSE otherwise.
+ * The ERROR_NO_MORE_FILES error value is returned by the + * GetLastError function if no modules exist or the snapshot does + * not contain module information. + */ + boolean Module32NextW(HANDLE hSnapshot, Tlhelp32.MODULEENTRY32W lpme); } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java index b2913f8176..f8d9f1cebf 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -686,13 +686,13 @@ public static final String extractVolumeGUID(String volumeGUIDPath) { * This function retrieves the full path of the executable file of a given process. * * @param hProcess - * Handle for the running process + * Handle for the running process * @param dwFlags - * 0 - The name should use the Win32 path format. - * 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format. + * 0 - The name should use the Win32 path format. + * 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format. * * @return the full path of the process's executable file of null if failed. To get extended error information, - * call GetLastError. + * call GetLastError. */ public static final String QueryFullProcessImageName(HANDLE hProcess, int dwFlags) { char[] path = new char[WinDef.MAX_PATH]; @@ -895,4 +895,60 @@ public boolean invoke(HMODULE module, Pointer type, Pointer name, Pointer lParam } return result; } + + /** + * Returns all the executable modules for a given process ID.
+ * + * @param processID + * The process ID to get executable modules for + * @return All the modules in the process. + */ + public static List getModules(int processID) { + HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(processID)); + if (snapshot == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + List modules = new ArrayList(); + Win32Exception we = null; + try { + Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W(); + modules.add(first); + + if (!Kernel32.INSTANCE.Module32FirstW(snapshot, first)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Tlhelp32.MODULEENTRY32W next = new Tlhelp32.MODULEENTRY32W(); + while (Kernel32.INSTANCE.Module32NextW(snapshot, next)) { + modules.add(next); + next = new Tlhelp32.MODULEENTRY32W(); + } + + int lastError = Kernel32.INSTANCE.GetLastError(); + // if we got a false from Module32Next, + // check to see if it returned false because we're genuinely done + // or if something went wrong. + if (lastError != W32Errors.ERROR_SUCCESS && lastError != W32Errors.ERROR_NO_MORE_FILES) { + throw new Win32Exception(lastError); + } + } catch (Win32Exception e) { + we = e; + } finally { + if (snapshot != null) { + if (!Kernel32.INSTANCE.CloseHandle(snapshot)) { + Win32Exception e = new Win32Exception(Kernel32.INSTANCE.GetLastError()); + if (we != null) { + e.addSuppressed(we); + } + we = e; + } + } + } + + if (we != null) { + throw we; + } + return modules; + } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java b/contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java index ccd671c487..1821d271fd 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java @@ -13,8 +13,11 @@ import java.util.Arrays; import java.util.List; +import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinDef.HMODULE; /** * Interface for the Tlhelp32.h header file. @@ -156,4 +159,109 @@ protected List getFieldOrder() { return Arrays.asList(new String[] { "dwSize", "cntUsage", "th32ProcessID", "th32DefaultHeapID", "th32ModuleID", "cntThreads", "th32ParentProcessID", "pcPriClassBase", "dwFlags", "szExeFile" }); } } + + + /** + * Describes an entry from a list of the modules belonging to the specified + * process. + * + * @see MSDN + */ + public class MODULEENTRY32W extends Structure { + + /** + * A representation of a MODULEENTRY32 structure as a reference + */ + public static class ByReference extends MODULEENTRY32W implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + public MODULEENTRY32W() { + dwSize = new WinDef.DWORD(size()); + } + + public MODULEENTRY32W(Pointer memory) { + super(memory); + read(); + } + + /** + * The size of the structure, in bytes. Before calling the Module32First + * function, set this member to sizeof(MODULEENTRY32). If you do not + * initialize dwSize, Module32First fails. + */ + public DWORD dwSize; + + /** + * This member is no longer used, and is always set to one. + */ + public DWORD th32ModuleID; + + /** + * The identifier of the process whose modules are to be examined. + */ + public DWORD th32ProcessID; + + /** + * The load count of the module, which is not generally meaningful, and + * usually equal to 0xFFFF. + */ + public DWORD GlblcntUsage; + + /** + * The load count of the module (same as GlblcntUsage), which is not + * generally meaningful, and usually equal to 0xFFFF. + */ + public DWORD ProccntUsage; + + /** + * The base address of the module in the context of the owning process. + */ + public Pointer modBaseAddr; + + /** + * The size of the module, in bytes. + */ + public DWORD modBaseSize; + + /** + * A handle to the module in the context of the owning process. + */ + public HMODULE hModule; + + /** + * The module name. + */ + public char[] szModule = new char[MAX_MODULE_NAME32 + 1]; + + /** + * The module path. + */ + public char[] szExePath = new char[Kernel32.MAX_PATH]; + + /** + * @return The module name. + */ + public String szModule() { + return Native.toString(this.szModule); + } + + /** + * @return The module path. + */ + public String szExePath() { + return Native.toString(this.szExePath); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "dwSize", "th32ModuleID", "th32ProcessID", "GlblcntUsage", + "ProccntUsage", "modBaseAddr", "modBaseSize", "hModule", "szModule", "szExePath" }); + } + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index c7992da61d..065290fd9d 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java @@ -334,7 +334,7 @@ public void testOpenProcess() { public void testQueryFullProcessImageName() { HANDLE h = Kernel32.INSTANCE.OpenProcess(0, false, Kernel32.INSTANCE.GetCurrentProcessId()); assertNotNull("Failed (" + Kernel32.INSTANCE.GetLastError() + ") to get process handle", h); - + char[] path = new char[WinDef.MAX_PATH]; IntByReference lpdwSize = new IntByReference(path.length); boolean b = Kernel32.INSTANCE.QueryFullProcessImageName(h, 0, path, lpdwSize); @@ -1043,4 +1043,76 @@ public boolean invoke(HMODULE module, Pointer type, Pointer lParam) { assertEquals("GetLastError should be set to 0", WinError.ERROR_SUCCESS, Kernel32.INSTANCE.GetLastError()); assertTrue("EnumResourceTypes should return some resource type names", types.size() > 0); } + + public void testModule32FirstW() { + HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(Kernel32.INSTANCE.GetCurrentProcessId())); + if (snapshot == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W(); + try { + if (!Kernel32.INSTANCE.Module32FirstW(snapshot, first)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } catch (Win32Exception e) { + we = e; + } finally { + if (snapshot != null) { + if (!Kernel32.INSTANCE.CloseHandle(snapshot)) { + Win32Exception e = new Win32Exception(Kernel32.INSTANCE.GetLastError()); + if (we != null) { + e.addSuppressed(we); + } + we = e; + } + } + } + + if (we != null) { + throw we; + } + + // not sure if this will be run against java.exe or javaw.exe but this + // check tests both + assertTrue("The first module in the current process should be java.exe or javaw.exe", first.szModule().startsWith("java")); + assertEquals("The process ID of the module ID should be our process ID", Kernel32.INSTANCE.GetCurrentProcessId(), first.th32ProcessID.intValue()); + } + + public void testModule32NextW() { + HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(Kernel32.INSTANCE.GetCurrentProcessId())); + if (snapshot == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W(); + try { + if (!Kernel32.INSTANCE.Module32NextW(snapshot, first)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } catch (Win32Exception e) { + we = e; + } finally { + if (snapshot != null) { + if (!Kernel32.INSTANCE.CloseHandle(snapshot)) { + Win32Exception e = new Win32Exception(Kernel32.INSTANCE.GetLastError()); + if (we != null) { + e.addSuppressed(we); + } + we = e; + } + } + } + + if (we != null) { + throw we; + } + + // not sure if this will be run against java.exe or javaw.exe but this + // check tests both + assertTrue("The first module in the current process should be java.exe or javaw.exe", first.szModule().startsWith("java")); + assertEquals("The process ID of the module ID should be our process ID", Kernel32.INSTANCE.GetCurrentProcessId(), first.th32ProcessID.intValue()); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java index 9da79052b2..90060c2be8 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java @@ -23,9 +23,9 @@ import java.util.List; import java.util.Map; +import com.sun.jna.platform.win32.Tlhelp32.MODULEENTRY32W; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER; -import com.sun.jna.ptr.IntByReference; import junit.framework.TestCase; @@ -293,4 +293,16 @@ public void testGetResourceNames() { assertTrue("explorer.exe should contain a resource of type '14' in it.", names.containsKey("14")); assertTrue("resource type 14 should have a name named ICO_MYCOMPUTER associated with it.", names.get("14").contains("ICO_MYCOMPUTER")); } + + public void testGetModules() { + List results = Kernel32Util.getModules(Kernel32.INSTANCE.GetCurrentProcessId()); + + // not sure if this will be run against java.exe or javaw.exe but these checks should work with both + assertNotNull("There should be some modules returned from this helper", results); + assertTrue("The first module in this process should be java.exe or javaw.exe", results.get(0).szModule().startsWith("java")); + + // since this is supposed to return all the modules in a process, there should be an EXE and at least 1 Windows DLL + // so assert total count is at least two + assertTrue("This is supposed to return all the modules in a process, so there should be an EXE and at least 1 Windows API DLL.", results.size() > 2); + } }