xref: /reactos/dll/win32/newdev/wizard.c (revision ccef43f3)
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 CALLBACK
583 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
584 {
585     BOOL bValid = FALSE;
586 
587     switch (uMsg)
588     {
589         case BFFM_INITIALIZED:
590         {
591             PCWSTR pszPath = ((PDEVINSTDATA)lpData)->CustomSearchPath;
592 
593             bValid = CheckBestDriver((PDEVINSTDATA)lpData, pszPath);
594             SendMessageW(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)pszPath);
595             SendMessageW(hwnd, BFFM_ENABLEOK, 0, bValid);
596             break;
597         }
598 
599         case BFFM_SELCHANGED:
600         {
601             WCHAR szDir[MAX_PATH];
602 
603             if (SHGetPathFromIDListW((LPITEMIDLIST)lParam, szDir))
604             {
605                 bValid = CheckBestDriver((PDEVINSTDATA)lpData, szDir);
606             }
607             PostMessageW(hwnd, BFFM_ENABLEOK, 0, bValid);
608             break;
609         }
610     }
611     return 0;
612 }
613 
614 static INT_PTR CALLBACK
615 CHSourceDlgProc(
616     IN HWND hwndDlg,
617     IN UINT uMsg,
618     IN WPARAM wParam,
619     IN LPARAM lParam)
620 {
621     PDEVINSTDATA DevInstData;
622 
623     /* Retrieve pointer to the global setup data */
624     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
625 
626     switch (uMsg)
627     {
628         case WM_INITDIALOG:
629         {
630             HWND hwndControl, hwndCombo;
631             DWORD dwStyle;
632             COMBOBOXINFO info = { sizeof(info) };
633 
634             /* Get pointer to the global setup data */
635             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
636             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
637 
638             hwndControl = GetParent(hwndDlg);
639 
640             /* Center the wizard window */
641             CenterWindow(hwndControl);
642 
643             /* Hide the system menu */
644             dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
645             SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
646 
647             hwndCombo = GetDlgItem(hwndDlg, IDC_COMBO_PATH);
648             PopulateCustomPathCombo(hwndCombo);
649 
650             GetComboBoxInfo(hwndCombo, &info);
651             SHAutoComplete(info.hwndItem, SHACF_FILESYS_DIRS);
652 
653             SendDlgItemMessage(
654                 hwndDlg,
655                 IDC_RADIO_SEARCHHERE,
656                 BM_SETCHECK,
657                 (WPARAM)TRUE,
658                 (LPARAM)0);
659             AutoDriver(hwndDlg, TRUE);
660             IncludePath(hwndDlg, FALSE);
661 
662             /* Disable manual driver choice for now */
663             EnableWindow(GetDlgItem(hwndDlg, IDC_RADIO_CHOOSE), FALSE);
664 
665             break;
666         }
667 
668         case WM_COMMAND:
669         {
670             switch (LOWORD(wParam))
671             {
672                 case IDC_RADIO_SEARCHHERE:
673                     AutoDriver(hwndDlg, TRUE);
674                     return TRUE;
675 
676                 case IDC_RADIO_CHOOSE:
677                     AutoDriver(hwndDlg, FALSE);
678                     return TRUE;
679 
680                 case IDC_CHECK_PATH:
681                     IncludePath(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH));
682                     return TRUE;
683 
684                 case IDC_BROWSE:
685                 {
686                     BROWSEINFOW bi = { 0 };
687                     LPITEMIDLIST pidl;
688                     WCHAR Title[MAX_PATH];
689                     WCHAR CustomSearchPath[MAX_PATH] = { 0 };
690                     INT len, idx = (INT)SendDlgItemMessageW(hwndDlg, IDC_COMBO_PATH, CB_GETCURSEL, 0, 0);
691                     LoadStringW(hDllInstance, IDS_BROWSE_FOR_FOLDER_TITLE, Title, _countof(Title));
692 
693                     if (idx == CB_ERR)
694                         len = GetWindowTextLengthW(GetDlgItem(hwndDlg, IDC_COMBO_PATH));
695                     else
696                         len = (INT)SendDlgItemMessageW(hwndDlg, IDC_COMBO_PATH, CB_GETLBTEXTLEN, idx, 0);
697 
698                     if (len < _countof(CustomSearchPath))
699                     {
700                         if (idx == CB_ERR)
701                             GetWindowTextW(GetDlgItem(hwndDlg, IDC_COMBO_PATH), CustomSearchPath, _countof(CustomSearchPath));
702                         else
703                             SendDlgItemMessageW(hwndDlg, IDC_COMBO_PATH, CB_GETLBTEXT, idx, (LPARAM)CustomSearchPath);
704                     }
705                     DevInstData->CustomSearchPath = CustomSearchPath;
706 
707                     bi.hwndOwner = hwndDlg;
708                     bi.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NONEWFOLDERBUTTON;
709                     bi.lpszTitle = Title;
710                     bi.lpfn = BrowseCallbackProc;
711                     bi.lParam = (LPARAM)DevInstData;
712                     pidl = SHBrowseForFolderW(&bi);
713                     if (pidl)
714                     {
715                         WCHAR Directory[MAX_PATH];
716                         IMalloc* malloc;
717 
718                         if (SHGetPathFromIDListW(pidl, Directory))
719                         {
720                             /* Set the IDC_COMBO_PATH text */
721                             SetWindowTextW(GetDlgItem(hwndDlg, IDC_COMBO_PATH), Directory);
722                         }
723 
724                         /* Free memory, if possible */
725                         if (SUCCEEDED(SHGetMalloc(&malloc)))
726                         {
727                             IMalloc_Free(malloc, pidl);
728                             IMalloc_Release(malloc);
729                         }
730                         return TRUE;
731                     }
732                     break;
733                 }
734             }
735             break;
736         }
737 
738         case WM_NOTIFY:
739         {
740             LPNMHDR lpnm = (LPNMHDR)lParam;
741 
742             switch (lpnm->code)
743             {
744                 case PSN_SETACTIVE:
745                     /* Enable the Next and Back buttons */
746                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
747                     break;
748 
749                 case PSN_WIZNEXT:
750                     /* Handle a Next button click, if necessary */
751                     if (IsDlgButtonChecked(hwndDlg, IDC_RADIO_SEARCHHERE))
752                     {
753                         SaveCustomPath(GetDlgItem(hwndDlg, IDC_COMBO_PATH));
754                         HeapFree(GetProcessHeap(), 0, DevInstData->CustomSearchPath);
755                         DevInstData->CustomSearchPath = NULL;
756                         if (PrepareFoldersToScan(
757                             DevInstData,
758                             IsDlgButtonChecked(hwndDlg, IDC_CHECK_MEDIA),
759                             IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH),
760                             GetDlgItem(hwndDlg, IDC_COMBO_PATH)))
761                         {
762                             PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_SEARCHDRV);
763                         }
764                         else
765                         {
766                             PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED);
767                         }
768                     }
769                     else
770                     {
771                         /* FIXME */;
772                     }
773                     return TRUE;
774 
775                 default:
776                     break;
777             }
778             break;
779         }
780 
781         default:
782             break;
783     }
784 
785     return FALSE;
786 }
787 
788 static INT_PTR CALLBACK
789 SearchDrvDlgProc(
790     IN HWND hwndDlg,
791     IN UINT uMsg,
792     IN WPARAM wParam,
793     IN LPARAM lParam)
794 {
795     PDEVINSTDATA DevInstData;
796     DWORD dwThreadId;
797 
798     /* Retrieve pointer to the global setup data */
799     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
800 
801     switch (uMsg)
802     {
803         case WM_INITDIALOG:
804         {
805             HWND hwndControl;
806             DWORD dwStyle;
807 
808             /* Get pointer to the global setup data */
809             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
810             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
811 
812             DevInstData->hDialog = hwndDlg;
813             hwndControl = GetParent(hwndDlg);
814 
815             /* Center the wizard window */
816             CenterWindow(hwndControl);
817 
818             SendDlgItemMessage(
819                 hwndDlg,
820                 IDC_DEVICE,
821                 WM_SETTEXT,
822                 0,
823                 (LPARAM)DevInstData->buffer);
824 
825             /* Hide the system menu */
826             dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
827             SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
828             break;
829         }
830 
831         case WM_SEARCH_FINISHED:
832         {
833             CloseHandle(hThread);
834             hThread = 0;
835             if (wParam == 0)
836                 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_NODRIVER);
837             else
838                 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLDRV);
839             break;
840         }
841 
842         case WM_NOTIFY:
843         {
844             LPNMHDR lpnm = (LPNMHDR)lParam;
845 
846             switch (lpnm->code)
847             {
848                 case PSN_SETACTIVE:
849                     PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK);
850                     /* Yes, we can safely ignore the problem (if any) */
851                     SetupDiDestroyDriverInfoList(
852                         DevInstData->hDevInfo,
853                         &DevInstData->devInfoData,
854                         SPDIT_COMPATDRIVER);
855                     hThread = CreateThread(NULL, 0, FindDriverProc, DevInstData, 0, &dwThreadId);
856                     break;
857 
858                 case PSN_KILLACTIVE:
859                     if (hThread != 0)
860                     {
861                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
862                         return TRUE;
863                     }
864                     break;
865 
866                 case PSN_WIZNEXT:
867                     /* Handle a Next button click, if necessary */
868                     break;
869 
870                 default:
871                     break;
872             }
873             break;
874         }
875 
876         default:
877             break;
878     }
879 
880     return FALSE;
881 }
882 
883 static INT_PTR CALLBACK
884 InstallDrvDlgProc(
885     IN HWND hwndDlg,
886     IN UINT uMsg,
887     IN WPARAM wParam,
888     IN LPARAM lParam)
889 {
890     PDEVINSTDATA DevInstData;
891     DWORD dwThreadId;
892 
893     /* Retrieve pointer to the global setup data */
894     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
895 
896     switch (uMsg)
897     {
898         case WM_INITDIALOG:
899         {
900             HWND hwndControl;
901             DWORD dwStyle;
902 
903             /* Get pointer to the global setup data */
904             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
905             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
906 
907             DevInstData->hDialog = hwndDlg;
908             hwndControl = GetParent(hwndDlg);
909 
910             /* Center the wizard window */
911             CenterWindow(hwndControl);
912 
913             SendDlgItemMessage(
914                 hwndDlg,
915                 IDC_DEVICE,
916                 WM_SETTEXT,
917                 0,
918                 (LPARAM)DevInstData->drvInfoData.Description);
919 
920             /* Hide the system menu */
921             dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
922             SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
923             break;
924         }
925 
926         case WM_INSTALL_FINISHED:
927         {
928             CloseHandle(hThread);
929             hThread = 0;
930             if (wParam == 0)
931             {
932                 SP_DEVINSTALL_PARAMS installParams;
933 
934                 SetFailedInstall(DevInstData->hDevInfo,
935                                  &DevInstData->devInfoData,
936                                  FALSE);
937 
938                 /* Should we reboot? */
939                 installParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
940                 if (SetupDiGetDeviceInstallParams(
941                     DevInstData->hDevInfo,
942                     &DevInstData->devInfoData,
943                     &installParams))
944                 {
945                     if (installParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))
946                     {
947                         PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_NEEDREBOOT);
948                     }
949                     else
950                         PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_FINISHPAGE);
951                     break;
952                 }
953             }
954             PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED);
955             break;
956         }
957 
958         case WM_NOTIFY:
959         {
960             LPNMHDR lpnm = (LPNMHDR)lParam;
961 
962             switch (lpnm->code)
963             {
964                 case PSN_SETACTIVE:
965                     PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK);
966                     hThread = CreateThread(NULL, 0, InstallDriverProc, DevInstData, 0, &dwThreadId);
967                     break;
968 
969                 case PSN_KILLACTIVE:
970                     if (hThread != 0)
971                     {
972                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
973                         return TRUE;
974                     }
975                     break;
976 
977                 case PSN_WIZNEXT:
978                     /* Handle a Next button click, if necessary */
979                     break;
980 
981                 default:
982                     break;
983             }
984             break;
985         }
986 
987         default:
988             break;
989     }
990 
991     return FALSE;
992 }
993 
994 static INT_PTR CALLBACK
995 NoDriverDlgProc(
996     IN HWND hwndDlg,
997     IN UINT uMsg,
998     IN WPARAM wParam,
999     IN LPARAM lParam)
1000 {
1001     PDEVINSTDATA DevInstData;
1002     HWND hwndControl;
1003 
1004     UNREFERENCED_PARAMETER(wParam);
1005 
1006     /* Get pointer to the global setup data */
1007     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1008 
1009     switch (uMsg)
1010     {
1011         case WM_INITDIALOG:
1012         {
1013             BOOL DisableableDevice = FALSE;
1014 
1015             /* Get pointer to the global setup data */
1016             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1017             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
1018 
1019             /* Center the wizard window */
1020             CenterWindow(GetParent(hwndDlg));
1021 
1022             hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1023             ShowWindow(hwndControl, SW_HIDE);
1024             EnableWindow(hwndControl, FALSE);
1025 
1026             /* Set title font */
1027             SendDlgItemMessage(
1028                 hwndDlg,
1029                 IDC_FINISHTITLE,
1030                 WM_SETFONT,
1031                 (WPARAM)DevInstData->hTitleFont,
1032                 (LPARAM)TRUE);
1033 
1034             /* disable the "do not show this dialog anymore" checkbox
1035              if the device cannot be disabled */
1036             CanDisableDevice(
1037                 DevInstData->devInfoData.DevInst,
1038                 NULL,
1039                 &DisableableDevice);
1040             EnableWindow(
1041                 GetDlgItem(hwndDlg, IDC_DONOTSHOWDLG),
1042                 DisableableDevice);
1043             break;
1044         }
1045 
1046         case WM_NOTIFY:
1047         {
1048             LPNMHDR lpnm = (LPNMHDR)lParam;
1049 
1050             switch (lpnm->code)
1051             {
1052                 case PSN_SETACTIVE:
1053                     /* Enable the correct buttons on for the active page */
1054                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
1055                     break;
1056 
1057                 case PSN_WIZBACK:
1058                     /* Handle a Back button click, if necessary */
1059                     hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1060                     ShowWindow(hwndControl, SW_SHOW);
1061                     EnableWindow(hwndControl, TRUE);
1062                     PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_CHSOURCE);
1063                     return TRUE;
1064 
1065                 case PSN_WIZFINISH:
1066                 {
1067                     BOOL DisableableDevice = FALSE;
1068                     BOOL IsStarted = FALSE;
1069 
1070                     if (CanDisableDevice(DevInstData->devInfoData.DevInst,
1071                             NULL,
1072                             &DisableableDevice) &&
1073                         DisableableDevice &&
1074                         IsDeviceStarted(
1075                             DevInstData->devInfoData.DevInst,
1076                             NULL,
1077                             &IsStarted) &&
1078                         !IsStarted &&
1079                         SendDlgItemMessage(
1080                             hwndDlg,
1081                             IDC_DONOTSHOWDLG,
1082                             BM_GETCHECK,
1083                             (WPARAM)0, (LPARAM)0) == BST_CHECKED)
1084                     {
1085                         /* disable the device */
1086                         StartDevice(
1087                             DevInstData->hDevInfo,
1088                             &DevInstData->devInfoData,
1089                             FALSE,
1090                             0,
1091                             NULL);
1092                     }
1093                     else
1094                     {
1095                         SetFailedInstall(DevInstData->hDevInfo,
1096                                          &DevInstData->devInfoData,
1097                                          FALSE);
1098                     }
1099                     break;
1100                 }
1101 
1102                 default:
1103                     break;
1104             }
1105             break;
1106         }
1107 
1108         default:
1109             break;
1110     }
1111 
1112     return FALSE;
1113 }
1114 
1115 static INT_PTR CALLBACK
1116 InstallFailedDlgProc(
1117     IN HWND hwndDlg,
1118     IN UINT uMsg,
1119     IN WPARAM wParam,
1120     IN LPARAM lParam)
1121 {
1122     PDEVINSTDATA DevInstData;
1123     UNREFERENCED_PARAMETER(wParam);
1124 
1125     /* Retrieve pointer to the global setup data */
1126     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1127 
1128     switch (uMsg)
1129     {
1130         case WM_INITDIALOG:
1131         {
1132             HWND hwndControl;
1133 
1134             /* Get pointer to the global setup data */
1135             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1136             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
1137 
1138             /* Center the wizard window */
1139             CenterWindow(GetParent(hwndDlg));
1140 
1141             hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1142             ShowWindow(hwndControl, SW_HIDE);
1143             EnableWindow(hwndControl, FALSE);
1144 
1145             SendDlgItemMessage(
1146                 hwndDlg,
1147                 IDC_DEVICE,
1148                 WM_SETTEXT,
1149                 0,
1150                 (LPARAM)DevInstData->drvInfoData.Description);
1151 
1152             /* Set title font */
1153             SendDlgItemMessage(
1154                 hwndDlg,
1155                 IDC_FINISHTITLE,
1156                 WM_SETFONT,
1157                 (WPARAM)DevInstData->hTitleFont,
1158                 (LPARAM)TRUE);
1159             break;
1160         }
1161 
1162         case WM_NOTIFY:
1163         {
1164             LPNMHDR lpnm = (LPNMHDR)lParam;
1165 
1166             switch (lpnm->code)
1167             {
1168                 case PSN_SETACTIVE:
1169                     /* Enable the correct buttons on for the active page */
1170                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
1171                     break;
1172 
1173                 case PSN_WIZBACK:
1174                     /* Handle a Back button click, if necessary */
1175                     break;
1176 
1177                 case PSN_WIZFINISH:
1178                     /* Handle a Finish button click, if necessary */
1179                     break;
1180 
1181                 default:
1182                     break;
1183             }
1184             break;
1185         }
1186 
1187         default:
1188             break;
1189     }
1190 
1191     return FALSE;
1192 }
1193 
1194 static INT_PTR CALLBACK
1195 NeedRebootDlgProc(
1196     IN HWND hwndDlg,
1197     IN UINT uMsg,
1198     IN WPARAM wParam,
1199     IN LPARAM lParam)
1200 {
1201     PDEVINSTDATA DevInstData;
1202     UNREFERENCED_PARAMETER(wParam);
1203 
1204     /* Retrieve pointer to the global setup data */
1205     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1206 
1207     switch (uMsg)
1208     {
1209         case WM_INITDIALOG:
1210         {
1211             HWND hwndControl;
1212 
1213             /* Get pointer to the global setup data */
1214             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1215             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
1216 
1217             /* Center the wizard window */
1218             CenterWindow(GetParent(hwndDlg));
1219 
1220             hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1221             ShowWindow(hwndControl, SW_HIDE);
1222             EnableWindow(hwndControl, FALSE);
1223 
1224             SendDlgItemMessage(
1225                 hwndDlg,
1226                 IDC_DEVICE,
1227                 WM_SETTEXT,
1228                 0,
1229                 (LPARAM)DevInstData->drvInfoData.Description);
1230 
1231             /* Set title font */
1232             SendDlgItemMessage(
1233                 hwndDlg,
1234                 IDC_FINISHTITLE,
1235                 WM_SETFONT,
1236                 (WPARAM)DevInstData->hTitleFont,
1237                 (LPARAM)TRUE);
1238             break;
1239         }
1240 
1241         case WM_NOTIFY:
1242         {
1243             LPNMHDR lpnm = (LPNMHDR)lParam;
1244 
1245             switch (lpnm->code)
1246             {
1247                 case PSN_SETACTIVE:
1248                     /* Enable the correct buttons on for the active page */
1249                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
1250                     break;
1251 
1252                 case PSN_WIZBACK:
1253                     /* Handle a Back button click, if necessary */
1254                     break;
1255 
1256                 case PSN_WIZFINISH:
1257                     /* Handle a Finish button click, if necessary */
1258                     break;
1259 
1260                 default:
1261                     break;
1262             }
1263             break;
1264         }
1265 
1266         default:
1267             break;
1268     }
1269 
1270     return FALSE;
1271 }
1272 
1273 static INT_PTR CALLBACK
1274 FinishDlgProc(
1275     IN HWND hwndDlg,
1276     IN UINT uMsg,
1277     IN WPARAM wParam,
1278     IN LPARAM lParam)
1279 {
1280     PDEVINSTDATA DevInstData;
1281     UNREFERENCED_PARAMETER(wParam);
1282 
1283     /* Retrieve pointer to the global setup data */
1284     DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1285 
1286     switch (uMsg)
1287     {
1288         case WM_INITDIALOG:
1289         {
1290             HWND hwndControl;
1291 
1292             /* Get pointer to the global setup data */
1293             DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1294             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)DevInstData);
1295 
1296             /* Center the wizard window */
1297             CenterWindow(GetParent(hwndDlg));
1298 
1299             hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1300             ShowWindow(hwndControl, SW_HIDE);
1301             EnableWindow(hwndControl, FALSE);
1302 
1303             SendDlgItemMessage(
1304                 hwndDlg,
1305                 IDC_DEVICE,
1306                 WM_SETTEXT,
1307                 0,
1308                 (LPARAM)DevInstData->drvInfoData.Description);
1309 
1310             /* Set title font */
1311             SendDlgItemMessage(
1312                 hwndDlg,
1313                 IDC_FINISHTITLE,
1314                 WM_SETFONT,
1315                 (WPARAM)DevInstData->hTitleFont,
1316                 (LPARAM)TRUE);
1317             break;
1318         }
1319 
1320         case WM_NOTIFY:
1321         {
1322             LPNMHDR lpnm = (LPNMHDR)lParam;
1323 
1324             switch (lpnm->code)
1325             {
1326                 case PSN_SETACTIVE:
1327                     /* Enable the correct buttons on for the active page */
1328                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
1329                     break;
1330 
1331                 case PSN_WIZBACK:
1332                     /* Handle a Back button click, if necessary */
1333                     break;
1334 
1335                 case PSN_WIZFINISH:
1336                     /* Handle a Finish button click, if necessary */
1337                     break;
1338 
1339                 default:
1340                     break;
1341             }
1342             break;
1343         }
1344 
1345         default:
1346             break;
1347     }
1348 
1349     return FALSE;
1350 }
1351 
1352 static HFONT
1353 CreateTitleFont(VOID)
1354 {
1355     NONCLIENTMETRICSW ncm;
1356     LOGFONTW LogFont;
1357     HDC hdc;
1358     INT FontSize;
1359     HFONT hFont;
1360 
1361     ncm.cbSize = sizeof(NONCLIENTMETRICSW);
1362     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
1363 
1364     LogFont = ncm.lfMessageFont;
1365     LogFont.lfWeight = FW_BOLD;
1366     wcscpy(LogFont.lfFaceName, L"MS Shell Dlg");
1367 
1368     hdc = GetDC(NULL);
1369     FontSize = 12;
1370     LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72;
1371     hFont = CreateFontIndirectW(&LogFont);
1372     ReleaseDC(NULL, hdc);
1373 
1374     return hFont;
1375 }
1376 
1377 BOOL
1378 DisplayWizard(
1379     IN PDEVINSTDATA DevInstData,
1380     IN HWND hwndParent,
1381     IN UINT startPage)
1382 {
1383     PROPSHEETHEADER psh = {0};
1384     HPROPSHEETPAGE ahpsp[IDD_MAXIMUMPAGE + 1];
1385     PROPSHEETPAGE psp = {0};
1386     HRESULT hr = CoInitialize(NULL); /* for SHAutoComplete */
1387 
1388     /* zero based index */
1389     startPage -= IDD_FIRSTPAGE;
1390 
1391     /* Create the Welcome page */
1392     ZeroMemory(&psp, sizeof(PROPSHEETPAGE));
1393     psp.dwSize = sizeof(PROPSHEETPAGE);
1394     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE;
1395     psp.hInstance = hDllInstance;
1396     psp.lParam = (LPARAM)DevInstData;
1397     psp.pszTitle = MAKEINTRESOURCE(DevInstData->bUpdate ? IDS_UPDATEWIZARDTITLE : IDS_INSTALLWIZARDTITLE);
1398     psp.pfnDlgProc = WelcomeDlgProc;
1399     psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE);
1400     ahpsp[IDD_WELCOMEPAGE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1401 
1402     /* Create the Select Source page */
1403     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE;
1404     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_CHSOURCE_TITLE);
1405     psp.pfnDlgProc = CHSourceDlgProc;
1406     psp.pszTemplate = MAKEINTRESOURCE(IDD_CHSOURCE);
1407     ahpsp[IDD_CHSOURCE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1408 
1409     /* Create the Search driver page */
1410     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE;
1411     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_SEARCHDRV_TITLE);
1412     psp.pfnDlgProc = SearchDrvDlgProc;
1413     psp.pszTemplate = MAKEINTRESOURCE(IDD_SEARCHDRV);
1414     ahpsp[IDD_SEARCHDRV-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1415 
1416     /* Create the Install driver page */
1417     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USETITLE;
1418     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALLDRV_TITLE);
1419     psp.pfnDlgProc = InstallDrvDlgProc;
1420     psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLDRV);
1421     ahpsp[IDD_INSTALLDRV-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1422 
1423     /* Create the No driver page */
1424     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE;
1425     psp.pfnDlgProc = NoDriverDlgProc;
1426     psp.pszTemplate = MAKEINTRESOURCE(IDD_NODRIVER);
1427     ahpsp[IDD_NODRIVER-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1428 
1429     /* Create the Install failed page */
1430     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE;
1431     psp.pfnDlgProc = InstallFailedDlgProc;
1432     psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFAILED);
1433     ahpsp[IDD_INSTALLFAILED-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1434 
1435     /* Create the Need reboot page */
1436     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE;
1437     psp.pfnDlgProc = NeedRebootDlgProc;
1438     psp.pszTemplate = MAKEINTRESOURCE(IDD_NEEDREBOOT);
1439     ahpsp[IDD_NEEDREBOOT-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1440 
1441     /* Create the Finish page */
1442     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER | PSP_USETITLE;
1443     psp.pfnDlgProc = FinishDlgProc;
1444     psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE);
1445     ahpsp[IDD_FINISHPAGE-IDD_FIRSTPAGE] = CreatePropertySheetPage(&psp);
1446 
1447     /* Create the property sheet */
1448     psh.dwSize = sizeof(PROPSHEETHEADER);
1449     psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
1450     psh.hInstance = hDllInstance;
1451     psh.hwndParent = hwndParent;
1452     psh.nPages = IDD_MAXIMUMPAGE + 1;
1453     psh.nStartPage = startPage;
1454     psh.phpage = ahpsp;
1455     psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
1456     psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
1457 
1458     /* Create title font */
1459     DevInstData->hTitleFont = CreateTitleFont();
1460 
1461     /* Display the wizard */
1462     PropertySheet(&psh);
1463 
1464     DeleteObject(DevInstData->hTitleFont);
1465 
1466     if (SUCCEEDED(hr))
1467         CoUninitialize();
1468     return TRUE;
1469 }
1470