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