2 * FormatMessage implementation
4 * Copyright 1996 Marcus Meissner
5 * Copyright 2009 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/unicode.h"
36 static const WCHAR kernel32W
[] = {'k','e','r','n','e','l','3','2',0};
38 /* Messages used by FormatMessage
40 * They can be specified either directly or using a message ID and
41 * loading them from the resource.
43 * The resourcedata has following format:
45 * 0: DWORD nrofentries
46 * nrofentries * subentry:
49 * 8: DWORD offset from start to the stringentries
51 * (lastentry-firstentry) * stringentry:
52 * 0: WORD len (0 marks end) [ includes the 4 byte header length ]
55 * (stringentry i of a subentry refers to the ID 'firstentry+i')
57 * Yes, ANSI strings in win32 resources. Go figure.
60 static const WCHAR PCNTFMTWSTR
[] = { '%','%','%','s',0 };
61 static const WCHAR FMTWSTR
[] = { '%','s',0 };
63 /**********************************************************************
64 * load_messageW (internal)
66 static LPWSTR
load_messageW( HMODULE module
, UINT id
, WORD lang
)
68 PRTL_MESSAGE_RESOURCE_ENTRY mre
;
72 DPRINT("module = %p, id = %08x\n", module
, id
);
74 if (!module
) module
= GetModuleHandleW( NULL
);
75 if ((status
= RtlFindMessage( module
, (ULONG
)RT_MESSAGETABLE
, lang
, id
, &mre
)) != STATUS_SUCCESS
)
77 SetLastError( RtlNtStatusToDosError(status
) );
81 if (mre
->Flags
& MESSAGE_RESOURCE_UNICODE
)
83 int len
= (strlenW( (const WCHAR
*)mre
->Text
) + 1) * sizeof(WCHAR
);
84 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
))) return NULL
;
85 memcpy( buffer
, mre
->Text
, len
);
89 int len
= MultiByteToWideChar( CP_ACP
, 0, (const char *)mre
->Text
, -1, NULL
, 0 );
90 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) ))) return NULL
;
91 MultiByteToWideChar( CP_ACP
, 0, (const char*)mre
->Text
, -1, buffer
, len
);
93 DPRINT("returning %S\n", buffer
);
98 /**********************************************************************
99 * load_messageA (internal)
101 static LPSTR
load_messageA( HMODULE module
, UINT id
, WORD lang
)
103 PRTL_MESSAGE_RESOURCE_ENTRY mre
;
107 DPRINT("module = %p, id = %08x\n", module
, id
);
109 if (!module
) module
= GetModuleHandleW( NULL
);
110 if ((status
= RtlFindMessage( module
, (ULONG
)RT_MESSAGETABLE
, lang
, id
, &mre
)) != STATUS_SUCCESS
)
112 SetLastError( RtlNtStatusToDosError(status
) );
116 if (mre
->Flags
& MESSAGE_RESOURCE_UNICODE
)
118 int len
= WideCharToMultiByte( CP_ACP
, 0, (const WCHAR
*)mre
->Text
, -1, NULL
, 0, NULL
, NULL
);
119 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
))) return NULL
;
120 WideCharToMultiByte( CP_ACP
, 0, (const WCHAR
*)mre
->Text
, -1, buffer
, len
, NULL
, NULL
);
124 int len
= strlen((const char*)mre
->Text
) + 1;
125 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
))) return NULL
;
126 memcpy( buffer
, mre
->Text
, len
);
128 DPRINT("returning %s\n", buffer
);
133 /**********************************************************************
136 static ULONG_PTR
get_arg( int nr
, DWORD flags
, struct format_args
*args
)
138 if (nr
== -1) nr
= args
->last
+ 1;
141 if (!args
->args
) args
->args
= HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR
) );
142 while (nr
> args
->last
)
143 args
->args
[args
->last
++] = va_arg( *args
->list
, ULONG_PTR
);
145 if (nr
> args
->last
) args
->last
= nr
;
146 return args
->args
[nr
- 1];
150 /**********************************************************************
151 * format_insertA (internal)
153 static LPCSTR
format_insertA( int insert
, LPCSTR format
, DWORD flags
,
154 struct format_args
*args
, LPSTR
*result
)
156 char *astring
= NULL
, *p
, fmt
[256];
160 if (*format
!= '!') /* simple string */
162 char *str
= (char *)get_arg( insert
, flags
, args
);
163 *result
= HeapAlloc( GetProcessHeap(), 0, strlen(str
) + 1 );
164 strcpy( *result
, str
);
172 while (*format
== '0' ||
181 p
+= sprintf( p
, "%lu", get_arg( insert
, flags
, args
));
185 else *p
++ = *format
++;
187 while (isdigit(*format
)) *p
++ = *format
++;
194 p
+= sprintf( p
, "%lu", get_arg( insert
, flags
, args
));
199 while (isdigit(*format
)) *p
++ = *format
++;
202 /* replicate MS bug: drop an argument when using va_list with width/precision */
203 if (insert
== -1 && args
->list
) args
->last
--;
204 arg
= get_arg( insert
, flags
, args
);
206 /* check for wide string format */
207 if ((format
[0] == 'l' && format
[1] == 's') ||
208 (format
[0] == 'l' && format
[1] == 'S') ||
209 (format
[0] == 'w' && format
[1] == 's') ||
212 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)arg
, -1, /*FIXME*/
213 NULL
, 0, NULL
, NULL
);
214 astring
= HeapAlloc( GetProcessHeap(), 0, len
);
215 WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)arg
, -1, astring
, len
, NULL
, NULL
);
216 arg
= (ULONG_PTR
)astring
;
219 /* check for wide character format */
220 else if ((format
[0] == 'l' && format
[1] == 'c') ||
221 (format
[0] == 'l' && format
[1] == 'C') ||
222 (format
[0] == 'w' && format
[1] == 'c') ||
226 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, &ch
, 1, NULL
, 0, NULL
, NULL
);
227 astring
= HeapAlloc( GetProcessHeap(), 0, len
+ 1 );
228 WideCharToMultiByte( CP_ACP
, 0, &ch
, 1, astring
, len
, NULL
, NULL
);
230 arg
= (ULONG_PTR
)astring
;
233 /* check for ascii string format */
234 else if ((format
[0] == 'h' && format
[1] == 's') ||
235 (format
[0] == 'h' && format
[1] == 'S'))
239 /* check for ascii character format */
240 else if ((format
[0] == 'h' && format
[1] == 'c') ||
241 (format
[0] == 'h' && format
[1] == 'C'))
245 /* FIXME: handle I64 etc. */
246 else while (*format
&& *format
!= '!') *p
++ = *format
++;
252 char *ret
= HeapAlloc( GetProcessHeap(), 0, size
);
253 int needed
= snprintf( ret
, size
, fmt
, arg
);
254 if (needed
== -1 || needed
>= size
)
256 HeapFree( GetProcessHeap(), 0, ret
);
257 size
= max( needed
+ 1, size
* 2 );
266 while (*format
&& *format
!= '!') format
++;
267 if (*format
== '!') format
++;
269 HeapFree( GetProcessHeap(), 0, astring
);
274 /**********************************************************************
275 * format_insertW (internal)
277 static LPCWSTR
format_insertW( int insert
, LPCWSTR format
, DWORD flags
,
278 struct format_args
*args
, LPWSTR
*result
)
280 static const WCHAR fmt_lu
[] = {'%','l','u',0};
281 WCHAR
*wstring
= NULL
, *p
, fmt
[256];
285 if (*format
!= '!') /* simple string */
287 WCHAR
*str
= (WCHAR
*)get_arg( insert
, flags
, args
);
288 *result
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) );
289 strcpyW( *result
, str
);
297 while (*format
== '0' ||
306 p
+= sprintfW( p
, fmt_lu
, get_arg( insert
, flags
, args
));
310 else *p
++ = *format
++;
312 while (isdigitW(*format
)) *p
++ = *format
++;
319 p
+= sprintfW( p
, fmt_lu
, get_arg( insert
, flags
, args
));
324 while (isdigitW(*format
)) *p
++ = *format
++;
327 /* replicate MS bug: drop an argument when using va_list with width/precision */
328 if (insert
== -1 && args
->list
) args
->last
--;
329 arg
= get_arg( insert
, flags
, args
);
331 /* check for ascii string format */
332 if ((format
[0] == 'h' && format
[1] == 's') ||
333 (format
[0] == 'h' && format
[1] == 'S') ||
336 DWORD len
= MultiByteToWideChar( CP_ACP
, 0, (char *)arg
, -1, /*FIXME*/ NULL
, 0 );
337 wstring
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
338 MultiByteToWideChar( CP_ACP
, 0, (char *)arg
, -1, wstring
, len
);
339 arg
= (ULONG_PTR
)wstring
;
342 /* check for ascii character format */
343 else if ((format
[0] == 'h' && format
[1] == 'c') ||
344 (format
[0] == 'h' && format
[1] == 'C') ||
348 wstring
= HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR
) );
349 MultiByteToWideChar( CP_ACP
, 0, &ch
, 1, wstring
, 1 );
351 arg
= (ULONG_PTR
)wstring
;
354 /* check for wide string format */
355 else if ((format
[0] == 'l' && format
[1] == 's') ||
356 (format
[0] == 'l' && format
[1] == 'S') ||
357 (format
[0] == 'w' && format
[1] == 's'))
361 /* check for wide character format */
362 else if ((format
[0] == 'l' && format
[1] == 'c') ||
363 (format
[0] == 'l' && format
[1] == 'C') ||
364 (format
[0] == 'w' && format
[1] == 'c'))
368 /* FIXME: handle I64 etc. */
369 else while (*format
&& *format
!= '!') *p
++ = *format
++;
375 WCHAR
*ret
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) );
376 int needed
= snprintfW( ret
, size
, fmt
, arg
);
377 if (needed
== -1 || needed
>= size
)
379 HeapFree( GetProcessHeap(), 0, ret
);
380 size
= max( needed
+ 1, size
* 2 );
389 while (*format
&& *format
!= '!') format
++;
390 if (*format
== '!') format
++;
392 HeapFree( GetProcessHeap(), 0, wstring
);
397 /***********************************************************************
398 * FormatMessageA (KERNEL32.@)
399 * FIXME: missing wrap,
401 DWORD WINAPI
FormatMessageA(
410 struct format_args format_args
;
416 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
419 HMODULE kernel32_handle
= GetModuleHandleW(kernel32W
);
421 DPRINT("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
422 dwFlags
,lpSource
,dwMessageId
,dwLanguageId
,lpBuffer
,nSize
,args
);
423 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
424 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
425 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
429 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
433 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
435 format_args
.args
= (ULONG_PTR
*)args
;
436 format_args
.list
= NULL
;
437 format_args
.last
= 0;
441 format_args
.args
= NULL
;
442 format_args
.list
= args
;
443 format_args
.last
= 0;
446 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
447 DPRINT1("FIXME: line wrapping (%u) not supported.\n", width
);
449 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
451 from
= HeapAlloc( GetProcessHeap(), 0, strlen(lpSource
) + 1 );
452 strcpy( from
, lpSource
);
456 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
457 from
= load_messageA( (HMODULE
)lpSource
, dwMessageId
, dwLanguageId
);
458 if (!from
&& (dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
))
459 from
= load_messageA( kernel32_handle
, dwMessageId
, dwLanguageId
);
462 target
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, 100);
466 #define ADD_TO_T(c) do { \
468 if ((DWORD)(t-target) == talloced) {\
469 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
470 t = target+talloced;\
477 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
493 case '1':case '2':case '3':case '4':case '5':
494 case '6':case '7':case '8':case '9':
497 case '0':case '1':case '2':case '3':
498 case '4':case '5':case '6':case '7':
501 insertnr
=insertnr
*10+*f
-'0';
508 f
= format_insertA( insertnr
, f
, dwFlags
, &format_args
, &str
);
509 for (x
= str
; *x
; x
++) ADD_TO_T(*x
);
510 HeapFree( GetProcessHeap(), 0, str
);
557 talloced
= strlen(target
)+1;
558 if (nSize
&& talloced
<nSize
) {
559 target
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
);
561 DPRINT("-- %S\n", target
);
562 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
563 *((LPVOID
*)lpBuffer
) = LocalAlloc(LMEM_ZEROINIT
,max(nSize
, talloced
));
564 memcpy(*(LPSTR
*)lpBuffer
,target
,talloced
);
566 lstrcpynA(lpBuffer
,target
,nSize
);
568 HeapFree(GetProcessHeap(),0,target
);
569 HeapFree(GetProcessHeap(),0,from
);
570 if (!(dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)) HeapFree( GetProcessHeap(), 0, format_args
.args
);
571 ret
= (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ? strlen(*(LPSTR
*)lpBuffer
) : strlen(lpBuffer
);
572 DPRINT("-- returning %d\n", ret
);
578 /***********************************************************************
579 * FormatMessageW (KERNEL32.@)
581 DWORD WINAPI
FormatMessageW(
590 struct format_args format_args
;
595 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
598 HMODULE kernel32_handle
= GetModuleHandleW(kernel32W
);
600 DPRINT("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
601 dwFlags
,lpSource
,dwMessageId
,dwLanguageId
,lpBuffer
,nSize
,args
);
602 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
603 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
604 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
608 SetLastError(ERROR_INVALID_PARAMETER
);
612 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
614 format_args
.args
= (ULONG_PTR
*)args
;
615 format_args
.list
= NULL
;
616 format_args
.last
= 0;
620 format_args
.args
= NULL
;
621 format_args
.list
= args
;
622 format_args
.last
= 0;
625 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
626 DPRINT1("FIXME: line wrapping not supported.\n");
628 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
) {
629 from
= HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource
) + 1) *
631 strcpyW( from
, lpSource
);
635 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
636 from
= load_messageW( (HMODULE
)lpSource
, dwMessageId
, dwLanguageId
);
637 if (!from
&& (dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
))
638 from
= load_messageW( kernel32_handle
, dwMessageId
, dwLanguageId
);
641 target
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, 100 * sizeof(WCHAR
) );
645 #define ADD_TO_T(c) do {\
647 if ((DWORD)(t-target) == talloced) {\
648 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
649 t = target+talloced;\
656 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
673 case '1':case '2':case '3':case '4':case '5':
674 case '6':case '7':case '8':case '9':
677 case '0':case '1':case '2':case '3':
678 case '4':case '5':case '6':case '7':
681 insertnr
=insertnr
*10+*f
-'0';
688 f
= format_insertW( insertnr
, f
, dwFlags
, &format_args
, &str
);
689 for (x
= str
; *x
; x
++) ADD_TO_T(*x
);
690 HeapFree( GetProcessHeap(), 0, str
);
737 talloced
= strlenW(target
)+1;
738 if (nSize
&& talloced
<nSize
)
739 target
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
*sizeof(WCHAR
));
740 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
741 /* nSize is the MINIMUM size */
742 DWORD len
= strlenW(target
) + 1;
743 *((LPVOID
*)lpBuffer
) = LocalAlloc(LMEM_ZEROINIT
,len
*sizeof(WCHAR
));
744 strcpyW(*(LPWSTR
*)lpBuffer
, target
);
746 else lstrcpynW(lpBuffer
, target
, nSize
);
748 HeapFree(GetProcessHeap(),0,target
);
749 HeapFree(GetProcessHeap(),0,from
);
750 if (!(dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)) HeapFree( GetProcessHeap(), 0, format_args
.args
);
751 DPRINT("ret=%S\n", (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
752 *(LPWSTR
*)lpBuffer
: lpBuffer
);
753 return (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
754 strlenW(*(LPWSTR
*)lpBuffer
):