Skip to content

Commit

Permalink
Add mapping for EnumProcesses to Psapi
Browse files Browse the repository at this point in the history
Added utility method QueryFullProcessImageName that takes a PID.
Fixed QueryFullProcessImageName to properly handle paths longer than 260
characters that can happen on Win10 / paths with UNC.
  • Loading branch information
T-Svensson authored and dbwiddis committed Jul 1, 2020
1 parent dee2615 commit 22dc037
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Features
* [#1200](https://github.com/java-native-access/jna/pull/1200): Add mappings for `libudev` to `c.s.j.p.linux.Udev` - [@dbwiddis](https://github.com/dbwiddis).
* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory including `c.s.j.p.unix.LibCAPI` types `size_t` and `ssize_t`, `c.s.j.p.linux.LibC` methods `munmap()`, `msync()`, and `close()`, `c.s.j.p.unix.LibCUtil` mapping `mmap()` and `ftruncate()`, and `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis).
* [#1209](https://github.com/java-native-access/jna/pull/1209): Add mappings for `Thread32First` and `Thread32Next` to `c.s.j.p.win32.Kernel32` - [@dbwiddis](https://github.com/dbwiddis).
* [#1214](https://github.com/java-native-access/jna/pull/1214): Add mapping for EnumProcesses to `c.s.j.p.win32.Psapi` and `c.s.j.p.win32.PsapiUtil` - [@T-Svensson](https://github.com/T-Svensson/).

Bug Fixes
---------
Expand Down
55 changes: 51 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 @@ -854,6 +854,47 @@ public static final String extractVolumeGUID(String volumeGUIDPath) {
return volumeGUIDPath.substring(VOLUME_GUID_PATH_PREFIX.length(), volumeGUIDPath.length() - VOLUME_GUID_PATH_SUFFIX.length());
}

/**
* This function retrieves the full path of the executable file of a given process identifier.
*
* @param pid
* Identifier 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.
*
* @return the full path of the process's executable file of null if failed. To get extended error information,
* call GetLastError.
*/
public static final String QueryFullProcessImageName(int pid, int dwFlags) {
HANDLE hProcess = null;
Win32Exception we = null;

try {
hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, pid);
if (hProcess == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return QueryFullProcessImageName(hProcess, dwFlags);
} catch (Win32Exception e) {
we = e;
throw we; // re-throw to invoke finally block
} finally {
try {
closeHandle(hProcess);
} catch (Win32Exception e) {
if (we == null) {
we = e;
} else {
we.addSuppressed(e);
}
}
if (we != null) {
throw we;
}
}
}

/**
*
* This function retrieves the full path of the executable file of a given process.
Expand All @@ -868,10 +909,16 @@ public static final String extractVolumeGUID(String volumeGUIDPath) {
* call GetLastError.
*/
public static final String QueryFullProcessImageName(HANDLE hProcess, int dwFlags) {
char[] path = new char[WinDef.MAX_PATH];
IntByReference lpdwSize = new IntByReference(path.length);
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, 0, path, lpdwSize))
return new String(path).substring(0, lpdwSize.getValue());
int size = WinDef.MAX_PATH; // Start with MAX_PATH, then increment with 1024 each iteration
IntByReference lpdwSize = new IntByReference();
do {
char[] lpExeName = new char[size];
lpdwSize.setValue(size);
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, dwFlags, lpExeName, lpdwSize)) {
return new String(lpExeName, 0, lpdwSize.getValue());
}
size += 1024;
} while (Kernel32.INSTANCE.GetLastError() == Kernel32.ERROR_INSUFFICIENT_BUFFER);
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

Expand Down
28 changes: 26 additions & 2 deletions contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
*/
package com.sun.jna.platform.win32;

import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
Expand Down Expand Up @@ -266,6 +264,32 @@ public interface Psapi extends StdCallLibrary {
*/
boolean GetPerformanceInfo(PERFORMANCE_INFORMATION pPerformanceInformation, int cb);

/**
* Retrieves the process identifier for each process object in the system. <br>
* It is a good idea to use a large array, because it is hard to predict how
* many processes there will be at the time you call EnumProcesses. <br>
* To determine how many processes were enumerated, divide the pBytesReturned
* value by sizeof(DWORD). There is no indication given when the buffer is too
* small to store all process identifiers. Therefore, if pBytesReturned equals
* cb, consider retrying the call with a larger array. <br>
* To obtain process handles for the processes whose identifiers you have just
* obtained, call the OpenProcess function.
*
* @param lpidProcess
* A pointer to an array that receives the list of process
* identifiers
* @param cb
* The size of the lpidProcess array, in bytes.
* @param lpcbNeeded
* The number of bytes returned in the pProcessIds array.
* @return If the function succeeds, the return value is nonzero. If the
* function fails, the return value is zero. To get extended error
* information, call GetLastError.
* @see <a href=
* "https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses">MSDN</a>
*/
boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);

@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
class MODULEINFO extends Structure {
public Pointer EntryPoint;
Expand Down
57 changes: 57 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* Copyright (c) 2020 Torbjörn Svensson, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna.platform.win32;

import java.util.Arrays;

import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.ptr.IntByReference;

/**
* Psapi utility API.
*
* @author Torbj&ouml;rn Svensson, azoff[at]svenskalinuxforeninen.se

This comment has been minimized.

Copy link
@T-Svensson

T-Svensson Jul 11, 2020

Author Contributor

I just noticed a small typo in the email address in this class. Would you mind changing it to svenskalinuxforeningen.se @matthiasblaesing before releasing 5.6.0?

This comment has been minimized.

Copy link
@matthiasblaesing

matthiasblaesing Jul 11, 2020

Member

Could you please create a PR for it. I'll merge it ASAP.

*/
public abstract class PsapiUtil {

/**
* Retrieves the process identifier for each process object in the system.
*
* @return Array of pids
*/
public static int[] enumProcesses() {
int size = 0;
int[] lpidProcess = null;
IntByReference lpcbNeeded = new IntByReference();
do {
size += 1024;
lpidProcess = new int[size];
if (!Psapi.INSTANCE.EnumProcesses(lpidProcess, size * DWORD.SIZE, lpcbNeeded)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
} while (size == lpcbNeeded.getValue() / DWORD.SIZE);

return Arrays.copyOf(lpidProcess, lpcbNeeded.getValue() / DWORD.SIZE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,28 @@ public final void testWritePrivateProfileSection() throws IOException {
}

public final void testQueryFullProcessImageName() {
HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, Kernel32.INSTANCE.GetCurrentProcessId());
int pid = Kernel32.INSTANCE.GetCurrentProcessId();

HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, pid);
assertNotNull("Failed (" + Kernel32.INSTANCE.GetLastError() + ") to get process handle", h);
try {
String name = Kernel32Util.QueryFullProcessImageName(h, 0);
assertNotNull("Failed to query process image name, null path returned", name);
assertTrue("Failed to query process image name, empty path returned", name.length() > 0);
} finally {
Kernel32Util.closeHandle(h);
}

String name = Kernel32Util.QueryFullProcessImageName(pid, 0);
assertNotNull("Failed to query process image name, null path returned", name);
assertTrue("Failed to query process image name, empty path returned", name.length() > 0);

try {
Kernel32Util.QueryFullProcessImageName(0, 0); // the system process
fail("Should never reach here");
} catch (Win32Exception expected) {
assertEquals("Should get Invalid Parameter error", Kernel32.ERROR_INVALID_PARAMETER, expected.getErrorCode());
}
}

public void testGetResource() {
Expand Down
25 changes: 25 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,29 @@ public void testGetPerformanceInfo() {
assertTrue(Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size()));
assertTrue(perfInfo.ProcessCount.intValue() > 0);
}

@Test
public void testEnumProcesses() {
int size = 0;
int[] lpidProcess = null;
IntByReference lpcbNeeded = new IntByReference();
do {
size += 1024;
lpidProcess = new int[size];
if (!Psapi.INSTANCE.EnumProcesses(lpidProcess, size * DWORD.SIZE, lpcbNeeded)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
} while (size == lpcbNeeded.getValue() / DWORD.SIZE);
assertTrue("Size of pid list in bytes should be a multiple of " + DWORD.SIZE, lpcbNeeded.getValue() % DWORD.SIZE == 0);

int myPid = Kernel32.INSTANCE.GetCurrentProcessId();
boolean foundMyPid = false;
for (int i = 0; i < lpcbNeeded.getValue() / DWORD.SIZE; i++) {
if (lpidProcess[i] == myPid) {
foundMyPid = true;
break;
}
}
assertTrue("List should contain my pid", foundMyPid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Copyright (c) 2020 Torbjörn Svensson, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna.platform.win32;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
* Applies API tests on {@link PsapiUtil}.
*
* @author Torbj&ouml;rn Svensson, azoff[at]svenskalinuxforeninen.se
*/
@SuppressWarnings("nls")
public class PsapiUtilTest {
@Test
public void enumProcesses() {
int[] pids = PsapiUtil.enumProcesses();
assertNotNull("List should not be null", pids);

int myPid = Kernel32.INSTANCE.GetCurrentProcessId();
boolean foundMyPid = false;
for (int i = 0; i < pids.length; i++) {
if (pids[i] == myPid) {
foundMyPid = true;
break;
}
}
assertTrue("List should contain my pid", foundMyPid);
}
}

0 comments on commit 22dc037

Please sign in to comment.