Skip to content

Commit 6579864

Browse files
committed
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 d471b15 commit 6579864

File tree

7 files changed

+245
-5
lines changed

7 files changed

+245
-5
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` - [@T-Svensson](https://github.com/T-Svensson/).
2223

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

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

+53-4
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,49 @@ 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+
*
859+
* This function retrieves the full path of the executable file of a given process identifier.
860+
*
861+
* @param pid
862+
* Identifier for the running process
863+
* @param dwFlags
864+
* 0 - The name should use the Win32 path format.
865+
* 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format.
866+
*
867+
* @return the full path of the process's executable file of null if failed. To get extended error information,
868+
* call GetLastError.
869+
*/
870+
public static final String QueryFullProcessImageName(int pid, int dwFlags) {
871+
HANDLE hProcess = null;
872+
Win32Exception we = null;
873+
874+
try {
875+
hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, pid);
876+
if (hProcess == null) {
877+
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
878+
}
879+
880+
return QueryFullProcessImageName(hProcess, dwFlags);
881+
} catch (Win32Exception e) {
882+
we = e;
883+
throw we; // re-throw to invoke finally block
884+
} finally {
885+
try {
886+
closeHandle(hProcess);
887+
} catch (Win32Exception e) {
888+
if (we == null) {
889+
we = e;
890+
} else {
891+
we.addSuppressed(e);
892+
}
893+
}
894+
if (we != null) {
895+
throw we;
896+
}
897+
}
898+
}
899+
857900
/**
858901
*
859902
* This function retrieves the full path of the executable file of a given process.
@@ -868,10 +911,16 @@ public static final String extractVolumeGUID(String volumeGUIDPath) {
868911
* call GetLastError.
869912
*/
870913
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());
914+
int size = 0;
915+
IntByReference lpdwSize = new IntByReference();
916+
do {
917+
size = size + (size == 0 ? WinDef.MAX_PATH : 1024); // Start with MAX_PATH, then increment with 1024 each iteration
918+
char[] lpExeName = new char[size];
919+
lpdwSize.setValue(size);
920+
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, dwFlags, lpExeName, lpdwSize)) {
921+
return new String(lpExeName, 0, lpdwSize.getValue());
922+
}
923+
} while (Kernel32.INSTANCE.GetLastError() == Kernel32.ERROR_INSUFFICIENT_BUFFER);
875924
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
876925
}
877926

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

+40
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,46 @@ public interface Psapi extends StdCallLibrary {
266266
*/
267267
boolean GetPerformanceInfo(PERFORMANCE_INFORMATION pPerformanceInformation, int cb);
268268

269+
/**
270+
* Retrieves the process identifier for each process object in the system.
271+
* <br>
272+
* It is a good idea to use a large array, because it is hard to predict
273+
* how many processes there will be at the time you call EnumProcesses.
274+
* <br>
275+
* To determine how many processes were enumerated, divide the
276+
* pBytesReturned value by sizeof(DWORD). There is no indication given
277+
* when the buffer is too small to store all process identifiers.
278+
* Therefore, if pBytesReturned equals cb, consider retrying the call
279+
* with a larger array.
280+
* <br>
281+
* To obtain process handles for the processes whose identifiers you
282+
* have just obtained, call the OpenProcess function.
283+
* <br>
284+
* Starting with Windows 7 and Windows Server 2008 R2, Psapi.h
285+
* establishes version numbers for the PSAPI functions. The PSAPI
286+
* version number affects the name used to call the function and the
287+
* library that a program must load.
288+
* <br>
289+
* If PSAPI_VERSION is 2 or greater, this function is defined as
290+
* K32EnumProcesses in Psapi.h and exported in Kernel32.lib and
291+
* Kernel32.dll. If PSAPI_VERSION is 1, this function is defined as
292+
* EnumProcesses in Psapi.h and exported in Psapi.lib and Psapi.dll as a
293+
* wrapper that calls K32EnumProcesses.
294+
*
295+
* @param lpidProcess
296+
* A pointer to an array that receives the list of process
297+
* identifiers
298+
* @param cb
299+
* The size of the pProcessIds array, in bytes.
300+
* @param lpcbNeeded
301+
* The number of bytes returned in the pProcessIds array.
302+
* @return If the function succeeds, the return value is nonzero. If the
303+
* function fails, the return value is zero. To get extended
304+
* error information, call GetLastError.
305+
* @see <a href="https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses">MSDN</a>
306+
*/
307+
boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);
308+
269309
@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
270310
class MODULEINFO extends Structure {
271311
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 = 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

+26
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import static org.junit.Assert.assertTrue;
2727

28+
import java.util.Arrays;
2829
import java.util.LinkedList;
2930
import java.util.List;
3031

@@ -248,4 +249,29 @@ public void testGetPerformanceInfo() {
248249
assertTrue(Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size()));
249250
assertTrue(perfInfo.ProcessCount.intValue() > 0);
250251
}
252+
253+
@Test
254+
public void testEnumProcesses() {
255+
int size = 0;
256+
int[] lpidProcess = null;
257+
IntByReference lpcbNeeded = new IntByReference();
258+
do {
259+
size = size + 1024;
260+
lpidProcess = new int[size];
261+
if (!Psapi.INSTANCE.EnumProcesses(lpidProcess, size * DWORD.SIZE, lpcbNeeded)) {
262+
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
263+
}
264+
} while (size == lpcbNeeded.getValue() / DWORD.SIZE);
265+
assertTrue("Size of pid list in bytes should be a multiple of " + DWORD.SIZE, lpcbNeeded.getValue() % DWORD.SIZE == 0);
266+
267+
int myPid = Kernel32.INSTANCE.GetCurrentProcessId();
268+
boolean foundMyPid = false;
269+
for (int i = 0; i < lpcbNeeded.getValue() / DWORD.SIZE; i++) {
270+
if (lpidProcess[i] == myPid) {
271+
foundMyPid = true;
272+
break;
273+
}
274+
}
275+
assertTrue("List should contain my pid", foundMyPid);
276+
}
251277
}
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)