2002-12-08 16:07:18 +00:00
|
|
|
/*
|
|
|
|
* FormatMessage implementation
|
|
|
|
*
|
|
|
|
* Copyright 1996 Marcus Meissner
|
2009-12-03 18:57:57 +00:00
|
|
|
* Copyright 2009 Alexandre Julliard
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2009-12-03 18:57:57 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2002-12-08 16:07:18 +00:00
|
|
|
*/
|
|
|
|
|
2004-01-23 21:16:04 +00:00
|
|
|
#include <k32.h>
|
[KERNEL32]: While working on the CMAKE branch, Amine and myself discovered a rather serious issue in kernel32 (and perhaps other libraries as well). Unlike rbuild, CMake does not allow you to export non-existant DLL functions (try it: add "poopyhead" in kernel32's exports under RBuild, and will it export "poopyhead", God knowing what that will actually link to).
As an additional feature on top of the "allow non-existing functions to be exported" "feature", because rbuild generates and links STDCALL function names without the proper decoration (vs. enforcing decoration at linking, but only removing it at export-time), this allows the definition (as an export) of a STDCALL function that is completely different from the actual function itself.
For example, the 5-parameter Foo function is normally Foo@20, while the 3-parameter Foo function woudl be Foo@12. Linking one against the other would fail (say, 2 parameters were added to Foo in a newer version). However, under RBUILD, both of these would appear as "Foo", and the linker/compiler would happilly connect the caller of Foo@3 (that has pushed 3 parameters) to the receiving side of Foo@5 (that is about to pop 5 parameters).
Even -if- decorations WERE to be applied, Foo@12 would STILL succeed, because of the first feature, which would enable the export of Foo@12 even though no such function exist.
In a further, bizare, twist of fate, the behavior of this RBUILD "feature", when the target function is not found, is to link the exported DLL TO ITSELF.
Therefore, one can see how, previously to this patch, kernel32.dll would import a dozen functions from itself (all the non-existing functions).
To really seal the deal, the behavior of exported functions used by kernel32, but that are actually forwarded to another DLL deserves a special mention.
GetLastError, for example, merely forwards to RtlGetLastWin32Error, so it is normal behavior to use a #define in the C code so that all internal calls to the function are routed correctly.
This did not happen, so instead, kernel32 tried importing/linking/exporting GetLastError, but this symbol is not found in the binary, because it is only a forwarder.
This caused kernel32 to import from itself (the behavior when an exported symbol is not found). When importing from itself, the loader would now find the _forwarded_ for GetLastError, and correctly link with ntdll.
What should be a one-liner of assembly (inline TEB access) thus became a triple-call indirection (GetLastError@0->StubLastError@0->__impGetLastError@0->__impRtlGetLastWin32Error->RtlGetLastWin32Error.
While analyzing these issues, we also realized a strange macro SetLastErrorByStatus that manually tried to perform what there already exists a function for: RtlSetLastNtStatusFromWin32Error.
And, in an exciting coda, we also realized that our Server 2003 Kernel32 exports more than a dozen Windows 95 APIs, through an auto-stub generation mechanism within winebuild, that gets linked as an object behind the scenes.
[KERNEL32]: Removed all Win95 exports, cleaned up exports.
[KERNEL32]: Fixed up set/get error macros by making them inline and/or calling the correct ntdll function.
[KERNEL32]: Removed bizare calls to Wine-internal/specific APIs from our core Win32 DLL.
[KERNEL32]: Wrote stubs for all functions which should be exported, and set the correct number of parameters for them.
[KERNEL32]: Kernel32 is smaller, loads faster, does not export Windows 95 functions, does not export non-existing functions, and does not import from itself anymore.
Note: This is one of the many failings of RBUILD the CMAKE system has helped us discover. I believe these issues are serious enough to warrant an immediate sync with trunk, but rest assured, there are many more completely broken, infinitely-regressing things that we discovered while switching to CMAKE.
svn path=/trunk/; revision=48475
2010-08-07 05:02:58 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
2010-09-21 17:14:22 +00:00
|
|
|
DEBUG_CHANNEL(resource);
|
2003-11-05 20:42:02 +00:00
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args
|
2003-11-05 20:42:02 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
ULONG_PTR *args;
|
|
|
|
__ms_va_list *list;
|
|
|
|
int last;
|
|
|
|
};
|
2003-11-19 22:19:17 +00:00
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
static const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2',0};
|
2003-11-05 20:42:02 +00:00
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
/* Messages used by FormatMessage
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* They can be specified either directly or using a message ID and
|
|
|
|
* loading them from the resource.
|
|
|
|
*
|
|
|
|
* The resourcedata has following format:
|
|
|
|
* start:
|
|
|
|
* 0: DWORD nrofentries
|
|
|
|
* nrofentries * subentry:
|
2009-12-03 18:57:57 +00:00
|
|
|
* 0: DWORD firstentry
|
|
|
|
* 4: DWORD lastentry
|
2002-12-08 16:07:18 +00:00
|
|
|
* 8: DWORD offset from start to the stringentries
|
|
|
|
*
|
|
|
|
* (lastentry-firstentry) * stringentry:
|
2009-12-03 18:57:57 +00:00
|
|
|
* 0: WORD len (0 marks end) [ includes the 4 byte header length ]
|
2002-12-08 16:07:18 +00:00
|
|
|
* 2: WORD flags
|
|
|
|
* 4: CHAR[len-4]
|
2009-12-03 18:57:57 +00:00
|
|
|
* (stringentry i of a subentry refers to the ID 'firstentry+i')
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* Yes, ANSI strings in win32 resources. Go figure.
|
|
|
|
*/
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
static const WCHAR PCNTFMTWSTR[] = { '%','%','%','s',0 };
|
|
|
|
static const WCHAR FMTWSTR[] = { '%','s',0 };
|
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/**********************************************************************
|
2010-07-08 15:20:46 +00:00
|
|
|
* load_message (internal)
|
2002-12-08 16:07:18 +00:00
|
|
|
*/
|
2010-07-08 15:20:46 +00:00
|
|
|
static LPWSTR load_message( HMODULE module, UINT id, WORD lang )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2010-06-27 21:11:57 +00:00
|
|
|
PMESSAGE_RESOURCE_ENTRY mre;
|
2009-11-16 01:37:12 +00:00
|
|
|
WCHAR *buffer;
|
2009-12-03 18:57:57 +00:00
|
|
|
NTSTATUS status;
|
2005-11-14 21:53:21 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("module = %p, id = %08x\n", module, id );
|
2005-11-14 21:53:21 +00:00
|
|
|
|
|
|
|
if (!module) module = GetModuleHandleW( NULL );
|
2009-12-03 18:57:57 +00:00
|
|
|
if ((status = RtlFindMessage( module, (ULONG)RT_MESSAGETABLE, lang, id, &mre )) != STATUS_SUCCESS)
|
2009-11-16 01:37:12 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
SetLastError( RtlNtStatusToDosError(status) );
|
2005-11-14 21:53:21 +00:00
|
|
|
return NULL;
|
2009-11-16 01:37:12 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
|
|
|
|
if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
|
|
|
|
{
|
2009-11-16 01:37:12 +00:00
|
|
|
int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
|
2009-11-16 01:37:12 +00:00
|
|
|
memcpy( buffer, mre->Text, len );
|
2003-11-05 20:42:02 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
else
|
|
|
|
{
|
2009-11-16 01:37:12 +00:00
|
|
|
int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 );
|
|
|
|
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len );
|
2003-01-07 17:29:09 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("returning %s\n", wine_dbgstr_w(buffer));
|
2005-11-14 21:53:21 +00:00
|
|
|
return buffer;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* get_arg (internal)
|
|
|
|
*/
|
|
|
|
static ULONG_PTR get_arg( int nr, DWORD flags, struct format_args *args )
|
|
|
|
{
|
|
|
|
if (nr == -1) nr = args->last + 1;
|
|
|
|
if (args->list)
|
|
|
|
{
|
|
|
|
if (!args->args) args->args = HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR) );
|
|
|
|
while (nr > args->last)
|
|
|
|
args->args[args->last++] = va_arg( *args->list, ULONG_PTR );
|
|
|
|
}
|
|
|
|
if (nr > args->last) args->last = nr;
|
|
|
|
return args->args[nr - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
2010-07-08 15:20:46 +00:00
|
|
|
* format_insert (internal)
|
2009-12-03 18:57:57 +00:00
|
|
|
*/
|
2010-07-08 15:20:46 +00:00
|
|
|
static LPCWSTR format_insert( BOOL unicode_caller, int insert, LPCWSTR format,
|
|
|
|
DWORD flags, struct format_args *args,
|
|
|
|
LPWSTR *result )
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
static const WCHAR fmt_lu[] = {'%','l','u',0};
|
|
|
|
WCHAR *wstring = NULL, *p, fmt[256];
|
2009-12-03 18:57:57 +00:00
|
|
|
ULONG_PTR arg;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
if (*format != '!') /* simple string */
|
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
arg = get_arg( insert, flags, args );
|
|
|
|
if (unicode_caller)
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
WCHAR *str = (WCHAR *)arg;
|
|
|
|
*result = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) );
|
|
|
|
strcpyW( *result, str );
|
2009-12-03 18:57:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
char *str = (char *)arg;
|
|
|
|
DWORD length = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
|
|
|
|
*result = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, str, -1, *result, length );
|
2009-12-03 18:57:57 +00:00
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
p = fmt;
|
|
|
|
*p++ = '%';
|
|
|
|
|
|
|
|
while (*format == '0' ||
|
|
|
|
*format == '+' ||
|
|
|
|
*format == '-' ||
|
|
|
|
*format == ' ' ||
|
|
|
|
*format == '*' ||
|
|
|
|
*format == '#')
|
|
|
|
{
|
|
|
|
if (*format == '*')
|
|
|
|
{
|
|
|
|
p += sprintfW( p, fmt_lu, get_arg( insert, flags, args ));
|
|
|
|
insert = -1;
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
else *p++ = *format++;
|
|
|
|
}
|
|
|
|
while (isdigitW(*format)) *p++ = *format++;
|
|
|
|
|
|
|
|
if (*format == '.')
|
|
|
|
{
|
|
|
|
*p++ = *format++;
|
|
|
|
if (*format == '*')
|
|
|
|
{
|
|
|
|
p += sprintfW( p, fmt_lu, get_arg( insert, flags, args ));
|
|
|
|
insert = -1;
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while (isdigitW(*format)) *p++ = *format++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* replicate MS bug: drop an argument when using va_list with width/precision */
|
|
|
|
if (insert == -1 && args->list) args->last--;
|
|
|
|
arg = get_arg( insert, flags, args );
|
|
|
|
|
|
|
|
/* check for ascii string format */
|
|
|
|
if ((format[0] == 'h' && format[1] == 's') ||
|
|
|
|
(format[0] == 'h' && format[1] == 'S') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(unicode_caller && format[0] == 'S') ||
|
|
|
|
(!unicode_caller && format[0] == 's'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD len = MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, NULL, 0 );
|
2009-12-03 18:57:57 +00:00
|
|
|
wstring = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, wstring, len );
|
|
|
|
arg = (ULONG_PTR)wstring;
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for ascii character format */
|
|
|
|
else if ((format[0] == 'h' && format[1] == 'c') ||
|
|
|
|
(format[0] == 'h' && format[1] == 'C') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(unicode_caller && format[0] == 'C') ||
|
|
|
|
(!unicode_caller && format[0] == 'c'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
|
|
|
char ch = arg;
|
|
|
|
wstring = HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, &ch, 1, wstring, 1 );
|
|
|
|
wstring[1] = 0;
|
|
|
|
arg = (ULONG_PTR)wstring;
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for wide string format */
|
|
|
|
else if ((format[0] == 'l' && format[1] == 's') ||
|
|
|
|
(format[0] == 'l' && format[1] == 'S') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(format[0] == 'w' && format[1] == 's') ||
|
|
|
|
(!unicode_caller && format[0] == 'S'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for wide character format */
|
|
|
|
else if ((format[0] == 'l' && format[1] == 'c') ||
|
|
|
|
(format[0] == 'l' && format[1] == 'C') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(format[0] == 'w' && format[1] == 'c') ||
|
|
|
|
(!unicode_caller && format[0] == 'C'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
|
|
|
*p++ = 'c';
|
|
|
|
}
|
|
|
|
/* FIXME: handle I64 etc. */
|
|
|
|
else while (*format && *format != '!') *p++ = *format++;
|
|
|
|
|
|
|
|
*p = 0;
|
|
|
|
size = 256;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
|
|
|
|
int needed = snprintfW( ret, size, fmt, arg );
|
|
|
|
if (needed == -1 || needed >= size)
|
|
|
|
{
|
|
|
|
HeapFree( GetProcessHeap(), 0, ret );
|
|
|
|
size = max( needed + 1, size * 2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*result = ret;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*format && *format != '!') format++;
|
|
|
|
if (*format == '!') format++;
|
|
|
|
|
|
|
|
HeapFree( GetProcessHeap(), 0, wstring );
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* format_message (internal)
|
|
|
|
*/
|
|
|
|
static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr,
|
|
|
|
struct format_args *format_args )
|
|
|
|
{
|
|
|
|
LPWSTR target,t;
|
|
|
|
DWORD talloced;
|
|
|
|
LPCWSTR f;
|
|
|
|
DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
|
|
|
BOOL eos = FALSE;
|
|
|
|
WCHAR ch;
|
|
|
|
|
|
|
|
target = t = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
|
|
|
|
talloced = 100;
|
|
|
|
|
|
|
|
#define ADD_TO_T(c) do {\
|
|
|
|
*t++=c;\
|
|
|
|
if ((DWORD)(t-target) == talloced) {\
|
|
|
|
target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
|
|
|
|
t = target+talloced;\
|
|
|
|
talloced*=2;\
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
f = fmtstr;
|
|
|
|
while (*f && !eos) {
|
|
|
|
if (*f=='%') {
|
|
|
|
int insertnr;
|
|
|
|
WCHAR *str,*x;
|
|
|
|
|
|
|
|
f++;
|
|
|
|
switch (*f) {
|
|
|
|
case '1':case '2':case '3':case '4':case '5':
|
|
|
|
case '6':case '7':case '8':case '9':
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
|
|
|
|
goto ignore_inserts;
|
|
|
|
else if (((dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->args) ||
|
|
|
|
(!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->list))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
HeapFree(GetProcessHeap(), 0, target);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
insertnr = *f-'0';
|
|
|
|
switch (f[1]) {
|
|
|
|
case '0':case '1':case '2':case '3':
|
|
|
|
case '4':case '5':case '6':case '7':
|
|
|
|
case '8':case '9':
|
|
|
|
f++;
|
|
|
|
insertnr = insertnr*10 + *f-'0';
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
f = format_insert( unicode_caller, insertnr, f, dwFlags, format_args, &str );
|
|
|
|
for (x = str; *x; x++) ADD_TO_T(*x);
|
|
|
|
HeapFree( GetProcessHeap(), 0, str );
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
ADD_TO_T('\r');
|
|
|
|
ADD_TO_T('\n');
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
ADD_TO_T('\r');
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
ADD_TO_T('\t');
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
eos = TRUE;
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case '\0':
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
HeapFree(GetProcessHeap(), 0, target);
|
|
|
|
return NULL;
|
|
|
|
ignore_inserts:
|
|
|
|
default:
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
|
|
|
|
ADD_TO_T('%');
|
|
|
|
ADD_TO_T(*f++);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ch = *f;
|
|
|
|
f++;
|
|
|
|
if (ch == '\r') {
|
|
|
|
if (*f == '\n')
|
|
|
|
f++;
|
|
|
|
if(width)
|
|
|
|
ADD_TO_T(' ');
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ADD_TO_T('\r');
|
|
|
|
ADD_TO_T('\n');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ch == '\n')
|
|
|
|
{
|
|
|
|
if(width)
|
|
|
|
ADD_TO_T(' ');
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ADD_TO_T('\r');
|
|
|
|
ADD_TO_T('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ADD_TO_T(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*t = '\0';
|
|
|
|
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
#undef ADD_TO_T
|
2009-12-03 18:57:57 +00:00
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* FormatMessageA (KERNEL32.@)
|
|
|
|
* FIXME: missing wrap,
|
|
|
|
*/
|
|
|
|
DWORD WINAPI FormatMessageA(
|
2005-11-14 21:53:21 +00:00
|
|
|
DWORD dwFlags,
|
|
|
|
LPCVOID lpSource,
|
|
|
|
DWORD dwMessageId,
|
|
|
|
DWORD dwLanguageId,
|
|
|
|
LPSTR lpBuffer,
|
|
|
|
DWORD nSize,
|
2009-12-03 18:57:57 +00:00
|
|
|
__ms_va_list* args )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args format_args;
|
|
|
|
DWORD ret = 0;
|
2010-07-08 15:20:46 +00:00
|
|
|
LPWSTR target;
|
|
|
|
DWORD destlength;
|
|
|
|
LPWSTR from;
|
2005-11-14 21:53:21 +00:00
|
|
|
DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
2009-12-03 18:57:57 +00:00
|
|
|
HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
|
2005-11-14 21:53:21 +00:00
|
|
|
dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
2008-09-13 08:48:18 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
if (!lpBuffer)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*(LPSTR *)lpBuffer = NULL;
|
2008-09-13 08:48:18 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
|
|
|
|
{
|
|
|
|
format_args.args = (ULONG_PTR *)args;
|
|
|
|
format_args.list = NULL;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format_args.args = NULL;
|
|
|
|
format_args.list = args;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
|
2010-07-08 15:20:46 +00:00
|
|
|
FIXME("line wrapping (%u) not supported.\n", width);
|
2002-12-08 16:07:18 +00:00
|
|
|
from = NULL;
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
|
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD length = MultiByteToWideChar(CP_ACP, 0, lpSource, -1, NULL, 0);
|
|
|
|
from = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, lpSource, -1, from, length);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
|
|
|
|
{
|
2002-12-08 16:07:18 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
|
2010-07-08 15:20:46 +00:00
|
|
|
from = load_message( (HMODULE)lpSource, dwMessageId, dwLanguageId );
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
|
2010-07-08 15:20:46 +00:00
|
|
|
from = load_message( kernel32_handle, dwMessageId, dwLanguageId );
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!from) return 0;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
target = format_message( FALSE, dwFlags, from, &format_args );
|
|
|
|
if (!target)
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- %s\n", debugstr_w(target));
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
WideCharToMultiByte(CP_ACP, 0, target, -1, lpBuffer, destlength, NULL, NULL);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
ret = destlength - 1; /* null terminator */
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
|
|
|
|
failure:
|
2005-11-14 21:53:21 +00:00
|
|
|
HeapFree(GetProcessHeap(),0,target);
|
|
|
|
HeapFree(GetProcessHeap(),0,from);
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- returning %u\n", ret);
|
2009-12-03 18:57:57 +00:00
|
|
|
return ret;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2009-12-03 18:57:57 +00:00
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* FormatMessageW (KERNEL32.@)
|
|
|
|
*/
|
|
|
|
DWORD WINAPI FormatMessageW(
|
2005-11-14 21:53:21 +00:00
|
|
|
DWORD dwFlags,
|
|
|
|
LPCVOID lpSource,
|
|
|
|
DWORD dwMessageId,
|
|
|
|
DWORD dwLanguageId,
|
|
|
|
LPWSTR lpBuffer,
|
|
|
|
DWORD nSize,
|
2009-12-03 18:57:57 +00:00
|
|
|
__ms_va_list* args )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args format_args;
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD ret = 0;
|
|
|
|
LPWSTR target;
|
2009-12-03 18:57:57 +00:00
|
|
|
DWORD talloced;
|
|
|
|
LPWSTR from;
|
2002-12-08 16:07:18 +00:00
|
|
|
DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
2009-12-03 18:57:57 +00:00
|
|
|
HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
|
2005-11-14 21:53:21 +00:00
|
|
|
dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2008-09-13 08:48:18 +00:00
|
|
|
if (!lpBuffer)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
|
|
|
*(LPWSTR *)lpBuffer = NULL;
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
|
|
|
|
{
|
|
|
|
format_args.args = (ULONG_PTR *)args;
|
|
|
|
format_args.list = NULL;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format_args.args = NULL;
|
|
|
|
format_args.list = args;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
|
2005-11-14 21:53:21 +00:00
|
|
|
if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
|
2010-07-08 15:20:46 +00:00
|
|
|
FIXME("line wrapping not supported.\n");
|
2002-12-08 16:07:18 +00:00
|
|
|
from = NULL;
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
|
2009-12-03 18:57:57 +00:00
|
|
|
from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
|
2005-11-14 21:53:21 +00:00
|
|
|
sizeof(WCHAR) );
|
2009-12-03 18:57:57 +00:00
|
|
|
strcpyW( from, lpSource );
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
|
|
|
|
{
|
2002-12-08 16:07:18 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
|
2010-07-08 15:20:46 +00:00
|
|
|
from = load_message( (HMODULE)lpSource, dwMessageId, dwLanguageId );
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
|
2010-07-08 15:20:46 +00:00
|
|
|
from = load_message( kernel32_handle, dwMessageId, dwLanguageId );
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!from) return 0;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
target = format_message( TRUE, dwFlags, from, &format_args );
|
|
|
|
if (!target)
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
talloced = strlenW(target)+1;
|
|
|
|
TRACE("-- %s\n",debugstr_w(target));
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
/* 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);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nSize < talloced)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
strcpyW(lpBuffer, target);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
ret = talloced - 1; /* null terminator */
|
|
|
|
failure:
|
2005-11-14 21:53:21 +00:00
|
|
|
HeapFree(GetProcessHeap(),0,target);
|
|
|
|
HeapFree(GetProcessHeap(),0,from);
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- returning %u\n", ret);
|
|
|
|
return ret;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|