1 /* 2 * ReactOS kernel 3 * Copyright (C) 2004 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS system libraries 22 * FILE: dll/win32/userenv/environment.c 23 * PURPOSE: User environment functions 24 * PROGRAMMER: Eric Kohl 25 */ 26 27 #include "precomp.h" 28 29 #define NDEBUG 30 #include <debug.h> 31 32 static 33 BOOL 34 SetUserEnvironmentVariable(PWSTR* Environment, 35 LPWSTR lpName, 36 LPWSTR lpValue, 37 BOOL bExpand) 38 { 39 NTSTATUS Status; 40 UNICODE_STRING Name; 41 UNICODE_STRING SrcValue, DstValue; 42 ULONG Length; 43 PVOID Buffer = NULL; 44 WCHAR ShortName[MAX_PATH]; 45 46 if (bExpand) 47 { 48 RtlInitUnicodeString(&SrcValue, lpValue); 49 50 Length = 2 * MAX_PATH * sizeof(WCHAR); 51 52 DstValue.Length = 0; 53 DstValue.MaximumLength = Length; 54 DstValue.Buffer = Buffer = LocalAlloc(LPTR, Length); 55 if (DstValue.Buffer == NULL) 56 { 57 DPRINT1("LocalAlloc() failed\n"); 58 return FALSE; 59 } 60 61 Status = RtlExpandEnvironmentStrings_U(*Environment, 62 &SrcValue, 63 &DstValue, 64 &Length); 65 if (!NT_SUCCESS(Status)) 66 { 67 DPRINT1("RtlExpandEnvironmentStrings_U() failed (Status %lx)\n", Status); 68 DPRINT1("Length %lu\n", Length); 69 70 if (Buffer) 71 LocalFree(Buffer); 72 73 return FALSE; 74 } 75 } 76 else 77 { 78 RtlInitUnicodeString(&DstValue, lpValue); 79 } 80 81 if (!_wcsicmp(lpName, L"TEMP") || !_wcsicmp(lpName, L"TMP")) 82 { 83 if (GetShortPathNameW(DstValue.Buffer, ShortName, ARRAYSIZE(ShortName))) 84 { 85 RtlInitUnicodeString(&DstValue, ShortName); 86 } 87 else 88 { 89 DPRINT("GetShortPathNameW() failed for %S (Error %lu)\n", DstValue.Buffer, GetLastError()); 90 } 91 } 92 93 RtlInitUnicodeString(&Name, lpName); 94 95 DPRINT("Value: %wZ\n", &DstValue); 96 97 Status = RtlSetEnvironmentVariable(Environment, 98 &Name, 99 &DstValue); 100 101 if (Buffer) 102 LocalFree(Buffer); 103 104 if (!NT_SUCCESS(Status)) 105 { 106 DPRINT1("RtlSetEnvironmentVariable() failed (Status %lx)\n", Status); 107 return FALSE; 108 } 109 110 return TRUE; 111 } 112 113 114 static 115 BOOL 116 AppendUserEnvironmentVariable(PWSTR* Environment, 117 LPWSTR lpName, 118 LPWSTR lpValue) 119 { 120 NTSTATUS Status; 121 UNICODE_STRING Name, Value; 122 123 RtlInitUnicodeString(&Name, lpName); 124 125 Value.Length = 0; 126 Value.MaximumLength = 1024 * sizeof(WCHAR); 127 Value.Buffer = LocalAlloc(LPTR, Value.MaximumLength); 128 if (Value.Buffer == NULL) 129 return FALSE; 130 131 Value.Buffer[0] = UNICODE_NULL; 132 133 Status = RtlQueryEnvironmentVariable_U(*Environment, 134 &Name, 135 &Value); 136 if (NT_SUCCESS(Status)) 137 RtlAppendUnicodeToString(&Value, L";"); 138 139 RtlAppendUnicodeToString(&Value, lpValue); 140 141 Status = RtlSetEnvironmentVariable(Environment, 142 &Name, 143 &Value); 144 LocalFree(Value.Buffer); 145 if (!NT_SUCCESS(Status)) 146 { 147 DPRINT1("RtlSetEnvironmentVariable() failed (Status %lx)\n", Status); 148 return FALSE; 149 } 150 151 return TRUE; 152 } 153 154 155 static 156 HKEY 157 GetCurrentUserKey(HANDLE hToken) 158 { 159 LONG Error; 160 UNICODE_STRING SidString; 161 HKEY hKey; 162 163 if (!GetUserSidStringFromToken(hToken, &SidString)) 164 { 165 DPRINT1("GetUserSidFromToken() failed\n"); 166 return NULL; 167 } 168 169 Error = RegOpenKeyExW(HKEY_USERS, 170 SidString.Buffer, 171 0, 172 MAXIMUM_ALLOWED, 173 &hKey); 174 if (Error != ERROR_SUCCESS) 175 { 176 DPRINT1("RegOpenKeyExW() failed (Error %ld)\n", Error); 177 RtlFreeUnicodeString(&SidString); 178 SetLastError((DWORD)Error); 179 return NULL; 180 } 181 182 RtlFreeUnicodeString(&SidString); 183 184 return hKey; 185 } 186 187 188 static 189 BOOL 190 GetUserAndDomainName(IN HANDLE hToken, 191 OUT LPWSTR *UserName, 192 OUT LPWSTR *DomainName) 193 { 194 BOOL bRet = TRUE; 195 PSID Sid = NULL; 196 LPWSTR lpUserName = NULL; 197 LPWSTR lpDomainName = NULL; 198 DWORD cbUserName = 0; 199 DWORD cbDomainName = 0; 200 SID_NAME_USE SidNameUse; 201 202 Sid = GetUserSid(hToken); 203 if (Sid == NULL) 204 { 205 DPRINT1("GetUserSid() failed\n"); 206 return FALSE; 207 } 208 209 if (!LookupAccountSidW(NULL, 210 Sid, 211 NULL, 212 &cbUserName, 213 NULL, 214 &cbDomainName, 215 &SidNameUse)) 216 { 217 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 218 { 219 bRet = FALSE; 220 goto done; 221 } 222 } 223 224 lpUserName = LocalAlloc(LPTR, cbUserName * sizeof(WCHAR)); 225 if (lpUserName == NULL) 226 { 227 bRet = FALSE; 228 goto done; 229 } 230 231 lpDomainName = LocalAlloc(LPTR, cbDomainName * sizeof(WCHAR)); 232 if (lpDomainName == NULL) 233 { 234 bRet = FALSE; 235 goto done; 236 } 237 238 if (!LookupAccountSidW(NULL, 239 Sid, 240 lpUserName, 241 &cbUserName, 242 lpDomainName, 243 &cbDomainName, 244 &SidNameUse)) 245 { 246 bRet = FALSE; 247 goto done; 248 } 249 250 *UserName = lpUserName; 251 *DomainName = lpDomainName; 252 253 done: 254 if (bRet == FALSE) 255 { 256 if (lpUserName != NULL) 257 LocalFree(lpUserName); 258 259 if (lpDomainName != NULL) 260 LocalFree(lpDomainName); 261 } 262 263 LocalFree(Sid); 264 265 return bRet; 266 } 267 268 269 static 270 BOOL 271 SetUserEnvironment(PWSTR* Environment, 272 HKEY hKey, 273 LPWSTR lpSubKeyName) 274 { 275 LONG Error; 276 HKEY hEnvKey; 277 DWORD dwValues; 278 DWORD dwMaxValueNameLength; 279 DWORD dwMaxValueDataLength; 280 DWORD dwValueNameLength; 281 DWORD dwValueDataLength; 282 DWORD dwType; 283 DWORD i; 284 LPWSTR lpValueName; 285 LPWSTR lpValueData; 286 287 Error = RegOpenKeyExW(hKey, 288 lpSubKeyName, 289 0, 290 KEY_QUERY_VALUE, 291 &hEnvKey); 292 if (Error != ERROR_SUCCESS) 293 { 294 DPRINT1("RegOpenKeyExW() failed (Error %ld)\n", Error); 295 SetLastError((DWORD)Error); 296 return FALSE; 297 } 298 299 Error = RegQueryInfoKey(hEnvKey, 300 NULL, 301 NULL, 302 NULL, 303 NULL, 304 NULL, 305 NULL, 306 &dwValues, 307 &dwMaxValueNameLength, 308 &dwMaxValueDataLength, 309 NULL, 310 NULL); 311 if (Error != ERROR_SUCCESS) 312 { 313 DPRINT1("RegQueryInforKey() failed (Error %ld)\n", Error); 314 RegCloseKey(hEnvKey); 315 SetLastError((DWORD)Error); 316 return FALSE; 317 } 318 319 if (dwValues == 0) 320 { 321 RegCloseKey(hEnvKey); 322 return TRUE; 323 } 324 325 /* Allocate buffers */ 326 dwMaxValueNameLength++; 327 lpValueName = LocalAlloc(LPTR, dwMaxValueNameLength * sizeof(WCHAR)); 328 if (lpValueName == NULL) 329 { 330 RegCloseKey(hEnvKey); 331 return FALSE; 332 } 333 334 lpValueData = LocalAlloc(LPTR, dwMaxValueDataLength); 335 if (lpValueData == NULL) 336 { 337 LocalFree(lpValueName); 338 RegCloseKey(hEnvKey); 339 return FALSE; 340 } 341 342 /* Enumerate values */ 343 for (i = 0; i < dwValues; i++) 344 { 345 dwValueNameLength = dwMaxValueNameLength; 346 dwValueDataLength = dwMaxValueDataLength; 347 348 Error = RegEnumValueW(hEnvKey, 349 i, 350 lpValueName, 351 &dwValueNameLength, 352 NULL, 353 &dwType, 354 (LPBYTE)lpValueData, 355 &dwValueDataLength); 356 if (Error == ERROR_SUCCESS) 357 { 358 if (!_wcsicmp(lpValueName, L"PATH")) 359 { 360 /* Append 'Path' environment variable */ 361 AppendUserEnvironmentVariable(Environment, 362 lpValueName, 363 lpValueData); 364 } 365 else 366 { 367 /* Set environment variable */ 368 SetUserEnvironmentVariable(Environment, 369 lpValueName, 370 lpValueData, 371 (dwType == REG_EXPAND_SZ)); 372 } 373 } 374 else 375 { 376 LocalFree(lpValueData); 377 LocalFree(lpValueName); 378 RegCloseKey(hEnvKey); 379 380 return FALSE; 381 } 382 } 383 384 LocalFree(lpValueData); 385 LocalFree(lpValueName); 386 RegCloseKey(hEnvKey); 387 388 return TRUE; 389 } 390 391 392 static 393 BOOL 394 SetSystemEnvironment(PWSTR* Environment) 395 { 396 LONG Error; 397 HKEY hEnvKey; 398 DWORD dwValues; 399 DWORD dwMaxValueNameLength; 400 DWORD dwMaxValueDataLength; 401 DWORD dwValueNameLength; 402 DWORD dwValueDataLength; 403 DWORD dwType; 404 DWORD i; 405 LPWSTR lpValueName; 406 LPWSTR lpValueData; 407 408 Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 409 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 410 0, 411 KEY_QUERY_VALUE, 412 &hEnvKey); 413 if (Error != ERROR_SUCCESS) 414 { 415 DPRINT1("RegOpenKeyExW() failed (Error %ld)\n", Error); 416 return FALSE; 417 } 418 419 Error = RegQueryInfoKey(hEnvKey, 420 NULL, 421 NULL, 422 NULL, 423 NULL, 424 NULL, 425 NULL, 426 &dwValues, 427 &dwMaxValueNameLength, 428 &dwMaxValueDataLength, 429 NULL, 430 NULL); 431 if (Error != ERROR_SUCCESS) 432 { 433 DPRINT1("RegQueryInforKey() failed (Error %ld)\n", Error); 434 RegCloseKey(hEnvKey); 435 return FALSE; 436 } 437 438 if (dwValues == 0) 439 { 440 RegCloseKey(hEnvKey); 441 return TRUE; 442 } 443 444 /* Allocate buffers */ 445 dwMaxValueNameLength++; 446 lpValueName = LocalAlloc(LPTR, dwMaxValueNameLength * sizeof(WCHAR)); 447 if (lpValueName == NULL) 448 { 449 RegCloseKey(hEnvKey); 450 return FALSE; 451 } 452 453 lpValueData = LocalAlloc(LPTR, dwMaxValueDataLength); 454 if (lpValueData == NULL) 455 { 456 LocalFree(lpValueName); 457 RegCloseKey(hEnvKey); 458 return FALSE; 459 } 460 461 /* Enumerate values */ 462 for (i = 0; i < dwValues; i++) 463 { 464 dwValueNameLength = dwMaxValueNameLength; 465 dwValueDataLength = dwMaxValueDataLength; 466 467 Error = RegEnumValueW(hEnvKey, 468 i, 469 lpValueName, 470 &dwValueNameLength, 471 NULL, 472 &dwType, 473 (LPBYTE)lpValueData, 474 &dwValueDataLength); 475 if (Error == ERROR_SUCCESS) 476 { 477 /* Set environment variable */ 478 SetUserEnvironmentVariable(Environment, 479 lpValueName, 480 lpValueData, 481 (dwType == REG_EXPAND_SZ)); 482 } 483 else 484 { 485 LocalFree(lpValueData); 486 LocalFree(lpValueName); 487 RegCloseKey(hEnvKey); 488 489 return FALSE; 490 } 491 } 492 493 LocalFree(lpValueData); 494 LocalFree(lpValueName); 495 RegCloseKey(hEnvKey); 496 497 return TRUE; 498 } 499 500 501 BOOL 502 WINAPI 503 CreateEnvironmentBlock(OUT LPVOID *lpEnvironment, 504 IN HANDLE hToken, 505 IN BOOL bInherit) 506 { 507 NTSTATUS Status; 508 LONG lError; 509 PWSTR* Environment = (PWSTR*)lpEnvironment; 510 DWORD Length; 511 DWORD dwType; 512 HKEY hKey; 513 HKEY hKeyUser; 514 LPWSTR lpUserName = NULL; 515 LPWSTR lpDomainName = NULL; 516 WCHAR Buffer[MAX_PATH]; 517 WCHAR szValue[1024]; 518 519 DPRINT("CreateEnvironmentBlock() called\n"); 520 521 if (lpEnvironment == NULL) 522 { 523 SetLastError(ERROR_INVALID_PARAMETER); 524 return FALSE; 525 } 526 527 Status = RtlCreateEnvironment((BOOLEAN)bInherit, Environment); 528 if (!NT_SUCCESS(Status)) 529 { 530 DPRINT1("RtlCreateEnvironment() failed (Status %lx)\n", Status); 531 SetLastError(RtlNtStatusToDosError(Status)); 532 return FALSE; 533 } 534 535 /* Set 'SystemRoot' variable */ 536 Length = ARRAYSIZE(Buffer); 537 if (GetEnvironmentVariableW(L"SystemRoot", Buffer, Length)) 538 { 539 SetUserEnvironmentVariable(Environment, 540 L"SystemRoot", 541 Buffer, 542 FALSE); 543 } 544 545 /* Set 'SystemDrive' variable */ 546 if (GetEnvironmentVariableW(L"SystemDrive", Buffer, Length)) 547 { 548 SetUserEnvironmentVariable(Environment, 549 L"SystemDrive", 550 Buffer, 551 FALSE); 552 } 553 554 /* Set variables from Session Manager */ 555 if (!SetSystemEnvironment(Environment)) 556 { 557 RtlDestroyEnvironment(*Environment); 558 return FALSE; 559 } 560 561 /* Set 'COMPUTERNAME' variable */ 562 Length = ARRAYSIZE(Buffer); 563 if (GetComputerNameW(Buffer, &Length)) 564 { 565 SetUserEnvironmentVariable(Environment, 566 L"COMPUTERNAME", 567 Buffer, 568 FALSE); 569 } 570 571 /* Set 'ALLUSERSPROFILE' variable */ 572 Length = ARRAYSIZE(Buffer); 573 if (GetAllUsersProfileDirectoryW(Buffer, &Length)) 574 { 575 SetUserEnvironmentVariable(Environment, 576 L"ALLUSERSPROFILE", 577 Buffer, 578 FALSE); 579 } 580 581 /* Set 'USERPROFILE' variable to the default users profile */ 582 Length = ARRAYSIZE(Buffer); 583 if (GetDefaultUserProfileDirectoryW(Buffer, &Length)) 584 { 585 SetUserEnvironmentVariable(Environment, 586 L"USERPROFILE", 587 Buffer, 588 TRUE); 589 } 590 591 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 592 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", 593 0, 594 KEY_READ, 595 &hKey); 596 if (lError == ERROR_SUCCESS) 597 { 598 Length = sizeof(szValue); 599 lError = RegQueryValueExW(hKey, 600 L"ProgramFilesDir", 601 NULL, 602 &dwType, 603 (LPBYTE)szValue, 604 &Length); 605 if (lError == ERROR_SUCCESS) 606 { 607 SetUserEnvironmentVariable(Environment, 608 L"ProgramFiles", 609 szValue, 610 FALSE); 611 } 612 613 Length = sizeof(szValue); 614 lError = RegQueryValueExW(hKey, 615 L"CommonFilesDir", 616 NULL, 617 &dwType, 618 (LPBYTE)szValue, 619 &Length); 620 if (lError == ERROR_SUCCESS) 621 { 622 SetUserEnvironmentVariable(Environment, 623 L"CommonProgramFiles", 624 szValue, 625 FALSE); 626 } 627 628 RegCloseKey(hKey); 629 } 630 631 /* 632 * If no user token is specified, the system environment variables are set 633 * and we stop here, otherwise continue setting the user-specific variables. 634 */ 635 if (hToken == NULL) 636 return TRUE; 637 638 hKeyUser = GetCurrentUserKey(hToken); 639 if (hKeyUser == NULL) 640 { 641 DPRINT1("GetCurrentUserKey() failed\n"); 642 RtlDestroyEnvironment(*Environment); 643 return FALSE; 644 } 645 646 /* Set 'USERPROFILE' variable */ 647 Length = ARRAYSIZE(Buffer); 648 if (GetUserProfileDirectoryW(hToken, Buffer, &Length)) 649 { 650 DWORD MinLen = 2; 651 652 SetUserEnvironmentVariable(Environment, 653 L"USERPROFILE", 654 Buffer, 655 FALSE); 656 657 // FIXME: Strangely enough the following two environment variables 658 // are not set by userenv.dll in Windows... See r68284 / CORE-9875 659 // FIXME2: This is done by msgina.dll !! 660 661 /* At least <drive letter>:<path> */ 662 if (Length > MinLen) 663 { 664 /* Set 'HOMEDRIVE' variable */ 665 StringCchCopyNW(szValue, ARRAYSIZE(Buffer), Buffer, MinLen); 666 SetUserEnvironmentVariable(Environment, 667 L"HOMEDRIVE", 668 szValue, 669 FALSE); 670 671 /* Set 'HOMEPATH' variable */ 672 StringCchCopyNW(szValue, ARRAYSIZE(Buffer), Buffer + MinLen, Length - MinLen); 673 SetUserEnvironmentVariable(Environment, 674 L"HOMEPATH", 675 szValue, 676 FALSE); 677 } 678 } 679 else 680 { 681 DPRINT1("GetUserProfileDirectoryW failed with error %lu\n", GetLastError()); 682 } 683 684 if (GetUserAndDomainName(hToken, 685 &lpUserName, 686 &lpDomainName)) 687 { 688 /* Set 'USERNAME' variable */ 689 SetUserEnvironmentVariable(Environment, 690 L"USERNAME", 691 lpUserName, 692 FALSE); 693 694 /* Set 'USERDOMAIN' variable */ 695 SetUserEnvironmentVariable(Environment, 696 L"USERDOMAIN", 697 lpDomainName, 698 FALSE); 699 700 if (lpUserName != NULL) 701 LocalFree(lpUserName); 702 703 if (lpDomainName != NULL) 704 LocalFree(lpDomainName); 705 } 706 707 /* Set user environment variables */ 708 SetUserEnvironment(Environment, 709 hKeyUser, 710 L"Environment"); 711 712 /* Set user volatile environment variables */ 713 SetUserEnvironment(Environment, 714 hKeyUser, 715 L"Volatile Environment"); 716 717 RegCloseKey(hKeyUser); 718 719 return TRUE; 720 } 721 722 723 BOOL 724 WINAPI 725 DestroyEnvironmentBlock(IN LPVOID lpEnvironment) 726 { 727 DPRINT("DestroyEnvironmentBlock() called\n"); 728 729 if (lpEnvironment == NULL) 730 { 731 SetLastError(ERROR_INVALID_PARAMETER); 732 return FALSE; 733 } 734 735 RtlDestroyEnvironment(lpEnvironment); 736 737 return TRUE; 738 } 739 740 741 BOOL 742 WINAPI 743 ExpandEnvironmentStringsForUserW(IN HANDLE hToken, 744 IN LPCWSTR lpSrc, 745 OUT LPWSTR lpDest, 746 IN DWORD dwSize) 747 { 748 BOOL Ret = FALSE; 749 PVOID lpEnvironment; 750 751 if (lpSrc == NULL || lpDest == NULL || dwSize == 0) 752 { 753 SetLastError(ERROR_INVALID_PARAMETER); 754 return FALSE; 755 } 756 757 if (CreateEnvironmentBlock(&lpEnvironment, 758 hToken, 759 FALSE)) 760 { 761 UNICODE_STRING SrcU, DestU; 762 NTSTATUS Status; 763 764 /* Initialize the strings */ 765 RtlInitUnicodeString(&SrcU, lpSrc); 766 DestU.Length = 0; 767 DestU.MaximumLength = dwSize * sizeof(WCHAR); 768 DestU.Buffer = lpDest; 769 770 /* Expand the strings */ 771 Status = RtlExpandEnvironmentStrings_U((PWSTR)lpEnvironment, 772 &SrcU, 773 &DestU, 774 NULL); 775 776 DestroyEnvironmentBlock(lpEnvironment); 777 778 if (NT_SUCCESS(Status)) 779 { 780 Ret = TRUE; 781 } 782 else 783 { 784 SetLastError(RtlNtStatusToDosError(Status)); 785 } 786 } 787 788 return Ret; 789 } 790 791 792 BOOL 793 WINAPI 794 ExpandEnvironmentStringsForUserA(IN HANDLE hToken, 795 IN LPCSTR lpSrc, 796 OUT LPSTR lpDest, 797 IN DWORD dwSize) 798 { 799 BOOL Ret = FALSE; 800 DWORD dwSrcLen; 801 LPWSTR lpSrcW = NULL, lpDestW = NULL; 802 803 if (lpSrc == NULL || lpDest == NULL || dwSize == 0) 804 { 805 SetLastError(ERROR_INVALID_PARAMETER); 806 return FALSE; 807 } 808 809 dwSrcLen = strlen(lpSrc); 810 lpSrcW = (LPWSTR)GlobalAlloc(GMEM_FIXED, 811 (dwSrcLen + 1) * sizeof(WCHAR)); 812 if (lpSrcW == NULL || 813 MultiByteToWideChar(CP_ACP, 814 0, 815 lpSrc, 816 -1, 817 lpSrcW, 818 dwSrcLen + 1) == 0) 819 { 820 goto Cleanup; 821 } 822 823 lpDestW = (LPWSTR)GlobalAlloc(GMEM_FIXED, 824 dwSize * sizeof(WCHAR)); 825 if (lpDestW == NULL) 826 goto Cleanup; 827 828 Ret = ExpandEnvironmentStringsForUserW(hToken, 829 lpSrcW, 830 lpDestW, 831 dwSize); 832 if (Ret) 833 { 834 if (WideCharToMultiByte(CP_ACP, 835 0, 836 lpDestW, 837 -1, 838 lpDest, 839 dwSize, 840 NULL, 841 NULL) == 0) 842 { 843 Ret = FALSE; 844 } 845 } 846 847 Cleanup: 848 if (lpSrcW != NULL) 849 GlobalFree((HGLOBAL)lpSrcW); 850 851 if (lpDestW != NULL) 852 GlobalFree((HGLOBAL)lpDestW); 853 854 return Ret; 855 } 856 857 /* EOF */ 858