1 /* 2 * New device installer (newdev.dll) 3 * 4 * Copyright 2006 Herv� Poussineau (hpoussin@reactos.org) 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 #include "newdev_private.h" 22 23 #include <wincon.h> 24 #include <cfgmgr32.h> 25 #include <shlobj.h> 26 #include <shlwapi.h> 27 28 HANDLE hThread; 29 30 static VOID 31 CenterWindow( 32 IN HWND hWnd) 33 { 34 HWND hWndParent; 35 RECT rcParent; 36 RECT rcWindow; 37 38 hWndParent = GetParent(hWnd); 39 if (hWndParent == NULL) 40 hWndParent = GetDesktopWindow(); 41 42 GetWindowRect(hWndParent, &rcParent); 43 GetWindowRect(hWnd, &rcWindow); 44 45 SetWindowPos( 46 hWnd, 47 HWND_TOP, 48 ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2, 49 ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2, 50 0, 51 0, 52 SWP_NOSIZE); 53 } 54 55 static BOOL 56 SetFailedInstall( 57 IN HDEVINFO DeviceInfoSet, 58 IN PSP_DEVINFO_DATA DevInfoData OPTIONAL, 59 IN BOOLEAN Set) 60 { 61 DWORD dwType, dwSize, dwFlags = 0; 62 63 dwSize = sizeof(dwFlags); 64 if (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet, 65 DevInfoData, 66 SPDRP_CONFIGFLAGS, 67 &dwType, 68 (PBYTE)&dwFlags, 69 dwSize, 70 &dwSize)) 71 { 72 return FALSE; 73 } 74 75 if (Set) 76 dwFlags |= CONFIGFLAG_FAILEDINSTALL; 77 else 78 dwFlags &= ~CONFIGFLAG_FAILEDINSTALL; 79 80 if (!SetupDiSetDeviceRegistryProperty(DeviceInfoSet, 81 DevInfoData, 82 SPDRP_CONFIGFLAGS, 83 (PBYTE)&dwFlags, 84 dwSize)) 85 { 86 87 return FALSE; 88 } 89 90 if (Set) 91 { 92 /* Set the 'Unknown' device class */ 93 PWSTR pszUnknown = L"Unknown"; 94 SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, 95 DevInfoData, 96 SPDRP_CLASS, 97 (PBYTE)pszUnknown, 98 (wcslen(pszUnknown) + 1) * sizeof(WCHAR)); 99 100 PWSTR pszUnknownGuid = L"{4D36E97E-E325-11CE-BFC1-08002BE10318}"; 101 SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, 102 DevInfoData, 103 SPDRP_CLASSGUID, 104 (PBYTE)pszUnknownGuid, 105 (wcslen(pszUnknownGuid) + 1) * sizeof(WCHAR)); 106 107 /* Set device problem code CM_PROB_FAILED_INSTALL */ 108 CM_Set_DevNode_Problem(DevInfoData->DevInst, 109 CM_PROB_FAILED_INSTALL, 110 CM_SET_DEVNODE_PROBLEM_OVERRIDE); 111 } 112 113 return TRUE; 114 } 115 116 static BOOL 117 CanDisableDevice( 118 IN DEVINST DevInst, 119 IN HMACHINE hMachine, 120 OUT BOOL *CanDisable) 121 { 122 CONFIGRET cr; 123 ULONG Status, ProblemNumber; 124 BOOL Ret = FALSE; 125 126 cr = CM_Get_DevNode_Status_Ex(&Status, 127 &ProblemNumber, 128 DevInst, 129 0, 130 hMachine); 131 if (cr == CR_SUCCESS) 132 { 133 *CanDisable = ((Status & DN_DISABLEABLE) != 0); 134 Ret = TRUE; 135 } 136 137 return Ret; 138 } 139 140 static BOOL 141 IsDeviceStarted( 142 IN DEVINST DevInst, 143 IN HMACHINE hMachine, 144 OUT BOOL *IsEnabled) 145 { 146 CONFIGRET cr; 147 ULONG Status, ProblemNumber; 148 BOOL Ret = FALSE; 149 150 cr = CM_Get_DevNode_Status_Ex( 151 &Status, 152 &ProblemNumber, 153 DevInst, 154 0, 155 hMachine); 156 if (cr == CR_SUCCESS) 157 { 158 *IsEnabled = ((Status & DN_STARTED) != 0); 159 Ret = TRUE; 160 } 161 162 return Ret; 163 } 164 165 static BOOL 166 StartDevice( 167 IN HDEVINFO DeviceInfoSet, 168 IN PSP_DEVINFO_DATA DevInfoData OPTIONAL, 169 IN BOOL bEnable, 170 IN DWORD HardwareProfile OPTIONAL, 171 OUT BOOL *bNeedReboot OPTIONAL) 172 { 173 SP_PROPCHANGE_PARAMS pcp; 174 SP_DEVINSTALL_PARAMS dp; 175 DWORD LastErr; 176 BOOL Ret = FALSE; 177 178 pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); 179 pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; 180 pcp.HwProfile = HardwareProfile; 181 182 if (bEnable) 183 { 184 /* try to enable the device on the global profile */ 185 pcp.StateChange = DICS_ENABLE; 186 pcp.Scope = DICS_FLAG_GLOBAL; 187 188 /* ignore errors */ 189 LastErr = GetLastError(); 190 if (SetupDiSetClassInstallParams( 191 DeviceInfoSet, 192 DevInfoData, 193 &pcp.ClassInstallHeader, 194 sizeof(SP_PROPCHANGE_PARAMS))) 195 { 196 SetupDiCallClassInstaller( 197 DIF_PROPERTYCHANGE, 198 DeviceInfoSet, 199 DevInfoData); 200 } 201 SetLastError(LastErr); 202 } 203 204 /* try config-specific */ 205 pcp.StateChange = (bEnable ? DICS_ENABLE : DICS_DISABLE); 206 pcp.Scope = DICS_FLAG_CONFIGSPECIFIC; 207 208 if (SetupDiSetClassInstallParams( 209 DeviceInfoSet, 210 DevInfoData, 211 &pcp.ClassInstallHeader, 212 sizeof(SP_PROPCHANGE_PARAMS)) && 213 SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, 214 DeviceInfoSet, 215 DevInfoData)) 216 { 217 dp.cbSize = sizeof(SP_DEVINSTALL_PARAMS); 218 if (SetupDiGetDeviceInstallParams( 219 DeviceInfoSet, 220 DevInfoData, 221 &dp)) 222 { 223 if (bNeedReboot != NULL) 224 { 225 *bNeedReboot = ((dp.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) != 0); 226 } 227 228 Ret = TRUE; 229 } 230 } 231 return Ret; 232 } 233 234 static DWORD WINAPI 235 FindDriverProc( 236 IN LPVOID lpParam) 237 { 238 PDEVINSTDATA DevInstData; 239 BOOL result = FALSE; 240 241 DevInstData = (PDEVINSTDATA)lpParam; 242 243 result = ScanFoldersForDriver(DevInstData); 244 245 if (result) 246 { 247 PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 1, 0); 248 } 249 else 250 { 251 /* Update device configuration */ 252 SetFailedInstall(DevInstData->hDevInfo, 253 &DevInstData->devInfoData, 254 TRUE); 255 256 PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 0, 0); 257 } 258 return 0; 259 } 260 261 static DWORD WINAPI 262 InstallDriverProc( 263 IN LPVOID lpParam) 264 { 265 PDEVINSTDATA DevInstData; 266 BOOL res; 267 268 DevInstData = (PDEVINSTDATA)lpParam; 269 res = InstallCurrentDriver(DevInstData); 270 PostMessage(DevInstData->hDialog, WM_INSTALL_FINISHED, res ? 0 : 1, 0); 271 return 0; 272 } 273 274 static VOID 275 PopulateCustomPathCombo( 276 IN HWND hwndCombo) 277 { 278 HKEY hKey = NULL; 279 DWORD dwRegType; 280 DWORD dwPathLength = 0; 281 LPWSTR Buffer = NULL; 282 LPCWSTR Path; 283 LONG rc; 284 285 (void)ComboBox_ResetContent(hwndCombo); 286 287 /* RegGetValue would have been better... */ 288 rc = RegOpenKeyEx( 289 HKEY_LOCAL_MACHINE, 290 REGSTR_PATH_SETUP REGSTR_KEY_SETUP, 291 0, 292 KEY_QUERY_VALUE, 293 &hKey); 294 if (rc != ERROR_SUCCESS) 295 { 296 TRACE("RegOpenKeyEx() failed with error 0x%lx\n", rc); 297 goto cleanup; 298 } 299 rc = RegQueryValueExW( 300 hKey, 301 L"Installation Sources", 302 NULL, 303 &dwRegType, 304 NULL, 305 &dwPathLength); 306 if (rc != ERROR_SUCCESS || dwRegType != REG_MULTI_SZ) 307 { 308 TRACE("RegQueryValueEx() failed with error 0x%lx\n", rc); 309 goto cleanup; 310 } 311 312 /* Allocate enough space to add 2 NULL chars at the end of the string */ 313 Buffer = HeapAlloc(GetProcessHeap(), 0, dwPathLength + 2 * sizeof(WCHAR)); 314 if (!Buffer) 315 { 316 TRACE("HeapAlloc() failed\n"); 317 goto cleanup; 318 } 319 rc = RegQueryValueExW( 320 hKey, 321 L"Installation Sources", 322 NULL, 323 NULL, 324 (LPBYTE)Buffer, 325 &dwPathLength); 326 if (rc != ERROR_SUCCESS) 327 { 328 TRACE("RegQueryValueEx() failed with error 0x%lx\n", rc); 329 goto cleanup; 330 } 331 332 Buffer[dwPathLength / sizeof(WCHAR)] = UNICODE_NULL; 333 Buffer[dwPathLength / sizeof(WCHAR) + 1] = UNICODE_NULL; 334 335 /* Populate combo box */ 336 for (Path = Buffer; *Path; Path += wcslen(Path) + 1) 337 { 338 (void)ComboBox_AddString(hwndCombo, Path); 339 if (Path == Buffer) 340 (void)ComboBox_SetCurSel(hwndCombo, 0); 341 } 342 343 cleanup: 344 if (hKey != NULL) 345 RegCloseKey(hKey); 346 HeapFree(GetProcessHeap(), 0, Buffer); 347 } 348 349 static VOID 350 SaveCustomPath( 351 IN HWND hwndCombo) 352 { 353 LPWSTR CustomPath = NULL; 354 DWORD CustomPathLength; 355 LPWSTR Buffer = NULL; 356 LPWSTR pBuffer; /* Pointer into Buffer */ 357 int ItemsCount, Length; 358 int i; 359 DWORD TotalLength = 0; 360 BOOL UseCustomPath = TRUE; 361 HKEY hKey = NULL; 362 LONG rc; 363 364 /* Get custom path */ 365 Length = ComboBox_GetTextLength(hwndCombo) + 1; 366 CustomPath = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR)); 367 if (!CustomPath) 368 { 369 TRACE("HeapAlloc() failed\n"); 370 goto cleanup; 371 } 372 CustomPathLength = GetWindowTextW(hwndCombo, CustomPath, Length) + 1; 373 374 /* Calculate length of the buffer */ 375 ItemsCount = ComboBox_GetCount(hwndCombo); 376 if (ItemsCount == CB_ERR) 377 { 378 TRACE("ComboBox_GetCount() failed\n"); 379 goto cleanup; 380 } 381 for (i = 0; i < ItemsCount; i++) 382 { 383 Length = ComboBox_GetLBTextLen(hwndCombo, i); 384 if (Length == CB_ERR) 385 { 386 TRACE("ComboBox_GetLBTextLen() failed\n"); 387 goto cleanup; 388 } 389 TotalLength += Length + 1; 390 } 391 TotalLength++; /* Final NULL char */ 392 393 /* Allocate buffer */ 394 Buffer = HeapAlloc(GetProcessHeap(), 0, (CustomPathLength + TotalLength + 1) * sizeof(WCHAR)); 395 if (!Buffer) 396 { 397 TRACE("HeapAlloc() failed\n"); 398 goto cleanup; 399 } 400 401 /* Fill the buffer */ 402 pBuffer = &Buffer[CustomPathLength]; 403 for (i = 0; i < ItemsCount; i++) 404 { 405 Length = ComboBox_GetLBText(hwndCombo, i, pBuffer); 406 if (Length == CB_ERR) 407 { 408 TRACE("ComboBox_GetLBText() failed\n"); 409 goto cleanup; 410 } 411 else if (UseCustomPath && _wcsicmp(CustomPath, pBuffer) == 0) 412 UseCustomPath = FALSE; 413 pBuffer += 1 + Length; 414 } 415 *pBuffer = '\0'; /* Add final NULL char */ 416 417 if (!UseCustomPath) 418 { 419 /* Nothing to save to registry */ 420 goto cleanup; 421 } 422 423 TotalLength += CustomPathLength; 424 wcscpy(Buffer, CustomPath); 425 426 /* Save the buffer */ 427 /* RegSetKeyValue would have been better... */ 428 rc = RegOpenKeyEx( 429 HKEY_LOCAL_MACHINE, 430 REGSTR_PATH_SETUP REGSTR_KEY_SETUP, 431 0, 432 KEY_SET_VALUE, 433 &hKey); 434 if (rc != ERROR_SUCCESS) 435 { 436 TRACE("RegOpenKeyEx() failed with error 0x%lx\n", rc); 437 goto cleanup; 438 } 439 rc = RegSetValueExW( 440 hKey, 441 L"Installation Sources", 442 0, 443 REG_MULTI_SZ, 444 (const BYTE*)Buffer, 445 TotalLength * sizeof(WCHAR)); 446 if (rc != ERROR_SUCCESS) 447 { 448 TRACE("RegSetValueEx() failed with error 0x%lx\n", rc); 449 goto cleanup; 450 } 451 452 cleanup: 453 if (hKey != NULL) 454 RegCloseKey(hKey); 455 HeapFree(GetProcessHeap(), 0, CustomPath); 456 HeapFree(GetProcessHeap(), 0, Buffer); 457 } 458 459 static INT_PTR CALLBACK 460 WelcomeDlgProc( 461 IN HWND hwndDlg, 462 IN UINT uMsg, 463 IN WPARAM wParam, 464 IN LPARAM lParam) 465 { 466 PDEVINSTDATA DevInstData; 467 UNREFERENCED_PARAMETER(wParam); 468 469 /* Retrieve pointer to the global setup data */ 470 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 471 472 switch (uMsg) 473 { 474 case WM_INITDIALOG: 475 { 476 HWND hwndControl; 477 DWORD dwStyle; 478 479 /* Get pointer to the global setup data */ 480 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 481 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 482 483 hwndControl = GetParent(hwndDlg); 484 485 /* Center the wizard window */ 486 CenterWindow(hwndControl); 487 488 /* Hide the system menu */ 489 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE); 490 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); 491 492 /* Set title font */ 493 SendDlgItemMessage( 494 hwndDlg, 495 IDC_WELCOMETITLE, 496 WM_SETFONT, 497 (WPARAM)DevInstData->hTitleFont, 498 (LPARAM)TRUE); 499 500 SendDlgItemMessage( 501 hwndDlg, 502 IDC_DEVICE, 503 WM_SETTEXT, 504 0, 505 (LPARAM)DevInstData->buffer); 506 507 SendDlgItemMessage( 508 hwndDlg, 509 IDC_RADIO_AUTO, 510 BM_SETCHECK, 511 (WPARAM)TRUE, 512 (LPARAM)0); 513 514 SetFailedInstall(DevInstData->hDevInfo, 515 &DevInstData->devInfoData, 516 TRUE); 517 break; 518 } 519 520 case WM_NOTIFY: 521 { 522 LPNMHDR lpnm = (LPNMHDR)lParam; 523 524 switch (lpnm->code) 525 { 526 case PSN_SETACTIVE: 527 /* Enable the Next button */ 528 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); 529 break; 530 531 case PSN_WIZNEXT: 532 /* Handle a Next button click, if necessary */ 533 if (SendDlgItemMessage(hwndDlg, IDC_RADIO_AUTO, BM_GETCHECK, (WPARAM)0, (LPARAM)0) == BST_CHECKED) 534 { 535 if (PrepareFoldersToScan(DevInstData, TRUE, FALSE, NULL)) 536 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_SEARCHDRV); 537 else 538 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED); 539 } 540 return TRUE; 541 542 default: 543 break; 544 } 545 break; 546 } 547 548 default: 549 break; 550 } 551 552 return FALSE; 553 } 554 555 static void 556 IncludePath(HWND Dlg, BOOL Enabled) 557 { 558 EnableWindow(GetDlgItem(Dlg, IDC_COMBO_PATH), Enabled); 559 EnableWindow(GetDlgItem(Dlg, IDC_BROWSE), Enabled); 560 } 561 562 static void 563 AutoDriver(HWND Dlg, BOOL Enabled) 564 { 565 EnableWindow(GetDlgItem(Dlg, IDC_CHECK_MEDIA), Enabled); 566 EnableWindow(GetDlgItem(Dlg, IDC_CHECK_PATH), Enabled); 567 IncludePath(Dlg, Enabled & IsDlgButtonChecked(Dlg, IDC_CHECK_PATH)); 568 } 569 570 static INT_PTR CALLBACK 571 CHSourceDlgProc( 572 IN HWND hwndDlg, 573 IN UINT uMsg, 574 IN WPARAM wParam, 575 IN LPARAM lParam) 576 { 577 PDEVINSTDATA DevInstData; 578 579 /* Retrieve pointer to the global setup data */ 580 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 581 582 switch (uMsg) 583 { 584 case WM_INITDIALOG: 585 { 586 HWND hwndControl, hwndCombo; 587 DWORD dwStyle; 588 COMBOBOXINFO info = { sizeof(info) }; 589 590 /* Get pointer to the global setup data */ 591 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 592 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 593 594 hwndControl = GetParent(hwndDlg); 595 596 /* Center the wizard window */ 597 CenterWindow(hwndControl); 598 599 /* Hide the system menu */ 600 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE); 601 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); 602 603 hwndCombo = GetDlgItem(hwndDlg, IDC_COMBO_PATH); 604 PopulateCustomPathCombo(hwndCombo); 605 606 GetComboBoxInfo(hwndCombo, &info); 607 SHAutoComplete(info.hwndItem, SHACF_FILESYS_DIRS); 608 609 SendDlgItemMessage( 610 hwndDlg, 611 IDC_RADIO_SEARCHHERE, 612 BM_SETCHECK, 613 (WPARAM)TRUE, 614 (LPARAM)0); 615 AutoDriver(hwndDlg, TRUE); 616 IncludePath(hwndDlg, FALSE); 617 618 /* Disable manual driver choice for now */ 619 EnableWindow(GetDlgItem(hwndDlg, IDC_RADIO_CHOOSE), FALSE); 620 621 break; 622 } 623 624 case WM_COMMAND: 625 { 626 switch (LOWORD(wParam)) 627 { 628 case IDC_RADIO_SEARCHHERE: 629 AutoDriver(hwndDlg, TRUE); 630 return TRUE; 631 632 case IDC_RADIO_CHOOSE: 633 AutoDriver(hwndDlg, FALSE); 634 return TRUE; 635 636 case IDC_CHECK_PATH: 637 IncludePath(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH)); 638 return TRUE; 639 640 case IDC_BROWSE: 641 { 642 BROWSEINFO bi = { 0 }; 643 LPITEMIDLIST pidl; 644 645 bi.hwndOwner = hwndDlg; 646 bi.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS; 647 pidl = SHBrowseForFolder(&bi); 648 if (pidl) 649 { 650 WCHAR Directory[MAX_PATH]; 651 IMalloc* malloc; 652 653 if (SHGetPathFromIDListW(pidl, Directory)) 654 { 655 /* Set the IDC_COMBO_PATH text */ 656 SetWindowTextW(GetDlgItem(hwndDlg, IDC_COMBO_PATH), Directory); 657 } 658 659 /* Free memory, if possible */ 660 if (SUCCEEDED(SHGetMalloc(&malloc))) 661 { 662 IMalloc_Free(malloc, pidl); 663 IMalloc_Release(malloc); 664 } 665 return TRUE; 666 } 667 break; 668 } 669 } 670 break; 671 } 672 673 case WM_NOTIFY: 674 { 675 LPNMHDR lpnm = (LPNMHDR)lParam; 676 677 switch (lpnm->code) 678 { 679 case PSN_SETACTIVE: 680 /* Enable the Next and Back buttons */ 681 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); 682 break; 683 684 case PSN_WIZNEXT: 685 /* Handle a Next button click, if necessary */ 686 if (IsDlgButtonChecked(hwndDlg, IDC_RADIO_SEARCHHERE)) 687 { 688 SaveCustomPath(GetDlgItem(hwndDlg, IDC_COMBO_PATH)); 689 HeapFree(GetProcessHeap(), 0, DevInstData->CustomSearchPath); 690 DevInstData->CustomSearchPath = NULL; 691 if (PrepareFoldersToScan( 692 DevInstData, 693 IsDlgButtonChecked(hwndDlg, IDC_CHECK_MEDIA), 694 IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH), 695 GetDlgItem(hwndDlg, IDC_COMBO_PATH))) 696 { 697 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_SEARCHDRV); 698 } 699 else 700 { 701 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED); 702 } 703 } 704 else 705 { 706 /* FIXME */; 707 } 708 return TRUE; 709 710 default: 711 break; 712 } 713 break; 714 } 715 716 default: 717 break; 718 } 719 720 return FALSE; 721 } 722 723 static INT_PTR CALLBACK 724 SearchDrvDlgProc( 725 IN HWND hwndDlg, 726 IN UINT uMsg, 727 IN WPARAM wParam, 728 IN LPARAM lParam) 729 { 730 PDEVINSTDATA DevInstData; 731 DWORD dwThreadId; 732 733 /* Retrieve pointer to the global setup data */ 734 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 735 736 switch (uMsg) 737 { 738 case WM_INITDIALOG: 739 { 740 HWND hwndControl; 741 DWORD dwStyle; 742 743 /* Get pointer to the global setup data */ 744 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 745 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 746 747 DevInstData->hDialog = hwndDlg; 748 hwndControl = GetParent(hwndDlg); 749 750 /* Center the wizard window */ 751 CenterWindow(hwndControl); 752 753 SendDlgItemMessage( 754 hwndDlg, 755 IDC_DEVICE, 756 WM_SETTEXT, 757 0, 758 (LPARAM)DevInstData->buffer); 759 760 /* Hide the system menu */ 761 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE); 762 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); 763 break; 764 } 765 766 case WM_SEARCH_FINISHED: 767 { 768 CloseHandle(hThread); 769 hThread = 0; 770 if (wParam == 0) 771 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_NODRIVER); 772 else 773 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLDRV); 774 break; 775 } 776 777 case WM_NOTIFY: 778 { 779 LPNMHDR lpnm = (LPNMHDR)lParam; 780 781 switch (lpnm->code) 782 { 783 case PSN_SETACTIVE: 784 PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK); 785 /* Yes, we can safely ignore the problem (if any) */ 786 SetupDiDestroyDriverInfoList( 787 DevInstData->hDevInfo, 788 &DevInstData->devInfoData, 789 SPDIT_COMPATDRIVER); 790 hThread = CreateThread(NULL, 0, FindDriverProc, DevInstData, 0, &dwThreadId); 791 break; 792 793 case PSN_KILLACTIVE: 794 if (hThread != 0) 795 { 796 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); 797 return TRUE; 798 } 799 break; 800 801 case PSN_WIZNEXT: 802 /* Handle a Next button click, if necessary */ 803 break; 804 805 default: 806 break; 807 } 808 break; 809 } 810 811 default: 812 break; 813 } 814 815 return FALSE; 816 } 817 818 static INT_PTR CALLBACK 819 InstallDrvDlgProc( 820 IN HWND hwndDlg, 821 IN UINT uMsg, 822 IN WPARAM wParam, 823 IN LPARAM lParam) 824 { 825 PDEVINSTDATA DevInstData; 826 DWORD dwThreadId; 827 828 /* Retrieve pointer to the global setup data */ 829 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 830 831 switch (uMsg) 832 { 833 case WM_INITDIALOG: 834 { 835 HWND hwndControl; 836 DWORD dwStyle; 837 838 /* Get pointer to the global setup data */ 839 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 840 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 841 842 DevInstData->hDialog = hwndDlg; 843 hwndControl = GetParent(hwndDlg); 844 845 /* Center the wizard window */ 846 CenterWindow(hwndControl); 847 848 SendDlgItemMessage( 849 hwndDlg, 850 IDC_DEVICE, 851 WM_SETTEXT, 852 0, 853 (LPARAM)DevInstData->drvInfoData.Description); 854 855 /* Hide the system menu */ 856 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE); 857 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); 858 break; 859 } 860 861 case WM_INSTALL_FINISHED: 862 { 863 CloseHandle(hThread); 864 hThread = 0; 865 if (wParam == 0) 866 { 867 SP_DEVINSTALL_PARAMS installParams; 868 869 SetFailedInstall(DevInstData->hDevInfo, 870 &DevInstData->devInfoData, 871 FALSE); 872 873 /* Should we reboot? */ 874 installParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); 875 if (SetupDiGetDeviceInstallParams( 876 DevInstData->hDevInfo, 877 &DevInstData->devInfoData, 878 &installParams)) 879 { 880 if (installParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) 881 { 882 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_NEEDREBOOT); 883 } 884 else 885 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_FINISHPAGE); 886 break; 887 } 888 } 889 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED); 890 break; 891 } 892 893 case WM_NOTIFY: 894 { 895 LPNMHDR lpnm = (LPNMHDR)lParam; 896 897 switch (lpnm->code) 898 { 899 case PSN_SETACTIVE: 900 PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK); 901 hThread = CreateThread(NULL, 0, InstallDriverProc, DevInstData, 0, &dwThreadId); 902 break; 903 904 case PSN_KILLACTIVE: 905 if (hThread != 0) 906 { 907 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); 908 return TRUE; 909 } 910 break; 911 912 case PSN_WIZNEXT: 913 /* Handle a Next button click, if necessary */ 914 break; 915 916 default: 917 break; 918 } 919 break; 920 } 921 922 default: 923 break; 924 } 925 926 return FALSE; 927 } 928 929 static INT_PTR CALLBACK 930 NoDriverDlgProc( 931 IN HWND hwndDlg, 932 IN UINT uMsg, 933 IN WPARAM wParam, 934 IN LPARAM lParam) 935 { 936 PDEVINSTDATA DevInstData; 937 HWND hwndControl; 938 939 UNREFERENCED_PARAMETER(wParam); 940 941 /* Get pointer to the global setup data */ 942 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 943 944 switch (uMsg) 945 { 946 case WM_INITDIALOG: 947 { 948 BOOL DisableableDevice = FALSE; 949 950 /* Get pointer to the global setup data */ 951 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 952 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 953 954 /* Center the wizard window */ 955 CenterWindow(GetParent(hwndDlg)); 956 957 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); 958 ShowWindow(hwndControl, SW_HIDE); 959 EnableWindow(hwndControl, FALSE); 960 961 /* Set title font */ 962 SendDlgItemMessage( 963 hwndDlg, 964 IDC_FINISHTITLE, 965 WM_SETFONT, 966 (WPARAM)DevInstData->hTitleFont, 967 (LPARAM)TRUE); 968 969 /* disable the "do not show this dialog anymore" checkbox 970 if the device cannot be disabled */ 971 CanDisableDevice( 972 DevInstData->devInfoData.DevInst, 973 NULL, 974 &DisableableDevice); 975 EnableWindow( 976 GetDlgItem(hwndDlg, IDC_DONOTSHOWDLG), 977 DisableableDevice); 978 break; 979 } 980 981 case WM_NOTIFY: 982 { 983 LPNMHDR lpnm = (LPNMHDR)lParam; 984 985 switch (lpnm->code) 986 { 987 case PSN_SETACTIVE: 988 /* Enable the correct buttons on for the active page */ 989 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH); 990 break; 991 992 case PSN_WIZBACK: 993 /* Handle a Back button click, if necessary */ 994 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); 995 ShowWindow(hwndControl, SW_SHOW); 996 EnableWindow(hwndControl, TRUE); 997 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_CHSOURCE); 998 return TRUE; 999 1000 case PSN_WIZFINISH: 1001 { 1002 BOOL DisableableDevice = FALSE; 1003 BOOL IsStarted = FALSE; 1004 1005 if (CanDisableDevice(DevInstData->devInfoData.DevInst, 1006 NULL, 1007 &DisableableDevice) && 1008 DisableableDevice && 1009 IsDeviceStarted( 1010 DevInstData->devInfoData.DevInst, 1011 NULL, 1012 &IsStarted) && 1013 !IsStarted && 1014 SendDlgItemMessage( 1015 hwndDlg, 1016 IDC_DONOTSHOWDLG, 1017 BM_GETCHECK, 1018 (WPARAM)0, (LPARAM)0) == BST_CHECKED) 1019 { 1020 /* disable the device */ 1021 StartDevice( 1022 DevInstData->hDevInfo, 1023 &DevInstData->devInfoData, 1024 FALSE, 1025 0, 1026 NULL); 1027 } 1028 else 1029 { 1030 SetFailedInstall(DevInstData->hDevInfo, 1031 &DevInstData->devInfoData, 1032 FALSE); 1033 } 1034 break; 1035 } 1036 1037 default: 1038 break; 1039 } 1040 break; 1041 } 1042 1043 default: 1044 break; 1045 } 1046 1047 return FALSE; 1048 } 1049 1050 static INT_PTR CALLBACK 1051 InstallFailedDlgProc( 1052 IN HWND hwndDlg, 1053 IN UINT uMsg, 1054 IN WPARAM wParam, 1055 IN LPARAM lParam) 1056 { 1057 PDEVINSTDATA DevInstData; 1058 UNREFERENCED_PARAMETER(wParam); 1059 1060 /* Retrieve pointer to the global setup data */ 1061 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 1062 1063 switch (uMsg) 1064 { 1065 case WM_INITDIALOG: 1066 { 1067 HWND hwndControl; 1068 1069 /* Get pointer to the global setup data */ 1070 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 1071 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 1072 1073 /* Center the wizard window */ 1074 CenterWindow(GetParent(hwndDlg)); 1075 1076 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); 1077 ShowWindow(hwndControl, SW_HIDE); 1078 EnableWindow(hwndControl, FALSE); 1079 1080 SendDlgItemMessage( 1081 hwndDlg, 1082 IDC_DEVICE, 1083 WM_SETTEXT, 1084 0, 1085 (LPARAM)DevInstData->drvInfoData.Description); 1086 1087 /* Set title font */ 1088 SendDlgItemMessage( 1089 hwndDlg, 1090 IDC_FINISHTITLE, 1091 WM_SETFONT, 1092 (WPARAM)DevInstData->hTitleFont, 1093 (LPARAM)TRUE); 1094 break; 1095 } 1096 1097 case WM_NOTIFY: 1098 { 1099 LPNMHDR lpnm = (LPNMHDR)lParam; 1100 1101 switch (lpnm->code) 1102 { 1103 case PSN_SETACTIVE: 1104 /* Enable the correct buttons on for the active page */ 1105 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH); 1106 break; 1107 1108 case PSN_WIZBACK: 1109 /* Handle a Back button click, if necessary */ 1110 break; 1111 1112 case PSN_WIZFINISH: 1113 /* Handle a Finish button click, if necessary */ 1114 break; 1115 1116 default: 1117 break; 1118 } 1119 break; 1120 } 1121 1122 default: 1123 break; 1124 } 1125 1126 return FALSE; 1127 } 1128 1129 static INT_PTR CALLBACK 1130 NeedRebootDlgProc( 1131 IN HWND hwndDlg, 1132 IN UINT uMsg, 1133 IN WPARAM wParam, 1134 IN LPARAM lParam) 1135 { 1136 PDEVINSTDATA DevInstData; 1137 UNREFERENCED_PARAMETER(wParam); 1138 1139 /* Retrieve pointer to the global setup data */ 1140 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 1141 1142 switch (uMsg) 1143 { 1144 case WM_INITDIALOG: 1145 { 1146 HWND hwndControl; 1147 1148 /* Get pointer to the global setup data */ 1149 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 1150 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 1151 1152 /* Center the wizard window */ 1153 CenterWindow(GetParent(hwndDlg)); 1154 1155 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); 1156 ShowWindow(hwndControl, SW_HIDE); 1157 EnableWindow(hwndControl, FALSE); 1158 1159 SendDlgItemMessage( 1160 hwndDlg, 1161 IDC_DEVICE, 1162 WM_SETTEXT, 1163 0, 1164 (LPARAM)DevInstData->drvInfoData.Description); 1165 1166 /* Set title font */ 1167 SendDlgItemMessage( 1168 hwndDlg, 1169 IDC_FINISHTITLE, 1170 WM_SETFONT, 1171 (WPARAM)DevInstData->hTitleFont, 1172 (LPARAM)TRUE); 1173 break; 1174 } 1175 1176 case WM_NOTIFY: 1177 { 1178 LPNMHDR lpnm = (LPNMHDR)lParam; 1179 1180 switch (lpnm->code) 1181 { 1182 case PSN_SETACTIVE: 1183 /* Enable the correct buttons on for the active page */ 1184 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH); 1185 break; 1186 1187 case PSN_WIZBACK: 1188 /* Handle a Back button click, if necessary */ 1189 break; 1190 1191 case PSN_WIZFINISH: 1192 /* Handle a Finish button click, if necessary */ 1193 break; 1194 1195 default: 1196 break; 1197 } 1198 break; 1199 } 1200 1201 default: 1202 break; 1203 } 1204 1205 return FALSE; 1206 } 1207 1208 static INT_PTR CALLBACK 1209 FinishDlgProc( 1210 IN HWND hwndDlg, 1211 IN UINT uMsg, 1212 IN WPARAM wParam, 1213 IN LPARAM lParam) 1214 { 1215 PDEVINSTDATA DevInstData; 1216 UNREFERENCED_PARAMETER(wParam); 1217 1218 /* Retrieve pointer to the global setup data */ 1219 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 1220 1221 switch (uMsg) 1222 { 1223 case WM_INITDIALOG: 1224 { 1225 HWND hwndControl; 1226 1227 /* Get pointer to the global setup data */ 1228 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; 1229 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData); 1230 1231 /* Center the wizard window */ 1232 CenterWindow(GetParent(hwndDlg)); 1233 1234 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); 1235 ShowWindow(hwndControl, SW_HIDE); 1236 EnableWindow(hwndControl, FALSE); 1237 1238 SendDlgItemMessage( 1239 hwndDlg, 1240 IDC_DEVICE, 1241 WM_SETTEXT, 1242 0, 1243 (LPARAM)DevInstData->drvInfoData.Description); 1244 1245 /* Set title font */ 1246 SendDlgItemMessage( 1247 hwndDlg, 1248 IDC_FINISHTITLE, 1249 WM_SETFONT, 1250 (WPARAM)DevInstData->hTitleFont, 1251 (LPARAM)TRUE); 1252 break; 1253 } 1254 1255 case WM_NOTIFY: 1256 { 1257 LPNMHDR lpnm = (LPNMHDR)lParam; 1258 1259 switch (lpnm->code) 1260 { 1261 case PSN_SETACTIVE: 1262 /* Enable the correct buttons on for the active page */ 1263 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH); 1264 break; 1265 1266 case PSN_WIZBACK: 1267 /* Handle a Back button click, if necessary */ 1268 break; 1269 1270 case PSN_WIZFINISH: 1271 /* Handle a Finish button click, if necessary */ 1272 break; 1273 1274 default: 1275 break; 1276 } 1277 break; 1278 } 1279 1280 default: 1281 break; 1282 } 1283 1284 return FALSE; 1285 } 1286 1287 static HFONT 1288 CreateTitleFont(VOID) 1289 { 1290 NONCLIENTMETRICSW ncm; 1291 LOGFONTW LogFont; 1292 HDC hdc; 1293 INT FontSize; 1294 HFONT hFont; 1295 1296 ncm.cbSize = sizeof(NONCLIENTMETRICSW); 1297 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); 1298 1299 LogFont = ncm.lfMessageFont; 1300 LogFont.lfWeight = FW_BOLD; 1301 wcscpy(LogFont.lfFaceName, L"MS Shell Dlg"); 1302 1303 hdc = GetDC(NULL); 1304 FontSize = 12; 1305 LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72; 1306 hFont = CreateFontIndirectW(&LogFont); 1307 ReleaseDC(NULL, hdc); 1308 1309 return hFont; 1310 } 1311 1312 BOOL 1313 DisplayWizard( 1314 IN PDEVINSTDATA DevInstData, 1315 IN HWND hwndParent, 1316 IN UINT startPage) 1317 { 1318 PROPSHEETHEADER psh = {0}; 1319 HPROPSHEETPAGE ahpsp[IDD_MAXIMUMPAGE + 1]; 1320 PROPSHEETPAGE psp = {0}; 1321 HRESULT hr = CoInitialize(NULL); /* for SHAutoComplete */ 1322 1323 /* zero based index */ 1324 startPage -= IDD_FIRSTPAGE; 1325 1326 /* Create the Welcome page */ 1327 ZeroMemory(&psp, sizeof(PROPSHEETPAGE)); 1328 psp.dwSize = sizeof(PROPSHEETPAGE); 1329 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE; 1330 psp.hInstance = hDllInstance; 1331 psp.lParam = (LPARAM)DevInstData; 1332 psp.pszTitle = MAKEINTRESOURCE(DevInstData->bUpdate ? IDS_UPDATEWIZARDTITLE : IDS_INSTALLWIZARDTITLE); 1333 psp.pfnDlgProc = WelcomeDlgProc; 1334 psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE); 1335 ahpsp[IDD_WELCOMEPAGE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1336 1337 /* Create the Select Source page */ 1338 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE; 1339 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_CHSOURCE_TITLE); 1340 psp.pfnDlgProc = CHSourceDlgProc; 1341 psp.pszTemplate = MAKEINTRESOURCE(IDD_CHSOURCE); 1342 ahpsp[IDD_CHSOURCE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1343 1344 /* Create the Search driver page */ 1345 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE; 1346 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_SEARCHDRV_TITLE); 1347 psp.pfnDlgProc = SearchDrvDlgProc; 1348 psp.pszTemplate = MAKEINTRESOURCE(IDD_SEARCHDRV); 1349 ahpsp[IDD_SEARCHDRV-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1350 1351 /* Create the Install driver page */ 1352 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE; 1353 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALLDRV_TITLE); 1354 psp.pfnDlgProc = InstallDrvDlgProc; 1355 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLDRV); 1356 ahpsp[IDD_INSTALLDRV-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1357 1358 /* Create the No driver page */ 1359 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE; 1360 psp.pfnDlgProc = NoDriverDlgProc; 1361 psp.pszTemplate = MAKEINTRESOURCE(IDD_NODRIVER); 1362 ahpsp[IDD_NODRIVER-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1363 1364 /* Create the Install failed page */ 1365 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE; 1366 psp.pfnDlgProc = InstallFailedDlgProc; 1367 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFAILED); 1368 ahpsp[IDD_INSTALLFAILED-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1369 1370 /* Create the Need reboot page */ 1371 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE; 1372 psp.pfnDlgProc = NeedRebootDlgProc; 1373 psp.pszTemplate = MAKEINTRESOURCE(IDD_NEEDREBOOT); 1374 ahpsp[IDD_NEEDREBOOT-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1375 1376 /* Create the Finish page */ 1377 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE; 1378 psp.pfnDlgProc = FinishDlgProc; 1379 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE); 1380 ahpsp[IDD_FINISHPAGE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp); 1381 1382 /* Create the property sheet */ 1383 psh.dwSize = sizeof(PROPSHEETHEADER); 1384 psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER; 1385 psh.hInstance = hDllInstance; 1386 psh.hwndParent = hwndParent; 1387 psh.nPages = IDD_MAXIMUMPAGE + 1; 1388 psh.nStartPage = startPage; 1389 psh.phpage = ahpsp; 1390 psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); 1391 psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER); 1392 1393 /* Create title font */ 1394 DevInstData->hTitleFont = CreateTitleFont(); 1395 1396 /* Display the wizard */ 1397 PropertySheet(&psh); 1398 1399 DeleteObject(DevInstData->hTitleFont); 1400 1401 if (SUCCEEDED(hr)) 1402 CoUninitialize(); 1403 return TRUE; 1404 } 1405