Skip to content

Commit 22dc037

Browse files
T-Svenssondbwiddis
authored andcommitted
Add mapping for EnumProcesses to Psapi
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.
1 parent dee2615 commit 22dc037

File tree

7 files changed

+228
-7
lines changed

7 files changed

+228
-7
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Features
1919
* [#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).
2020
* [#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).
2121
* [#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).
22+
* [#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/).
2223

2324
Bug Fixes
2425
---------

contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java

+51-4
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,47 @@ public static final String extractVolumeGUID(String volumeGUIDPath) {
854854
return volumeGUIDPath.substring(VOLUME_GUID_PATH_PREFIX.length(), volumeGUIDPath.length() - VOLUME_GUID_PATH_SUFFIX.length());
855855
}
856856

857+
/**
858+
* This function retrieves the full path of the executable file of a given process identifier.
859+
*
860+
* @param pid
861+
* Identifier for the running process
862+
* @param dwFlags
863+
* 0 - The name should use the Win32 path format.
864+
* 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format.
865+
*
866+
* @return the full path of the process's executable file of null if failed. To get extended error information,
867+
* call GetLastError.
868+
*/
869+
public static final String QueryFullProcessImageName(int pid, int dwFlags) {
870+
HANDLE hProcess = null;
871+
Win32Exception we = null;
872+
873+
try {
874+
hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, pid);
875+
if (hProcess == null) {
876+
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
877+
}
878+
return QueryFullProcessImageName(hProcess, dwFlags);
879+
} catch (Win32Exception e) {
880+
we = e;
881+
throw we; // re-throw to invoke finally block
882+
} finally {
883+
try {
884+
closeHandle(hProcess);
885+
} catch (Win32Exception e) {
886+
if (we == null) {
887+
we = e;
888+
} else {
889+
we.addSuppressed(e);
890+
}
891+
}
892+
if (we != null) {
893+
throw we;
894+
}
895+
}
896+
}
897+
857898
/**
858899
*
859900
* This function retrieves the full path of the executable file of a given process.
@@ -868,10 +909,16 @@ public static final String extractVolumeGUID(String volumeGUIDPath) {
868909
* call GetLastError.
869910
*/
870911
public static final String QueryFullProcessImageName(HANDLE hProcess, int dwFlags) {
871-
char[] path = new char[WinDef.MAX_PATH];
872-
IntByReference lpdwSize = new IntByReference(path.length);
873-
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, 0, path, lpdwSize))
874-
return new String(path).substring(0, lpdwSize.getValue());
912+
int size = WinDef.MAX_PATH; // Start with MAX_PATH, then increment with 1024 each iteration
913+
IntByReference lpdwSize = new IntByReference();
914+
do {
915+
char[] lpExeName = new char[size];
916+
lpdwSize.setValue(size);
917+
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, dwFlags, lpExeName, lpdwSize)) {
918+
return new String(lpExeName, 0, lpdwSize.getValue());
919+
}
920+
size += 1024;
921+
} while (Kernel32.INSTANCE.GetLastError() == Kernel32.ERROR_INSUFFICIENT_BUFFER);
875922
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
876923
}
877924

contrib/platform/src/com/sun/jna/platform/win32/Psapi.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
*/
2424
package com.sun.jna.platform.win32;
2525

26-
import java.util.List;
27-
2826
import com.sun.jna.Native;
2927
import com.sun.jna.Pointer;
3028
import com.sun.jna.Structure;
@@ -266,6 +264,32 @@ public interface Psapi extends StdCallLibrary {
266264
*/
267265
boolean GetPerformanceInfo(PERFORMANCE_INFORMATION pPerformanceInformation, int cb);
268266

267+
/**
268+
* Retrieves the process identifier for each process object in the system. <br>
269+
* It is a good idea to use a large array, because it is hard to predict how
270+
* many processes there will be at the time you call EnumProcesses. <br>
271+
* To determine how many processes were enumerated, divide the pBytesReturned
272+
* value by sizeof(DWORD). There is no indication given when the buffer is too
273+
* small to store all process identifiers. Therefore, if pBytesReturned equals
274+
* cb, consider retrying the call with a larger array. <br>
275+
* To obtain process handles for the processes whose identifiers you have just
276+
* obtained, call the OpenProcess function.
277+
*
278+
* @param lpidProcess
279+
* A pointer to an array that receives the list of process
280+
* identifiers
281+
* @param cb
282+
* The size of the lpidProcess array, in bytes.
283+
* @param lpcbNeeded
284+
* The number of bytes returned in the pProcessIds array.
285+
* @return If the function succeeds, the return value is nonzero. If the
286+
* function fails, the return value is zero. To get extended error
287+
* information, call GetLastError.
288+
* @see <a href=
289+
* "https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses">MSDN</a>
290+
*/
291+
boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);
292+
269293
@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
270294
class MODULEINFO extends Structure {
271295
public Pointer EntryPoint;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* Copyright (c) 2020 Torbjörn Svensson, All Rights Reserved
2+
*
3+
* The contents of this file is dual-licensed under 2
4+
* alternative Open Source/Free licenses: LGPL 2.1 or later and
5+
* Apache License 2.0. (starting with JNA version 4.0.0).
6+
*
7+
* You can freely decide which license you want to apply to
8+
* the project.
9+
*
10+
* You may obtain a copy of the LGPL License at:
11+
*
12+
* http://www.gnu.org/licenses/licenses.html
13+
*
14+
* A copy is also included in the downloadable source code package
15+
* containing JNA, in file "LGPL2.1".
16+
*
17+
* You may obtain a copy of the Apache License at:
18+
*
19+
* http://www.apache.org/licenses/
20+
*
21+
* A copy is also included in the downloadable source code package
22+
* containing JNA, in file "AL2.0".
23+
*/
24+
package com.sun.jna.platform.win32;
25+
26+
import java.util.Arrays;
27+
28+
import com.sun.jna.platform.win32.WinDef.DWORD;
29+
import com.sun.jna.ptr.IntByReference;
30+
31+
/**
32+
* Psapi utility API.
33+
*
34+
* @author Torbj&ouml;rn Svensson, azoff[at]svenskalinuxforeninen.se
35+
*/
36+
public abstract class PsapiUtil {
37+
38+
/**
39+
* Retrieves the process identifier for each process object in the system.
40+
*
41+
* @return Array of pids
42+
*/
43+
public static int[] enumProcesses() {
44+
int size = 0;
45+
int[] lpidProcess = null;
46+
IntByReference lpcbNeeded = new IntByReference();
47+
do {
48+
size += 1024;
49+
lpidProcess = new int[size];
50+
if (!Psapi.INSTANCE.EnumProcesses(lpidProcess, size * DWORD.SIZE, lpcbNeeded)) {
51+
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
52+
}
53+
} while (size == lpcbNeeded.getValue() / DWORD.SIZE);
54+
55+
return Arrays.copyOf(lpidProcess, lpcbNeeded.getValue() / DWORD.SIZE);
56+
}
57+
}

contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -313,14 +313,28 @@ public final void testWritePrivateProfileSection() throws IOException {
313313
}
314314

315315
public final void testQueryFullProcessImageName() {
316-
HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, Kernel32.INSTANCE.GetCurrentProcessId());
316+
int pid = Kernel32.INSTANCE.GetCurrentProcessId();
317+
318+
HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, pid);
317319
assertNotNull("Failed (" + Kernel32.INSTANCE.GetLastError() + ") to get process handle", h);
318320
try {
319321
String name = Kernel32Util.QueryFullProcessImageName(h, 0);
322+
assertNotNull("Failed to query process image name, null path returned", name);
320323
assertTrue("Failed to query process image name, empty path returned", name.length() > 0);
321324
} finally {
322325
Kernel32Util.closeHandle(h);
323326
}
327+
328+
String name = Kernel32Util.QueryFullProcessImageName(pid, 0);
329+
assertNotNull("Failed to query process image name, null path returned", name);
330+
assertTrue("Failed to query process image name, empty path returned", name.length() > 0);
331+
332+
try {
333+
Kernel32Util.QueryFullProcessImageName(0, 0); // the system process
334+
fail("Should never reach here");
335+
} catch (Win32Exception expected) {
336+
assertEquals("Should get Invalid Parameter error", Kernel32.ERROR_INVALID_PARAMETER, expected.getErrorCode());
337+
}
324338
}
325339

326340
public void testGetResource() {

contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java

+25
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,29 @@ public void testGetPerformanceInfo() {
248248
assertTrue(Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size()));
249249
assertTrue(perfInfo.ProcessCount.intValue() > 0);
250250
}
251+
252+
@Test
253+
public void testEnumProcesses() {
254+
int size = 0;
255+
int[] lpidProcess = null;
256+
IntByReference lpcbNeeded = new IntByReference();
257+
do {
258+
size += 1024;
259+
lpidProcess = new int[size];
260+
if (!Psapi.INSTANCE.EnumProcesses(lpidProcess, size * DWORD.SIZE, lpcbNeeded)) {
261+
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
262+
}
263+
} while (size == lpcbNeeded.getValue() / DWORD.SIZE);
264+
assertTrue("Size of pid list in bytes should be a multiple of " + DWORD.SIZE, lpcbNeeded.getValue() % DWORD.SIZE == 0);
265+
266+
int myPid = Kernel32.INSTANCE.GetCurrentProcessId();
267+
boolean foundMyPid = false;
268+
for (int i = 0; i < lpcbNeeded.getValue() / DWORD.SIZE; i++) {
269+
if (lpidProcess[i] == myPid) {
270+
foundMyPid = true;
271+
break;
272+
}
273+
}
274+
assertTrue("List should contain my pid", foundMyPid);
275+
}
251276
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* Copyright (c) 2020 Torbjörn Svensson, All Rights Reserved
2+
*
3+
* The contents of this file is dual-licensed under 2
4+
* alternative Open Source/Free licenses: LGPL 2.1 or later and
5+
* Apache License 2.0. (starting with JNA version 4.0.0).
6+
*
7+
* You can freely decide which license you want to apply to
8+
* the project.
9+
*
10+
* You may obtain a copy of the LGPL License at:
11+
*
12+
* http://www.gnu.org/licenses/licenses.html
13+
*
14+
* A copy is also included in the downloadable source code package
15+
* containing JNA, in file "LGPL2.1".
16+
*
17+
* You may obtain a copy of the Apache License at:
18+
*
19+
* http://www.apache.org/licenses/
20+
*
21+
* A copy is also included in the downloadable source code package
22+
* containing JNA, in file "AL2.0".
23+
*/
24+
package com.sun.jna.platform.win32;
25+
26+
import static org.junit.Assert.assertNotNull;
27+
import static org.junit.Assert.assertTrue;
28+
29+
import org.junit.Test;
30+
31+
/**
32+
* Applies API tests on {@link PsapiUtil}.
33+
*
34+
* @author Torbj&ouml;rn Svensson, azoff[at]svenskalinuxforeninen.se
35+
*/
36+
@SuppressWarnings("nls")
37+
public class PsapiUtilTest {
38+
@Test
39+
public void enumProcesses() {
40+
int[] pids = PsapiUtil.enumProcesses();
41+
assertNotNull("List should not be null", pids);
42+
43+
int myPid = Kernel32.INSTANCE.GetCurrentProcessId();
44+
boolean foundMyPid = false;
45+
for (int i = 0; i < pids.length; i++) {
46+
if (pids[i] == myPid) {
47+
foundMyPid = true;
48+
break;
49+
}
50+
}
51+
assertTrue("List should contain my pid", foundMyPid);
52+
}
53+
}

0 commit comments

Comments
 (0)