Commit 7b012817 authored by Alexandre Julliard's avatar Alexandre Julliard

kernelbase: Reimplement FormatMessageA/W using RtlFormatMessage().

parent 3214ca84
......@@ -16,7 +16,6 @@ C_SRCS = \
editline.c \
environ.c \
file.c \
format_msg.c \
heap.c \
kernel_main.c \
lcformat.c \
......
......@@ -522,8 +522,8 @@
@ stdcall -import FlushViewOfFile(ptr long)
@ stdcall FoldStringA(long str long ptr long)
@ stdcall -import FoldStringW(long wstr long ptr long)
@ stdcall FormatMessageA(long ptr long long ptr long ptr)
@ stdcall FormatMessageW(long ptr long long ptr long ptr)
@ stdcall -import FormatMessageA(long ptr long long ptr long ptr)
@ stdcall -import FormatMessageW(long ptr long long ptr long ptr)
@ stdcall FreeConsole()
@ stdcall -import FreeEnvironmentStringsA(ptr)
@ stdcall -import FreeEnvironmentStringsW(ptr)
......
......@@ -77,7 +77,7 @@ static void test_message_from_string_wide(void)
error = GetLastError();
ok(!lstrcmpW(L"", out), "failed out=%s\n", wine_dbgstr_w(out));
ok(r==0, "succeeded: r=%d\n", r);
ok((error==0xdeadbeef) || (error == ERROR_NO_WORK_DONE), "last error %u\n", error);
ok(error == ERROR_NO_WORK_DONE || broken(error == 0xdeadbeef), "last error %u\n", error);
/* format placeholder with no specifier */
SetLastError(0xdeadbeef);
......@@ -94,8 +94,7 @@ static void test_message_from_string_wide(void)
lstrcpyW( out, L"xxxxxx" );
r = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, L"test%", 0, 0, out, ARRAY_SIZE(out), NULL);
error = GetLastError();
ok(!lstrcmpW( out, L"xxxxxx" ) ||
broken(!lstrcmpW(out, L"testxx")), /* W2K3+ */
ok(!lstrcmpW(out, L"testxx") || broken(!lstrcmpW( out, L"xxxxxx" )), /* winxp */
"Expected the buffer to be unchanged\n");
ok(r==0, "succeeded: r=%d\n", r);
ok(error==ERROR_INVALID_PARAMETER, "last error %u\n", error);
......@@ -359,7 +358,7 @@ static void test_message_from_string(void)
r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "", 0, 0, out, ARRAY_SIZE(out), NULL);
ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the buffer to be untouched\n");
ok(r==0, "succeeded: r=%d\n", r);
ok((GetLastError()==0xdeadbeef) || (GetLastError() == ERROR_NO_WORK_DONE),
ok(GetLastError() == ERROR_NO_WORK_DONE || broken(GetLastError() == 0xdeadbeef),
"last error %u\n", GetLastError());
/* format placeholder with no specifier */
......@@ -661,8 +660,8 @@ static void test_message_ignore_inserts(void)
ARRAY_SIZE(out), NULL);
ok(ret == 0, "Expected FormatMessageA to return 0, got %d\n", ret);
ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the output buffer to be untouched\n");
ok((GetLastError() == 0xdeadbeef) || (GetLastError() == ERROR_NO_WORK_DONE),
"Expected GetLastError() to return 0xdeadbeef or ERROR_NO_WORK_DONE, got %u\n", GetLastError());
ok(GetLastError() == ERROR_NO_WORK_DONE || broken(GetLastError() == 0xdeadbeef),
"Expected GetLastError() to return ERROR_NO_WORK_DONE, got %u\n", GetLastError());
/* Insert sequences are ignored. */
ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS, "test%1%2!*.*s!%99", 0, 0, out,
......@@ -751,8 +750,8 @@ static void test_message_ignore_inserts_wide(void)
ARRAY_SIZE(out), NULL);
ok(ret == 0, "Expected FormatMessageW to return 0, got %d\n", ret);
ok(!lstrcmpW(L"", out), "Expected the output buffer to be the empty string, got %s\n", wine_dbgstr_w(out));
ok((GetLastError() == 0xdeadbeef) || (GetLastError() == ERROR_NO_WORK_DONE),
"Expected GetLastError() to return 0xdeadbeef or ERROR_NO_WORK_DONE, got %u\n", GetLastError());
ok(GetLastError() == ERROR_NO_WORK_DONE || broken(GetLastError() == 0xdeadbeef),
"Expected GetLastError() to return ERROR_NO_WORK_DONE, got %u\n", GetLastError());
/* Insert sequences are ignored. */
ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS, L"test%1%2!*.*s!%99", 0, 0, out,
......@@ -1066,7 +1065,6 @@ static void test_message_insufficient_buffer_wide(void)
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
"Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n",
GetLastError());
todo_wine
ok(!memcmp(out, L"\0xxxxx", 6 * sizeof(WCHAR)) ||
broken(!lstrcmpW( out, L"xxxxxx" )), /* winxp */
"Expected the buffer to be truncated\n");
......@@ -1078,7 +1076,6 @@ static void test_message_insufficient_buffer_wide(void)
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
"Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n",
GetLastError());
todo_wine
ok(!memcmp(out, L"tes\0xx", 6 * sizeof(WCHAR)) ||
broken(!lstrcmpW( out, L"xxxxxx" )), /* winxp */
"Expected the buffer to be truncated\n");
......@@ -1185,8 +1182,8 @@ static void test_message_allocate_buffer(void)
"", 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) || (GetLastError() == ERROR_NO_WORK_DONE),
"Expected GetLastError() to return 0xdeadbeef or ERROR_NO_WORK_DONE, got %u\n", GetLastError());
ok(GetLastError() == ERROR_NO_WORK_DONE || broken(GetLastError() == 0xdeadbeef),
"Expected GetLastError() to return ERROR_NO_WORK_DONE, got %u\n", GetLastError());
buf = (char *)0xdeadbeef;
ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
......@@ -1279,8 +1276,8 @@ static void test_message_allocate_buffer_wide(void)
L"", 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) || (GetLastError() == ERROR_NO_WORK_DONE),
"Expected GetLastError() to return 0xdeadbeef or ERROR_NO_WORK_DONE, got %u\n", GetLastError());
ok(GetLastError() == ERROR_NO_WORK_DONE || broken(GetLastError() == 0xdeadbeef),
"Expected GetLastError() to return ERROR_NO_WORK_DONE, got %u\n", GetLastError());
buf = (WCHAR *)0xdeadbeef;
ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
......@@ -1637,18 +1634,14 @@ static void test_message_from_64bit_number(void)
r = doitW(FORMAT_MESSAGE_FROM_STRING, L"%1!I64u!", 0, 0, outW, ARRAY_SIZE(outW),
unsigned_tests[i].number);
MultiByteToWideChar(CP_ACP, 0, unsigned_tests[i].expected, -1, expW, ARRAY_SIZE(expW));
todo_wine {
ok(!lstrcmpW(outW, expW),"[%d] failed, expected %s, got %s\n", i,
unsigned_tests[i].expected, wine_dbgstr_w(outW));
ok(r == unsigned_tests[i].len,"[%d] failed: r=%d\n", i, r);
}
r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!I64u!",
0, 0, outA, sizeof(outA), unsigned_tests[i].number);
todo_wine {
ok(!strcmp(outA, unsigned_tests[i].expected),"[%d] failed, expected %s, got %s\n", i,
unsigned_tests[i].expected, outA);
ok(r == unsigned_tests[i].len,"[%d] failed: r=%d\n", i, r);
}
}
for (i = 0; i < ARRAY_SIZE(signed_tests); i++)
......@@ -1656,18 +1649,14 @@ todo_wine {
r = doitW(FORMAT_MESSAGE_FROM_STRING, L"%1!I64d!", 0, 0, outW, ARRAY_SIZE(outW),
signed_tests[i].number);
MultiByteToWideChar(CP_ACP, 0, signed_tests[i].expected, -1, expW, ARRAY_SIZE(expW));
todo_wine {
ok(!lstrcmpW(outW, expW),"[%d] failed, expected %s, got %s\n", i,
signed_tests[i].expected, wine_dbgstr_w(outW));
ok(r == signed_tests[i].len,"[%d] failed: r=%d\n", i, r);
}
r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!I64d!",
0, 0, outA, sizeof(outA), signed_tests[i].number);
todo_wine {
ok(!strcmp(outA, signed_tests[i].expected),"[%d] failed, expected %s, got %s\n", i,
signed_tests[i].expected, outA);
ok(r == signed_tests[i].len,"[%d] failed: r=%d\n", i, r);
}
}
}
......
......@@ -387,8 +387,8 @@
@ stdcall FoldStringW(long wstr long ptr long)
# @ stub ForceSyncFgPolicyInternal
# @ stub FormatApplicationUserModelId
@ stdcall FormatMessageA(long ptr long long ptr long ptr) kernel32.FormatMessageA
@ stdcall FormatMessageW(long ptr long long ptr long ptr) kernel32.FormatMessageW
@ stdcall FormatMessageA(long ptr long long ptr long ptr)
@ stdcall FormatMessageW(long ptr long long ptr long ptr)
@ stdcall FreeConsole()
@ stdcall FreeEnvironmentStringsA(ptr) FreeEnvironmentStringsW
@ stdcall FreeEnvironmentStringsW(ptr)
......
......@@ -3639,6 +3639,197 @@ INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen,
}
static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang,
BOOL ansi, WCHAR **buffer )
{
DWORD len;
if (!(flags & FORMAT_MESSAGE_FROM_STRING))
{
const MESSAGE_RESOURCE_ENTRY *entry;
NTSTATUS status = STATUS_INVALID_PARAMETER;
if (flags & FORMAT_MESSAGE_FROM_HMODULE)
{
HMODULE module = (HMODULE)src;
if (!module) module = GetModuleHandleW( 0 );
status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry );
}
if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
{
/* Fold win32 hresult to its embedded error code. */
if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32)
id = HRESULT_CODE( id );
status = RtlFindMessage( kernel32_handle, RT_MESSAGETABLE, lang, id, &entry );
}
if (!set_ntstatus( status )) return NULL;
src = entry->Text;
ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE);
}
if (!ansi) return src;
len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len );
return *buffer;
}
/***********************************************************************
* FormatMessageA (kernelbase.@)
*/
DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid,
char *buffer, DWORD size, __ms_va_list *args )
{
DWORD ret = 0;
ULONG len, retsize = 0;
ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
const WCHAR *src;
WCHAR *result, *message = NULL;
NTSTATUS status;
TRACE( "(0x%x,%p,%d,0x%x,%p,%d,%p)\n", flags, source, msgid, langid, buffer, size, args );
if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
{
if (!buffer)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return 0;
}
*(char **)buffer = NULL;
}
if (size >= 32768)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (width == 0xff) width = ~0u;
if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0;
if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 )))
status = STATUS_NO_MEMORY;
else
status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
result, 65536, &retsize );
HeapFree( GetProcessHeap(), 0, message );
if (status == STATUS_BUFFER_OVERFLOW)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
goto done;
}
if (!set_ntstatus( status )) goto done;
len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL );
if (len <= 1)
{
SetLastError( ERROR_NO_WORK_DONE );
goto done;
}
if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
{
char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len ));
if (!buf)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
goto done;
}
*(char **)buffer = buf;
WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL );
}
else if (len > size)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
goto done;
}
else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL );
ret = len - 1;
done:
HeapFree( GetProcessHeap(), 0, result );
return ret;
}
/***********************************************************************
* FormatMessageW (kernelbase.@)
*/
DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid,
WCHAR *buffer, DWORD size, __ms_va_list *args )
{
ULONG retsize = 0;
ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK);
const WCHAR *src;
WCHAR *message = NULL;
NTSTATUS status;
TRACE( "(0x%x,%p,%d,0x%x,%p,%d,%p)\n", flags, source, msgid, langid, buffer, size, args );
if (!buffer)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (width == 0xff) width = ~0u;
if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL;
if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0;
if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
{
WCHAR *result;
ULONG alloc = max( size * sizeof(WCHAR), 65536 );
for (;;)
{
if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc )))
{
status = STATUS_NO_MEMORY;
break;
}
status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
result, alloc, &retsize );
if (!status)
{
if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );
else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
result, max( retsize, size * sizeof(WCHAR) ));
break;
}
HeapFree( GetProcessHeap(), 0, result );
if (status != STATUS_BUFFER_OVERFLOW) break;
alloc *= 2;
}
}
else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS),
FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args,
buffer, size * sizeof(WCHAR), &retsize );
HeapFree( GetProcessHeap(), 0, message );
if (status == STATUS_BUFFER_OVERFLOW)
{
if (size) buffer[size - 1] = 0;
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
if (!set_ntstatus( status )) return 0;
if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE );
return retsize / sizeof(WCHAR) - 1;
}
/******************************************************************************
* GetACP (kernelbase.@)
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment