1 /* 2 * Setupapi miscellaneous functions 3 * 4 * Copyright 2005 Eric Kohl 5 * Copyright 2007 Hans Leidekker 6 * 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. 11 * 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. 16 * 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 20 */ 21 22 #include "setupapi_private.h" 23 24 #include <winver.h> 25 #include <lzexpand.h> 26 27 /* Unicode constants */ 28 static const WCHAR BackSlash[] = {'\\',0}; 29 static const WCHAR TranslationRegKey[] = {'\\','V','e','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0}; 30 31 /* Handles and critical sections for the SetupLog API */ 32 static HANDLE setupact = INVALID_HANDLE_VALUE; 33 static HANDLE setuperr = INVALID_HANDLE_VALUE; 34 static CRITICAL_SECTION setupapi_cs; 35 static CRITICAL_SECTION_DEBUG critsect_debug = 36 { 37 0, 0, &setupapi_cs, 38 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 39 0, 0, { (DWORD_PTR)(__FILE__ ": setupapi_cs") } 40 }; 41 static CRITICAL_SECTION setupapi_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; 42 43 DWORD 44 GetFunctionPointer( 45 IN PWSTR InstallerName, 46 OUT HMODULE* ModulePointer, 47 OUT PVOID* FunctionPointer) 48 { 49 HMODULE hModule = NULL; 50 LPSTR FunctionNameA = NULL; 51 PWCHAR Comma; 52 DWORD rc; 53 54 *ModulePointer = NULL; 55 *FunctionPointer = NULL; 56 57 Comma = strchrW(InstallerName, ','); 58 if (!Comma) 59 { 60 rc = ERROR_INVALID_PARAMETER; 61 goto cleanup; 62 } 63 64 /* Load library */ 65 *Comma = '\0'; 66 hModule = LoadLibraryW(InstallerName); 67 *Comma = ','; 68 if (!hModule) 69 { 70 rc = GetLastError(); 71 goto cleanup; 72 } 73 74 /* Skip comma spaces */ 75 while (*Comma == ',' || isspaceW(*Comma)) 76 Comma++; 77 78 /* W->A conversion for function name */ 79 FunctionNameA = pSetupUnicodeToMultiByte(Comma, CP_ACP); 80 if (!FunctionNameA) 81 { 82 rc = GetLastError(); 83 goto cleanup; 84 } 85 86 /* Search function */ 87 *FunctionPointer = GetProcAddress(hModule, FunctionNameA); 88 if (!*FunctionPointer) 89 { 90 rc = GetLastError(); 91 goto cleanup; 92 } 93 94 *ModulePointer = hModule; 95 rc = ERROR_SUCCESS; 96 97 cleanup: 98 if (rc != ERROR_SUCCESS && hModule) 99 FreeLibrary(hModule); 100 MyFree(FunctionNameA); 101 return rc; 102 } 103 104 DWORD 105 FreeFunctionPointer( 106 IN HMODULE ModulePointer, 107 IN PVOID FunctionPointer) 108 { 109 if (ModulePointer == NULL) 110 return ERROR_SUCCESS; 111 if (FreeLibrary(ModulePointer)) 112 return ERROR_SUCCESS; 113 else 114 return GetLastError(); 115 } 116 117 /************************************************************************** 118 * MyFree [SETUPAPI.@] 119 * 120 * Frees an allocated memory block from the process heap. 121 * 122 * PARAMS 123 * lpMem [I] pointer to memory block which will be freed 124 * 125 * RETURNS 126 * None 127 */ 128 VOID WINAPI MyFree(LPVOID lpMem) 129 { 130 TRACE("%p\n", lpMem); 131 HeapFree(GetProcessHeap(), 0, lpMem); 132 } 133 134 135 /************************************************************************** 136 * MyMalloc [SETUPAPI.@] 137 * 138 * Allocates memory block from the process heap. 139 * 140 * PARAMS 141 * dwSize [I] size of the allocated memory block 142 * 143 * RETURNS 144 * Success: pointer to allocated memory block 145 * Failure: NULL 146 */ 147 LPVOID WINAPI MyMalloc(DWORD dwSize) 148 { 149 TRACE("%lu\n", dwSize); 150 return HeapAlloc(GetProcessHeap(), 0, dwSize); 151 } 152 153 154 /************************************************************************** 155 * MyRealloc [SETUPAPI.@] 156 * 157 * Changes the size of an allocated memory block or allocates a memory 158 * block from the process heap. 159 * 160 * PARAMS 161 * lpSrc [I] pointer to memory block which will be resized 162 * dwSize [I] new size of the memory block 163 * 164 * RETURNS 165 * Success: pointer to the resized memory block 166 * Failure: NULL 167 * 168 * NOTES 169 * If lpSrc is a NULL-pointer, then MyRealloc allocates a memory 170 * block like MyMalloc. 171 */ 172 LPVOID WINAPI MyRealloc(LPVOID lpSrc, DWORD dwSize) 173 { 174 TRACE("%p %lu\n", lpSrc, dwSize); 175 176 if (lpSrc == NULL) 177 return HeapAlloc(GetProcessHeap(), 0, dwSize); 178 179 return HeapReAlloc(GetProcessHeap(), 0, lpSrc, dwSize); 180 } 181 182 183 /************************************************************************** 184 * pSetupDuplicateString [SETUPAPI.@] 185 * 186 * Duplicates a unicode string. 187 * 188 * PARAMS 189 * lpSrc [I] pointer to the unicode string that will be duplicated 190 * 191 * RETURNS 192 * Success: pointer to the duplicated unicode string 193 * Failure: NULL 194 * 195 * NOTES 196 * Call MyFree() to release the duplicated string. 197 */ 198 LPWSTR WINAPI pSetupDuplicateString(LPCWSTR lpSrc) 199 { 200 LPWSTR lpDst; 201 202 TRACE("%s\n", debugstr_w(lpSrc)); 203 204 lpDst = MyMalloc((lstrlenW(lpSrc) + 1) * sizeof(WCHAR)); 205 if (lpDst == NULL) 206 return NULL; 207 208 strcpyW(lpDst, lpSrc); 209 210 return lpDst; 211 } 212 213 214 /************************************************************************** 215 * QueryRegistryValue [SETUPAPI.@] 216 * 217 * Retrieves value data from the registry and allocates memory for the 218 * value data. 219 * 220 * PARAMS 221 * hKey [I] Handle of the key to query 222 * lpValueName [I] Name of value under hkey to query 223 * lpData [O] Destination for the values contents, 224 * lpType [O] Destination for the value type 225 * lpcbData [O] Destination for the size of data 226 * 227 * RETURNS 228 * Success: ERROR_SUCCESS 229 * Failure: Otherwise 230 * 231 * NOTES 232 * Use MyFree to release the lpData buffer. 233 */ 234 LONG WINAPI QueryRegistryValue(HKEY hKey, 235 LPCWSTR lpValueName, 236 LPBYTE *lpData, 237 LPDWORD lpType, 238 LPDWORD lpcbData) 239 { 240 LONG lError; 241 242 TRACE("%p %s %p %p %p\n", 243 hKey, debugstr_w(lpValueName), lpData, lpType, lpcbData); 244 245 /* Get required buffer size */ 246 *lpcbData = 0; 247 lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, NULL, lpcbData); 248 if (lError != ERROR_SUCCESS) 249 return lError; 250 251 /* Allocate buffer */ 252 *lpData = MyMalloc(*lpcbData); 253 if (*lpData == NULL) 254 return ERROR_NOT_ENOUGH_MEMORY; 255 256 /* Query registry value */ 257 lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, *lpData, lpcbData); 258 if (lError != ERROR_SUCCESS) 259 MyFree(*lpData); 260 261 return lError; 262 } 263 264 265 /************************************************************************** 266 * pSetupMultiByteToUnicode [SETUPAPI.@] 267 * 268 * Converts a multi-byte string to a Unicode string. 269 * 270 * PARAMS 271 * lpMultiByteStr [I] Multi-byte string to be converted 272 * uCodePage [I] Code page 273 * 274 * RETURNS 275 * Success: pointer to the converted Unicode string 276 * Failure: NULL 277 * 278 * NOTE 279 * Use MyFree to release the returned Unicode string. 280 */ 281 LPWSTR WINAPI pSetupMultiByteToUnicode(LPCSTR lpMultiByteStr, UINT uCodePage) 282 { 283 LPWSTR lpUnicodeStr; 284 int nLength; 285 286 TRACE("%s %d\n", debugstr_a(lpMultiByteStr), uCodePage); 287 288 nLength = MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, 289 -1, NULL, 0); 290 if (nLength == 0) 291 return NULL; 292 293 lpUnicodeStr = MyMalloc(nLength * sizeof(WCHAR)); 294 if (lpUnicodeStr == NULL) 295 { 296 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 297 return NULL; 298 } 299 300 if (!MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, 301 nLength, lpUnicodeStr, nLength)) 302 { 303 MyFree(lpUnicodeStr); 304 return NULL; 305 } 306 307 return lpUnicodeStr; 308 } 309 310 311 /************************************************************************** 312 * pSetupUnicodeToMultiByte [SETUPAPI.@] 313 * 314 * Converts a Unicode string to a multi-byte string. 315 * 316 * PARAMS 317 * lpUnicodeStr [I] Unicode string to be converted 318 * uCodePage [I] Code page 319 * 320 * RETURNS 321 * Success: pointer to the converted multi-byte string 322 * Failure: NULL 323 * 324 * NOTE 325 * Use MyFree to release the returned multi-byte string. 326 */ 327 LPSTR WINAPI pSetupUnicodeToMultiByte(LPCWSTR lpUnicodeStr, UINT uCodePage) 328 { 329 LPSTR lpMultiByteStr; 330 int nLength; 331 332 TRACE("%s %d\n", debugstr_w(lpUnicodeStr), uCodePage); 333 334 nLength = WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, 335 NULL, 0, NULL, NULL); 336 if (nLength == 0) 337 return NULL; 338 339 lpMultiByteStr = MyMalloc(nLength); 340 if (lpMultiByteStr == NULL) 341 return NULL; 342 343 if (!WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, 344 lpMultiByteStr, nLength, NULL, NULL)) 345 { 346 MyFree(lpMultiByteStr); 347 return NULL; 348 } 349 350 return lpMultiByteStr; 351 } 352 353 354 /************************************************************************** 355 * DoesUserHavePrivilege [SETUPAPI.@] 356 * 357 * Check whether the current user has got a given privilege. 358 * 359 * PARAMS 360 * lpPrivilegeName [I] Name of the privilege to be checked 361 * 362 * RETURNS 363 * Success: TRUE 364 * Failure: FALSE 365 */ 366 BOOL WINAPI DoesUserHavePrivilege(LPCWSTR lpPrivilegeName) 367 { 368 HANDLE hToken; 369 DWORD dwSize; 370 PTOKEN_PRIVILEGES lpPrivileges; 371 LUID PrivilegeLuid; 372 DWORD i; 373 BOOL bResult = FALSE; 374 375 TRACE("%s\n", debugstr_w(lpPrivilegeName)); 376 377 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) 378 return FALSE; 379 380 if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) 381 { 382 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 383 { 384 CloseHandle(hToken); 385 return FALSE; 386 } 387 } 388 389 lpPrivileges = MyMalloc(dwSize); 390 if (lpPrivileges == NULL) 391 { 392 CloseHandle(hToken); 393 return FALSE; 394 } 395 396 if (!GetTokenInformation(hToken, TokenPrivileges, lpPrivileges, dwSize, &dwSize)) 397 { 398 MyFree(lpPrivileges); 399 CloseHandle(hToken); 400 return FALSE; 401 } 402 403 CloseHandle(hToken); 404 405 if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, &PrivilegeLuid)) 406 { 407 MyFree(lpPrivileges); 408 return FALSE; 409 } 410 411 for (i = 0; i < lpPrivileges->PrivilegeCount; i++) 412 { 413 if (lpPrivileges->Privileges[i].Luid.HighPart == PrivilegeLuid.HighPart && 414 lpPrivileges->Privileges[i].Luid.LowPart == PrivilegeLuid.LowPart) 415 { 416 bResult = TRUE; 417 } 418 } 419 420 MyFree(lpPrivileges); 421 422 return bResult; 423 } 424 425 426 /************************************************************************** 427 * pSetupEnablePrivilege [SETUPAPI.@] 428 * 429 * Enables or disables one of the current users privileges. 430 * 431 * PARAMS 432 * lpPrivilegeName [I] Name of the privilege to be changed 433 * bEnable [I] TRUE: Enables the privilege 434 * FALSE: Disables the privilege 435 * 436 * RETURNS 437 * Success: TRUE 438 * Failure: FALSE 439 */ 440 BOOL WINAPI pSetupEnablePrivilege(LPCWSTR lpPrivilegeName, BOOL bEnable) 441 { 442 TOKEN_PRIVILEGES Privileges; 443 HANDLE hToken; 444 BOOL bResult; 445 446 TRACE("%s %s\n", debugstr_w(lpPrivilegeName), bEnable ? "TRUE" : "FALSE"); 447 448 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 449 return FALSE; 450 451 Privileges.PrivilegeCount = 1; 452 Privileges.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0; 453 454 if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, 455 &Privileges.Privileges[0].Luid)) 456 { 457 CloseHandle(hToken); 458 return FALSE; 459 } 460 461 bResult = AdjustTokenPrivileges(hToken, FALSE, &Privileges, 0, NULL, NULL); 462 463 CloseHandle(hToken); 464 465 return bResult; 466 } 467 468 469 /************************************************************************** 470 * DelayedMove [SETUPAPI.@] 471 * 472 * Moves a file upon the next reboot. 473 * 474 * PARAMS 475 * lpExistingFileName [I] Current file name 476 * lpNewFileName [I] New file name 477 * 478 * RETURNS 479 * Success: TRUE 480 * Failure: FALSE 481 */ 482 BOOL WINAPI DelayedMove(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) 483 { 484 return MoveFileExW(lpExistingFileName, lpNewFileName, 485 MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT); 486 } 487 488 489 /************************************************************************** 490 * FileExists [SETUPAPI.@] 491 * 492 * Checks whether a file exists. 493 * 494 * PARAMS 495 * lpFileName [I] Name of the file to check 496 * lpNewFileName [O] Optional information about the existing file 497 * 498 * RETURNS 499 * Success: TRUE 500 * Failure: FALSE 501 */ 502 BOOL WINAPI FileExists(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFileFindData) 503 { 504 WIN32_FIND_DATAW FindData; 505 HANDLE hFind; 506 UINT uErrorMode; 507 DWORD dwError; 508 509 uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); 510 511 hFind = FindFirstFileW(lpFileName, &FindData); 512 if (hFind == INVALID_HANDLE_VALUE) 513 { 514 dwError = GetLastError(); 515 SetErrorMode(uErrorMode); 516 SetLastError(dwError); 517 return FALSE; 518 } 519 520 FindClose(hFind); 521 522 if (lpFileFindData) 523 memcpy(lpFileFindData, &FindData, sizeof(WIN32_FIND_DATAW)); 524 525 SetErrorMode(uErrorMode); 526 527 return TRUE; 528 } 529 530 531 /************************************************************************** 532 * CaptureStringArg [SETUPAPI.@] 533 * 534 * Captures a UNICODE string. 535 * 536 * PARAMS 537 * lpSrc [I] UNICODE string to be captured 538 * lpDst [O] Pointer to the captured UNICODE string 539 * 540 * RETURNS 541 * Success: ERROR_SUCCESS 542 * Failure: ERROR_INVALID_PARAMETER 543 * 544 * NOTE 545 * Call MyFree to release the captured UNICODE string. 546 */ 547 DWORD WINAPI CaptureStringArg(LPCWSTR pSrc, LPWSTR *pDst) 548 { 549 if (pDst == NULL) 550 return ERROR_INVALID_PARAMETER; 551 552 *pDst = pSetupDuplicateString(pSrc); 553 554 return ERROR_SUCCESS; 555 } 556 557 558 /************************************************************************** 559 * pSetupCaptureAndConvertAnsiArg [SETUPAPI.@] 560 * 561 * Captures an ANSI string and converts it to a UNICODE string. 562 * 563 * PARAMS 564 * lpSrc [I] ANSI string to be captured 565 * lpDst [O] Pointer to the captured UNICODE string 566 * 567 * RETURNS 568 * Success: ERROR_SUCCESS 569 * Failure: ERROR_INVALID_PARAMETER 570 * 571 * NOTE 572 * Call MyFree to release the captured UNICODE string. 573 */ 574 DWORD WINAPI pSetupCaptureAndConvertAnsiArg(LPCSTR pSrc, LPWSTR *pDst) 575 { 576 if (pDst == NULL) 577 return ERROR_INVALID_PARAMETER; 578 579 *pDst = pSetupMultiByteToUnicode(pSrc, CP_ACP); 580 581 return ERROR_SUCCESS; 582 } 583 584 585 /************************************************************************** 586 * pSetupOpenAndMapFileForRead [SETUPAPI.@] 587 * 588 * Open and map a file to a buffer. 589 * 590 * PARAMS 591 * lpFileName [I] Name of the file to be opened 592 * lpSize [O] Pointer to the file size 593 * lpFile [0] Pointer to the file handle 594 * lpMapping [0] Pointer to the mapping handle 595 * lpBuffer [0] Pointer to the file buffer 596 * 597 * RETURNS 598 * Success: ERROR_SUCCESS 599 * Failure: Other 600 * 601 * NOTE 602 * Call UnmapAndCloseFile to release the file. 603 */ 604 DWORD WINAPI pSetupOpenAndMapFileForRead(LPCWSTR lpFileName, 605 LPDWORD lpSize, 606 LPHANDLE lpFile, 607 LPHANDLE lpMapping, 608 LPVOID *lpBuffer) 609 { 610 DWORD dwError; 611 612 TRACE("%s %p %p %p %p\n", 613 debugstr_w(lpFileName), lpSize, lpFile, lpMapping, lpBuffer); 614 615 *lpFile = CreateFileW(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, 616 OPEN_EXISTING, 0, NULL); 617 if (*lpFile == INVALID_HANDLE_VALUE) 618 return GetLastError(); 619 620 *lpSize = GetFileSize(*lpFile, NULL); 621 if (*lpSize == INVALID_FILE_SIZE) 622 { 623 dwError = GetLastError(); 624 CloseHandle(*lpFile); 625 return dwError; 626 } 627 628 *lpMapping = CreateFileMappingW(*lpFile, NULL, PAGE_READONLY, 0, 629 *lpSize, NULL); 630 if (*lpMapping == NULL) 631 { 632 dwError = GetLastError(); 633 CloseHandle(*lpFile); 634 return dwError; 635 } 636 637 *lpBuffer = MapViewOfFile(*lpMapping, FILE_MAP_READ, 0, 0, *lpSize); 638 if (*lpBuffer == NULL) 639 { 640 dwError = GetLastError(); 641 CloseHandle(*lpMapping); 642 CloseHandle(*lpFile); 643 return dwError; 644 } 645 646 return ERROR_SUCCESS; 647 } 648 649 650 /************************************************************************** 651 * pSetupUnmapAndCloseFile [SETUPAPI.@] 652 * 653 * Unmap and close a mapped file. 654 * 655 * PARAMS 656 * hFile [I] Handle to the file 657 * hMapping [I] Handle to the file mapping 658 * lpBuffer [I] Pointer to the file buffer 659 * 660 * RETURNS 661 * Success: TRUE 662 * Failure: FALSE 663 */ 664 BOOL WINAPI pSetupUnmapAndCloseFile(HANDLE hFile, HANDLE hMapping, LPVOID lpBuffer) 665 { 666 TRACE("%p %p %p\n", 667 hFile, hMapping, lpBuffer); 668 669 if (!UnmapViewOfFile(lpBuffer)) 670 return FALSE; 671 672 if (!CloseHandle(hMapping)) 673 return FALSE; 674 675 if (!CloseHandle(hFile)) 676 return FALSE; 677 678 return TRUE; 679 } 680 681 682 /************************************************************************** 683 * StampFileSecurity [SETUPAPI.@] 684 * 685 * Assign a new security descriptor to the given file. 686 * 687 * PARAMS 688 * lpFileName [I] Name of the file 689 * pSecurityDescriptor [I] New security descriptor 690 * 691 * RETURNS 692 * Success: ERROR_SUCCESS 693 * Failure: other 694 */ 695 DWORD WINAPI StampFileSecurity(LPCWSTR lpFileName, PSECURITY_DESCRIPTOR pSecurityDescriptor) 696 { 697 TRACE("%s %p\n", debugstr_w(lpFileName), pSecurityDescriptor); 698 699 if (!SetFileSecurityW(lpFileName, OWNER_SECURITY_INFORMATION | 700 GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, 701 pSecurityDescriptor)) 702 return GetLastError(); 703 704 return ERROR_SUCCESS; 705 } 706 707 708 /************************************************************************** 709 * TakeOwnershipOfFile [SETUPAPI.@] 710 * 711 * Takes the ownership of the given file. 712 * 713 * PARAMS 714 * lpFileName [I] Name of the file 715 * 716 * RETURNS 717 * Success: ERROR_SUCCESS 718 * Failure: other 719 */ 720 DWORD WINAPI TakeOwnershipOfFile(LPCWSTR lpFileName) 721 { 722 SECURITY_DESCRIPTOR SecDesc; 723 HANDLE hToken = NULL; 724 PTOKEN_OWNER pOwner = NULL; 725 DWORD dwError; 726 DWORD dwSize; 727 728 TRACE("%s\n", debugstr_w(lpFileName)); 729 730 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) 731 return GetLastError(); 732 733 if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &dwSize)) 734 { 735 goto fail; 736 } 737 738 pOwner = (PTOKEN_OWNER)MyMalloc(dwSize); 739 if (pOwner == NULL) 740 { 741 CloseHandle(hToken); 742 return ERROR_NOT_ENOUGH_MEMORY; 743 } 744 745 if (!GetTokenInformation(hToken, TokenOwner, pOwner, dwSize, &dwSize)) 746 { 747 goto fail; 748 } 749 750 if (!InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION)) 751 { 752 goto fail; 753 } 754 755 if (!SetSecurityDescriptorOwner(&SecDesc, pOwner->Owner, FALSE)) 756 { 757 goto fail; 758 } 759 760 if (!SetFileSecurityW(lpFileName, OWNER_SECURITY_INFORMATION, &SecDesc)) 761 { 762 goto fail; 763 } 764 765 MyFree(pOwner); 766 CloseHandle(hToken); 767 768 return ERROR_SUCCESS; 769 770 fail:; 771 dwError = GetLastError(); 772 773 MyFree(pOwner); 774 775 if (hToken != NULL) 776 CloseHandle(hToken); 777 778 return dwError; 779 } 780 781 782 /************************************************************************** 783 * RetreiveFileSecurity [SETUPAPI.@] 784 * 785 * Retrieve the security descriptor that is associated with the given file. 786 * 787 * PARAMS 788 * lpFileName [I] Name of the file 789 * 790 * RETURNS 791 * Success: ERROR_SUCCESS 792 * Failure: other 793 */ 794 DWORD WINAPI RetreiveFileSecurity(LPCWSTR lpFileName, 795 PSECURITY_DESCRIPTOR *pSecurityDescriptor) 796 { 797 PSECURITY_DESCRIPTOR SecDesc; 798 DWORD dwSize = 0x100; 799 DWORD dwError; 800 801 TRACE("%s %p\n", debugstr_w(lpFileName), pSecurityDescriptor); 802 803 SecDesc = MyMalloc(dwSize); 804 if (SecDesc == NULL) 805 return ERROR_NOT_ENOUGH_MEMORY; 806 807 if (GetFileSecurityW(lpFileName, OWNER_SECURITY_INFORMATION | 808 GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, 809 SecDesc, dwSize, &dwSize)) 810 { 811 *pSecurityDescriptor = SecDesc; 812 return ERROR_SUCCESS; 813 } 814 815 dwError = GetLastError(); 816 if (dwError != ERROR_INSUFFICIENT_BUFFER) 817 { 818 MyFree(SecDesc); 819 return dwError; 820 } 821 822 SecDesc = MyRealloc(SecDesc, dwSize); 823 if (SecDesc == NULL) 824 return ERROR_NOT_ENOUGH_MEMORY; 825 826 if (GetFileSecurityW(lpFileName, OWNER_SECURITY_INFORMATION | 827 GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, 828 SecDesc, dwSize, &dwSize)) 829 { 830 *pSecurityDescriptor = SecDesc; 831 return ERROR_SUCCESS; 832 } 833 834 dwError = GetLastError(); 835 MyFree(SecDesc); 836 837 return dwError; 838 } 839 840 841 /* 842 * See: https://msdn.microsoft.com/en-us/library/bb432397(v=vs.85).aspx 843 * for more information. 844 */ 845 DWORD GlobalSetupFlags = 0; 846 847 /*********************************************************************** 848 * pSetupGetGlobalFlags (SETUPAPI.@) 849 */ 850 DWORD WINAPI pSetupGetGlobalFlags(void) 851 { 852 return GlobalSetupFlags; 853 } 854 855 /*********************************************************************** 856 * pSetupModifyGlobalFlags (SETUPAPI.@) 857 */ 858 void WINAPI pSetupModifyGlobalFlags( DWORD mask, DWORD flags ) 859 { 860 FIXME( "stub\n" ); 861 GlobalSetupFlags = (GlobalSetupFlags & ~mask) | (flags & mask); 862 } 863 864 /*********************************************************************** 865 * pSetupSetGlobalFlags (SETUPAPI.@) 866 */ 867 void WINAPI pSetupSetGlobalFlags( DWORD flags ) 868 { 869 pSetupModifyGlobalFlags(0xFFFFFFFF, flags); 870 } 871 872 /*********************************************************************** 873 * SetupGetNonInteractiveMode (SETUPAPI.@) 874 */ 875 BOOL WINAPI SetupGetNonInteractiveMode(VOID) 876 { 877 return (GlobalSetupFlags & PSPGF_NONINTERACTIVE); 878 } 879 880 /*********************************************************************** 881 * SetupSetNonInteractiveMode (SETUPAPI.@) 882 */ 883 BOOL WINAPI SetupSetNonInteractiveMode(BOOL NonInteractiveFlag) 884 { 885 BOOL OldValue; 886 887 OldValue = (GlobalSetupFlags & PSPGF_NONINTERACTIVE); 888 pSetupModifyGlobalFlags(PSPGF_NONINTERACTIVE, 889 NonInteractiveFlag ? PSPGF_NONINTERACTIVE : 0); 890 891 return OldValue; 892 } 893 894 /*********************************************************************** 895 * AssertFail (SETUPAPI.@) 896 * 897 * Shows an assert fail error messagebox 898 * 899 * PARAMS 900 * lpFile [I] file where assert failed 901 * uLine [I] line number in file 902 * lpMessage [I] assert message 903 * 904 */ 905 VOID WINAPI AssertFail(LPSTR lpFile, UINT uLine, LPSTR lpMessage) 906 { 907 CHAR szModule[MAX_PATH]; 908 CHAR szBuffer[2048]; 909 LPSTR lpName; 910 // LPSTR lpBuffer; 911 912 TRACE("%s %u %s\n", lpFile, uLine, lpMessage); 913 914 GetModuleFileNameA(hInstance, szModule, MAX_PATH); 915 lpName = strrchr(szModule, '\\'); 916 if (lpName != NULL) 917 lpName++; 918 else 919 lpName = szModule; 920 921 wsprintfA(szBuffer, 922 "Assertion failure at line %u in file %s: %s\n\nCall DebugBreak()?", 923 uLine, lpFile, lpMessage); 924 925 if (MessageBoxA(NULL, szBuffer, lpName, MB_SETFOREGROUND | 926 MB_TASKMODAL | MB_ICONERROR | MB_YESNO) == IDYES) 927 DebugBreak(); 928 } 929 930 931 /************************************************************************** 932 * GetSetFileTimestamp [SETUPAPI.@] 933 * 934 * Gets or sets a files timestamp. 935 * 936 * PARAMS 937 * lpFileName [I] File name 938 * lpCreationTime [I/O] Creation time 939 * lpLastAccessTime [I/O] Last access time 940 * lpLastWriteTime [I/O] Last write time 941 * bSetFileTime [I] TRUE: Set file times 942 * FALSE: Get file times 943 * 944 * RETURNS 945 * Success: ERROR_SUCCESS 946 * Failure: other 947 */ 948 DWORD WINAPI GetSetFileTimestamp(LPCWSTR lpFileName, 949 LPFILETIME lpCreationTime, 950 LPFILETIME lpLastAccessTime, 951 LPFILETIME lpLastWriteTime, 952 BOOLEAN bSetFileTime) 953 { 954 HANDLE hFile; 955 BOOLEAN bRet; 956 DWORD dwError = ERROR_SUCCESS; 957 958 TRACE("%s %p %p %p %x\n", debugstr_w(lpFileName), lpCreationTime, 959 lpLastAccessTime, lpLastWriteTime, bSetFileTime); 960 961 hFile = CreateFileW(lpFileName, 962 bSetFileTime ? GENERIC_WRITE : GENERIC_READ, 963 FILE_SHARE_READ | FILE_SHARE_WRITE, 964 NULL, 965 OPEN_EXISTING, 966 0, 967 NULL); 968 969 if (hFile == INVALID_HANDLE_VALUE) 970 return GetLastError(); 971 972 if (bSetFileTime) 973 bRet = SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); 974 else 975 bRet = GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); 976 977 if (bRet == FALSE) 978 dwError = GetLastError(); 979 980 CloseHandle(hFile); 981 982 return dwError; 983 } 984 985 986 /************************************************************************** 987 * pSetupGetFileTitle [SETUPAPI.@] 988 * 989 * Returns a pointer to the last part of a fully qualified file name. 990 * 991 * PARAMS 992 * lpFileName [I] File name 993 * 994 * RETURNS 995 * Pointer to a files name. 996 */ 997 LPWSTR WINAPI 998 pSetupGetFileTitle(LPCWSTR lpFileName) 999 { 1000 LPWSTR ptr; 1001 LPWSTR ret; 1002 WCHAR c; 1003 1004 TRACE("%s\n", debugstr_w(lpFileName)); 1005 1006 ptr = (LPWSTR)lpFileName; 1007 ret = ptr; 1008 while (TRUE) 1009 { 1010 c = *ptr; 1011 1012 if (c == 0) 1013 break; 1014 1015 ptr++; 1016 if (c == (WCHAR)'\\' || c == (WCHAR)'/' || c == (WCHAR)':') 1017 ret = ptr; 1018 } 1019 1020 return ret; 1021 } 1022 1023 1024 /************************************************************************** 1025 * pSetupConcatenatePaths [SETUPAPI.@] 1026 * 1027 * Concatenates two paths. 1028 * 1029 * PARAMS 1030 * lpPath [I/O] Path to append path to 1031 * lpAppend [I] Path to append 1032 * dwBufferSize [I] Size of the path buffer 1033 * lpRequiredSize [O] Required size for the concatenated path. Optional 1034 * 1035 * RETURNS 1036 * Success: TRUE 1037 * Failure: FALSE 1038 */ 1039 BOOL WINAPI 1040 pSetupConcatenatePaths(LPWSTR lpPath, 1041 LPCWSTR lpAppend, 1042 DWORD dwBufferSize, 1043 LPDWORD lpRequiredSize) 1044 { 1045 DWORD dwPathSize; 1046 DWORD dwAppendSize; 1047 DWORD dwTotalSize; 1048 BOOL bBackslash = FALSE; 1049 1050 TRACE("%s %s %lu %p\n", debugstr_w(lpPath), debugstr_w(lpAppend), 1051 dwBufferSize, lpRequiredSize); 1052 1053 dwPathSize = lstrlenW(lpPath); 1054 1055 /* Ignore trailing backslash */ 1056 if (lpPath[dwPathSize - 1] == (WCHAR)'\\') 1057 dwPathSize--; 1058 1059 dwAppendSize = lstrlenW(lpAppend); 1060 1061 /* Does the source string have a leading backslash? */ 1062 if (lpAppend[0] == (WCHAR)'\\') 1063 { 1064 bBackslash = TRUE; 1065 dwAppendSize--; 1066 } 1067 1068 dwTotalSize = dwPathSize + dwAppendSize + 2; 1069 if (lpRequiredSize != NULL) 1070 *lpRequiredSize = dwTotalSize; 1071 1072 /* Append a backslash to the destination string */ 1073 if (bBackslash == FALSE) 1074 { 1075 if (dwPathSize < dwBufferSize) 1076 { 1077 lpPath[dwPathSize - 1] = (WCHAR)'\\'; 1078 dwPathSize++; 1079 } 1080 } 1081 1082 if (dwPathSize + dwAppendSize < dwBufferSize) 1083 { 1084 lstrcpynW(&lpPath[dwPathSize], 1085 lpAppend, 1086 dwAppendSize); 1087 } 1088 1089 if (dwBufferSize >= dwTotalSize) 1090 lpPath[dwTotalSize - 1] = 0; 1091 1092 return (dwBufferSize >= dwTotalSize); 1093 } 1094 1095 1096 /************************************************************************** 1097 * pSetupCenterWindowRelativeToParent [SETUPAPI.@] 1098 * 1099 * Centers a window relative to its parent. 1100 * 1101 * PARAMS 1102 * hwnd [I] Window to center. 1103 * 1104 * RETURNS 1105 * None 1106 */ 1107 VOID WINAPI 1108 pSetupCenterWindowRelativeToParent(HWND hwnd) 1109 { 1110 HWND hwndOwner; 1111 POINT ptOrigin; 1112 RECT rcWindow; 1113 RECT rcOwner; 1114 INT nWindowWidth, nWindowHeight; 1115 INT nOwnerWidth, nOwnerHeight; 1116 INT posX, posY; 1117 1118 hwndOwner = GetWindow(hwnd, GW_OWNER); 1119 if (hwndOwner == NULL) 1120 return; 1121 1122 ptOrigin.x = 0; 1123 ptOrigin.y = 0; 1124 ClientToScreen(hwndOwner, &ptOrigin); 1125 1126 GetWindowRect(hwnd, &rcWindow); 1127 GetClientRect(hwndOwner, &rcOwner); 1128 1129 nWindowWidth = rcWindow.right - rcWindow.left; 1130 nWindowHeight = rcWindow.bottom - rcWindow.top; 1131 1132 nOwnerWidth = rcOwner.right - rcOwner.left; 1133 nOwnerHeight = rcOwner.bottom - rcOwner.top; 1134 1135 posX = ((nOwnerWidth - nWindowWidth) / 2) + ptOrigin.x; 1136 posY = ((nOwnerHeight - nWindowHeight) / 2) + ptOrigin.y; 1137 1138 MoveWindow(hwnd, posX, posY, nWindowWidth, nWindowHeight, 0); 1139 } 1140 1141 1142 /************************************************************************** 1143 * pSetupGetVersionInfoFromImage [SETUPAPI.@] 1144 * 1145 * Retrieves version information for a given file. 1146 * 1147 * PARAMS 1148 * lpFileName [I] File name 1149 * lpFileVersion [O] Pointer to the full file version 1150 * lpVersionVarSize [O] Pointer to the size of the variable version 1151 * information 1152 * 1153 * RETURNS 1154 * Success: TRUE 1155 * Failure: FALSE 1156 */ 1157 BOOL WINAPI 1158 pSetupGetVersionInfoFromImage(LPWSTR lpFileName, 1159 PULARGE_INTEGER lpFileVersion, 1160 LPWORD lpVersionVarSize) 1161 { 1162 DWORD dwHandle; 1163 DWORD dwSize; 1164 LPVOID lpInfo; 1165 UINT uSize; 1166 VS_FIXEDFILEINFO *lpFixedInfo; 1167 LPWORD lpVarSize; 1168 1169 dwSize = GetFileVersionInfoSizeW(lpFileName, &dwHandle); 1170 if (dwSize == 0) 1171 return FALSE; 1172 1173 lpInfo = MyMalloc(dwSize); 1174 if (lpInfo == NULL) 1175 return FALSE; 1176 1177 if (!GetFileVersionInfoW(lpFileName, 0, dwSize, lpInfo)) 1178 { 1179 MyFree(lpInfo); 1180 return FALSE; 1181 } 1182 1183 if (!VerQueryValueW(lpInfo, BackSlash, 1184 (LPVOID*)&lpFixedInfo, &uSize)) 1185 { 1186 MyFree(lpInfo); 1187 return FALSE; 1188 } 1189 1190 lpFileVersion->LowPart = lpFixedInfo->dwFileVersionLS; 1191 lpFileVersion->HighPart = lpFixedInfo->dwFileVersionMS; 1192 1193 *lpVersionVarSize = 0; 1194 if (!VerQueryValueW(lpInfo, TranslationRegKey, 1195 (LPVOID*)&lpVarSize, &uSize)) 1196 { 1197 MyFree(lpInfo); 1198 return TRUE; 1199 } 1200 1201 if (uSize >= 4) 1202 { 1203 *lpVersionVarSize = *lpVarSize; 1204 } 1205 1206 MyFree(lpInfo); 1207 1208 return TRUE; 1209 } 1210 1211 /*********************************************************************** 1212 * SetupUninstallOEMInfW (SETUPAPI.@) 1213 */ 1214 BOOL WINAPI SetupUninstallOEMInfW( PCWSTR inf_file, DWORD flags, PVOID reserved ) 1215 { 1216 static const WCHAR infW[] = {'\\','i','n','f','\\',0}; 1217 WCHAR target[MAX_PATH]; 1218 1219 TRACE("%s, 0x%08x, %p\n", debugstr_w(inf_file), flags, reserved); 1220 1221 if (!inf_file) 1222 { 1223 SetLastError(ERROR_INVALID_PARAMETER); 1224 return FALSE; 1225 } 1226 1227 if (!GetWindowsDirectoryW( target, sizeof(target)/sizeof(WCHAR) )) return FALSE; 1228 1229 strcatW( target, infW ); 1230 strcatW( target, inf_file ); 1231 1232 if (flags & SUOI_FORCEDELETE) 1233 return DeleteFileW(target); 1234 1235 FIXME("not deleting %s\n", debugstr_w(target)); 1236 1237 return TRUE; 1238 } 1239 1240 /*********************************************************************** 1241 * SetupUninstallOEMInfA (SETUPAPI.@) 1242 */ 1243 BOOL WINAPI SetupUninstallOEMInfA( PCSTR inf_file, DWORD flags, PVOID reserved ) 1244 { 1245 BOOL ret; 1246 WCHAR *inf_fileW = NULL; 1247 1248 TRACE("%s, 0x%08x, %p\n", debugstr_a(inf_file), flags, reserved); 1249 1250 if (inf_file && !(inf_fileW = strdupAtoW( inf_file ))) return FALSE; 1251 ret = SetupUninstallOEMInfW( inf_fileW, flags, reserved ); 1252 HeapFree( GetProcessHeap(), 0, inf_fileW ); 1253 return ret; 1254 } 1255 1256 /*********************************************************************** 1257 * InstallCatalog (SETUPAPI.@) 1258 */ 1259 DWORD WINAPI InstallCatalog( LPCSTR catalog, LPCSTR basename, LPSTR fullname ) 1260 { 1261 FIXME("%s, %s, %p\n", debugstr_a(catalog), debugstr_a(basename), fullname); 1262 return 0; 1263 } 1264 1265 /*********************************************************************** 1266 * pSetupInstallCatalog (SETUPAPI.@) 1267 */ 1268 DWORD WINAPI pSetupInstallCatalog( LPCWSTR catalog, LPCWSTR basename, LPWSTR fullname ) 1269 { 1270 HCATADMIN admin; 1271 HCATINFO cat; 1272 1273 TRACE ("%s, %s, %p\n", debugstr_w(catalog), debugstr_w(basename), fullname); 1274 1275 if (!CryptCATAdminAcquireContext(&admin,NULL,0)) 1276 return GetLastError(); 1277 1278 if (!(cat = CryptCATAdminAddCatalog( admin, (PWSTR)catalog, (PWSTR)basename, 0 ))) 1279 { 1280 DWORD rc = GetLastError(); 1281 CryptCATAdminReleaseContext(admin, 0); 1282 return rc; 1283 } 1284 CryptCATAdminReleaseCatalogContext(admin, cat, 0); 1285 CryptCATAdminReleaseContext(admin,0); 1286 1287 if (fullname) 1288 FIXME("not returning full installed catalog path\n"); 1289 1290 return NO_ERROR; 1291 } 1292 1293 static UINT detect_compression_type( LPCWSTR file ) 1294 { 1295 DWORD size; 1296 HANDLE handle; 1297 UINT type = FILE_COMPRESSION_NONE; 1298 static const BYTE LZ_MAGIC[] = { 0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33 }; 1299 static const BYTE MSZIP_MAGIC[] = { 0x4b, 0x57, 0x41, 0x4a }; 1300 static const BYTE NTCAB_MAGIC[] = { 0x4d, 0x53, 0x43, 0x46 }; 1301 BYTE buffer[8]; 1302 1303 handle = CreateFileW( file, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL ); 1304 if (handle == INVALID_HANDLE_VALUE) 1305 { 1306 ERR("cannot open file %s\n", debugstr_w(file)); 1307 return FILE_COMPRESSION_NONE; 1308 } 1309 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL ) || size != sizeof(buffer)) 1310 { 1311 CloseHandle( handle ); 1312 return FILE_COMPRESSION_NONE; 1313 } 1314 if (!memcmp( buffer, LZ_MAGIC, sizeof(LZ_MAGIC) )) type = FILE_COMPRESSION_WINLZA; 1315 else if (!memcmp( buffer, MSZIP_MAGIC, sizeof(MSZIP_MAGIC) )) type = FILE_COMPRESSION_MSZIP; 1316 else if (!memcmp( buffer, NTCAB_MAGIC, sizeof(NTCAB_MAGIC) )) type = FILE_COMPRESSION_MSZIP; /* not a typo */ 1317 1318 CloseHandle( handle ); 1319 return type; 1320 } 1321 1322 static BOOL get_file_size( LPCWSTR file, DWORD *size ) 1323 { 1324 HANDLE handle; 1325 1326 handle = CreateFileW( file, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL ); 1327 if (handle == INVALID_HANDLE_VALUE) 1328 { 1329 ERR("cannot open file %s\n", debugstr_w(file)); 1330 return FALSE; 1331 } 1332 *size = GetFileSize( handle, NULL ); 1333 CloseHandle( handle ); 1334 return TRUE; 1335 } 1336 1337 static BOOL get_file_sizes_none( LPCWSTR source, DWORD *source_size, DWORD *target_size ) 1338 { 1339 DWORD size; 1340 1341 if (!get_file_size( source, &size )) return FALSE; 1342 if (source_size) *source_size = size; 1343 if (target_size) *target_size = size; 1344 return TRUE; 1345 } 1346 1347 static BOOL get_file_sizes_lz( LPCWSTR source, DWORD *source_size, DWORD *target_size ) 1348 { 1349 DWORD size; 1350 BOOL ret = TRUE; 1351 1352 if (source_size) 1353 { 1354 if (!get_file_size( source, &size )) ret = FALSE; 1355 else *source_size = size; 1356 } 1357 if (target_size) 1358 { 1359 INT file; 1360 OFSTRUCT of; 1361 1362 if ((file = LZOpenFileW( (LPWSTR)source, &of, OF_READ )) < 0) 1363 { 1364 ERR("cannot open source file for reading\n"); 1365 return FALSE; 1366 } 1367 *target_size = LZSeek( file, 0, 2 ); 1368 LZClose( file ); 1369 } 1370 return ret; 1371 } 1372 1373 static UINT CALLBACK file_compression_info_callback( PVOID context, UINT notification, UINT_PTR param1, UINT_PTR param2 ) 1374 { 1375 DWORD *size = context; 1376 FILE_IN_CABINET_INFO_W *info = (FILE_IN_CABINET_INFO_W *)param1; 1377 1378 switch (notification) 1379 { 1380 case SPFILENOTIFY_FILEINCABINET: 1381 { 1382 *size = info->FileSize; 1383 return FILEOP_SKIP; 1384 } 1385 default: return NO_ERROR; 1386 } 1387 } 1388 1389 static BOOL get_file_sizes_cab( LPCWSTR source, DWORD *source_size, DWORD *target_size ) 1390 { 1391 DWORD size; 1392 BOOL ret = TRUE; 1393 1394 if (source_size) 1395 { 1396 if (!get_file_size( source, &size )) ret = FALSE; 1397 else *source_size = size; 1398 } 1399 if (target_size) 1400 { 1401 ret = SetupIterateCabinetW( source, 0, file_compression_info_callback, target_size ); 1402 } 1403 return ret; 1404 } 1405 1406 /*********************************************************************** 1407 * SetupGetFileCompressionInfoExA (SETUPAPI.@) 1408 * 1409 * See SetupGetFileCompressionInfoExW. 1410 */ 1411 BOOL WINAPI SetupGetFileCompressionInfoExA( PCSTR source, PSTR name, DWORD len, PDWORD required, 1412 PDWORD source_size, PDWORD target_size, PUINT type ) 1413 { 1414 BOOL ret; 1415 WCHAR *nameW = NULL, *sourceW = NULL; 1416 DWORD nb_chars = 0; 1417 LPSTR nameA; 1418 1419 TRACE("%s, %p, %d, %p, %p, %p, %p\n", debugstr_a(source), name, len, required, 1420 source_size, target_size, type); 1421 1422 if (!source || !(sourceW = pSetupMultiByteToUnicode( source, CP_ACP ))) return FALSE; 1423 1424 if (name) 1425 { 1426 ret = SetupGetFileCompressionInfoExW( sourceW, NULL, 0, &nb_chars, NULL, NULL, NULL ); 1427 if (!(nameW = HeapAlloc( GetProcessHeap(), 0, nb_chars * sizeof(WCHAR) ))) 1428 { 1429 MyFree( sourceW ); 1430 return FALSE; 1431 } 1432 } 1433 ret = SetupGetFileCompressionInfoExW( sourceW, nameW, nb_chars, &nb_chars, source_size, target_size, type ); 1434 if (ret) 1435 { 1436 if ((nameA = pSetupUnicodeToMultiByte( nameW, CP_ACP ))) 1437 { 1438 if (name && len >= nb_chars) lstrcpyA( name, nameA ); 1439 else 1440 { 1441 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 1442 ret = FALSE; 1443 } 1444 MyFree( nameA ); 1445 } 1446 } 1447 if (required) *required = nb_chars; 1448 HeapFree( GetProcessHeap(), 0, nameW ); 1449 MyFree( sourceW ); 1450 1451 return ret; 1452 } 1453 1454 /*********************************************************************** 1455 * SetupGetFileCompressionInfoExW (SETUPAPI.@) 1456 * 1457 * Get compression type and compressed/uncompressed sizes of a given file. 1458 * 1459 * PARAMS 1460 * source [I] File to examine. 1461 * name [O] Actual filename used. 1462 * len [I] Length in characters of 'name' buffer. 1463 * required [O] Number of characters written to 'name'. 1464 * source_size [O] Size of compressed file. 1465 * target_size [O] Size of uncompressed file. 1466 * type [O] Compression type. 1467 * 1468 * RETURNS 1469 * Success: TRUE 1470 * Failure: FALSE 1471 */ 1472 BOOL WINAPI SetupGetFileCompressionInfoExW( PCWSTR source, PWSTR name, DWORD len, PDWORD required, 1473 PDWORD source_size, PDWORD target_size, PUINT type ) 1474 { 1475 UINT comp; 1476 BOOL ret = FALSE; 1477 DWORD source_len; 1478 1479 TRACE("%s, %p, %d, %p, %p, %p, %p\n", debugstr_w(source), name, len, required, 1480 source_size, target_size, type); 1481 1482 if (!source) return FALSE; 1483 1484 source_len = lstrlenW( source ) + 1; 1485 if (required) *required = source_len; 1486 if (name && len >= source_len) 1487 { 1488 lstrcpyW( name, source ); 1489 ret = TRUE; 1490 } 1491 else return FALSE; 1492 1493 comp = detect_compression_type( source ); 1494 if (type) *type = comp; 1495 1496 switch (comp) 1497 { 1498 case FILE_COMPRESSION_MSZIP: 1499 case FILE_COMPRESSION_NTCAB: ret = get_file_sizes_cab( source, source_size, target_size ); break; 1500 case FILE_COMPRESSION_NONE: ret = get_file_sizes_none( source, source_size, target_size ); break; 1501 case FILE_COMPRESSION_WINLZA: ret = get_file_sizes_lz( source, source_size, target_size ); break; 1502 default: break; 1503 } 1504 return ret; 1505 } 1506 1507 /*********************************************************************** 1508 * SetupGetFileCompressionInfoA (SETUPAPI.@) 1509 * 1510 * See SetupGetFileCompressionInfoW. 1511 */ 1512 DWORD WINAPI SetupGetFileCompressionInfoA( PCSTR source, PSTR *name, PDWORD source_size, 1513 PDWORD target_size, PUINT type ) 1514 { 1515 BOOL ret; 1516 DWORD error, required; 1517 LPSTR actual_name; 1518 1519 TRACE("%s, %p, %p, %p, %p\n", debugstr_a(source), name, source_size, target_size, type); 1520 1521 if (!source || !name || !source_size || !target_size || !type) 1522 return ERROR_INVALID_PARAMETER; 1523 1524 ret = SetupGetFileCompressionInfoExA( source, NULL, 0, &required, NULL, NULL, NULL ); 1525 if (!(actual_name = MyMalloc( required ))) return ERROR_NOT_ENOUGH_MEMORY; 1526 1527 ret = SetupGetFileCompressionInfoExA( source, actual_name, required, &required, 1528 source_size, target_size, type ); 1529 if (!ret) 1530 { 1531 error = GetLastError(); 1532 MyFree( actual_name ); 1533 return error; 1534 } 1535 *name = actual_name; 1536 return ERROR_SUCCESS; 1537 } 1538 1539 /*********************************************************************** 1540 * SetupGetFileCompressionInfoW (SETUPAPI.@) 1541 * 1542 * Get compression type and compressed/uncompressed sizes of a given file. 1543 * 1544 * PARAMS 1545 * source [I] File to examine. 1546 * name [O] Actual filename used. 1547 * source_size [O] Size of compressed file. 1548 * target_size [O] Size of uncompressed file. 1549 * type [O] Compression type. 1550 * 1551 * RETURNS 1552 * Success: ERROR_SUCCESS 1553 * Failure: Win32 error code. 1554 */ 1555 DWORD WINAPI SetupGetFileCompressionInfoW( PCWSTR source, PWSTR *name, PDWORD source_size, 1556 PDWORD target_size, PUINT type ) 1557 { 1558 BOOL ret; 1559 DWORD error, required; 1560 LPWSTR actual_name; 1561 1562 TRACE("%s, %p, %p, %p, %p\n", debugstr_w(source), name, source_size, target_size, type); 1563 1564 if (!source || !name || !source_size || !target_size || !type) 1565 return ERROR_INVALID_PARAMETER; 1566 1567 ret = SetupGetFileCompressionInfoExW( source, NULL, 0, &required, NULL, NULL, NULL ); 1568 if (!(actual_name = MyMalloc( required*sizeof(WCHAR) ))) return ERROR_NOT_ENOUGH_MEMORY; 1569 1570 ret = SetupGetFileCompressionInfoExW( source, actual_name, required, &required, 1571 source_size, target_size, type ); 1572 if (!ret) 1573 { 1574 error = GetLastError(); 1575 MyFree( actual_name ); 1576 return error; 1577 } 1578 *name = actual_name; 1579 return ERROR_SUCCESS; 1580 } 1581 1582 static DWORD decompress_file_lz( LPCWSTR source, LPCWSTR target ) 1583 { 1584 DWORD ret; 1585 LONG error; 1586 INT src, dst; 1587 OFSTRUCT sof, dof; 1588 1589 if ((src = LZOpenFileW( (LPWSTR)source, &sof, OF_READ )) < 0) 1590 { 1591 ERR("cannot open source file for reading\n"); 1592 return ERROR_FILE_NOT_FOUND; 1593 } 1594 if ((dst = LZOpenFileW( (LPWSTR)target, &dof, OF_CREATE )) < 0) 1595 { 1596 ERR("cannot open target file for writing\n"); 1597 LZClose( src ); 1598 return ERROR_FILE_NOT_FOUND; 1599 } 1600 if ((error = LZCopy( src, dst )) >= 0) ret = ERROR_SUCCESS; 1601 else 1602 { 1603 WARN("failed to decompress file %d\n", error); 1604 ret = ERROR_INVALID_DATA; 1605 } 1606 1607 LZClose( src ); 1608 LZClose( dst ); 1609 return ret; 1610 } 1611 1612 struct callback_context 1613 { 1614 BOOL has_extracted; 1615 LPCWSTR target; 1616 }; 1617 1618 static UINT CALLBACK decompress_or_copy_callback( PVOID context, UINT notification, UINT_PTR param1, UINT_PTR param2 ) 1619 { 1620 struct callback_context *context_info = context; 1621 FILE_IN_CABINET_INFO_W *info = (FILE_IN_CABINET_INFO_W *)param1; 1622 1623 switch (notification) 1624 { 1625 case SPFILENOTIFY_FILEINCABINET: 1626 { 1627 if (context_info->has_extracted) 1628 return FILEOP_ABORT; 1629 1630 TRACE("Requesting extraction of cabinet file %s\n", 1631 wine_dbgstr_w(info->NameInCabinet)); 1632 strcpyW( info->FullTargetName, context_info->target ); 1633 context_info->has_extracted = TRUE; 1634 return FILEOP_DOIT; 1635 } 1636 default: return NO_ERROR; 1637 } 1638 } 1639 1640 static DWORD decompress_file_cab( LPCWSTR source, LPCWSTR target ) 1641 { 1642 struct callback_context context = {0, target}; 1643 BOOL ret; 1644 1645 ret = SetupIterateCabinetW( source, 0, decompress_or_copy_callback, &context ); 1646 1647 if (ret) return ERROR_SUCCESS; 1648 else return GetLastError(); 1649 } 1650 1651 /*********************************************************************** 1652 * SetupDecompressOrCopyFileA (SETUPAPI.@) 1653 * 1654 * See SetupDecompressOrCopyFileW. 1655 */ 1656 DWORD WINAPI SetupDecompressOrCopyFileA( PCSTR source, PCSTR target, PUINT type ) 1657 { 1658 DWORD ret = 0; 1659 WCHAR *sourceW = NULL, *targetW = NULL; 1660 1661 if (source && !(sourceW = pSetupMultiByteToUnicode( source, CP_ACP ))) return FALSE; 1662 if (target && !(targetW = pSetupMultiByteToUnicode( target, CP_ACP ))) 1663 { 1664 MyFree( sourceW ); 1665 return ERROR_NOT_ENOUGH_MEMORY; 1666 } 1667 1668 ret = SetupDecompressOrCopyFileW( sourceW, targetW, type ); 1669 1670 MyFree( sourceW ); 1671 MyFree( targetW ); 1672 1673 return ret; 1674 } 1675 1676 /*********************************************************************** 1677 * SetupDecompressOrCopyFileW (SETUPAPI.@) 1678 * 1679 * Copy a file and decompress it if needed. 1680 * 1681 * PARAMS 1682 * source [I] File to copy. 1683 * target [I] Filename of the copy. 1684 * type [I] Compression type. 1685 * 1686 * RETURNS 1687 * Success: ERROR_SUCCESS 1688 * Failure: Win32 error code. 1689 */ 1690 DWORD WINAPI SetupDecompressOrCopyFileW( PCWSTR source, PCWSTR target, PUINT type ) 1691 { 1692 UINT comp; 1693 DWORD ret = ERROR_INVALID_PARAMETER; 1694 1695 if (!source || !target) return ERROR_INVALID_PARAMETER; 1696 1697 if (!type) comp = detect_compression_type( source ); 1698 else comp = *type; 1699 1700 switch (comp) 1701 { 1702 case FILE_COMPRESSION_NONE: 1703 if (CopyFileW( source, target, FALSE )) ret = ERROR_SUCCESS; 1704 else ret = GetLastError(); 1705 break; 1706 case FILE_COMPRESSION_WINLZA: 1707 ret = decompress_file_lz( source, target ); 1708 break; 1709 case FILE_COMPRESSION_NTCAB: 1710 case FILE_COMPRESSION_MSZIP: 1711 ret = decompress_file_cab( source, target ); 1712 break; 1713 default: 1714 WARN("unknown compression type %d\n", comp); 1715 break; 1716 } 1717 1718 TRACE("%s -> %s %d\n", debugstr_w(source), debugstr_w(target), comp); 1719 return ret; 1720 } 1721 1722 /* 1723 * implemented (used by pSetupGuidFromString) 1724 */ 1725 static BOOL TrimGuidString(PCWSTR szString, LPWSTR szNewString) 1726 { 1727 WCHAR szBuffer[39]; 1728 INT Index; 1729 1730 if (wcslen(szString) == 38) 1731 { 1732 if ((szString[0] == L'{') && (szString[37] == L'}')) 1733 { 1734 for (Index = 0; Index < wcslen(szString); Index++) 1735 szBuffer[Index] = szString[Index + 1]; 1736 1737 szBuffer[36] = L'\0'; 1738 wcscpy(szNewString, szBuffer); 1739 return TRUE; 1740 } 1741 } 1742 szNewString[0] = L'\0'; 1743 return FALSE; 1744 } 1745 1746 /* 1747 * implemented 1748 */ 1749 DWORD 1750 WINAPI 1751 pSetupGuidFromString(PCWSTR pString, LPGUID lpGUID) 1752 { 1753 RPC_STATUS Status; 1754 WCHAR szBuffer[39]; 1755 1756 if (!TrimGuidString(pString, szBuffer)) 1757 { 1758 return RPC_S_INVALID_STRING_UUID; 1759 } 1760 1761 Status = UuidFromStringW(szBuffer, lpGUID); 1762 if (Status != RPC_S_OK) 1763 { 1764 return RPC_S_INVALID_STRING_UUID; 1765 } 1766 1767 return NO_ERROR; 1768 } 1769 1770 /* 1771 * implemented 1772 */ 1773 DWORD 1774 WINAPI 1775 pSetupStringFromGuid(LPGUID lpGUID, PWSTR pString, DWORD dwStringLen) 1776 { 1777 RPC_STATUS Status; 1778 RPC_WSTR rpcBuffer; 1779 WCHAR szBuffer[39]; 1780 1781 if (dwStringLen < 39) 1782 { 1783 return ERROR_INSUFFICIENT_BUFFER; 1784 } 1785 1786 Status = UuidToStringW(lpGUID, &rpcBuffer); 1787 if (Status != RPC_S_OK) 1788 { 1789 return Status; 1790 } 1791 1792 wcscpy(szBuffer, L"{"); 1793 wcscat(szBuffer, rpcBuffer); 1794 wcscat(szBuffer, L"}"); 1795 1796 wcscpy(pString, szBuffer); 1797 1798 RpcStringFreeW(&rpcBuffer); 1799 return NO_ERROR; 1800 } 1801 1802 /* 1803 * implemented 1804 */ 1805 BOOL 1806 WINAPI 1807 pSetupIsGuidNull(LPGUID lpGUID) 1808 { 1809 return IsEqualGUID(lpGUID, &GUID_NULL); 1810 } 1811 1812 /* 1813 * implemented 1814 */ 1815 BOOL 1816 WINAPI 1817 pSetupIsUserAdmin(VOID) 1818 { 1819 SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY}; 1820 BOOL bResult = FALSE; 1821 PSID lpSid; 1822 1823 if (!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, 1824 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, 1825 &lpSid)) 1826 { 1827 return FALSE; 1828 } 1829 1830 if (!CheckTokenMembership(NULL, lpSid, &bResult)) 1831 { 1832 bResult = FALSE; 1833 } 1834 1835 FreeSid(lpSid); 1836 1837 return bResult; 1838 } 1839 1840 /*********************************************************************** 1841 * SetupInitializeFileLogW(SETUPAPI.@) 1842 */ 1843 HSPFILELOG WINAPI SetupInitializeFileLogW(LPCWSTR LogFileName, DWORD Flags) 1844 { 1845 struct FileLog * Log; 1846 HANDLE hLog; 1847 WCHAR Windir[MAX_PATH]; 1848 DWORD ret; 1849 1850 TRACE("%s, 0x%x\n",debugstr_w(LogFileName),Flags); 1851 1852 if (Flags & SPFILELOG_SYSTEMLOG) 1853 { 1854 if (!pSetupIsUserAdmin() && !(Flags & SPFILELOG_QUERYONLY)) 1855 { 1856 /* insufficient privileges */ 1857 SetLastError(ERROR_ACCESS_DENIED); 1858 return INVALID_HANDLE_VALUE; 1859 } 1860 1861 if (LogFileName || (Flags & SPFILELOG_FORCENEW)) 1862 { 1863 /* invalid parameter */ 1864 SetLastError(ERROR_INVALID_PARAMETER); 1865 return INVALID_HANDLE_VALUE; 1866 } 1867 1868 ret = GetSystemWindowsDirectoryW(Windir, MAX_PATH); 1869 if (!ret || ret >= MAX_PATH) 1870 { 1871 /* generic failure */ 1872 return INVALID_HANDLE_VALUE; 1873 } 1874 1875 /* append path */ 1876 wcscat(Windir, L"repair\\setup.log"); 1877 } 1878 else 1879 { 1880 if (!LogFileName) 1881 { 1882 /* invalid parameter */ 1883 SetLastError(ERROR_INVALID_PARAMETER); 1884 return INVALID_HANDLE_VALUE; 1885 } 1886 /* copy filename */ 1887 wcsncpy(Windir, LogFileName, MAX_PATH); 1888 } 1889 1890 if (FileExists(Windir, NULL)) 1891 { 1892 /* take ownership */ 1893 ret = TakeOwnershipOfFile(Windir); 1894 1895 if (ret != ERROR_SUCCESS) 1896 { 1897 /* failed */ 1898 SetLastError(ret); 1899 return INVALID_HANDLE_VALUE; 1900 } 1901 1902 if (!SetFileAttributesW(Windir, FILE_ATTRIBUTE_NORMAL)) 1903 { 1904 /* failed */ 1905 return INVALID_HANDLE_VALUE; 1906 } 1907 1908 if ((Flags & SPFILELOG_FORCENEW)) 1909 { 1910 if (!DeleteFileW(Windir)) 1911 { 1912 /* failed */ 1913 return INVALID_HANDLE_VALUE; 1914 } 1915 } 1916 } 1917 1918 /* open log file */ 1919 hLog = CreateFileW(Windir, 1920 (Flags & SPFILELOG_QUERYONLY) ? GENERIC_READ : GENERIC_WRITE, 1921 FILE_SHARE_READ | FILE_SHARE_WRITE, 1922 NULL, 1923 OPEN_ALWAYS, 1924 FILE_ATTRIBUTE_NORMAL, 1925 NULL); 1926 1927 if (hLog == INVALID_HANDLE_VALUE) 1928 { 1929 /* failed */ 1930 return INVALID_HANDLE_VALUE; 1931 } 1932 1933 /* close log handle */ 1934 CloseHandle(hLog); 1935 1936 /* allocate file log struct */ 1937 Log = HeapAlloc(GetProcessHeap(), 0, sizeof(struct FileLog)); 1938 if (!Log) 1939 { 1940 /* not enough memory */ 1941 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1942 return INVALID_HANDLE_VALUE; 1943 } 1944 1945 /* initialize log */ 1946 Log->LogName = HeapAlloc(GetProcessHeap(), 0, (wcslen(Windir)+1) * sizeof(WCHAR)); 1947 if (!Log->LogName) 1948 { 1949 /* not enough memory */ 1950 HeapFree(GetProcessHeap(), 0, Log); 1951 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1952 return INVALID_HANDLE_VALUE; 1953 } 1954 1955 wcscpy(Log->LogName, Windir); 1956 Log->ReadOnly = (Flags & SPFILELOG_QUERYONLY); 1957 Log->SystemLog = (Flags & SPFILELOG_SYSTEMLOG); 1958 1959 return (HSPFILELOG)Log; 1960 } 1961 1962 /*********************************************************************** 1963 * SetupInitializeFileLogA(SETUPAPI.@) 1964 */ 1965 HSPFILELOG WINAPI SetupInitializeFileLogA(LPCSTR LogFileName, DWORD Flags) 1966 { 1967 HSPFILELOG hLog; 1968 LPWSTR LogFileNameW = NULL; 1969 1970 TRACE("%s, 0x%x\n",debugstr_a(LogFileName),Flags); 1971 1972 if (LogFileName) 1973 { 1974 LogFileNameW = strdupAtoW(LogFileName); 1975 1976 if (!LogFileNameW) 1977 { 1978 /* not enough memory */ 1979 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1980 return INVALID_HANDLE_VALUE; 1981 } 1982 1983 hLog = SetupInitializeFileLogW(LogFileNameW, Flags); 1984 HeapFree(GetProcessHeap(), 0, LogFileNameW); 1985 } 1986 else 1987 { 1988 hLog = SetupInitializeFileLogW(NULL, Flags); 1989 } 1990 1991 return hLog; 1992 } 1993 1994 /*********************************************************************** 1995 * SetupTerminateFileLog(SETUPAPI.@) 1996 */ 1997 BOOL WINAPI SetupTerminateFileLog(HANDLE FileLogHandle) 1998 { 1999 struct FileLog * Log; 2000 2001 TRACE ("%p\n",FileLogHandle); 2002 2003 Log = (struct FileLog *)FileLogHandle; 2004 2005 /* free file log handle */ 2006 HeapFree(GetProcessHeap(), 0, Log->LogName); 2007 HeapFree(GetProcessHeap(), 0, Log); 2008 2009 SetLastError(ERROR_SUCCESS); 2010 2011 return TRUE; 2012 } 2013 2014 /*********************************************************************** 2015 * SetupCloseLog(SETUPAPI.@) 2016 */ 2017 void WINAPI SetupCloseLog(void) 2018 { 2019 EnterCriticalSection(&setupapi_cs); 2020 2021 CloseHandle(setupact); 2022 setupact = INVALID_HANDLE_VALUE; 2023 2024 CloseHandle(setuperr); 2025 setuperr = INVALID_HANDLE_VALUE; 2026 2027 LeaveCriticalSection(&setupapi_cs); 2028 } 2029 2030 /*********************************************************************** 2031 * SetupOpenLog(SETUPAPI.@) 2032 */ 2033 BOOL WINAPI SetupOpenLog(BOOL reserved) 2034 { 2035 WCHAR path[MAX_PATH]; 2036 2037 static const WCHAR setupactlog[] = {'\\','s','e','t','u','p','a','c','t','.','l','o','g',0}; 2038 static const WCHAR setuperrlog[] = {'\\','s','e','t','u','p','e','r','r','.','l','o','g',0}; 2039 2040 EnterCriticalSection(&setupapi_cs); 2041 2042 if (setupact != INVALID_HANDLE_VALUE && setuperr != INVALID_HANDLE_VALUE) 2043 { 2044 LeaveCriticalSection(&setupapi_cs); 2045 return TRUE; 2046 } 2047 2048 GetWindowsDirectoryW(path, MAX_PATH); 2049 lstrcatW(path, setupactlog); 2050 2051 setupact = CreateFileW(path, FILE_GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 2052 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 2053 if (setupact == INVALID_HANDLE_VALUE) 2054 { 2055 LeaveCriticalSection(&setupapi_cs); 2056 return FALSE; 2057 } 2058 2059 SetFilePointer(setupact, 0, NULL, FILE_END); 2060 2061 GetWindowsDirectoryW(path, MAX_PATH); 2062 lstrcatW(path, setuperrlog); 2063 2064 setuperr = CreateFileW(path, FILE_GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 2065 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 2066 if (setuperr == INVALID_HANDLE_VALUE) 2067 { 2068 CloseHandle(setupact); 2069 setupact = INVALID_HANDLE_VALUE; 2070 LeaveCriticalSection(&setupapi_cs); 2071 return FALSE; 2072 } 2073 2074 SetFilePointer(setuperr, 0, NULL, FILE_END); 2075 2076 LeaveCriticalSection(&setupapi_cs); 2077 2078 return TRUE; 2079 } 2080 2081 /*********************************************************************** 2082 * SetupLogErrorA(SETUPAPI.@) 2083 */ 2084 BOOL WINAPI SetupLogErrorA(LPCSTR message, LogSeverity severity) 2085 { 2086 static const char null[] = "(null)"; 2087 BOOL ret; 2088 DWORD written; 2089 DWORD len; 2090 2091 EnterCriticalSection(&setupapi_cs); 2092 2093 if (setupact == INVALID_HANDLE_VALUE || setuperr == INVALID_HANDLE_VALUE) 2094 { 2095 SetLastError(ERROR_FILE_INVALID); 2096 ret = FALSE; 2097 goto done; 2098 } 2099 2100 if (message == NULL) 2101 message = null; 2102 2103 len = lstrlenA(message); 2104 2105 ret = WriteFile(setupact, message, len, &written, NULL); 2106 if (!ret) 2107 goto done; 2108 2109 if (severity >= LogSevMaximum) 2110 { 2111 ret = FALSE; 2112 goto done; 2113 } 2114 2115 if (severity > LogSevInformation) 2116 ret = WriteFile(setuperr, message, len, &written, NULL); 2117 2118 done: 2119 LeaveCriticalSection(&setupapi_cs); 2120 return ret; 2121 } 2122 2123 /*********************************************************************** 2124 * SetupLogErrorW(SETUPAPI.@) 2125 */ 2126 BOOL WINAPI SetupLogErrorW(LPCWSTR message, LogSeverity severity) 2127 { 2128 LPSTR msg = NULL; 2129 DWORD len; 2130 BOOL ret; 2131 2132 if (message) 2133 { 2134 len = WideCharToMultiByte(CP_ACP, 0, message, -1, NULL, 0, NULL, NULL); 2135 msg = HeapAlloc(GetProcessHeap(), 0, len); 2136 if (msg == NULL) 2137 { 2138 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2139 return FALSE; 2140 } 2141 WideCharToMultiByte(CP_ACP, 0, message, -1, msg, len, NULL, NULL); 2142 } 2143 2144 /* This is the normal way to proceed. The log files are ASCII files 2145 * and W is to be converted. 2146 */ 2147 ret = SetupLogErrorA(msg, severity); 2148 2149 HeapFree(GetProcessHeap(), 0, msg); 2150 return ret; 2151 } 2152