diff --git a/CHANGES.md b/CHANGES.md
index 09ebd6b588..7ceca9538d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -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
---------
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 d4a5394e83..175690c256 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
@@ -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.
@@ -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());
}
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java b/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
index c6c89c5180..0d5b662af2 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
@@ -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;
@@ -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.
+ * 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.
+ * 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.
+ * 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 MSDN
+ */
+ boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);
+
@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
class MODULEINFO extends Structure {
public Pointer EntryPoint;
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
new file mode 100644
index 0000000000..ca293ce0e9
--- /dev/null
+++ b/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
@@ -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örn Svensson, azoff[at]svenskalinuxforeninen.se
+ */
+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);
+ }
+}
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 20684a07dc..5d326eace7 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java
@@ -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() {
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java b/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
index 44404e896b..7af0c9b275 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
@@ -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);
+ }
}
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java
new file mode 100644
index 0000000000..eba02c1498
--- /dev/null
+++ b/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java
@@ -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ö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);
+ }
+}