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
25 static ULONG gDebugChannel
= resource
;
34 static const WCHAR kernel32W
[] = {'k','e','r','n','e','l','3','2',0};
36 /* Messages used by FormatMessage
38 * They can be specified either directly or using a message ID and
39 * loading them from the resource.
41 * The resourcedata has following format:
43 * 0: DWORD nrofentries
44 * nrofentries * subentry:
47 * 8: DWORD offset from start to the stringentries
49 * (lastentry-firstentry) * stringentry:
50 * 0: WORD len (0 marks end) [ includes the 4 byte header length ]
53 * (stringentry i of a subentry refers to the ID 'firstentry+i')
55 * Yes, ANSI strings in win32 resources. Go figure.
58 static const WCHAR PCNTFMTWSTR
[] = { '%','%','%','s',0 };
59 static const WCHAR FMTWSTR
[] = { '%','s',0 };
61 /**********************************************************************
62 * load_message (internal)
64 static LPWSTR
load_message( HMODULE module
, UINT id
, WORD lang
)
66 PMESSAGE_RESOURCE_ENTRY mre
;
70 TRACE("module = %p, id = %08x\n", module
, id
);
72 if (!module
) module
= GetModuleHandleW( NULL
);
73 if ((status
= RtlFindMessage( module
, (ULONG
)RT_MESSAGETABLE
, lang
, id
, &mre
)) != STATUS_SUCCESS
)
75 SetLastError( RtlNtStatusToDosError(status
) );
79 if (mre
->Flags
& MESSAGE_RESOURCE_UNICODE
)
81 int len
= (strlenW( (const WCHAR
*)mre
->Text
) + 1) * sizeof(WCHAR
);
82 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
))) return NULL
;
83 memcpy( buffer
, mre
->Text
, len
);
87 int len
= MultiByteToWideChar( CP_ACP
, 0, (const char *)mre
->Text
, -1, NULL
, 0 );
88 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) ))) return NULL
;
89 MultiByteToWideChar( CP_ACP
, 0, (const char*)mre
->Text
, -1, buffer
, len
);
91 TRACE("returning %s\n", wine_dbgstr_w(buffer
));
95 /**********************************************************************
98 static ULONG_PTR
get_arg( int nr
, DWORD flags
, struct format_args
*args
)
100 if (nr
== -1) nr
= args
->last
+ 1;
103 if (!args
->args
) args
->args
= HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR
) );
104 while (nr
> args
->last
)
105 args
->args
[args
->last
++] = va_arg( *args
->list
, ULONG_PTR
);
107 if (nr
> args
->last
) args
->last
= nr
;
108 return args
->args
[nr
- 1];
111 /**********************************************************************
112 * format_insert (internal)
114 static LPCWSTR
format_insert( BOOL unicode_caller
, int insert
, LPCWSTR format
,
115 DWORD flags
, struct format_args
*args
,
118 static const WCHAR fmt_lu
[] = {'%','l','u',0};
119 WCHAR
*wstring
= NULL
, *p
, fmt
[256];
123 if (*format
!= '!') /* simple string */
125 arg
= get_arg( insert
, flags
, args
);
128 WCHAR
*str
= (WCHAR
*)arg
;
129 *result
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) );
130 strcpyW( *result
, str
);
134 char *str
= (char *)arg
;
135 DWORD length
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
136 *result
= HeapAlloc( GetProcessHeap(), 0, length
* sizeof(WCHAR
) );
137 MultiByteToWideChar( CP_ACP
, 0, str
, -1, *result
, length
);
146 while (*format
== '0' ||
155 p
+= sprintfW( p
, fmt_lu
, get_arg( insert
, flags
, args
));
159 else *p
++ = *format
++;
161 while (isdigitW(*format
)) *p
++ = *format
++;
168 p
+= sprintfW( p
, fmt_lu
, get_arg( insert
, flags
, args
));
173 while (isdigitW(*format
)) *p
++ = *format
++;
176 /* replicate MS bug: drop an argument when using va_list with width/precision */
177 if (insert
== -1 && args
->list
) args
->last
--;
178 arg
= get_arg( insert
, flags
, args
);
180 /* check for ascii string format */
181 if ((format
[0] == 'h' && format
[1] == 's') ||
182 (format
[0] == 'h' && format
[1] == 'S') ||
183 (unicode_caller
&& format
[0] == 'S') ||
184 (!unicode_caller
&& format
[0] == 's'))
186 DWORD len
= MultiByteToWideChar( CP_ACP
, 0, (char *)arg
, -1, NULL
, 0 );
187 wstring
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
188 MultiByteToWideChar( CP_ACP
, 0, (char *)arg
, -1, wstring
, len
);
189 arg
= (ULONG_PTR
)wstring
;
192 /* check for ascii character format */
193 else if ((format
[0] == 'h' && format
[1] == 'c') ||
194 (format
[0] == 'h' && format
[1] == 'C') ||
195 (unicode_caller
&& format
[0] == 'C') ||
196 (!unicode_caller
&& format
[0] == 'c'))
199 wstring
= HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR
) );
200 MultiByteToWideChar( CP_ACP
, 0, &ch
, 1, wstring
, 1 );
202 arg
= (ULONG_PTR
)wstring
;
205 /* check for wide string format */
206 else if ((format
[0] == 'l' && format
[1] == 's') ||
207 (format
[0] == 'l' && format
[1] == 'S') ||
208 (format
[0] == 'w' && format
[1] == 's') ||
209 (!unicode_caller
&& format
[0] == 'S'))
213 /* check for wide character format */
214 else if ((format
[0] == 'l' && format
[1] == 'c') ||
215 (format
[0] == 'l' && format
[1] == 'C') ||
216 (format
[0] == 'w' && format
[1] == 'c') ||
217 (!unicode_caller
&& format
[0] == 'C'))
221 /* FIXME: handle I64 etc. */
222 else while (*format
&& *format
!= '!') *p
++ = *format
++;
228 WCHAR
*ret
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) );
229 int needed
= snprintfW( ret
, size
, fmt
, arg
);
230 if (needed
== -1 || needed
>= size
)
232 HeapFree( GetProcessHeap(), 0, ret
);
233 size
= max( needed
+ 1, size
* 2 );
242 while (*format
&& *format
!= '!') format
++;
243 if (*format
== '!') format
++;
245 HeapFree( GetProcessHeap(), 0, wstring
);
249 /**********************************************************************
250 * format_message (internal)
252 static LPWSTR
format_message( BOOL unicode_caller
, DWORD dwFlags
, LPCWSTR fmtstr
,
253 struct format_args
*format_args
)
258 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
262 target
= t
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, 100 * sizeof(WCHAR
) );
265 #define ADD_TO_T(c) do {\
267 if ((DWORD)(t-target) == talloced) {\
268 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
269 t = target+talloced;\
282 case '1':case '2':case '3':case '4':case '5':
283 case '6':case '7':case '8':case '9':
284 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
)
286 else if (((dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
) && !format_args
->args
) ||
287 (!(dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
) && !format_args
->list
))
289 SetLastError(ERROR_INVALID_PARAMETER
);
290 HeapFree(GetProcessHeap(), 0, target
);
295 case '0':case '1':case '2':case '3':
296 case '4':case '5':case '6':case '7':
299 insertnr
= insertnr
*10 + *f
-'0';
306 f
= format_insert( unicode_caller
, insertnr
, f
, dwFlags
, format_args
, &str
);
307 for (x
= str
; *x
; x
++) ADD_TO_T(*x
);
308 HeapFree( GetProcessHeap(), 0, str
);
328 SetLastError(ERROR_INVALID_PARAMETER
);
329 HeapFree(GetProcessHeap(), 0, target
);
333 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
)
373 /***********************************************************************
374 * FormatMessageA (KERNEL32.@)
375 * FIXME: missing wrap,
377 DWORD WINAPI
FormatMessageA(
386 struct format_args format_args
;
391 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
392 HMODULE kernel32_handle
= GetModuleHandleW(kernel32W
);
394 TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
395 dwFlags
,lpSource
,dwMessageId
,dwLanguageId
,lpBuffer
,nSize
,args
);
397 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
401 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
405 *(LPSTR
*)lpBuffer
= NULL
;
408 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
410 format_args
.args
= (ULONG_PTR
*)args
;
411 format_args
.list
= NULL
;
412 format_args
.last
= 0;
416 format_args
.args
= NULL
;
417 format_args
.list
= args
;
418 format_args
.last
= 0;
421 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
422 FIXME("line wrapping (%u) not supported.\n", width
);
424 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
426 DWORD length
= MultiByteToWideChar(CP_ACP
, 0, lpSource
, -1, NULL
, 0);
427 from
= HeapAlloc( GetProcessHeap(), 0, length
* sizeof(WCHAR
) );
428 MultiByteToWideChar(CP_ACP
, 0, lpSource
, -1, from
, length
);
430 else if (dwFlags
& (FORMAT_MESSAGE_FROM_HMODULE
| FORMAT_MESSAGE_FROM_SYSTEM
))
432 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
433 from
= load_message( (HMODULE
)lpSource
, dwMessageId
, dwLanguageId
);
434 if (!from
&& (dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
))
435 from
= load_message( kernel32_handle
, dwMessageId
, dwLanguageId
);
440 SetLastError(ERROR_INVALID_PARAMETER
);
444 target
= format_message( FALSE
, dwFlags
, from
, &format_args
);
448 TRACE("-- %s\n", debugstr_w(target
));
450 /* Only try writing to an output buffer if there are processed characters
451 * in the temporary output buffer. */
454 destlength
= WideCharToMultiByte(CP_ACP
, 0, target
, -1, NULL
, 0, NULL
, NULL
);
455 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
457 LPSTR buf
= LocalAlloc(LMEM_ZEROINIT
, max(nSize
, destlength
));
458 WideCharToMultiByte(CP_ACP
, 0, target
, -1, buf
, destlength
, NULL
, NULL
);
459 *((LPSTR
*)lpBuffer
) = buf
;
463 if (nSize
< destlength
)
465 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
468 WideCharToMultiByte(CP_ACP
, 0, target
, -1, lpBuffer
, destlength
, NULL
, NULL
);
470 ret
= destlength
- 1; /* null terminator */
474 HeapFree(GetProcessHeap(),0,target
);
475 HeapFree(GetProcessHeap(),0,from
);
476 if (!(dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)) HeapFree( GetProcessHeap(), 0, format_args
.args
);
477 TRACE("-- returning %u\n", ret
);
481 /***********************************************************************
482 * FormatMessageW (KERNEL32.@)
484 DWORD WINAPI
FormatMessageW(
493 struct format_args format_args
;
498 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
499 HMODULE kernel32_handle
= GetModuleHandleW(kernel32W
);
501 TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
502 dwFlags
,lpSource
,dwMessageId
,dwLanguageId
,lpBuffer
,nSize
,args
);
506 SetLastError(ERROR_INVALID_PARAMETER
);
510 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
511 *(LPWSTR
*)lpBuffer
= NULL
;
513 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
515 format_args
.args
= (ULONG_PTR
*)args
;
516 format_args
.list
= NULL
;
517 format_args
.last
= 0;
521 format_args
.args
= NULL
;
522 format_args
.list
= args
;
523 format_args
.last
= 0;
526 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
527 FIXME("line wrapping not supported.\n");
529 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
) {
530 from
= HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource
) + 1) *
532 strcpyW( from
, lpSource
);
534 else if (dwFlags
& (FORMAT_MESSAGE_FROM_HMODULE
| FORMAT_MESSAGE_FROM_SYSTEM
))
536 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
537 from
= load_message( (HMODULE
)lpSource
, dwMessageId
, dwLanguageId
);
538 if (!from
&& (dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
))
539 from
= load_message( kernel32_handle
, dwMessageId
, dwLanguageId
);
544 SetLastError(ERROR_INVALID_PARAMETER
);
548 target
= format_message( TRUE
, dwFlags
, from
, &format_args
);
552 talloced
= strlenW(target
)+1;
553 TRACE("-- %s\n",debugstr_w(target
));
555 /* Only allocate a buffer if there are processed characters in the
556 * temporary output buffer. If a caller supplies the buffer, then
557 * a null terminator will be written to it. */
558 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
)
562 /* nSize is the MINIMUM size */
563 *((LPVOID
*)lpBuffer
) = LocalAlloc(LMEM_ZEROINIT
, max(nSize
, talloced
)*sizeof(WCHAR
));
564 strcpyW(*(LPWSTR
*)lpBuffer
, target
);
569 if (nSize
< talloced
)
571 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
574 strcpyW(lpBuffer
, target
);
577 ret
= talloced
- 1; /* null terminator */
579 HeapFree(GetProcessHeap(),0,target
);
580 HeapFree(GetProcessHeap(),0,from
);
581 if (!(dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)) HeapFree( GetProcessHeap(), 0, format_args
.args
);
582 TRACE("-- returning %u\n", ret
);