xref: /reactos/dll/win32/newdev/wizard.c (revision 9393fc32)
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