From daf3ed2748ec3b29dc74eea3938aefd6a8f64b11 Mon Sep 17 00:00:00 2001 From: Oleksandr Klymenko Date: Thu, 1 Dec 2022 22:45:15 +0200 Subject: [PATCH] Provide multilingual support of Kernel32Util.formatMessage() - provided multilingual support of Kernel32Util methods using Kernel32.INSTANCE.FormatMessage() - overloaded methods - test is written - updated CHANGES.md --- CHANGES.md | 1 + .../sun/jna/platform/win32/Kernel32Util.java | 72 +++++++++++++++++-- .../jna/platform/win32/Kernel32UtilTest.java | 12 ++++ 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7c10f121f6..58a5e05999 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Features * [#1459](https://github.com/java-native-access/jna/pull/1459): Add `VirtualLock` and `VirtualUnlock` in `c.s.j.p.win32.Kernel32` - [@matthiasblaesing](https://github.com/matthiasblaesing). * [#1471](https://github.com/java-native-access/jna/pull/1471): Add `c.s.j.p.win32.Advapi32Util#isCurrentProcessElevated` and associated Types - [@dbwiddis](https://github.com/dbwiddis). * [#1474](https://github.com/java-native-access/jna/pull/1474): Add `c.s.j.p.win32.WbemCli#IWbemClassObject.IWbemQualifierSet`, `IWbemServices.GetObject`, `IWbemContext.SetValue` and associated methods - [@rchateauneu](https://github.com/rchateauneu). +* [#1482](https://github.com/java-native-access/jna/pull/1482): Add multilingual support of `Kernel32Util.formatMessage` - [@overpathz](https://github.com/overpathz). 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 9654fc52b1..db9373a361 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -178,14 +178,35 @@ public static void closeHandle(HANDLE h) { } } + /** + * Format a message from a code. + * + * @param code The error code + * @return Formatted message in the default locale. + */ + public static String formatMessage(int code) { + return formatMessage(code, 0, 0); + } + /** * Format a message from the value obtained from * {@link Kernel32#GetLastError()} or {@link Native#getLastError()}. * + *

If you pass in zero, FormatMessage looks for a message for LANGIDs in the following order:

+ *
    + *
  1. Language neutral
  2. + *
  3. Thread LANGID, based on the thread's locale value
  4. + *
  5. User default LANGID, based on the user's default locale value
  6. + *
  7. System default LANGID, based on the system default locale value
  8. + *
  9. US English
  10. + *
+ * * @param code The error code - * @return Formatted message. + * @param primaryLangId The primary language identifier + * @param sublangId The sublanguage identifier + * @return Formatted message in the specified locale. */ - public static String formatMessage(int code) { + public static String formatMessage(int code, int primaryLangId, int sublangId) { PointerByReference buffer = new PointerByReference(); int nLen = Kernel32.INSTANCE.FormatMessage( WinBase.FORMAT_MESSAGE_ALLOCATE_BUFFER @@ -193,7 +214,7 @@ public static String formatMessage(int code) { | WinBase.FORMAT_MESSAGE_IGNORE_INSERTS, null, code, - 0, // TODO: // MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT) + WinNT.LocaleMacros.MAKELANGID(primaryLangId, sublangId), buffer, 0, null); if (nLen == 0) { throw new LastErrorException(Native.getLastError()); @@ -213,32 +234,71 @@ public static String formatMessage(int code) { * * @param code * HRESULT - * @return Formatted message. + * @return Formatted message in the default locale. */ public static String formatMessage(HRESULT code) { return formatMessage(code.intValue()); } + /** + * Format a message from an HRESULT. + * + * @param code + * HRESULT + * @param primaryLangId + * The primary language identifier + * @param sublangId + * The primary language identifier + * @return Formatted message in the specified locale. + */ + public static String formatMessage(HRESULT code, int primaryLangId, int sublangId) { + return formatMessage(code.intValue(), primaryLangId, sublangId); + } + /** * Format a system message from an error code. * * @param code * Error code, typically a result of GetLastError. - * @return Formatted message. + * @return Formatted message in the default locale. */ public static String formatMessageFromLastErrorCode(int code) { return formatMessage(W32Errors.HRESULT_FROM_WIN32(code)); } + /** + * Format a system message from an error code. + * + * @param code + * Error code, typically a result of GetLastError. + * @param primaryLangId + * The primary language identifier + * @param sublangId + * The primary language identifier + * @return Formatted message in the specified locale. + */ + public static String formatMessageFromLastErrorCode(int code, int primaryLangId, int sublangId) { + return formatMessage(W32Errors.HRESULT_FROM_WIN32(code), primaryLangId, sublangId); + } + /** * @return Obtains the human-readable error message text from the last error - * that occurred by invocating {@code Kernel32.GetLastError()}. + * that occurred by invocating {@code Kernel32.GetLastError()} in the default locale. */ public static String getLastErrorMessage() { return Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE .GetLastError()); } + /** + * @return Obtains the human-readable error message text from the last error + * that occurred by invocating {@code Kernel32.GetLastError()} in the specified locale. + */ + public static String getLastErrorMessage(int primaryLangId, int sublangId) { + return Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE + .GetLastError(), primaryLangId, sublangId); + } + /** * Return the path designated for temporary files. * 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 cd96bfbee1..cdb5c373f4 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java @@ -159,6 +159,18 @@ public void testFormatMessageFromHR() { } } + public void testFormatMessageFromErrorCodeWithNonEnglishLocale() { + int errorCode = W32Errors.S_OK.intValue(); + String formattedMsgInDefaultLocale = Kernel32Util.formatMessage(errorCode); + // primary and sub languages id's of the english locale, because it is present on most machines + String formattedMsgInEnglishLocale = Kernel32Util.formatMessage(errorCode, 9, 1); + if(AbstractWin32TestSupport.isEnglishLocale) { + assertEquals(formattedMsgInDefaultLocale, formattedMsgInEnglishLocale); + } else { + assertNotSame(formattedMsgInDefaultLocale, formattedMsgInEnglishLocale); + } + } + public void testGetTempPath() { assertTrue(Kernel32Util.getTempPath().length() > 0); }