diff --git a/CHANGES.md b/CHANGES.md
index d8a80ce078..9557593dd6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@ Next Release (5.13.0)
Features
--------
+* [#1454](https://github.com/java-native-access/jna/pull/1454): Add `c.s.j.p.win32.Psapi.QueryWorkingSetEx` and associated Types - [@crain-32](https://github.com/Crain-32).
Bug Fixes
---------
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 dfd13d204d..43cb959e17 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/Psapi.java
@@ -26,8 +26,10 @@
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
+import com.sun.jna.Structure.ByReference;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
+import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinNT.HANDLE;
@@ -292,6 +294,18 @@ public interface Psapi extends StdCallLibrary {
*/
boolean EnumProcesses(int[] lpidProcess, int cb, IntByReference lpcbNeeded);
+ /**
+ * Retrieves extended information about the pages at specific
+ * virtual addresses in the address space of the specified process.
+ *
+ * @param hProcess A Handle to the Process
+ * @param pv A pointer to an array of PSAPI_WORKING_SET_EX_INFORMATION structures
+ * @param cb The size of the pv buffer, in bytes.
+ * @return If the function succeeds, the return value is nonzero.
+ * @see MSDN
+ */
+ boolean QueryWorkingSetEx(HANDLE hProcess, Pointer pv, int cb);
+
@FieldOrder({"lpBaseOfDll", "SizeOfImage", "EntryPoint"})
class MODULEINFO extends Structure {
public Pointer EntryPoint;
@@ -320,4 +334,17 @@ class PERFORMANCE_INFORMATION extends Structure {
public DWORD ProcessCount;
public DWORD ThreadCount;
}
+
+ @FieldOrder({"Flags", "Data"})
+ class PSAPI_WORKING_SET_EX_BLOCK extends Structure implements ByReference {
+ public ULONG_PTR Flags;
+ public ULONG_PTR[] Data = new ULONG_PTR[Native.POINTER_SIZE == 8 ? 1 : 2];
+ }
+
+
+ @FieldOrder({"VirtualAddress", "VirtualAttributes"})
+ class PSAPI_WORKING_SET_EX_INFORMATION extends Structure {
+ public Pointer VirtualAddress;
+ public PSAPI_WORKING_SET_EX_BLOCK VirtualAttributes;
+ }
}
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
index efa5bacf45..4d6a87f62f 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/PsapiUtil.java
@@ -28,6 +28,7 @@
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.Psapi.PSAPI_WORKING_SET_EX_BLOCK;
import com.sun.jna.ptr.IntByReference;
/**
@@ -87,4 +88,73 @@ public static String GetProcessImageFileName(HANDLE hProcess) {
}
}
}
+
+ /**
+ * If this bit is 1, the other values in the PSAPI_WORKING_SET_EX_BLOCK are valid.
+ */
+ public static boolean isPsapiWorkingSetExBlockValid(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 0) == 1;
+ }
+
+ /**
+ * The number of processes that share the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. The maximum value of this member is 7.
+ */
+ public static int getPsapiWorkingSetExBlockShareCount(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 3, 1);
+ }
+
+ /**
+ * The memory protection attributes of the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. For a list of values see below.
+ * @see Memory Protection Constants.
+ */
+ public static int getPsapiWorkingSetExBlockWin32Protection(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(),11, 3 + 1);
+ }
+
+ /**
+ * If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK can be shared.
+ */
+ public static boolean isPsapiWorkingSetExBlockShared(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 11 + 3 + 1) == 1;
+ }
+
+ /**
+ * The NUMA node of the page addressed by the PSAPI_WORKING_SET_EX_BLOCK. The maximum value of this member is 63.
+ */
+ public static int getPsapiWorkingSetExBlockNode(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 6, 1 + 11 + 3 + 1);
+ }
+
+ /**
+ * If this bit is 1, the virtual page addressed by the PSAPI_WORKING_SET_EX_BLOCK is locked in physical memory.
+ */
+ public static boolean isPsapiWorkingSetExBlockLocked(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1, 6 + 1 + 11 + 3 + 1) == 1;
+ }
+
+ /**
+ * If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK is a large page.
+ */
+ public static boolean isPsapiWorkingSetExBlockLargePage(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(),1, 1 + 6 + 1 + 11 + 3 + 1) == 1;
+ }
+
+ /**
+ * If this bit is 1, the page addressed by the PSAPI_WORKING_SET_EX_BLOCK is has been reported as bad.
+ */
+ public static boolean isPsapiWorkingSetExBlockBad(PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock) {
+ return PsapiUtil.getBitFieldValue(psapiWorkingSetExBlock.Data[0].longValue(), 1,1 + 1 + 1 + 6 + 1 + 11 + 3 + 1) == 1;
+ }
+
+ /**
+ * Returns a Right Shifted and Masked version of value.
+ */
+ public static int getBitFieldValue(final long value, final int maskLength, final int rightShiftAmount) {
+ long bitMask = 0;
+
+ for (int l = 0; l < maskLength; l++) {
+ bitMask |= 1 << l;
+ }
+ return (int) ((value >>> rightShiftAmount) & bitMask);
+ }
}
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 7af0c9b275..a617d1db7f 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java
@@ -24,6 +24,8 @@
package com.sun.jna.platform.win32;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
import java.util.LinkedList;
import java.util.List;
@@ -34,12 +36,15 @@
import com.sun.jna.Memory;
import com.sun.jna.Native;
+import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi.MODULEINFO;
import com.sun.jna.platform.win32.Psapi.PERFORMANCE_INFORMATION;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION;
+import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
import com.sun.jna.ptr.IntByReference;
/**
@@ -273,4 +278,42 @@ public void testEnumProcesses() {
}
assertTrue("List should contain my pid", foundMyPid);
}
+
+ @Test
+ public void testQueryWorkingSetEx() {
+ Win32Exception we = null;
+ HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess();
+ MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
+ try {
+ SIZE_T bytesRead = Kernel32.INSTANCE.VirtualQueryEx(selfHandle, Pointer.NULL, mbi, new SIZE_T(mbi.size()));
+ assertNotEquals("Kernel should be able to read this Process' Bytes", bytesRead.intValue(), 0);
+ Psapi.PSAPI_WORKING_SET_EX_INFORMATION pswsi = new Psapi.PSAPI_WORKING_SET_EX_INFORMATION();
+ pswsi.VirtualAddress = mbi.baseAddress;
+ if (!Psapi.INSTANCE.QueryWorkingSetEx(selfHandle, pswsi.VirtualAddress, pswsi.size())) {
+ throw new Win32Exception(Native.getLastError());
+ }
+ assertTrue("Virual Attributes should not be null", pswsi.VirtualAttributes != null);
+ if (Psapi.INSTANCE.QueryWorkingSetEx(new HANDLE(), pswsi.VirtualAddress, pswsi.size())) {
+ throw new Win32Exception(Native.getLastError());
+ }
+ assertFalse("This line should never be called", true);
+ } catch (Win32Exception e) {
+ we = e;
+ throw we; // re-throw to invoke finally block
+ } finally {
+ try {
+ Kernel32Util.closeHandle(selfHandle);
+ } catch (Win32Exception e) {
+ if (we == null) {
+ we = e;
+ } else {
+ we.addSuppressedReflected(e);
+ }
+ }
+ if (we != null) {
+ throw we;
+ }
+ }
+ }
}
+
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java
index d8578ad3d5..d4c52fd3a4 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/PsapiUtilTest.java
@@ -25,6 +25,19 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotEquals;
+
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Native;
+import com.sun.jna.platform.win32.Psapi.MODULEINFO;
+import com.sun.jna.platform.win32.Psapi.PERFORMANCE_INFORMATION;
+import com.sun.jna.platform.win32.WinDef.DWORD;
+import com.sun.jna.platform.win32.WinDef.HMODULE;
+import com.sun.jna.platform.win32.WinDef.HWND;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION;
+import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
import org.junit.Test;
@@ -50,4 +63,39 @@ public void enumProcesses() {
}
assertTrue("List should contain my pid", foundMyPid);
}
+
+ @Test
+ public void testPsapiWorkingSetExBlockUtils() {
+ Win32Exception we = null;
+ HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess();
+ MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
+ try {
+ SIZE_T bytesRead = Kernel32.INSTANCE.VirtualQueryEx(selfHandle, Pointer.NULL, mbi, new SIZE_T(mbi.size()));
+ assertNotEquals("Kernel should be able to read this Process' Bytes", bytesRead.intValue(), 0);
+ Psapi.PSAPI_WORKING_SET_EX_INFORMATION pswsi = new Psapi.PSAPI_WORKING_SET_EX_INFORMATION();
+ pswsi.VirtualAddress = mbi.baseAddress;
+ if (!Psapi.INSTANCE.QueryWorkingSetEx(selfHandle, pswsi.VirtualAddress, pswsi.size())) {
+ throw new Win32Exception(Native.getLastError());
+ }
+ assertTrue("Virual Attributes should not be null", pswsi.VirtualAttributes != null);
+ Psapi.PSAPI_WORKING_SET_EX_BLOCK psapiWorkingSetExBlock = pswsi.VirtualAttributes;
+ assertTrue("The JVM Page should be valid", PsapiUtil.isPsapiWorkingSetExBlockValid(psapiWorkingSetExBlock));
+ } catch (Win32Exception e) {
+ we = e;
+ throw we; // re-throw to invoke finally block
+ } finally {
+ try {
+ Kernel32Util.closeHandle(selfHandle);
+ } catch (Win32Exception e) {
+ if (we == null) {
+ we = e;
+ } else {
+ we.addSuppressedReflected(e);
+ }
+ }
+ if (we != null) {
+ throw we;
+ }
+ }
+ }
}