From 8c1036fd49a6b664b7d6980f07bd95324942f96c Mon Sep 17 00:00:00 2001 From: dEajL3kA Date: Mon, 7 Aug 2023 12:18:57 +0200 Subject: [PATCH] Added missing GetPriorityClass(), SetPriorityClass(), GetThreadPriority() and SetThreadPriority() methods to Kernel32 class. Added test cases for Kernel32.SetPriorityClass(), Kernel32.GetPriorityClass(), Kernel32.GetThreadPriority() and Kernel32.SetThreadPriority(). Also added some useful helper methods to Kernel32Util class, including tests. --- .../com/sun/jna/platform/win32/Kernel32.java | 77 ++++++ .../sun/jna/platform/win32/Kernel32Util.java | 236 +++++++++++++++++- .../sun/jna/platform/win32/Kernel32Test.java | 20 ++ .../jna/platform/win32/Kernel32UtilTest.java | 75 ++++++ 4 files changed, 404 insertions(+), 4 deletions(-) diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index 14a1bd772b..c22fa5a52b 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -68,6 +68,44 @@ public interface Kernel32 extends StdCallLibrary, WinNT, Wincon { */ int LOAD_LIBRARY_AS_DATAFILE = 0x2; + /** + * Process priority classes + */ + DWORD NORMAL_PRIORITY_CLASS = new DWORD(0x00000020L); + DWORD IDLE_PRIORITY_CLASS = new DWORD(0x00000040L); + DWORD HIGH_PRIORITY_CLASS = new DWORD(0x00000080L); + DWORD REALTIME_PRIORITY_CLASS = new DWORD(0x00000100L); + DWORD BELOW_NORMAL_PRIORITY_CLASS = new DWORD(0x00004000L); + DWORD ABOVE_NORMAL_PRIORITY_CLASS = new DWORD(0x00008000L); + + /** + * Process mode flags + */ + DWORD PROCESS_MODE_BACKGROUND_BEGIN = new DWORD(0x00100000L); + DWORD PROCESS_MODE_BACKGROUND_END = new DWORD(0x00200000L); + + /** + * Thread priorities + */ + int THREAD_PRIORITY_IDLE = -15; + int THREAD_PRIORITY_LOWEST = -2; + int THREAD_PRIORITY_BELOW_NORMAL = -1; + int THREAD_PRIORITY_NORMAL = 0; + int THREAD_PRIORITY_ABOVE_NORMAL = 1; + int THREAD_PRIORITY_HIGHEST = 2; + int THREAD_PRIORITY_TIME_CRITICAL = 15; + + /** + * Thread mode flags + */ + int THREAD_MODE_BACKGROUND_BEGIN = 0x10000; + int THREAD_MODE_BACKGROUND_END = 0x20000; + + /** + * Thread priority error code + */ + int THREAD_PRIORITY_ERROR_RETURN = 0x7FFFFFFF; + /** * Reads data from the specified file or input/output (I/O) device. Reads * occur at the position specified by the file pointer if supported by the @@ -4152,6 +4190,45 @@ Pointer VirtualAllocEx(HANDLE hProcess, Pointer lpAddress, SIZE_T dwSize, */ boolean GetExitCodeThread(HANDLE hThread, IntByReference exitCode); + /** + * Gets the priority class of the specified process. + * + * @param hProcess A handle to the process. + * @return If the function succeeds, the return value is the priority class of + * the specified process. + *

If the function fails, the return value is zero.

+ */ + DWORD GetPriorityClass(HANDLE hProcess); + + /** + * Sets the priority class for the specified process. + * + * @param hProcess A handle to the process. + * @param dwPriorityClass The priority class for the process. + * @return If the function succeeds, the return value is nonzero. + */ + boolean SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass); + + /** + * Gets the priority value of the specified thread. + * + * @param hProcess A handle to the process. + * @return If the function succeeds, the return value is the priority class of + * the specified thread. + *

If the function fails, the return value is + * THREAD_PRIORITY_ERROR_RETURN.

+ */ + int GetThreadPriority(HANDLE hProcess); + + /** + * Sets the priority value for the specified thread. + * + * @param hThread A handle to the thread whose priority value is to be set. + * @param nPriority The priority value for the thread. + * @return If the function succeeds, the return value is nonzero. + */ + boolean SetThreadPriority(HANDLE hThread, int nPriority); + /** * Releases, decommits, or releases and decommits a region of memory within * the virtual address space of a specified process. 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 87f1abb703..3a533fda25 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -1259,11 +1259,239 @@ public static String expandEnvironmentStrings(String input) { } /** - * Close the given handle, then possibly throw the pending Win32Exception + * Gets the priority class of the current process. * - * @param we The pending Win32Exception, can be NULL. - * @throws Win32Exception if an error occurs, or there was a pending exception. + * @return The priority class of the current process. + * @throws Win32Exception if an error occurs. */ + public static DWORD getCurrentProcessPriority() { + final DWORD dwPriorityClass = Kernel32.INSTANCE.GetPriorityClass(Kernel32.INSTANCE.GetCurrentProcess()); + if (dwPriorityClass.longValue() == 0L) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + return dwPriorityClass; + } + + /** + * Sets the priority class for the current process. + * + * @param dwPriorityClass The priority class for the process. + * @throws Win32Exception if an error occurs. + */ + public static void setCurrentProcessPriority(final DWORD dwPriorityClass) { + if (!isValidPriorityClass(dwPriorityClass)) { + throw new IllegalArgumentException("The given priority value is invalid!"); + } + if (!Kernel32.INSTANCE.SetPriorityClass(Kernel32.INSTANCE.GetCurrentProcess(), dwPriorityClass)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Enables or disables "background" processing mode for the current process + * + * @param enable If true, enables "background" processing mode, otherwise disables it. + * @throws Win32Exception if an error occurs. + */ + public static void setCurrentProcessBackgroundMode(final boolean enable) { + // Note: PROCESS_MODE_BACKGROUN_{BEGIN,END} only works with the "current" process handle! + final DWORD dwPriorityClass = enable ? Kernel32.PROCESS_MODE_BACKGROUND_BEGIN : Kernel32.PROCESS_MODE_BACKGROUND_END; + if (!Kernel32.INSTANCE.SetPriorityClass(Kernel32.INSTANCE.GetCurrentProcess(), dwPriorityClass)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Gets the priority value of the current thread. + * + * @return The priority value of the current thread. + * @throws Win32Exception if an error occurs. + */ + public static int getCurrentThreadPriority() { + final int nPriority = Kernel32.INSTANCE.GetThreadPriority(Kernel32.INSTANCE.GetCurrentThread()); + if (nPriority == Kernel32.THREAD_PRIORITY_ERROR_RETURN) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + return nPriority; + } + + /** + * Sets the priority value for the current thread. + * + * @param nPriority The priority value for the thread. + * @throws Win32Exception if an error occurs. + */ + public static void setCurrentThreadPriority(final int nPriority) { + if (!isValidThreadPriority(nPriority)) { + throw new IllegalArgumentException("The given priority value is invalid!"); + } + if (!Kernel32.INSTANCE.SetThreadPriority(Kernel32.INSTANCE.GetCurrentThread(), nPriority)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Enables or disables "background" processing mode for the current thread + * + * @param enable If true, enables "background" processing mode, otherwise disables it. + * @throws Win32Exception if an error occurs. + */ + public static void setCurrentThreadBackgroundMode(final boolean enable) { + // Note: THREAD_MODE_BACKGROUND_{BEGIN,END} only works with the "current" thread handle! + final int nPriority = enable ? Kernel32.THREAD_MODE_BACKGROUND_BEGIN : Kernel32.THREAD_MODE_BACKGROUND_END; + if (!Kernel32.INSTANCE.SetThreadPriority(Kernel32.INSTANCE.GetCurrentThread(), nPriority)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Gets the priority class of the specified process. + * + * @param pid Identifier for the running process. + * @throws Win32Exception if an error occurs. + */ + public static DWORD getProcessPriority(final int pid) { + final HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION , false, pid); + if (hProcess == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + try { + final DWORD dwPriorityClass = Kernel32.INSTANCE.GetPriorityClass(hProcess); + if (dwPriorityClass.longValue() == 0L) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + return dwPriorityClass; + } catch (final Win32Exception e) { + throw we = e; // re-throw to avoid return value! + } finally { + cleanUp(hProcess, we); + } + } + + /** + * Sets the priority class for the specified process. + * + * @param pid Identifier for the running process. + * @param dwPriorityClass The priority class for the process. + * @throws Win32Exception if an error occurs. + */ + public static void setProcessPriority(final int pid, final DWORD dwPriorityClass) { + if (!isValidPriorityClass(dwPriorityClass)) { + throw new IllegalArgumentException("The given priority value is invalid!"); + } + + final HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_SET_INFORMATION, false, pid); + if (hProcess == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + try { + if (!Kernel32.INSTANCE.SetPriorityClass(hProcess, dwPriorityClass)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } catch (final Win32Exception e) { + we = e; + } finally { + cleanUp(hProcess, we); + } + } + + /** + * Gets the priority value of the specified thread. + * + * @param tid Identifier for the running thread. + * @throws Win32Exception if an error occurs. + */ + public static int getThreadPriority(final int tid) { + final HANDLE hThread = Kernel32.INSTANCE.OpenThread(WinNT.THREAD_QUERY_INFORMATION, false, tid); + if (hThread == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + try { + final int nPriority = Kernel32.INSTANCE.GetThreadPriority(hThread); + if (nPriority == Kernel32.THREAD_PRIORITY_ERROR_RETURN) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + return nPriority; + } catch (final Win32Exception e) { + throw we = e; // re-throw to avoid return value! + } finally { + cleanUp(hThread, we); + } + } + + /** + * Sets the priority value for the specified thread. + * + * @param tid Identifier for the running thread. + * @param nPriority The priority value for the thread. + * @throws Win32Exception if an error occurs. + */ + public static void setThreadPriority(final int tid, final int nPriority) { + if (!isValidThreadPriority(nPriority)) { + throw new IllegalArgumentException("The given priority value is invalid!"); + } + + final HANDLE hThread = Kernel32.INSTANCE.OpenThread(WinNT.THREAD_SET_INFORMATION, false, tid); + if (hThread == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + Win32Exception we = null; + try { + if (!Kernel32.INSTANCE.SetThreadPriority(hThread, nPriority)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } catch (final Win32Exception e) { + we = e; + } finally { + cleanUp(hThread, we); + } + } + + /** + * Test whether the given priority class is valid. + *

Intentionally does not accept "background" processing mode flags!

+ * + * @param dwPriorityClass The priority value to test. + * @return Returns true, if and only if the given priority value was valid + */ + public static boolean isValidPriorityClass(final DWORD dwPriorityClass) { + return Kernel32.NORMAL_PRIORITY_CLASS.equals(dwPriorityClass) || + Kernel32.IDLE_PRIORITY_CLASS.equals(dwPriorityClass) || + Kernel32.HIGH_PRIORITY_CLASS.equals(dwPriorityClass) || + Kernel32.REALTIME_PRIORITY_CLASS.equals(dwPriorityClass) || + Kernel32.BELOW_NORMAL_PRIORITY_CLASS.equals(dwPriorityClass) || + Kernel32.ABOVE_NORMAL_PRIORITY_CLASS.equals(dwPriorityClass); + } + + /** + * Test whether the given thread priority is valid. + *

Intentionally does not accept "background" processing mode flags!

+ * + * @param nPriority The priority value to test. + * @return Returns true, if and only if the given priority value was valid + */ + public static boolean isValidThreadPriority(final int nPriority) { + switch(nPriority) { + case Kernel32.THREAD_PRIORITY_IDLE: + case Kernel32.THREAD_PRIORITY_LOWEST: + case Kernel32.THREAD_PRIORITY_BELOW_NORMAL: + case Kernel32.THREAD_PRIORITY_NORMAL: + case Kernel32.THREAD_PRIORITY_ABOVE_NORMAL: + case Kernel32.THREAD_PRIORITY_HIGHEST: + case Kernel32.THREAD_PRIORITY_TIME_CRITICAL: + return true; + default: + return false; + } + } + private static void cleanUp(final HANDLE h, Win32Exception we) { try { closeHandle(h); @@ -1276,6 +1504,6 @@ private static void cleanUp(final HANDLE h, Win32Exception we) { } if (we != null) { throw we; - } + } } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index d964c70dec..d19df5c648 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java @@ -2099,4 +2099,24 @@ public void testVirtualLockUnlock() { // Unlocking an unlocked region should fail assertFalse(Kernel32.INSTANCE.VirtualUnlock(mem, new SIZE_T(4096))); } + + public void testGetPriorityClass() { + final HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess(); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.INSTANCE.GetPriorityClass(selfHandle))); + } + + public void testSetPriorityClass() { + final HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess(); + assertTrue(Kernel32.INSTANCE.SetPriorityClass(selfHandle, Kernel32.HIGH_PRIORITY_CLASS)); + } + + public void testGetThreadPriority() { + final HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentProcess(); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.INSTANCE.GetThreadPriority(selfHandle))); + } + + public void testSetThreadPriority() { + final HANDLE selfHandle = Kernel32.INSTANCE.GetCurrentThread(); + assertTrue(Kernel32.INSTANCE.SetThreadPriority(selfHandle, Kernel32.THREAD_PRIORITY_ABOVE_NORMAL)); + } } 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 cdb5c373f4..b8f48cf69a 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java @@ -37,6 +37,7 @@ import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Tlhelp32.MODULEENTRY32W; +import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinNT.CACHE_RELATIONSHIP; import com.sun.jna.platform.win32.WinNT.GROUP_RELATIONSHIP; import com.sun.jna.platform.win32.WinNT.HANDLE; @@ -513,4 +514,78 @@ public void testGetLogicalProcessorInformationEx() { assertTrue(cache.associativity == WinNT.CACHE_FULLY_ASSOCIATIVE || cache.associativity > 0); } } + + public void testGetCurrentProcessPriority() { + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32Util.getCurrentProcessPriority())); + } + + public void testSetCurrentProcessPriority() { + Kernel32Util.setCurrentProcessPriority(Kernel32.HIGH_PRIORITY_CLASS); + } + + public void testSetCurrentProcessBackgroundMode() { + Kernel32Util.setCurrentProcessBackgroundMode(true); + } + + public void testGetCurrentThreadPriority() { + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32Util.getCurrentThreadPriority())); + } + + public void testSetCurrentThreadPriority() { + Kernel32Util.setCurrentThreadPriority(Kernel32.THREAD_PRIORITY_ABOVE_NORMAL); + } + + public void testSetCurrentThreadBackgroundMode() { + Kernel32Util.setCurrentThreadBackgroundMode(true); + } + + public void testGetProcessPriority() { + final int pid = Kernel32.INSTANCE.GetCurrentProcessId(); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32Util.getProcessPriority(pid))); + } + + public void testSetProcessPriority() { + final int pid = Kernel32.INSTANCE.GetCurrentProcessId(); + Kernel32Util.setProcessPriority(pid, Kernel32.HIGH_PRIORITY_CLASS); + } + + public void testGetThreadPriority() { + final int tid = Kernel32.INSTANCE.GetCurrentThreadId(); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32Util.getThreadPriority(tid))); + } + + public void testSetThreadPriority() { + final int tid = Kernel32.INSTANCE.GetCurrentThreadId(); + Kernel32Util.setThreadPriority(tid, Kernel32.THREAD_PRIORITY_ABOVE_NORMAL); + } + + public void testIsValidPriorityClass() { + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.NORMAL_PRIORITY_CLASS)); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.IDLE_PRIORITY_CLASS)); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.HIGH_PRIORITY_CLASS)); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.REALTIME_PRIORITY_CLASS)); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.BELOW_NORMAL_PRIORITY_CLASS)); + assertTrue(Kernel32Util.isValidPriorityClass(Kernel32.ABOVE_NORMAL_PRIORITY_CLASS)); + assertFalse(Kernel32Util.isValidPriorityClass(new DWORD(0L))); + assertFalse(Kernel32Util.isValidPriorityClass(new DWORD(1L))); + assertFalse(Kernel32Util.isValidPriorityClass(new DWORD(Long.MAX_VALUE))); + assertFalse(Kernel32Util.isValidPriorityClass(Kernel32.PROCESS_MODE_BACKGROUND_BEGIN)); + assertFalse(Kernel32Util.isValidPriorityClass(Kernel32.PROCESS_MODE_BACKGROUND_END)); + } + + public void testIsValidThreadPriority() { + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_IDLE)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_LOWEST)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_BELOW_NORMAL)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_NORMAL)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_ABOVE_NORMAL)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_HIGHEST)); + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_PRIORITY_TIME_CRITICAL)); + assertFalse(Kernel32Util.isValidThreadPriority( 3)); + assertFalse(Kernel32Util.isValidThreadPriority( -3)); + assertFalse(Kernel32Util.isValidThreadPriority( 16)); + assertFalse(Kernel32Util.isValidThreadPriority(-16)); + assertFalse(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_MODE_BACKGROUND_BEGIN)); + assertFalse(Kernel32Util.isValidThreadPriority(Kernel32.THREAD_MODE_BACKGROUND_END)); + } }