Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added Module32First and Module32Next to Kernel32 #552

Merged
merged 1 commit into from
Dec 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------
Expand Down
49 changes: 42 additions & 7 deletions contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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 <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684218(v=vs.85).aspx">MSDN</a>
* @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.<br>
* 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 <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684221(v=vs.85).aspx">MSDN</a>
* @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.<br>
* 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);
}
64 changes: 60 additions & 4 deletions contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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.<br>
*
* @param processID
* The process ID to get executable modules for
* @return All the modules in the process.
*/
public static List<Tlhelp32.MODULEENTRY32W> getModules(int processID) {
HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(processID));
if (snapshot == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

List<Tlhelp32.MODULEENTRY32W> modules = new ArrayList<Tlhelp32.MODULEENTRY32W>();
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;
}
}
108 changes: 108 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684225(v=vs.85).aspx">MSDN</a>
*/
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];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that because of these fields, this Structure definition may only be used with the -W version of the functions which use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The official MSDN page says these fields are TCHAR.

Mappings.md says that TCHAR could map to char or byte.

If I convert these two fields to byte arrays (and, I figure, also double the size of both arrays), would that make this work with both the Unicode and Ansi version of Module32First and Module32Next?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not hack it. TCHAR means we need two separate structures here unless @twall has a better solution? I vote for MODULEENTRY32A and MODULEENTRY32W with tests for both and the Util functions using the W version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviving a couple of comments that got hidden by an outdated diff:

@twall suggested this in a now hidden comment:

Because this depends on a Structure with explicit native wide strings, you should explicitly map to Module32FirstW. I believe you should still be able to reference with Module32First in client code.

I see the com.sun.jna.FunctionMapper interface but I'm unsure how I could use it surgically (without potentially impacting the rest of Kernel32).

Is there something I should mark up Module32First and Module32Next with (e.g. an annotation)? I'd hate to break something else in Kernel32.

Last but not least, note that the Unicode functions do end in -W but the ANSI versions do not end in -A according to MSDN.


Duplicating what I have here and adding a W to the end where appropriate is probably the easiest, quickest, and least likely to have a weird side effect.

@dblock and @twall - I await your decision.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am +1 for the easiest, quickest and least likely to have side effects. I say go for it, I don't think @twall will disagree :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the same implementation but add the -W to make it obvious. Making the struct compatible or available to ANSI version is not important.

On Dec 6, 2015, at 9:30 PM, Michael Freeman notifications@github.com wrote:

In contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java:

  •    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];
    
    Reviving a couple of comments that got hidden by an outdated diff:

@twall suggested this in a now hidden comment:

Because this depends on a Structure with explicit native wide strings, you should explicitly map to Module32FirstW. I believe you should still be able to reference with Module32First in client code.

I see the com.sun.jna.FunctionMapper interface but I'm unsure how I could use it surgically (without potentially impacting the rest of Kernel32).

Is there something I should mark up Module32First and Module32Next with (e.g. an annotation)? I'd hate to break something else in Kernel32.

Last but not least, note that the Unicode functions do end in -W but the ANSI versions do not end in -A according to MSDN.

Duplicating what I have here and adding a W to the end where appropriate is probably the easiest, quickest, and least likely to have a weird side effect.

@dblock and @twall - I await your decision.


Reply to this email directly or view it on GitHub.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What DB said :)

On Dec 7, 2015, at 1:38 PM, Daniel Doubrovkine (dB.) @dblockdotorg notifications@github.com wrote:

In contrib/platform/src/com/sun/jna/platform/win32/Tlhelp32.java:

  •    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];
    

I am +1 for the easiest, quickest and least likely to have side effects. I say go for it, I don't think @twall will disagree :)


Reply to this email directly or view it on GitHub.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added W to Module32First, Module32Next, and MODULEENTRY32, rebased against master, and, well, take a look and let me know what else it needs.


/**
* @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<String> getFieldOrder() {
return Arrays.asList(new String[] { "dwSize", "th32ModuleID", "th32ProcessID", "GlblcntUsage",
"ProccntUsage", "modBaseAddr", "modBaseSize", "hModule", "szModule", "szExePath" });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<MODULEENTRY32W> 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);
}
}