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