Fixed some issues found by winapi_check.
[wine] / dlls / kernel / format_msg.c
1 /*
2  * FormatMessage implementation
3  *
4  * Copyright 1996 Marcus Meissner
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "wine/unicode.h"
32
33 #include "heap.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(resource);
37
38
39 /* Messages...used by FormatMessage32* (KERNEL32.something)
40  *
41  * They can be specified either directly or using a message ID and
42  * loading them from the resource.
43  *
44  * The resourcedata has following format:
45  * start:
46  * 0: DWORD nrofentries
47  * nrofentries * subentry:
48  *      0: DWORD firstentry
49  *      4: DWORD lastentry
50  *      8: DWORD offset from start to the stringentries
51  *
52  * (lastentry-firstentry) * stringentry:
53  * 0: WORD len (0 marks end)    [ includes the 4 byte header length ]
54  * 2: WORD flags
55  * 4: CHAR[len-4]
56  *      (stringentry i of a subentry refers to the ID 'firstentry+i')
57  *
58  * Yes, ANSI strings in win32 resources. Go figure.
59  */
60
61 /**********************************************************************
62  *      load_messageA           (internal)
63  */
64 static INT load_messageA( HMODULE instance, UINT id, WORD lang,
65                           LPSTR buffer, INT buflen )
66 {
67     HGLOBAL     hmem;
68     HRSRC       hrsrc;
69     PMESSAGE_RESOURCE_DATA      mrd;
70     PMESSAGE_RESOURCE_BLOCK     mrb;
71     PMESSAGE_RESOURCE_ENTRY     mre;
72     int         i,slen;
73
74     TRACE("instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
75
76     /*FIXME: I am not sure about the '1' ... But I've only seen those entries*/
77     hrsrc = FindResourceExW(instance,RT_MESSAGETABLEW,(LPWSTR)1,lang);
78     if (!hrsrc) return 0;
79     hmem = LoadResource( instance, hrsrc );
80     if (!hmem) return 0;
81
82     mrd = (PMESSAGE_RESOURCE_DATA)LockResource(hmem);
83     mre = NULL;
84     mrb = &(mrd->Blocks[0]);
85     for (i=mrd->NumberOfBlocks;i--;) {
86         if ((id>=mrb->LowId) && (id<=mrb->HighId)) {
87             mre = (PMESSAGE_RESOURCE_ENTRY)(((char*)mrd)+mrb->OffsetToEntries);
88             id  -= mrb->LowId;
89             break;
90         }
91         mrb++;
92     }
93     if (!mre)
94         return 0;
95     for (i=id;i--;) {
96         if (!mre->Length)
97                 return 0;
98         mre = (PMESSAGE_RESOURCE_ENTRY)(((char*)mre)+mre->Length);
99     }
100     slen=mre->Length;
101     TRACE("     - strlen=%d\n",slen);
102     i = min(buflen - 1, slen);
103     if (buffer == NULL)
104         return slen;
105     if (i>0) {
106         if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
107             WideCharToMultiByte( CP_ACP, 0, (LPWSTR)mre->Text, -1, buffer, i, NULL, NULL );
108         else
109             lstrcpynA(buffer, (LPSTR)mre->Text, i);
110         buffer[i]=0;
111     } else {
112         if (buflen>1) {
113             buffer[0]=0;
114             return 0;
115         }
116     }
117     if (buffer)
118             TRACE("'%s' copied !\n", buffer);
119     return i;
120 }
121
122 #if 0  /* FIXME */
123 /**********************************************************************
124  *      load_messageW   (internal)
125  */
126 static INT load_messageW( HMODULE instance, UINT id, WORD lang,
127                           LPWSTR buffer, INT buflen )
128 {
129     INT retval;
130     LPSTR buffer2 = NULL;
131     if (buffer && buflen)
132         buffer2 = HeapAlloc( GetProcessHeap(), 0, buflen );
133     retval = load_messageA(instance,id,lang,buffer2,buflen);
134     if (buffer)
135     {
136         if (retval) {
137             lstrcpynAtoW( buffer, buffer2, buflen );
138             retval = strlenW( buffer );
139         }
140         HeapFree( GetProcessHeap(), 0, buffer2 );
141     }
142     return retval;
143 }
144 #endif
145
146
147 /***********************************************************************
148  *           FormatMessageA   (KERNEL32.@)
149  * FIXME: missing wrap,
150  */
151 DWORD WINAPI FormatMessageA(
152         DWORD   dwFlags,
153         LPCVOID lpSource,
154         DWORD   dwMessageId,
155         DWORD   dwLanguageId,
156         LPSTR   lpBuffer,
157         DWORD   nSize,
158         va_list* _args )
159 {
160     LPDWORD args=(LPDWORD)_args;
161 #if defined(__i386__) || defined(__sparc__)
162 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
163     LPSTR       target,t;
164     DWORD       talloced;
165     LPSTR       from,f;
166     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
167     BOOL    eos = FALSE;
168     INT bufsize;
169     HMODULE     hmodule = (HMODULE)lpSource;
170     CHAR        ch;
171
172     TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
173           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
174     if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
175         && (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)) return 0;
176     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
177         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
178            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
179
180     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
181         FIXME("line wrapping (%lu) not supported.\n", width);
182     from = NULL;
183     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
184     {
185         from = HeapAlloc( GetProcessHeap(), 0, strlen((LPSTR)lpSource)+1 );
186         strcpy( from, (LPSTR)lpSource );
187     }
188     else {
189         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
190             hmodule = GetModuleHandleA("kernel32");
191         bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
192         if (!bufsize) {
193             if (dwLanguageId) {
194                 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
195                 return 0;
196             }
197             bufsize=load_messageA(hmodule,dwMessageId,
198                                   MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
199             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
200                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
201             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
202                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
203             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
204                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
205             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
206                                                 MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
207             if (!bufsize) {
208                 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
209                 return 0;
210             }
211         }
212         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
213         load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
214     }
215     target      = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
216     t   = target;
217     talloced= 100;
218
219 #define ADD_TO_T(c) do { \
220         *t++=c;\
221         if (t-target == talloced) {\
222             target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
223             t = target+talloced;\
224             talloced*=2;\
225       }\
226 } while (0)
227
228     if (from) {
229         f=from;
230         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
231             while (*f && !eos)
232                 ADD_TO_T(*f++);
233         }
234         else {
235             while (*f && !eos) {
236                 if (*f=='%') {
237                     int insertnr;
238                     char *fmtstr,*x,*lastf;
239                     DWORD *argliststart;
240
241                     fmtstr = NULL;
242                     lastf = f;
243                     f++;
244                     if (!*f) {
245                         ADD_TO_T('%');
246                         continue;
247                     }
248                     switch (*f) {
249                     case '1':case '2':case '3':case '4':case '5':
250                     case '6':case '7':case '8':case '9':
251                         insertnr=*f-'0';
252                         switch (f[1]) {
253                         case '0':case '1':case '2':case '3':
254                         case '4':case '5':case '6':case '7':
255                         case '8':case '9':
256                             f++;
257                             insertnr=insertnr*10+*f-'0';
258                             f++;
259                             break;
260                         default:
261                             f++;
262                             break;
263                         }
264                         if (*f=='!') {
265                             f++;
266                             if (NULL!=(x=strchr(f,'!'))) {
267                                 *x='\0';
268                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
269                                 sprintf(fmtstr,"%%%s",f);
270                                 f=x+1;
271                             } else {
272                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
273                                 sprintf(fmtstr,"%%%s",f);
274                                 f+=strlen(f); /*at \0*/
275                             }
276                         } else {
277                             if(!args) break;
278                             fmtstr = HeapAlloc(GetProcessHeap(),0,3);
279                             strcpy( fmtstr, "%s" );
280                         }
281                         if (args) {
282                             int sz;
283                             LPSTR b;
284
285                             if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
286                                 argliststart=args+insertnr-1;
287                             else
288                                 argliststart=(*(DWORD**)args)+insertnr-1;
289
290                                 /* FIXME: precision and width components are not handled correctly */
291                             if ( (strcmp(fmtstr, "%ls") == 0) || (strcmp(fmtstr,"%S") == 0) ) {
292                                 sz = WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, NULL, 0, NULL, NULL);
293                                 b = HeapAlloc(GetProcessHeap(), 0, sz);
294                                 WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, b, sz, NULL, NULL);
295                             } else {
296                                 b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000);
297                                 /* CMF - This makes a BIG assumption about va_list */
298                                 TRACE("A BIG assumption\n");
299                                 vsnprintf(b, sz, fmtstr, (va_list) argliststart);
300                             }
301                             for (x=b; *x; x++) ADD_TO_T(*x);
302
303                             HeapFree(GetProcessHeap(),0,b);
304                         } else {
305                                 /* NULL args - copy formatstr
306                                  * (probably wrong)
307                                  */
308                             while ((lastf<f)&&(*lastf)) {
309                                 ADD_TO_T(*lastf++);
310                             }
311                         }
312                         HeapFree(GetProcessHeap(),0,fmtstr);
313                         break;
314                     case 'n':
315                         ADD_TO_T('\r');
316                         ADD_TO_T('\n');
317                         f++;
318                         break;
319                     case '0':
320                         eos = TRUE;
321                         f++;
322                         break;
323                     default:
324                         ADD_TO_T(*f++);
325                         break;
326                     }
327                 } else {
328                     ch = *f;
329                     f++;
330                     if (ch == '\r') {
331                         if (*f == '\n')
332                             f++;
333                         if(width)
334                             ADD_TO_T(' ');
335                         else
336                         {
337                             ADD_TO_T('\r');
338                             ADD_TO_T('\n');
339                         }
340                     } else {
341                         if (ch == '\n')
342                         {
343                             if(width)
344                                 ADD_TO_T(' ');
345                             else
346                             {
347                                 ADD_TO_T('\r');
348                                 ADD_TO_T('\n');
349                             }
350                         }
351                         else
352                             ADD_TO_T(ch);
353                     }
354                 }
355             }
356         }
357         *t='\0';
358     }
359     talloced = strlen(target)+1;
360     if (nSize && talloced<nSize) {
361         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
362     }
363     TRACE("-- %s\n",debugstr_a(target));
364     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
365         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,max(nSize, talloced));
366         memcpy(*(LPSTR*)lpBuffer,target,talloced);
367     } else {
368         lstrcpynA(lpBuffer,target,nSize);
369     }
370     HeapFree(GetProcessHeap(),0,target);
371     if (from) HeapFree(GetProcessHeap(),0,from);
372     TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?  strlen(*(LPSTR*)lpBuffer):strlen(lpBuffer));
373     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
374         strlen(*(LPSTR*)lpBuffer):
375             strlen(lpBuffer);
376 #else
377     return 0;
378 #endif /* __i386__ */
379 }
380 #undef ADD_TO_T
381
382
383 /***********************************************************************
384  *           FormatMessageW   (KERNEL32.@)
385  */
386 DWORD WINAPI FormatMessageW(
387         DWORD   dwFlags,
388         LPCVOID lpSource,
389         DWORD   dwMessageId,
390         DWORD   dwLanguageId,
391         LPWSTR  lpBuffer,
392         DWORD   nSize,
393         va_list* _args )
394 {
395     LPDWORD args=(LPDWORD)_args;
396 #if defined(__i386__) || defined(__sparc__)
397 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
398     LPSTR target,t;
399     DWORD talloced;
400     LPSTR from,f;
401     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
402     BOOL eos = FALSE;
403     INT bufsize;
404     HMODULE hmodule = (HMODULE)lpSource;
405     CHAR ch;
406
407     TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
408           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
409     if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
410         && (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)) return 0;
411     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
412         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
413            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
414
415     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
416         FIXME("line wrapping not supported.\n");
417     from = NULL;
418     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
419         from = HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lpSource);
420     }
421     else {
422         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
423             hmodule = GetModuleHandleA("kernel32");
424         bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
425         if (!bufsize) {
426             if (dwLanguageId) {
427                 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
428                 return 0;
429             }
430             bufsize=load_messageA(hmodule,dwMessageId,
431                                   MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
432             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
433                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
434             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
435                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
436             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
437                                                 MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
438             if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
439                                                 MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
440             if (!bufsize) {
441                 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
442                 return 0;
443             }
444         }
445         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
446         load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
447     }
448     target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 );
449     t = target;
450     talloced= 100;
451
452 #define ADD_TO_T(c)  do {\
453     *t++=c;\
454     if (t-target == talloced) {\
455         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
456         t = target+talloced;\
457         talloced*=2;\
458     } \
459 } while (0)
460
461     if (from) {
462         f=from;
463         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
464             while (*f && !eos)
465                 ADD_TO_T(*f++);
466         }
467         else {
468             while (*f && !eos) {
469                 if (*f=='%') {
470                     int insertnr;
471                     char *fmtstr,*sprintfbuf,*x;
472                     DWORD *argliststart;
473
474                     fmtstr = NULL;
475                     f++;
476                     if (!*f) {
477                         ADD_TO_T('%');
478                         continue;
479                     }
480
481                     switch (*f) {
482                     case '1':case '2':case '3':case '4':case '5':
483                     case '6':case '7':case '8':case '9':
484                         insertnr=*f-'0';
485                         switch (f[1]) {
486                         case '0':case '1':case '2':case '3':
487                         case '4':case '5':case '6':case '7':
488                         case '8':case '9':
489                             f++;
490                             insertnr=insertnr*10+*f-'0';
491                             f++;
492                             break;
493                         default:
494                             f++;
495                             break;
496                         }
497                         if (*f=='!') {
498                             f++;
499                             if (NULL!=(x=strchr(f,'!'))) {
500                                 *x='\0';
501                                 fmtstr=HeapAlloc( GetProcessHeap(), 0, strlen(f)+2);
502                                 sprintf(fmtstr,"%%%s",f);
503                                 f=x+1;
504                             } else {
505                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f));
506                                 sprintf(fmtstr,"%%%s",f);
507                                 f+=strlen(f); /*at \0*/
508                             }
509                         } else {
510                             if(!args) break;
511                             fmtstr = HeapAlloc( GetProcessHeap(),0,3);
512                             strcpy( fmtstr, "%s" );
513                         }
514                         if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
515                             argliststart=args+insertnr-1;
516                         else
517                             argliststart=(*(DWORD**)args)+insertnr-1;
518
519                         if (fmtstr[strlen(fmtstr)-1]=='s' && argliststart[0]) {
520                             DWORD xarr[3];
521
522                             xarr[0]=(DWORD)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)(*(argliststart+0)));
523                             /* possible invalid pointers */
524                             xarr[1]=*(argliststart+1);
525                             xarr[2]=*(argliststart+2);
526                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,strlenW((LPWSTR)argliststart[0])*2+1);
527
528                             /* CMF - This makes a BIG assumption about va_list */
529                             vsprintf(sprintfbuf, fmtstr, (va_list) xarr);
530                             HeapFree(GetProcessHeap(), 0, (LPVOID) xarr[0]);
531                         } else {
532                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
533
534                             /* CMF - This makes a BIG assumption about va_list */
535                             vsprintf(sprintfbuf, fmtstr, (va_list) argliststart);
536                         }
537                         x=sprintfbuf;
538                         while (*x) {
539                             ADD_TO_T(*x++);
540                         }
541                         HeapFree(GetProcessHeap(),0,sprintfbuf);
542                         HeapFree(GetProcessHeap(),0,fmtstr);
543                         break;
544                     case 'n':
545                         ADD_TO_T('\r');
546                         ADD_TO_T('\n');
547                         f++;
548                         break;
549                     case '0':
550                         eos = TRUE;
551                         f++;
552                         break;
553                     default:
554                         ADD_TO_T(*f++);
555                         break;
556                     }
557                 } else {
558                     ch = *f;
559                     f++;
560                     if (ch == '\r') {
561                         if (*f == '\n')
562                             f++;
563                         if(width)
564                             ADD_TO_T(' ');
565                         else
566                         {
567                             ADD_TO_T('\r');
568                             ADD_TO_T('\n');
569                         }
570                     } else {
571                         if (ch == '\n')
572                         {
573                             if(width)
574                                 ADD_TO_T(' ');
575                             else
576                             {
577                                 ADD_TO_T('\r');
578                                 ADD_TO_T('\n');
579                             }
580                         }
581                         else
582                             ADD_TO_T(ch);
583                     }
584                 }
585             }
586         }
587         *t='\0';
588     }
589     talloced = strlen(target)+1;
590     if (nSize && talloced<nSize)
591         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
592     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
593         /* nSize is the MINIMUM size */
594         DWORD len = MultiByteToWideChar( CP_ACP, 0, target, -1, NULL, 0 );
595         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,len*sizeof(WCHAR));
596         MultiByteToWideChar( CP_ACP, 0, target, -1, *(LPWSTR*)lpBuffer, len );
597     }
598     else
599     {
600         if (nSize > 0 && !MultiByteToWideChar( CP_ACP, 0, target, -1, lpBuffer, nSize ))
601             lpBuffer[nSize-1] = 0;
602     }
603     HeapFree(GetProcessHeap(),0,target);
604     if (from) HeapFree(GetProcessHeap(),0,from);
605     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
606         strlenW(*(LPWSTR*)lpBuffer):
607             strlenW(lpBuffer);
608 #else
609     return 0;
610 #endif /* __i386__ */
611 }
612 #undef ADD_TO_T