src/windows/native/java/lang/ProcessImpl_md.c

Print this page
rev 7704 : 8016579: (process) IOException thrown by ProcessBuilder.start() method is incorrectly encoded

@@ -110,24 +110,74 @@
         JNU_ReleaseStringPlatformChars(env, cmd0, exe);
     }
     return newFlag;
 }
 
+/* We have THREE locales in action:
+ * 1. Thread default locale - dictates UNICODE-to-8bit conversion
+ * 2. System locale that defines the message localization
+ * 3. The file name locale
+ * Each locale could be an extended locale, that means that text cannot be
+ * mapped to 8bit sequence without multibyte encoding.
+ * VM is ready for that, if text is UTF-8.
+ * Here we make the work right from the beginning.
+ */
+size_t os_error_message(int errnum, WCHAR* utf16_OSErrorMsg, size_t maxMsgLength) {
+    size_t n = (size_t)FormatMessageW(
+            FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL,
+            (DWORD)errnum,
+            0,
+            utf16_OSErrorMsg,
+            (DWORD)maxMsgLength,
+            NULL);
+    if (n > 3) {
+        // Drop final '.', CR, LF
+        if (utf16_OSErrorMsg[n - 1] == L'\n') --n;
+        if (utf16_OSErrorMsg[n - 1] == L'\r') --n;
+        if (utf16_OSErrorMsg[n - 1] == L'.') --n;
+        utf16_OSErrorMsg[n] = L'\0';
+    }
+    return n;
+}
+
+#define MESSAGE_LENGTH (256 + 100)
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+
 static void
-win32Error(JNIEnv *env, const char *functionName)
+win32Error(JNIEnv *env, const WCHAR *functionName)
 {
-    static const char * const format = "%s error=%d, %s";
-    static const char * const fallbackFormat = "%s failed, error=%d";
-    char buf[256];
-    char errmsg[sizeof(buf) + 100];
-    const int errnum = GetLastError();
-    const int n = JVM_GetLastErrorString(buf, sizeof(buf));
-    if (n > 0)
-        sprintf(errmsg, format, functionName, errnum, buf);
-    else
-        sprintf(errmsg, fallbackFormat, functionName, errnum);
-    JNU_ThrowIOException(env, errmsg);
+    WCHAR utf16_OSErrorMsg[MESSAGE_LENGTH - 100];
+    WCHAR utf16_javaMessage[MESSAGE_LENGTH];
+    /*Good suggestion about 2-bytes-per-symbol in localized error reports*/
+    char  utf8_javaMessage[MESSAGE_LENGTH*2];
+    const int errnum = (int)GetLastError();
+    int n = os_error_message(errnum, utf16_OSErrorMsg, ARRAY_SIZE(utf16_OSErrorMsg));
+    n = (n > 0)
+        ? swprintf(utf16_javaMessage, MESSAGE_LENGTH, L"%s error=%d, %s", functionName, errnum, utf16_OSErrorMsg)
+        : swprintf(utf16_javaMessage, MESSAGE_LENGTH, L"%s failed, error=%d", functionName, errnum);
+
+    if (n > 0) /*terminate '\0' is not a part of conversion procedure*/
+        n = WideCharToMultiByte(
+            CP_UTF8,
+            0,
+            utf16_javaMessage,
+            n, /*by creation n <= MESSAGE_LENGTH*/
+            utf8_javaMessage,
+            MESSAGE_LENGTH*2,
+            NULL,
+            NULL);
+
+    /*no way to die*/
+    {
+        const char *errorMessage = "Secondary error while OS message extraction";
+        if (n > 0) {
+            utf8_javaMessage[min(MESSAGE_LENGTH*2 - 1, n)] = '\0';
+            errorMessage = utf8_javaMessage;
+        }
+        JNU_ThrowIOException(env, errorMessage);
+    }
 }
 
 static void
 closeSafely(HANDLE handle)
 {

@@ -191,11 +241,11 @@
             NULL, /* we would like to inherit
                      default process access,
                      instead of 'Everybody' access */
             PIPE_SIZE))
         {
-            win32Error(env, "CreatePipe");
+            win32Error(env, L"CreatePipe");
             return FALSE;
         } else {
             /* [thisProcessEnd] has no the inherit flag because
                the [lpPipeAttributes] param of [CreatePipe]
                had the NULL value. */

@@ -347,11 +397,11 @@
                         (LPVOID)penvBlock,/* environment block */
                         (LPCWSTR)pdir,    /* change to the new current directory */
                         &si,              /* (in)  startup information */
                         &pi))             /* (out) process information */
                     {
-                        win32Error(env, "CreateProcess");
+                        win32Error(env, L"CreateProcess");
                     } else {
                         closeSafely(pi.hThread);
                         ret = (jlong)pi.hProcess;
                     }
                 }

@@ -408,11 +458,11 @@
 JNIEXPORT jint JNICALL
 Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle)
 {
     DWORD exit_code;
     if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0)
-        win32Error(env, "GetExitCodeProcess");
+        win32Error(env, L"GetExitCodeProcess");
     return exit_code;
 }
 
 JNIEXPORT jint JNICALL
 Java_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored)

@@ -429,11 +479,11 @@
 
     if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
                                FALSE,    /* Wait for ANY event */
                                INFINITE) /* Wait forever */
         == WAIT_FAILED)
-        win32Error(env, "WaitForMultipleObjects");
+        win32Error(env, L"WaitForMultipleObjects");
 }
 
 JNIEXPORT void JNICALL
 Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
 {