Andrew Nguyen : kernel32: Correct output buffer behavior with empty input strings for FormatMessageA /W.

Alexandre Julliard julliard at winehq.org
Wed Apr 28 16:35:47 CDT 2010


Module: wine
Branch: master
Commit: 2193b9e887b109a7134d3318406be665a45471af
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=2193b9e887b109a7134d3318406be665a45471af

Author: Andrew Nguyen <anguyen at codeweavers.com>
Date:   Wed Apr 28 04:20:32 2010 -0500

kernel32: Correct output buffer behavior with empty input strings for FormatMessageA/W.

---

 dlls/kernel32/format_msg.c       |   48 +++++++++++++++++--------
 dlls/kernel32/tests/format_msg.c |   71 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 101 insertions(+), 18 deletions(-)

diff --git a/dlls/kernel32/format_msg.c b/dlls/kernel32/format_msg.c
index feed8a1..185c459 100644
--- a/dlls/kernel32/format_msg.c
+++ b/dlls/kernel32/format_msg.c
@@ -446,22 +446,30 @@ DWORD WINAPI FormatMessageA(
         goto failure;
 
     TRACE("-- %s\n", debugstr_w(target));
-    destlength = WideCharToMultiByte(CP_ACP, 0, target, -1, NULL, 0, NULL, NULL);
-    if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
-        LPSTR buf = LocalAlloc(LMEM_ZEROINIT, max(nSize, destlength));
-        WideCharToMultiByte(CP_ACP, 0, target, -1, buf, destlength, NULL, NULL);
-        *((LPSTR*)lpBuffer) = buf;
-    } else {
-        if (nSize < destlength)
+
+    /* Only try writing to an output buffer if there are processed characters
+     * in the temporary output buffer. */
+    if (*target)
+    {
+        destlength = WideCharToMultiByte(CP_ACP, 0, target, -1, NULL, 0, NULL, NULL);
+        if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
         {
-            SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            goto failure;
+            LPSTR buf = LocalAlloc(LMEM_ZEROINIT, max(nSize, destlength));
+            WideCharToMultiByte(CP_ACP, 0, target, -1, buf, destlength, NULL, NULL);
+            *((LPSTR*)lpBuffer) = buf;
         }
-
-        WideCharToMultiByte(CP_ACP, 0, target, -1, lpBuffer, destlength, NULL, NULL);
+        else
+        {
+            if (nSize < destlength)
+            {
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                goto failure;
+            }
+            WideCharToMultiByte(CP_ACP, 0, target, -1, lpBuffer, destlength, NULL, NULL);
+        }
+        ret = destlength - 1; /* null terminator */
     }
 
-    ret = destlength - 1; /* null terminator */
 failure:
     HeapFree(GetProcessHeap(),0,target);
     HeapFree(GetProcessHeap(),0,from);
@@ -542,10 +550,18 @@ DWORD WINAPI FormatMessageW(
 
     talloced = strlenW(target)+1;
     TRACE("-- %s\n",debugstr_w(target));
-    if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
-        /* nSize is the MINIMUM size */
-        *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT, max(nSize, talloced)*sizeof(WCHAR));
-        strcpyW(*(LPWSTR*)lpBuffer, target);
+
+    /* Only allocate a buffer if there are processed characters in the
+     * temporary output buffer. If a caller supplies the buffer, then
+     * a null terminator will be written to it. */
+    if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
+    {
+        if (*target)
+        {
+            /* nSize is the MINIMUM size */
+            *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT, max(nSize, talloced)*sizeof(WCHAR));
+            strcpyW(*(LPWSTR*)lpBuffer, target);
+        }
     }
     else
     {
diff --git a/dlls/kernel32/tests/format_msg.c b/dlls/kernel32/tests/format_msg.c
index e1f5b3e..9021c09 100644
--- a/dlls/kernel32/tests/format_msg.c
+++ b/dlls/kernel32/tests/format_msg.c
@@ -53,6 +53,7 @@ static DWORD __cdecl doitW(DWORD flags, LPCVOID src, DWORD msg_id, DWORD lang_id
 static void test_message_from_string_wide(void)
 {
     static const WCHAR test[]        = {'t','e','s','t',0};
+    static const WCHAR empty[]       = {0};
     static const WCHAR te[]          = {'t','e',0};
     static const WCHAR st[]          = {'s','t',0};
     static const WCHAR t[]           = {'t',0};
@@ -144,6 +145,25 @@ static void test_message_from_string_wide(void)
     ok(!lstrcmpW(test, out), "failed out=%s\n", wine_dbgstr_w(out));
     ok(r==4, "failed: r=%d\n", r);
 
+    /* null string, crashes on Windows */
+    if (0)
+    {
+        SetLastError(0xdeadbeef);
+        memcpy(out, init_buf, sizeof(init_buf));
+        r = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
+            0, out, sizeof(out)/sizeof(WCHAR), NULL);
+    }
+
+    /* empty string */
+    SetLastError(0xdeadbeef);
+    memcpy(out, init_buf, sizeof(init_buf));
+    r = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, empty, 0,
+        0, out, sizeof(out)/sizeof(WCHAR), NULL);
+    error = GetLastError();
+    ok(!lstrcmpW(empty, out), "failed out=%s\n", wine_dbgstr_w(out));
+    ok(r==0, "succeeded: r=%d\n", r);
+    ok(error==0xdeadbeef, "last error %u\n", error);
+
     /* format placeholder with no specifier */
     SetLastError(0xdeadbeef);
     memcpy(out, init_buf, sizeof(init_buf));
@@ -420,6 +440,27 @@ static void test_message_from_string(void)
     ok(!strcmp("test", out),"failed out=[%s]\n",out);
     ok(r==4,"failed: r=%d\n",r);
 
+    /* null string, crashes on Windows */
+    if (0)
+    {
+        SetLastError(0xdeadbeef);
+        memcpy(out, init_buf, sizeof(init_buf));
+        r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
+            0, out, sizeof(out)/sizeof(CHAR), NULL);
+    }
+
+    /* empty string */
+    SetLastError(0xdeadbeef);
+    memcpy(out, init_buf, sizeof(init_buf));
+    r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "", 0,
+        0, out, sizeof(out)/sizeof(CHAR), NULL);
+    ok(!memcmp(out, init_buf, sizeof(init_buf)) ||
+       broken(!strcmp("", out)), /* Win9x */
+       "Expected the buffer to be untouched\n");
+    ok(r==0, "succeeded: r=%d\n", r);
+    ok(GetLastError()==0xdeadbeef,
+       "last error %u\n", GetLastError());
+
     /* format placeholder with no specifier */
     SetLastError(0xdeadbeef);
     memcpy(out, init_buf, sizeof(init_buf));
@@ -898,6 +939,15 @@ static void test_message_allocate_buffer(void)
      * in any case be safe for FormatMessageA to allocate in the manner that
      * MSDN suggests. */
 
+    SetLastError(0xdeadbeef);
+    buf = (char *)0xdeadbeef;
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                         "", 0, 0, (char *)&buf, 0, NULL);
+    ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret);
+    ok(buf == NULL, "Expected output buffer pointer to be NULL\n");
+    ok(GetLastError() == 0xdeadbeef,
+       "Expected last error to be untouched, got %u\n", GetLastError());
+
     buf = (char *)0xdeadbeef;
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                          "test", 0, 0, (char *)&buf, 0, NULL);
@@ -966,6 +1016,7 @@ static void test_message_allocate_buffer(void)
 
 static void test_message_allocate_buffer_wide(void)
 {
+    static const WCHAR empty[] = {0};
     static const WCHAR test[] = {'t','e','s','t',0};
 
     DWORD ret;
@@ -979,13 +1030,29 @@ static void test_message_allocate_buffer_wide(void)
         return;
     }
 
-    /* While MSDN suggests that FormatMessageA allocates a buffer whose size is
+    /* While MSDN suggests that FormatMessageW allocates a buffer whose size is
      * the larger of the output string and the requested buffer size, the tests
      * will not try to determine the actual size of the buffer allocated, as
      * the return value of LocalSize cannot be trusted for the purpose, and it should
-     * in any case be safe for FormatMessageA to allocate in the manner that
+     * in any case be safe for FormatMessageW to allocate in the manner that
      * MSDN suggests. */
 
+    if (0) /* crashes on Windows */
+    {
+        buf = (WCHAR *)0xdeadbeef;
+        ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                             NULL, 0, 0, (WCHAR *)&buf, 0, NULL);
+    }
+
+    SetLastError(0xdeadbeef);
+    buf = (WCHAR *)0xdeadbeef;
+    ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                         empty, 0, 0, (WCHAR *)&buf, 0, NULL);
+    ok(ret == 0, "Expected FormatMessageW to return 0, got %u\n", ret);
+    ok(buf == NULL, "Expected output buffer pointer to be NULL\n");
+    ok(GetLastError() == 0xdeadbeef,
+       "Expected last error to be untouched, got %u\n", GetLastError());
+
     buf = (WCHAR *)0xdeadbeef;
     ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                          test, 0, 0, (WCHAR *)&buf, 0, NULL);




More information about the wine-cvs mailing list