diff --git a/CHANGES.md b/CHANGES.md index 8df24df9a..ba3dacfc3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Next Release (5.14.0) Features -------- * [#1534](https://github.com/java-native-access/jna/pull/1534): Add `GetMethod`, `Put`, `SpawnInstance` to `c.s.j.p.win32.COM.WbemCli#IWbemClassObject` and `ExecMethod` to `c.s.j.p.win32.COM.WbemCli#IWbemServices` - [@faddom](https://github.com/faddom). +* [#1544](https://github.com/java-native-access/jna/pull/1544): Add `GetPriorityClass`, `SetPriorityClass`, `GetThreadPriority`, `SetThreadPriority` and associated constants to `c.s.j.p.win32.Kernel32` - [@dEajL3kA](https://github.com/dEajL3kA). Bug Fixes --------- 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 14a1bd772..c22fa5a52 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 e34cb3647..27ecd88eb 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -392,23 +392,10 @@ public static int getFileType(String fileName) throws FileNotFoundException { default: return type; } - } catch(Win32Exception e) { - err = e; - throw err; // re-throw so finally block executed + } catch(final Win32Exception e) { + throw err = e; // re-throw to avoid return value! } finally { - try { - closeHandle(hFile); - } catch(Win32Exception e) { - if (err == null) { - err = e; - } else { - err.addSuppressedReflected(e); - } - } - - if (err != null) { - throw err; - } + cleanUp(hFile, err); } } @@ -944,22 +931,10 @@ public static final String QueryFullProcessImageName(int pid, int dwFlags) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } return QueryFullProcessImageName(hProcess, dwFlags); - } catch (Win32Exception e) { - we = e; - throw we; // re-throw to invoke finally block + } catch (final Win32Exception e) { + throw we = e; // re-throw to avoid return value! } finally { - try { - closeHandle(hProcess); - } catch (Win32Exception e) { - if (we == null) { - we = e; - } else { - we.addSuppressedReflected(e); - } - } - if (we != null) { - throw we; - } + cleanUp(hProcess, we); } } @@ -1223,23 +1198,10 @@ public static List getModules(int processID) { } return modules; - } catch (Win32Exception e) { - we = e; - throw we; // re-throw so finally block is executed + } catch (final Win32Exception e) { + throw we = e; // re-throw to avoid return value! } finally { - try { - closeHandle(snapshot); - } catch(Win32Exception e) { - if (we == null) { - we = e; - } else { - we.addSuppressedReflected(e); - } - } - - if (we != null) { - throw we; - } + cleanUp(snapshot, we); } } @@ -1295,4 +1257,253 @@ public static String expandEnvironmentStrings(String input) { return resultMemory.getString(0); } } + + /** + * Gets the priority class of the current process. + * + * @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 (!isValidPriorityClass(dwPriorityClass)) { + 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 (!isValidThreadPriority(nPriority)) { + 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 (!isValidPriorityClass(dwPriorityClass)) { + 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 (!isValidThreadPriority(nPriority)) { + 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); + } catch (final 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/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index d964c70de..f8b6971c3 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.GetCurrentThread(); + 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 cdb5c373f..ca57fce34 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,90 @@ 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() { + try { + Kernel32Util.setCurrentProcessBackgroundMode(true); + } finally { + try { + Kernel32Util.setCurrentProcessBackgroundMode(false); // Reset the "background" mode! + } catch (Exception e) { } + } + } + + public void testGetCurrentThreadPriority() { + assertTrue(Kernel32Util.isValidThreadPriority(Kernel32Util.getCurrentThreadPriority())); + } + + public void testSetCurrentThreadPriority() { + Kernel32Util.setCurrentThreadPriority(Kernel32.THREAD_PRIORITY_ABOVE_NORMAL); + } + + public void testSetCurrentThreadBackgroundMode() { + try { + Kernel32Util.setCurrentThreadBackgroundMode(true); + } finally { + try { + Kernel32Util.setCurrentThreadBackgroundMode(false); // Reset the "background" mode! + } catch (Exception e) { } + } + } + + 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(0xFFFFFFFF))); + 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)); + } }