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