1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * PURPOSE:         Vista functions
5  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <k32_vista.h>
11 
12 #if _WIN32_WINNT != _WIN32_WINNT_VISTA
13 #error "This file must be compiled with _WIN32_WINNT == _WIN32_WINNT_VISTA"
14 #endif
15 
16 // This is defined only in ntifs.h
17 #define REPARSE_DATA_BUFFER_HEADER_SIZE   FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 /* PUBLIC FUNCTIONS ***********************************************************/
23 
24 /*
25  * @implemented
26  */
27 BOOL
28 WINAPI
29 QueryFullProcessImageNameW(HANDLE hProcess,
30                            DWORD dwFlags,
31                            LPWSTR lpExeName,
32                            PDWORD pdwSize)
33 {
34     BYTE Buffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
35     UNICODE_STRING *DynamicBuffer = NULL;
36     UNICODE_STRING *Result = NULL;
37     NTSTATUS Status;
38     DWORD Needed;
39 
40     Status = NtQueryInformationProcess(hProcess,
41                                        ProcessImageFileName,
42                                        Buffer,
43                                        sizeof(Buffer) - sizeof(WCHAR),
44                                        &Needed);
45     if (Status == STATUS_INFO_LENGTH_MISMATCH)
46     {
47         DynamicBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Needed + sizeof(WCHAR));
48         if (!DynamicBuffer)
49         {
50             BaseSetLastNTError(STATUS_NO_MEMORY);
51             return FALSE;
52         }
53 
54         Status = NtQueryInformationProcess(hProcess,
55                                            ProcessImageFileName,
56                                            (LPBYTE)DynamicBuffer,
57                                            Needed,
58                                            &Needed);
59         Result = DynamicBuffer;
60     }
61     else Result = (PUNICODE_STRING)Buffer;
62 
63     if (!NT_SUCCESS(Status)) goto Cleanup;
64 
65     if (Result->Length / sizeof(WCHAR) + 1 > *pdwSize)
66     {
67         Status = STATUS_BUFFER_TOO_SMALL;
68         goto Cleanup;
69     }
70 
71     *pdwSize = Result->Length / sizeof(WCHAR);
72     memcpy(lpExeName, Result->Buffer, Result->Length);
73     lpExeName[*pdwSize] = 0;
74 
75 Cleanup:
76     RtlFreeHeap(RtlGetProcessHeap(), 0, DynamicBuffer);
77 
78     if (!NT_SUCCESS(Status))
79     {
80         BaseSetLastNTError(Status);
81     }
82 
83     return !Status;
84 }
85 
86 
87 /*
88  * @implemented
89  */
90 BOOL
91 WINAPI
92 QueryFullProcessImageNameA(HANDLE hProcess,
93                            DWORD dwFlags,
94                            LPSTR lpExeName,
95                            PDWORD pdwSize)
96 {
97     DWORD pdwSizeW = *pdwSize;
98     BOOL Result;
99     LPWSTR lpExeNameW;
100 
101     lpExeNameW = RtlAllocateHeap(RtlGetProcessHeap(),
102                                  HEAP_ZERO_MEMORY,
103                                  *pdwSize * sizeof(WCHAR));
104     if (!lpExeNameW)
105     {
106         BaseSetLastNTError(STATUS_NO_MEMORY);
107         return FALSE;
108     }
109 
110     Result = QueryFullProcessImageNameW(hProcess, dwFlags, lpExeNameW, &pdwSizeW);
111 
112     if (Result)
113         Result = (0 != WideCharToMultiByte(CP_ACP, 0,
114                                            lpExeNameW,
115                                            -1,
116                                            lpExeName,
117                                            *pdwSize,
118                                            NULL, NULL));
119 
120     if (Result)
121         *pdwSize = strlen(lpExeName);
122 
123     RtlFreeHeap(RtlGetProcessHeap(), 0, lpExeNameW);
124     return Result;
125 }
126 
127 
128 /*
129  * @unimplemented
130  */
131 HRESULT
132 WINAPI
133 GetApplicationRecoveryCallback(IN HANDLE hProcess,
134                                OUT APPLICATION_RECOVERY_CALLBACK* pRecoveryCallback,
135                                OUT PVOID* ppvParameter,
136                                PDWORD dwPingInterval,
137                                PDWORD dwFlags)
138 {
139     UNIMPLEMENTED;
140     return E_FAIL;
141 }
142 
143 
144 /*
145  * @unimplemented
146  */
147 HRESULT
148 WINAPI
149 GetApplicationRestart(IN HANDLE hProcess,
150                       OUT PWSTR pwzCommandline  OPTIONAL,
151                       IN OUT PDWORD pcchSize,
152                       OUT PDWORD pdwFlags  OPTIONAL)
153 {
154     UNIMPLEMENTED;
155     return E_FAIL;
156 }
157 
158 
159 /*
160  * @unimplemented
161  */
162 VOID
163 WINAPI
164 ApplicationRecoveryFinished(IN BOOL bSuccess)
165 {
166     UNIMPLEMENTED;
167 }
168 
169 
170 /*
171  * @unimplemented
172  */
173 HRESULT
174 WINAPI
175 ApplicationRecoveryInProgress(OUT PBOOL pbCancelled)
176 {
177     UNIMPLEMENTED;
178     return E_FAIL;
179 }
180 
181 
182 /*
183  * @unimplemented
184  */
185 HRESULT
186 WINAPI
187 RegisterApplicationRecoveryCallback(IN APPLICATION_RECOVERY_CALLBACK pRecoveryCallback,
188                                     IN PVOID pvParameter  OPTIONAL,
189                                     DWORD dwPingInterval,
190                                     DWORD dwFlags)
191 {
192     UNIMPLEMENTED;
193     return E_FAIL;
194 }
195 
196 
197 /*
198  * @unimplemented
199  */
200 HRESULT
201 WINAPI
202 RegisterApplicationRestart(IN PCWSTR pwzCommandline  OPTIONAL,
203                            IN DWORD dwFlags)
204 {
205     UNIMPLEMENTED;
206     return E_FAIL;
207 }
208 
209 
210 /*
211  * @implemented
212  */
213 BOOLEAN
214 WINAPI
215 CreateSymbolicLinkW(IN LPCWSTR lpSymlinkFileName,
216                     IN LPCWSTR lpTargetFileName,
217                     IN DWORD dwFlags)
218 {
219     IO_STATUS_BLOCK IoStatusBlock;
220     OBJECT_ATTRIBUTES ObjectAttributes;
221     HANDLE hSymlink = NULL;
222     UNICODE_STRING SymlinkFileName = { 0, 0, NULL };
223     UNICODE_STRING TargetFileName = { 0, 0, NULL };
224     BOOLEAN bAllocatedTarget = FALSE, bRelativePath = FALSE;
225     LPWSTR lpTargetFullFileName = NULL;
226     SIZE_T cbPrintName;
227     SIZE_T cbReparseData;
228     PREPARSE_DATA_BUFFER pReparseData = NULL;
229     PBYTE pBufTail;
230     NTSTATUS Status;
231     ULONG dwCreateOptions;
232     DWORD dwErr;
233 
234     if(!lpSymlinkFileName || !lpTargetFileName || (dwFlags | SYMBOLIC_LINK_FLAG_DIRECTORY) != SYMBOLIC_LINK_FLAG_DIRECTORY)
235     {
236         SetLastError(ERROR_INVALID_PARAMETER);
237         return FALSE;
238     }
239 
240     if(dwFlags & SYMBOLIC_LINK_FLAG_DIRECTORY)
241         dwCreateOptions = FILE_DIRECTORY_FILE;
242     else
243         dwCreateOptions = FILE_NON_DIRECTORY_FILE;
244 
245     switch(RtlDetermineDosPathNameType_U(lpTargetFileName))
246     {
247     case RtlPathTypeUnknown:
248     case RtlPathTypeRooted:
249     case RtlPathTypeRelative:
250         bRelativePath = TRUE;
251         RtlInitUnicodeString(&TargetFileName, lpTargetFileName);
252         break;
253 
254     case RtlPathTypeDriveRelative:
255         {
256             LPWSTR FilePart;
257             SIZE_T cchTargetFullFileName;
258 
259             cchTargetFullFileName = GetFullPathNameW(lpTargetFileName, 0, NULL, &FilePart);
260 
261             if(cchTargetFullFileName == 0)
262             {
263                 dwErr = GetLastError();
264                 goto Cleanup;
265             }
266 
267             lpTargetFullFileName = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchTargetFullFileName * sizeof(WCHAR));
268 
269             if(lpTargetFullFileName == NULL)
270             {
271                 dwErr = ERROR_NOT_ENOUGH_MEMORY;
272                 goto Cleanup;
273             }
274 
275             if(GetFullPathNameW(lpTargetFileName, cchTargetFullFileName, lpTargetFullFileName, &FilePart) == 0)
276             {
277                 dwErr = GetLastError();
278                 goto Cleanup;
279             }
280         }
281 
282         lpTargetFileName = lpTargetFullFileName;
283 
284         // fallthrough
285 
286     case RtlPathTypeUncAbsolute:
287     case RtlPathTypeDriveAbsolute:
288     case RtlPathTypeLocalDevice:
289     case RtlPathTypeRootLocalDevice:
290     default:
291         if(!RtlDosPathNameToNtPathName_U(lpTargetFileName, &TargetFileName, NULL, NULL))
292         {
293             bAllocatedTarget = TRUE;
294             dwErr = ERROR_INVALID_PARAMETER;
295             goto Cleanup;
296         }
297     }
298 
299     cbPrintName = wcslen(lpTargetFileName) * sizeof(WCHAR);
300     cbReparseData = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + TargetFileName.Length + cbPrintName;
301     pReparseData = RtlAllocateHeap(RtlGetProcessHeap(), 0, cbReparseData);
302 
303     if(pReparseData == NULL)
304     {
305         dwErr = ERROR_NOT_ENOUGH_MEMORY;
306         goto Cleanup;
307     }
308 
309     pBufTail = (PBYTE)(pReparseData->SymbolicLinkReparseBuffer.PathBuffer);
310 
311     pReparseData->ReparseTag = (ULONG)IO_REPARSE_TAG_SYMLINK;
312     pReparseData->ReparseDataLength = (USHORT)cbReparseData - REPARSE_DATA_BUFFER_HEADER_SIZE;
313     pReparseData->Reserved = 0;
314 
315     pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
316     pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = TargetFileName.Length;
317     pBufTail += pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset;
318     RtlCopyMemory(pBufTail, TargetFileName.Buffer, TargetFileName.Length);
319 
320     pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
321     pReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)cbPrintName;
322     pBufTail += pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset;
323     RtlCopyMemory(pBufTail, lpTargetFileName, cbPrintName);
324 
325     pReparseData->SymbolicLinkReparseBuffer.Flags = 0;
326 
327     if(bRelativePath)
328         pReparseData->SymbolicLinkReparseBuffer.Flags |= 1; // TODO! give this lone flag a name
329 
330     if(!RtlDosPathNameToNtPathName_U(lpSymlinkFileName, &SymlinkFileName, NULL, NULL))
331     {
332         dwErr = ERROR_PATH_NOT_FOUND;
333         goto Cleanup;
334     }
335 
336     InitializeObjectAttributes(&ObjectAttributes, &SymlinkFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
337 
338     Status = NtCreateFile
339     (
340         &hSymlink,
341         FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE,
342         &ObjectAttributes,
343         &IoStatusBlock,
344         NULL,
345         FILE_ATTRIBUTE_NORMAL,
346         0,
347         FILE_CREATE,
348         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT | dwCreateOptions,
349         NULL,
350         0
351     );
352 
353     if(!NT_SUCCESS(Status))
354     {
355         dwErr = RtlNtStatusToDosError(Status);
356         goto Cleanup;
357     }
358 
359     Status = NtFsControlFile
360     (
361         hSymlink,
362         NULL,
363         NULL,
364         NULL,
365         &IoStatusBlock,
366         FSCTL_SET_REPARSE_POINT,
367         pReparseData,
368         cbReparseData,
369         NULL,
370         0
371     );
372 
373     if(!NT_SUCCESS(Status))
374     {
375         FILE_DISPOSITION_INFORMATION DispInfo;
376         DispInfo.DeleteFile = TRUE;
377         NtSetInformationFile(hSymlink, &IoStatusBlock, &DispInfo, sizeof(DispInfo), FileDispositionInformation);
378 
379         dwErr = RtlNtStatusToDosError(Status);
380         goto Cleanup;
381     }
382 
383     dwErr = NO_ERROR;
384 
385 Cleanup:
386     if(hSymlink)
387         NtClose(hSymlink);
388 
389     RtlFreeUnicodeString(&SymlinkFileName);
390     if (bAllocatedTarget)
391     {
392         RtlFreeHeap(RtlGetProcessHeap(),
393                     0,
394                     TargetFileName.Buffer);
395     }
396 
397     if(lpTargetFullFileName)
398         RtlFreeHeap(RtlGetProcessHeap(), 0, lpTargetFullFileName);
399 
400     if(pReparseData)
401         RtlFreeHeap(RtlGetProcessHeap(), 0, pReparseData);
402 
403     if(dwErr)
404     {
405         SetLastError(dwErr);
406         return FALSE;
407     }
408 
409     return TRUE;
410 }
411 
412 
413 /*
414  * @implemented
415  */
416 BOOLEAN
417 NTAPI
418 CreateSymbolicLinkA(IN LPCSTR lpSymlinkFileName,
419                     IN LPCSTR lpTargetFileName,
420                     IN DWORD dwFlags)
421 {
422     PWCHAR SymlinkW, TargetW;
423     BOOLEAN Ret;
424 
425     if(!lpSymlinkFileName || !lpTargetFileName)
426     {
427         SetLastError(ERROR_INVALID_PARAMETER);
428         return FALSE;
429     }
430 
431     if (!(SymlinkW = FilenameA2W(lpSymlinkFileName, FALSE)))
432         return FALSE;
433 
434     if (!(TargetW = FilenameA2W(lpTargetFileName, TRUE)))
435         return FALSE;
436 
437     Ret = CreateSymbolicLinkW(SymlinkW,
438                               TargetW,
439                               dwFlags);
440 
441     RtlFreeHeap(RtlGetProcessHeap(), 0, SymlinkW);
442     RtlFreeHeap(RtlGetProcessHeap(), 0, TargetW);
443 
444     return Ret;
445 }
446 
447 
448 /*
449  * @unimplemented
450  */
451 DWORD
452 WINAPI
453 GetFinalPathNameByHandleW(IN HANDLE hFile,
454                           OUT LPWSTR lpszFilePath,
455                           IN DWORD cchFilePath,
456                           IN DWORD dwFlags)
457 {
458     if (dwFlags & ~(VOLUME_NAME_DOS | VOLUME_NAME_GUID | VOLUME_NAME_NT |
459                     VOLUME_NAME_NONE | FILE_NAME_NORMALIZED | FILE_NAME_OPENED))
460     {
461         SetLastError(ERROR_INVALID_PARAMETER);
462         return 0;
463     }
464 
465     UNIMPLEMENTED;
466     return 0;
467 }
468 
469 
470 /*
471  * @implemented
472  */
473 DWORD
474 WINAPI
475 GetFinalPathNameByHandleA(IN HANDLE hFile,
476                           OUT LPSTR lpszFilePath,
477                           IN DWORD cchFilePath,
478                           IN DWORD dwFlags)
479 {
480     WCHAR FilePathW[MAX_PATH];
481     UNICODE_STRING FilePathU;
482     DWORD PrevLastError;
483     DWORD Ret = 0;
484 
485     if (cchFilePath != 0 &&
486         cchFilePath > sizeof(FilePathW) / sizeof(FilePathW[0]))
487     {
488         FilePathU.Length = 0;
489         FilePathU.MaximumLength = (USHORT)cchFilePath * sizeof(WCHAR);
490         FilePathU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
491                                            0,
492                                            FilePathU.MaximumLength);
493         if (FilePathU.Buffer == NULL)
494         {
495             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
496             return 0;
497         }
498     }
499     else
500     {
501         FilePathU.Length = 0;
502         FilePathU.MaximumLength = sizeof(FilePathW);
503         FilePathU.Buffer = FilePathW;
504     }
505 
506     /* save the last error code */
507     PrevLastError = GetLastError();
508     SetLastError(ERROR_SUCCESS);
509 
510     /* call the unicode version that does all the work */
511     Ret = GetFinalPathNameByHandleW(hFile,
512                                     FilePathU.Buffer,
513                                     cchFilePath,
514                                     dwFlags);
515 
516     if (GetLastError() == ERROR_SUCCESS)
517     {
518         /* no error, restore the last error code and convert the string */
519         SetLastError(PrevLastError);
520 
521         Ret = FilenameU2A_FitOrFail(lpszFilePath,
522                                     cchFilePath,
523                                     &FilePathU);
524     }
525 
526     /* free allocated memory if necessary */
527     if (FilePathU.Buffer != FilePathW)
528     {
529         RtlFreeHeap(RtlGetProcessHeap(),
530                     0,
531                     FilePathU.Buffer);
532     }
533 
534     return Ret;
535 }
536 
537 
538 /*
539  * @unimplemented
540  */
541 BOOL
542 WINAPI
543 SetFileBandwidthReservation(IN HANDLE hFile,
544                             IN DWORD nPeriodMilliseconds,
545                             IN DWORD nBytesPerPeriod,
546                             IN BOOL bDiscardable,
547                             OUT LPDWORD lpTransferSize,
548                             OUT LPDWORD lpNumOutstandingRequests)
549 {
550     UNIMPLEMENTED;
551     return FALSE;
552 }
553 
554 
555 /*
556  * @unimplemented
557  */
558 BOOL
559 WINAPI
560 GetFileBandwidthReservation(IN HANDLE hFile,
561                             OUT LPDWORD lpPeriodMilliseconds,
562                             OUT LPDWORD lpBytesPerPeriod,
563                             OUT LPBOOL pDiscardable,
564                             OUT LPDWORD lpTransferSize,
565                             OUT LPDWORD lpNumOutstandingRequests)
566 {
567     UNIMPLEMENTED;
568     return FALSE;
569 }
570 
571 
572 /*
573  * @unimplemented
574  */
575 HANDLE
576 WINAPI
577 OpenFileById(IN HANDLE hFile,
578              IN LPFILE_ID_DESCRIPTOR lpFileID,
579              IN DWORD dwDesiredAccess,
580              IN DWORD dwShareMode,
581              IN LPSECURITY_ATTRIBUTES lpSecurityAttributes  OPTIONAL,
582              IN DWORD dwFlags)
583 {
584     UNIMPLEMENTED;
585     return INVALID_HANDLE_VALUE;
586 }
587 
588 
589 
590 /*
591   Vista+ MUI support functions
592 
593   References:
594    Evolution of MUI Support across Windows Versions: http://msdn.microsoft.com/en-US/library/ee264317.aspx
595    Comparing Windows XP Professional Multilingual Options: http://technet.microsoft.com/en-us/library/bb457045.aspx
596 
597   More info:
598    http://msdn.microsoft.com/en-us/goglobal/bb978454.aspx
599    http://msdn.microsoft.com/en-us/library/dd319074.aspx
600 */
601 
602 /* FUNCTIONS *****************************************************************/
603 
604 BOOL
605 WINAPI
606 GetFileMUIInfo(
607     DWORD dwFlags,
608     PCWSTR pcwszFilePath,
609     PFILEMUIINFO pFileMUIInfo,
610     DWORD *pcbFileMUIInfo)
611 {
612     DPRINT1("%x %p %p %p\n", dwFlags, pcwszFilePath, pFileMUIInfo, pcbFileMUIInfo);
613     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
614     return FALSE;
615 }
616 
617 /*
618  * @unimplemented
619  */
620 BOOL
621 WINAPI
622 GetFileMUIPath(
623     DWORD dwFlags,
624     PCWSTR pcwszFilePath,
625     PWSTR pwszLanguage,
626     PULONG pcchLanguage,
627     PWSTR pwszFileMUIPath,
628     PULONG pcchFileMUIPath,
629     PULONGLONG pululEnumerator)
630 {
631     DPRINT1("%x %p %p %p %p %p\n", dwFlags, pcwszFilePath, pwszLanguage, pwszFileMUIPath, pcchFileMUIPath, pululEnumerator);
632     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
633     return FALSE;
634 }
635 
636 /*
637  * @unimplemented
638  */
639 #if 0 // This is Windows 7+
640 BOOL
641 WINAPI
642 GetProcessPreferredUILanguages(
643     DWORD dwFlags,
644     PULONG pulNumLanguages,
645     PZZWSTR pwszLanguagesBuffer,
646     PULONG pcchLanguagesBuffer)
647 {
648     DPRINT1("%x %p %p %p\n", dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
649     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
650     return FALSE;
651 }
652 #endif
653 
654 /*
655 * @unimplemented
656 */
657 BOOL
658 WINAPI
659 GetSystemPreferredUILanguages(
660     DWORD dwFlags,
661     PULONG pulNumLanguages,
662     PZZWSTR pwszLanguagesBuffer,
663     PULONG pcchLanguagesBuffer)
664 {
665     DPRINT1("%x %p %p %p\n", dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
666     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
667     return FALSE;
668 }
669 
670 /*
671  * @unimplemented
672  */
673 BOOL
674 WINAPI
675 GetThreadPreferredUILanguages(
676     DWORD dwFlags,
677     PULONG pulNumLanguages,
678     PZZWSTR pwszLanguagesBuffer,
679     PULONG pcchLanguagesBuffer)
680 {
681     DPRINT1("%x %p %p %p\n", dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
682     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
683     return FALSE;
684 }
685 
686 /*
687  * @unimplemented
688  */
689 LANGID
690 WINAPI
691 GetThreadUILanguage(VOID)
692 {
693     UNIMPLEMENTED;
694     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
695     return 0;
696 }
697 
698 /*
699  * @unimplemented
700  */
701 BOOL
702 WINAPI
703 GetUILanguageInfo(
704     DWORD dwFlags,
705     PCZZWSTR pwmszLanguage,
706     PZZWSTR pwszFallbackLanguages,
707     PDWORD pcchFallbackLanguages,
708     PDWORD pdwAttributes)
709 {
710     DPRINT1("%x %p %p %p %p\n", dwFlags, pwmszLanguage, pwszFallbackLanguages, pcchFallbackLanguages, pdwAttributes);
711     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
712     return FALSE;
713 }
714 
715 
716 /*
717  * @unimplemented
718  */
719 BOOL
720 WINAPI
721 GetUserPreferredUILanguages(
722     DWORD dwFlags,
723     PULONG pulNumLanguages,
724     PZZWSTR pwszLanguagesBuffer,
725     PULONG pcchLanguagesBuffer)
726 {
727     DPRINT1("%x %p %p %p\n", dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
728     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
729     return FALSE;
730 }
731 
732 /*
733  * @unimplemented
734  */
735 #if 0 // Tis is Windows 7+
736 BOOL
737 WINAPI
738 SetProcessPreferredUILanguages(
739     DWORD dwFlags,
740     PCZZWSTR pwszLanguagesBuffer,
741     PULONG pulNumLanguages)
742 {
743     DPRINT1("%x %p %p\n", dwFlags, pwszLanguagesBuffer, pulNumLanguages);
744     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
745     return FALSE;
746 }
747 #endif
748 
749 /*
750  * @unimplemented
751  */
752 BOOL
753 WINAPI
754 SetThreadPreferredUILanguages(
755     DWORD dwFlags,
756     PCZZWSTR pwszLanguagesBuffer,
757     PULONG pulNumLanguages
758     )
759 {
760     DPRINT1("%x %p %p\n", dwFlags, pwszLanguagesBuffer, pulNumLanguages);
761     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
762     return FALSE;
763 }
764 
765