1 /* $Id: errormsg.c,v 1.8 2003/08/11 05:58:02 jimtabor Exp $
3 * reactos/lib/kernel32/misc/errormsg.c
7 * FormatMessage implementation
9 * Copyright 1996 Marcus Meissner
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <ddk/ntddk.h>
29 #include <kernel32/kernel32.h>
30 #include <kernel32/error.h>
37 //#include <ntdll/ntdll.h>
39 //#define DPRINTF DPRINT
41 //#define SetLastError(x)
46 //#define MAKEINTRESOURCE(i) (LPTSTR) ((DWORD) ((WORD) (i)))
47 //#define MAKEINTRESOURCEA(i) (LPTSTR) ((DWORD) ((WORD) (i)))
48 //#define MAKEINTRESOURCEW(i) (LPTSTR) ((DWORD) ((WORD) (i)))
50 //#define MAKEINTRESOURCEA(i) (LPSTR)((ULONG_PTR)((WORD)(i)))
51 //#define MAKEINTRESOURCEW(i) (LPWSTR)((ULONG_PTR)((WORD)(i)))
52 //#define MAKEINTRESOURCE WINELIB_NAME_AW(MAKEINTRESOURCE)
56 int HEAP_strdupWtoA(HANDLE hHeap
, int flags
, LPWSTR lpSource
)
69 //#include "winbase.h"
70 //#include "winerror.h"
71 //#include "winuser.h"
73 //#include "wine/unicode.h"
75 //#include "wine/debug.h"
77 //WINE_DEFAULT_DEBUG_CHANNEL(resource);
79 typedef struct tagMESSAGE_RESOURCE_ENTRY
{
83 } MESSAGE_RESOURCE_ENTRY
,*PMESSAGE_RESOURCE_ENTRY
;
84 #define MESSAGE_RESOURCE_UNICODE 0x0001
86 typedef struct tagMESSAGE_RESOURCE_BLOCK
{
89 DWORD OffsetToEntries
;
90 } MESSAGE_RESOURCE_BLOCK
,*PMESSAGE_RESOURCE_BLOCK
;
92 typedef struct tagMESSAGE_RESOURCE_DATA
{
94 MESSAGE_RESOURCE_BLOCK Blocks
[ 1 ];
95 } MESSAGE_RESOURCE_DATA
,*PMESSAGE_RESOURCE_DATA
;
98 //#define RT_RCDATAA MAKEINTRESOURCEA(10)
99 //#define RT_RCDATAW MAKEINTRESOURCEW(10)
100 ////#define RT_RCDATA WINELIB_NAME_AW(RT_RCDATA)
101 //#define RT_MESSAGETABLEA MAKEINTRESOURCEA(11)
102 //#define RT_MESSAGETABLEW MAKEINTRESOURCEW(11)
103 ////#define RT_MESSAGETABLE WINELIB_NAME_AW(RT_MESSAGETABLE)
105 /* Messages...used by FormatMessage32* (KERNEL32.something)
107 * They can be specified either directly or using a message ID and
108 * loading them from the resource.
110 * The resourcedata has following format:
112 * 0: DWORD nrofentries
113 * nrofentries * subentry:
114 * 0: DWORD firstentry
116 * 8: DWORD offset from start to the stringentries
118 * (lastentry-firstentry) * stringentry:
119 * 0: WORD len (0 marks end) [ includes the 4 byte header length ]
122 * (stringentry i of a subentry refers to the ID 'firstentry+i')
124 * Yes, ANSI strings in win32 resources. Go figure.
127 /**********************************************************************
128 * load_messageA (internal)
130 static INT
load_messageA( HMODULE instance
, UINT id
, WORD lang
,
131 LPSTR buffer
, INT buflen
)
135 PMESSAGE_RESOURCE_DATA mrd
;
136 PMESSAGE_RESOURCE_BLOCK mrb
;
137 PMESSAGE_RESOURCE_ENTRY mre
;
140 //TRACE("instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
142 /*FIXME: I am not sure about the '1' ... But I've only seen those entries*/
143 hrsrc
= FindResourceExW(instance
,RT_MESSAGETABLEW
,(LPWSTR
)1,lang
);
144 if (!hrsrc
) return 0;
145 hmem
= LoadResource( instance
, hrsrc
);
148 mrd
= (PMESSAGE_RESOURCE_DATA
)LockResource(hmem
);
150 mrb
= &(mrd
->Blocks
[0]);
151 for (i
=mrd
->NumberOfBlocks
;i
--;) {
152 if ((id
>=mrb
->LowId
) && (id
<=mrb
->HighId
)) {
153 mre
= (PMESSAGE_RESOURCE_ENTRY
)(((char*)mrd
)+mrb
->OffsetToEntries
);
164 mre
= (PMESSAGE_RESOURCE_ENTRY
)(((char*)mre
)+mre
->Length
);
167 //TRACE(" - strlen=%d\n",slen);
168 i
= min(buflen
- 1, slen
);
172 if (mre
->Flags
& MESSAGE_RESOURCE_UNICODE
)
173 WideCharToMultiByte( CP_ACP
, 0, (LPWSTR
)mre
->Text
, -1, buffer
, i
, NULL
, NULL
);
175 lstrcpynA(buffer
, (LPSTR
)mre
->Text
, i
);
184 //TRACE("'%s' copied !\n", buffer);
185 TRACE("'%s'\n", buffer
);
191 /**********************************************************************
192 * load_messageW (internal)
194 static INT
load_messageW( HMODULE instance
, UINT id
, WORD lang
,
195 LPWSTR buffer
, INT buflen
)
198 LPSTR buffer2
= NULL
;
199 if (buffer
&& buflen
)
200 buffer2
= HeapAlloc( GetProcessHeap(), 0, buflen
);
201 retval
= load_messageA(instance
,id
,lang
,buffer2
,buflen
);
205 lstrcpynAtoW( buffer
, buffer2
, buflen
);
206 retval
= lstrlenW( buffer
);
208 HeapFree( GetProcessHeap(), 0, buffer2
);
215 /***********************************************************************
216 * FormatMessageA (KERNEL32.@)
217 * FIXME: missing wrap,
219 DWORD WINAPI
FormatMessageA(
228 LPDWORD args
=(LPDWORD
)_args
;
229 #if defined(__i386__) || defined(__sparc__)
230 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
234 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
237 HMODULE hmodule
= (HMODULE
)lpSource
;
240 //TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n", dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
241 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
242 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
243 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
245 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
246 FIXME("line wrapping (%lu) not supported.\n", width
);
248 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
250 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA((LPSTR
)lpSource
)+1 );
251 strcpy( from
, (LPSTR
)lpSource
);
256 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
258 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
259 if ((!bufsize
) && (!dwLanguageId
)) {
260 bufsize
=load_messageA(hmodule
,dwMessageId
,
261 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
262 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
263 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
264 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
265 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
266 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
267 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
268 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
269 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
272 if ((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
) && (!bufsize
))
274 hmodule
= GetModuleHandleA("kernel32");
275 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
276 if ((!bufsize
) && (!dwLanguageId
)) {
277 bufsize
=load_messageA(hmodule
,dwMessageId
,
278 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
279 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
280 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
281 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
282 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
283 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
284 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
285 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
286 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
291 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND
);
295 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize
+ 1 );
296 load_messageA(hmodule
,dwMessageId
,dwLanguageId
,from
,bufsize
+1);
298 target
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 100);
302 #define ADD_TO_T(c) do { \
304 if (t-target == talloced) {\
305 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
306 t = target+talloced;\
313 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
321 char *fmtstr
,*x
,*lastf
;
332 case '1':case '2':case '3':case '4':case '5':
333 case '6':case '7':case '8':case '9':
336 case '0':case '1':case '2':case '3':
337 case '4':case '5':case '6':case '7':
340 insertnr
=insertnr
*10+*f
-'0';
349 if (NULL
!=(x
=strchr(f
,'!'))) {
351 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
)+2);
352 sprintf(fmtstr
,"%%%s",f
);
355 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
)+2);
356 sprintf(fmtstr
,"%%%s",f
);
357 f
+=lstrlenA(f
); /*at \0*/
361 fmtstr
= RtlAllocateHeap(RtlGetProcessHeap(),0,3);
362 strcpy( fmtstr
, "%s" );
368 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
369 argliststart
=args
+insertnr
-1;
371 argliststart
=(*(DWORD
**)args
)+insertnr
-1;
373 /* FIXME: precision and width components are not handled correctly */
374 if ( (strcmp(fmtstr
, "%ls") == 0) || (strcmp(fmtstr
,"%S") == 0) ) {
375 sz
= WideCharToMultiByte( CP_ACP
, 0, *(WCHAR
**)argliststart
, -1, NULL
, 0, NULL
, NULL
);
376 b
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sz
);
377 WideCharToMultiByte( CP_ACP
, 0, *(WCHAR
**)argliststart
, -1, b
, sz
, NULL
, NULL
);
379 b
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sz
= 1000);
380 /* CMF - This makes a BIG assumption about va_list */
381 TRACE("A BIG assumption\n");
382 //vsnprintf(b, sz, fmtstr, (va_list) argliststart);
384 for (x
=b
; *x
; x
++) ADD_TO_T(*x
);
386 RtlFreeHeap(RtlGetProcessHeap(),0,b
);
388 /* NULL args - copy formatstr
391 while ((lastf
<f
)&&(*lastf
)) {
395 RtlFreeHeap(GetProcessHeap(),0,fmtstr
);
442 talloced
= lstrlenA(target
)+1;
443 if (nSize
&& talloced
<nSize
) {
444 target
= (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
);
446 //TRACE("-- %s\n",debugstr_a(target));
447 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
448 *((LPVOID
*)lpBuffer
) = (LPVOID
)LocalAlloc(GMEM_ZEROINIT
,max(nSize
, talloced
));
449 memcpy(*(LPSTR
*)lpBuffer
,target
,talloced
);
451 lstrcpynA(lpBuffer
,target
,nSize
);
453 RtlFreeHeap(RtlGetProcessHeap(),0,target
);
454 if (from
) RtlFreeHeap(RtlGetProcessHeap(),0,from
);
455 //TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? lstrlenA(*(LPSTR*)lpBuffer):lstrlenA(lpBuffer));
456 return (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
457 lstrlenA(*(LPSTR
*)lpBuffer
):
461 #endif /* __i386__ */
466 /***********************************************************************
467 * FormatMessageW (KERNEL32.@)
469 DWORD WINAPI
FormatMessageW(
478 LPDWORD args
=(LPDWORD
)_args
;
479 #if defined(__i386__) || defined(__sparc__)
480 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
484 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
487 HMODULE hmodule
= (HMODULE
)lpSource
;
490 //TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n", dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
491 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
492 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
493 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
495 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
) {
496 FIXME("line wrapping not supported.\n");
499 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
) {
500 from
= (LPSTR
)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR
)lpSource
);
505 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
507 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
508 if ((!bufsize
) && (!dwLanguageId
)) {
509 bufsize
=load_messageA(hmodule
,dwMessageId
,
510 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
511 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
512 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
513 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
514 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
515 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
516 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
517 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
518 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
521 if ((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
) && (!bufsize
))
523 hmodule
= GetModuleHandleA("kernel32");
524 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
525 if ((!bufsize
) && (!dwLanguageId
)) {
526 bufsize
=load_messageA(hmodule
,dwMessageId
,
527 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
528 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
529 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
530 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
531 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
532 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
533 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
534 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
535 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
540 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND
);
544 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize
+ 1 );
545 load_messageA(hmodule
,dwMessageId
,dwLanguageId
,from
,bufsize
+1);
547 target
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 100 );
551 #define ADD_TO_T(c) do {\
553 if (t-target == talloced) {\
554 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
555 t = target+talloced;\
562 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
570 char *fmtstr
,*sprintfbuf
,*x
;
581 case '1':case '2':case '3':case '4':case '5':
582 case '6':case '7':case '8':case '9':
585 case '0':case '1':case '2':case '3':
586 case '4':case '5':case '6':case '7':
589 insertnr
=insertnr
*10+*f
-'0';
598 if (NULL
!=(x
=strchr(f
,'!'))) {
600 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA(f
)+2);
601 sprintf(fmtstr
,"%%%s",f
);
604 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
));
605 sprintf(fmtstr
,"%%%s",f
);
606 f
+=lstrlenA(f
); /*at \0*/
610 fmtstr
= RtlAllocateHeap(RtlGetProcessHeap(),0,3);
611 strcpy( fmtstr
, "%s" );
613 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
614 argliststart
=args
+insertnr
-1;
616 argliststart
=(*(DWORD
**)args
)+insertnr
-1;
618 if (fmtstr
[lstrlenA(fmtstr
)-1]=='s' && argliststart
[0]) {
621 xarr
[0]=(DWORD
)HEAP_strdupWtoA(RtlGetProcessHeap(),0,(LPWSTR
)(*(argliststart
+0)));
622 /* possible invalid pointers */
623 xarr
[1]=*(argliststart
+1);
624 xarr
[2]=*(argliststart
+2);
625 sprintfbuf
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenW((LPWSTR
)argliststart
[0])*2+1);
627 /* CMF - This makes a BIG assumption about va_list */
628 vsprintf(sprintfbuf
, fmtstr
, (va_list) xarr
);
629 RtlFreeHeap(RtlGetProcessHeap(), 0, (LPVOID
) xarr
[0]);
631 sprintfbuf
=RtlAllocateHeap(RtlGetProcessHeap(),0,100);
633 /* CMF - This makes a BIG assumption about va_list */
634 vsprintf(sprintfbuf
, fmtstr
, (va_list) argliststart
);
640 RtlFreeHeap(RtlGetProcessHeap(),0,sprintfbuf
);
641 RtlFreeHeap(RtlGetProcessHeap(),0,fmtstr
);
688 talloced
= lstrlenA(target
)+1;
689 if (nSize
&& talloced
<nSize
)
690 target
= (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
);
691 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
692 /* nSize is the MINIMUM size */
693 DWORD len
= MultiByteToWideChar( CP_ACP
, 0, target
, -1, NULL
, 0 );
694 *((LPVOID
*)lpBuffer
) = (LPVOID
)LocalAlloc(GMEM_ZEROINIT
,len
*sizeof(WCHAR
));
695 MultiByteToWideChar( CP_ACP
, 0, target
, -1, *(LPWSTR
*)lpBuffer
, len
);
699 if (nSize
> 0 && !MultiByteToWideChar( CP_ACP
, 0, target
, -1, lpBuffer
, nSize
))
700 lpBuffer
[nSize
-1] = 0;
702 RtlFreeHeap(RtlGetProcessHeap(),0,target
);
703 if (from
) RtlFreeHeap(RtlGetProcessHeap(),0,from
);
704 return (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
705 lstrlenW(*(LPWSTR
*)lpBuffer
):
709 #endif /* __i386__ */
735 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
757 #define MAX_MSG_STR_LEN 200
759 if (lpBuffer
!= NULL
) {
761 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
762 pBuf
= LocalAlloc(LPTR
, max(nSize
, MAX_MSG_STR_LEN
));
766 *(LPSTR
*)lpBuffer
= pBuf
;
768 pBuf
= *(LPSTR
*)lpBuffer
;
771 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
) {
775 //FORMAT_MESSAGE_IGNORE_INSERTS
776 //FORMAT_MESSAGE_FROM_STRING
777 //FORMAT_MESSAGE_FROM_HMODULE
778 //FORMAT_MESSAGE_FROM_SYSTEM
779 //FORMAT_MESSAGE_ARGUMENT_ARRAY
784 FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
787 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
793 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);