Don't stop the search after the first driver found. Next ones may be better
[reactos.git] / reactos / dll / win32 / newdev / wizard.c
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #define YDEBUG
22 #include "newdev_private.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(newdev);
25
26 HANDLE hThread;
27
28 static VOID
29 CenterWindow(
30 IN HWND hWnd)
31 {
32 HWND hWndParent;
33 RECT rcParent;
34 RECT rcWindow;
35
36 hWndParent = GetParent(hWnd);
37 if (hWndParent == NULL)
38 hWndParent = GetDesktopWindow();
39
40 GetWindowRect(hWndParent, &rcParent);
41 GetWindowRect(hWnd, &rcWindow);
42
43 SetWindowPos(
44 hWnd,
45 HWND_TOP,
46 ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2,
47 ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2,
48 0,
49 0,
50 SWP_NOSIZE);
51 }
52
53 static BOOL
54 CanDisableDevice(
55 IN DEVINST DevInst,
56 IN HMACHINE hMachine,
57 OUT BOOL *CanDisable)
58 {
59 #if 0
60 /* hpoussin, Dec 2005. I've disabled this code because
61 * ntoskrnl never sets the DN_DISABLEABLE flag.
62 */
63 CONFIGRET cr;
64 ULONG Status, ProblemNumber;
65 BOOL Ret = FALSE;
66
67 cr = CM_Get_DevNode_Status_Ex(
68 &Status,
69 &ProblemNumber,
70 DevInst,
71 0,
72 hMachine);
73 if (cr == CR_SUCCESS)
74 {
75 *CanDisable = ((Status & DN_DISABLEABLE) != 0);
76 Ret = TRUE;
77 }
78
79 return Ret;
80 #else
81 *CanDisable = TRUE;
82 return TRUE;
83 #endif
84 }
85
86 static BOOL
87 IsDeviceStarted(
88 IN DEVINST DevInst,
89 IN HMACHINE hMachine,
90 OUT BOOL *IsEnabled)
91 {
92 CONFIGRET cr;
93 ULONG Status, ProblemNumber;
94 BOOL Ret = FALSE;
95
96 cr = CM_Get_DevNode_Status_Ex(
97 &Status,
98 &ProblemNumber,
99 DevInst,
100 0,
101 hMachine);
102 if (cr == CR_SUCCESS)
103 {
104 *IsEnabled = ((Status & DN_STARTED) != 0);
105 Ret = TRUE;
106 }
107
108 return Ret;
109 }
110
111 static BOOL
112 StartDevice(
113 IN HDEVINFO DeviceInfoSet,
114 IN PSP_DEVINFO_DATA DevInfoData OPTIONAL,
115 IN BOOL bEnable,
116 IN DWORD HardwareProfile OPTIONAL,
117 OUT BOOL *bNeedReboot OPTIONAL)
118 {
119 SP_PROPCHANGE_PARAMS pcp;
120 SP_DEVINSTALL_PARAMS dp;
121 DWORD LastErr;
122 BOOL Ret = FALSE;
123
124 pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
125 pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
126 pcp.HwProfile = HardwareProfile;
127
128 if (bEnable)
129 {
130 /* try to enable the device on the global profile */
131 pcp.StateChange = DICS_ENABLE;
132 pcp.Scope = DICS_FLAG_GLOBAL;
133
134 /* ignore errors */
135 LastErr = GetLastError();
136 if (SetupDiSetClassInstallParams(
137 DeviceInfoSet,
138 DevInfoData,
139 &pcp.ClassInstallHeader,
140 sizeof(SP_PROPCHANGE_PARAMS)))
141 {
142 SetupDiCallClassInstaller(
143 DIF_PROPERTYCHANGE,
144 DeviceInfoSet,
145 DevInfoData);
146 }
147 SetLastError(LastErr);
148 }
149
150 /* try config-specific */
151 pcp.StateChange = (bEnable ? DICS_ENABLE : DICS_DISABLE);
152 pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
153
154 if (SetupDiSetClassInstallParams(
155 DeviceInfoSet,
156 DevInfoData,
157 &pcp.ClassInstallHeader,
158 sizeof(SP_PROPCHANGE_PARAMS)) &&
159 SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
160 DeviceInfoSet,
161 DevInfoData))
162 {
163 dp.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
164 if (SetupDiGetDeviceInstallParams(
165 DeviceInfoSet,
166 DevInfoData,
167 &dp))
168 {
169 if (bNeedReboot != NULL)
170 {
171 *bNeedReboot = ((dp.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) != 0);
172 }
173
174 Ret = TRUE;
175 }
176 }
177 return Ret;
178 }
179
180 static BOOL
181 PrepareFoldersToScan(
182 IN PDEVINSTDATA DevInstData,
183 IN HWND hwndDlg)
184 {
185 TCHAR drive[] = {'?',':',0};
186 DWORD dwDrives = 0;
187 DWORD i;
188 UINT nType;
189 DWORD CustomTextLength = 0;
190 DWORD LengthNeeded = 0;
191 LPTSTR Buffer;
192
193 TRACE("Include removable devices: %s\n", IsDlgButtonChecked(hwndDlg, IDC_CHECK_MEDIA) ? "yes" : "no");
194 TRACE("Include custom path : %s\n", IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH) ? "yes" : "no");
195
196 /* Calculate length needed to store the search paths */
197 if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_MEDIA))
198 {
199 dwDrives = GetLogicalDrives();
200 for (drive[0] = 'A', i = 1; drive[0] <= 'Z'; drive[0]++, i <<= 1)
201 {
202 if (dwDrives & i)
203 {
204 nType = GetDriveType(drive);
205 if (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM)
206 {
207 LengthNeeded += 3;
208 }
209 }
210 }
211 }
212 if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH))
213 {
214 CustomTextLength = 1 + SendDlgItemMessage(
215 hwndDlg,
216 IDC_COMBO_PATH,
217 WM_GETTEXTLENGTH,
218 (WPARAM)0,
219 (LPARAM)0);
220 LengthNeeded += CustomTextLength;
221 }
222
223 /* Allocate space for search paths */
224 HeapFree(GetProcessHeap(), 0, DevInstData->CustomSearchPath);
225 DevInstData->CustomSearchPath = Buffer = HeapAlloc(
226 GetProcessHeap(),
227 0,
228 (LengthNeeded + 1) * sizeof(TCHAR));
229 if (!Buffer)
230 {
231 TRACE("HeapAlloc() failed\n");
232 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
233 return FALSE;
234 }
235
236 /* Fill search paths */
237 if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_MEDIA))
238 {
239 for (drive[0] = 'A', i = 1; drive[0] <= 'Z'; drive[0]++, i <<= 1)
240 {
241 if (dwDrives & i)
242 {
243 nType = GetDriveType(drive);
244 if (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM)
245 {
246 Buffer += 1 + _stprintf(Buffer, drive);
247 }
248 }
249 }
250 }
251 if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH))
252 {
253 Buffer += 1 + SendDlgItemMessage(
254 hwndDlg,
255 IDC_COMBO_PATH,
256 WM_GETTEXT,
257 (WPARAM)CustomTextLength,
258 (LPARAM)Buffer);
259 }
260 *Buffer = _T('\0');
261
262 return TRUE;
263 }
264
265 static DWORD WINAPI
266 FindDriverProc(
267 IN LPVOID lpParam)
268 {
269 PDEVINSTDATA DevInstData;
270 DWORD config_flags;
271 BOOL result = FALSE;
272
273 DevInstData = (PDEVINSTDATA)lpParam;
274
275 /* Yes, we can safely ignore the problem (if any) */
276 SetupDiDestroyDriverInfoList(
277 DevInstData->hDevInfo,
278 &DevInstData->devInfoData,
279 SPDIT_COMPATDRIVER);
280
281 if (!DevInstData->CustomSearchPath)
282 {
283 /* Search in default location */
284 result = SearchDriver(DevInstData, NULL, NULL);
285 }
286 else
287 {
288 /* Search only in specified paths */
289 /* We need to check all specified directories to be
290 * sure to find the best driver for the device.
291 */
292 LPCTSTR Path;
293 for (Path = DevInstData->CustomSearchPath; *Path != '\0'; Path += _tcslen(Path) + 1)
294 {
295 TRACE("Search driver in %S\n", Path);
296 if (_tcslen(Path) == 2 && Path[1] == ':')
297 {
298 if (SearchDriverRecursive(DevInstData, Path))
299 result = TRUE;
300 }
301 else
302 {
303 if (SearchDriver(DevInstData, Path, NULL))
304 result = TRUE;
305 }
306 }
307 }
308
309 if (result)
310 {
311 PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 1, 0);
312 }
313 else
314 {
315 /* Update device configuration */
316 if (SetupDiGetDeviceRegistryProperty(
317 DevInstData->hDevInfo,
318 &DevInstData->devInfoData,
319 SPDRP_CONFIGFLAGS,
320 NULL,
321 (BYTE *)&config_flags,
322 sizeof(config_flags),
323 NULL))
324 {
325 config_flags |= CONFIGFLAG_FAILEDINSTALL;
326 SetupDiSetDeviceRegistryProperty(
327 DevInstData->hDevInfo,
328 &DevInstData->devInfoData,
329 SPDRP_CONFIGFLAGS,
330 (BYTE *)&config_flags, sizeof(config_flags));
331 }
332
333 PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 0, 0);
334 }
335 return 0;
336 }
337
338 static DWORD WINAPI
339 InstallDriverProc(
340 IN LPVOID lpParam)
341 {
342 PDEVINSTDATA DevInstData;
343 BOOL res;
344
345 DevInstData = (PDEVINSTDATA)lpParam;
346 res = InstallCurrentDriver(DevInstData);
347 PostMessage(DevInstData->hDialog, WM_INSTALL_FINISHED, res ? 0 : 1, 0);
348 return 0;
349 }
350
351 static VOID
352 PopulateCustomPathCombo(
353 IN HWND hwndCombo)
354 {
355 HKEY hKey = NULL;
356 DWORD dwRegType;
357 DWORD dwPathLength;
358 LPTSTR Buffer = NULL;
359 LPCTSTR Path;
360 LONG rc;
361
362 (void)ComboBox_ResetContent(hwndCombo);
363
364 /* RegGetValue would have been better... */
365 rc = RegOpenKeyEx(
366 HKEY_LOCAL_MACHINE,
367 REGSTR_PATH_SETUP REGSTR_KEY_SETUP,
368 0,
369 KEY_QUERY_VALUE,
370 &hKey);
371 if (rc != ERROR_SUCCESS)
372 {
373 TRACE("RegOpenKeyEx() failed with error 0x%lx\n", rc);
374 goto cleanup;
375 }
376 rc = RegQueryValueEx(
377 hKey,
378 _T("Installation Sources"),
379 NULL,
380 &dwRegType,
381 NULL,
382 &dwPathLength);
383 if (rc != ERROR_SUCCESS || dwRegType != REG_MULTI_SZ)
384 {
385 TRACE("RegQueryValueEx() failed with error 0x%lx\n", rc);
386 goto cleanup;
387 }
388 /* Allocate enough space to add 2 NULL chars at the end of the string */
389 Buffer = HeapAlloc(GetProcessHeap(), 0, dwPathLength + 2 * sizeof(TCHAR));
390 if (!Buffer)
391 {
392 TRACE("HeapAlloc() failed\n");
393 goto cleanup;
394 }
395 rc = RegQueryValueEx(
396 hKey,
397 _T("Installation Sources"),
398 NULL,
399 NULL,
400 (LPBYTE)Buffer,
401 &dwPathLength);
402 if (rc != ERROR_SUCCESS)
403 {
404 TRACE("RegQueryValueEx() failed with error 0x%lx\n", rc);
405 goto cleanup;
406 }
407 Buffer[dwPathLength] = Buffer[dwPathLength + 1] = '\0';
408
409 /* Populate combo box */
410 for (Path = Buffer; *Path; Path += _tcslen(Path))
411 (void)ComboBox_AddString(hwndCombo, Path);
412 (void)ComboBox_SetCurSel(hwndCombo, 0);
413
414 cleanup:
415 if (hKey != NULL)
416 RegCloseKey(hKey);
417 HeapFree(GetProcessHeap(), 0, Buffer);
418 }
419
420 static VOID
421 SaveCustomPath(
422 IN HWND hwndCombo)
423 {
424 FIXME("Stub\n");
425 }
426
427 static INT_PTR CALLBACK
428 WelcomeDlgProc(
429 IN HWND hwndDlg,
430 IN UINT uMsg,
431 IN WPARAM wParam,
432 IN LPARAM lParam)
433 {
434 PDEVINSTDATA DevInstData;
435
436 /* Retrieve pointer to the global setup data */
437 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
438
439 switch (uMsg)
440 {
441 case WM_INITDIALOG:
442 {
443 HWND hwndControl;
444 DWORD dwStyle;
445
446 /* Get pointer to the global setup data */
447 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
448 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
449
450 hwndControl = GetParent(hwndDlg);
451
452 /* Center the wizard window */
453 CenterWindow(hwndControl);
454
455 /* Hide the system menu */
456 dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
457 SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
458
459 /* Set title font */
460 SendDlgItemMessage(
461 hwndDlg,
462 IDC_WELCOMETITLE,
463 WM_SETFONT,
464 (WPARAM)DevInstData->hTitleFont,
465 (LPARAM)TRUE);
466
467 SendDlgItemMessage(
468 hwndDlg,
469 IDC_DEVICE,
470 WM_SETTEXT,
471 0,
472 (LPARAM)DevInstData->buffer);
473
474 SendDlgItemMessage(
475 hwndDlg,
476 IDC_RADIO_AUTO,
477 BM_SETCHECK,
478 (WPARAM)TRUE,
479 (LPARAM)0);
480 break;
481 }
482
483 case WM_NOTIFY:
484 {
485 LPNMHDR lpnm = (LPNMHDR)lParam;
486
487 switch (lpnm->code)
488 {
489 case PSN_SETACTIVE:
490 /* Enable the Next button */
491 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
492 break;
493
494 case PSN_WIZNEXT:
495 /* Handle a Next button click, if necessary */
496 if (SendDlgItemMessage(hwndDlg, IDC_RADIO_AUTO, BM_GETCHECK, (WPARAM)0, (LPARAM)0) == BST_CHECKED)
497 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_SEARCHDRV);
498 return TRUE;
499
500 default:
501 break;
502 }
503 break;
504 }
505
506 default:
507 break;
508 }
509
510 return FALSE;
511 }
512
513 static void
514 IncludePath(HWND Dlg, BOOL Enabled)
515 {
516 EnableWindow(GetDlgItem(Dlg, IDC_COMBO_PATH), Enabled);
517 EnableWindow(GetDlgItem(Dlg, IDC_BROWSE), /*FIXME: Enabled*/ FALSE);
518 }
519
520 static void
521 AutoDriver(HWND Dlg, BOOL Enabled)
522 {
523 EnableWindow(GetDlgItem(Dlg, IDC_CHECK_MEDIA), Enabled);
524 EnableWindow(GetDlgItem(Dlg, IDC_CHECK_PATH), Enabled);
525 IncludePath(Dlg, Enabled & IsDlgButtonChecked(Dlg, IDC_CHECK_PATH));
526 }
527
528 static INT_PTR CALLBACK
529 CHSourceDlgProc(
530 IN HWND hwndDlg,
531 IN UINT uMsg,
532 IN WPARAM wParam,
533 IN LPARAM lParam)
534 {
535 PDEVINSTDATA DevInstData;
536
537 /* Retrieve pointer to the global setup data */
538 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
539
540 switch (uMsg)
541 {
542 case WM_INITDIALOG:
543 {
544 HWND hwndControl;
545 DWORD dwStyle;
546
547 /* Get pointer to the global setup data */
548 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
549 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
550
551 hwndControl = GetParent(hwndDlg);
552
553 /* Center the wizard window */
554 CenterWindow(hwndControl);
555
556 /* Hide the system menu */
557 dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
558 SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
559
560 PopulateCustomPathCombo(GetDlgItem(hwndDlg, IDC_COMBO_PATH));
561
562 SendDlgItemMessage(
563 hwndDlg,
564 IDC_RADIO_SEARCHHERE,
565 BM_SETCHECK,
566 (WPARAM)TRUE,
567 (LPARAM)0);
568 AutoDriver(hwndDlg, TRUE);
569 IncludePath(hwndDlg, FALSE);
570
571 /* Disable manual driver choice for now */
572 EnableWindow(GetDlgItem(hwndDlg, IDC_RADIO_CHOOSE), FALSE);
573
574 break;
575 }
576
577 case WM_COMMAND:
578 {
579 switch (LOWORD(wParam))
580 {
581 case IDC_RADIO_SEARCHHERE:
582 AutoDriver(hwndDlg, TRUE);
583 return TRUE;
584
585 case IDC_RADIO_CHOOSE:
586 AutoDriver(hwndDlg, FALSE);
587 return TRUE;
588
589 case IDC_CHECK_PATH:
590 IncludePath(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_PATH));
591 return TRUE;
592
593 case IDC_BROWSE:
594 /* FIXME: set the IDC_COMBO_PATH text */
595 FIXME("Should display browse folder dialog\n");
596 return FALSE;
597 }
598 break;
599 }
600
601 case WM_NOTIFY:
602 {
603 LPNMHDR lpnm = (LPNMHDR)lParam;
604
605 switch (lpnm->code)
606 {
607 case PSN_SETACTIVE:
608 /* Enable the Next and Back buttons */
609 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
610 break;
611
612 case PSN_WIZNEXT:
613 /* Handle a Next button click, if necessary */
614 if (IsDlgButtonChecked(hwndDlg, IDC_RADIO_SEARCHHERE))
615 {
616 SaveCustomPath(GetDlgItem(hwndDlg, IDC_COMBO_PATH));
617 HeapFree(GetProcessHeap(), 0, DevInstData->CustomSearchPath);
618 DevInstData->CustomSearchPath = NULL;
619 if (PrepareFoldersToScan(DevInstData, hwndDlg))
620 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_SEARCHDRV);
621 else
622 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED);
623 }
624 else
625 /* FIXME */;
626 return TRUE;
627
628 default:
629 break;
630 }
631 break;
632 }
633
634 default:
635 break;
636 }
637
638 return FALSE;
639 }
640
641 static INT_PTR CALLBACK
642 SearchDrvDlgProc(
643 IN HWND hwndDlg,
644 IN UINT uMsg,
645 IN WPARAM wParam,
646 IN LPARAM lParam)
647 {
648 PDEVINSTDATA DevInstData;
649 DWORD dwThreadId;
650
651 /* Retrieve pointer to the global setup data */
652 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
653
654 switch (uMsg)
655 {
656 case WM_INITDIALOG:
657 {
658 HWND hwndControl;
659 DWORD dwStyle;
660
661 /* Get pointer to the global setup data */
662 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
663 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
664
665 DevInstData->hDialog = hwndDlg;
666 hwndControl = GetParent(hwndDlg);
667
668 /* Center the wizard window */
669 CenterWindow(hwndControl);
670
671 SendDlgItemMessage(
672 hwndDlg,
673 IDC_DEVICE,
674 WM_SETTEXT,
675 0,
676 (LPARAM)DevInstData->buffer);
677
678 /* Hide the system menu */
679 dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
680 SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
681 break;
682 }
683
684 case WM_SEARCH_FINISHED:
685 {
686 CloseHandle(hThread);
687 hThread = 0;
688 if (wParam == 0)
689 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_NODRIVER);
690 else
691 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLDRV);
692 break;
693 }
694
695 case WM_NOTIFY:
696 {
697 LPNMHDR lpnm = (LPNMHDR)lParam;
698
699 switch (lpnm->code)
700 {
701 case PSN_SETACTIVE:
702 PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK);
703 hThread = CreateThread(NULL, 0, FindDriverProc, DevInstData, 0, &dwThreadId);
704 break;
705
706 case PSN_KILLACTIVE:
707 if (hThread != 0)
708 {
709 SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE);
710 return TRUE;
711 }
712 break;
713
714 case PSN_WIZNEXT:
715 /* Handle a Next button click, if necessary */
716 break;
717
718 default:
719 break;
720 }
721 break;
722 }
723
724 default:
725 break;
726 }
727
728 return FALSE;
729 }
730
731 static INT_PTR CALLBACK
732 InstallDrvDlgProc(
733 IN HWND hwndDlg,
734 IN UINT uMsg,
735 IN WPARAM wParam,
736 IN LPARAM lParam)
737 {
738 PDEVINSTDATA DevInstData;
739 DWORD dwThreadId;
740
741 /* Retrieve pointer to the global setup data */
742 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
743
744 switch (uMsg)
745 {
746 case WM_INITDIALOG:
747 {
748 HWND hwndControl;
749 DWORD dwStyle;
750
751 /* Get pointer to the global setup data */
752 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
753 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
754
755 DevInstData->hDialog = hwndDlg;
756 hwndControl = GetParent(hwndDlg);
757
758 /* Center the wizard window */
759 CenterWindow(hwndControl);
760
761 SendDlgItemMessage(
762 hwndDlg,
763 IDC_DEVICE,
764 WM_SETTEXT,
765 0,
766 (LPARAM)DevInstData->drvInfoData.Description);
767
768 /* Hide the system menu */
769 dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
770 SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
771 break;
772 }
773
774 case WM_INSTALL_FINISHED:
775 {
776 CloseHandle(hThread);
777 hThread = 0;
778 if (wParam == 0)
779 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_FINISHPAGE);
780 else
781 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_INSTALLFAILED);
782 break;
783 }
784
785 case WM_NOTIFY:
786 {
787 LPNMHDR lpnm = (LPNMHDR)lParam;
788
789 switch (lpnm->code)
790 {
791 case PSN_SETACTIVE:
792 PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK);
793 hThread = CreateThread(NULL, 0, InstallDriverProc, DevInstData, 0, &dwThreadId);
794 break;
795
796 case PSN_KILLACTIVE:
797 if (hThread != 0)
798 {
799 SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE);
800 return TRUE;
801 }
802 break;
803
804 case PSN_WIZNEXT:
805 /* Handle a Next button click, if necessary */
806 break;
807
808 default:
809 break;
810 }
811 break;
812 }
813
814 default:
815 break;
816 }
817
818 return FALSE;
819 }
820
821 static INT_PTR CALLBACK
822 NoDriverDlgProc(
823 IN HWND hwndDlg,
824 IN UINT uMsg,
825 IN WPARAM wParam,
826 IN LPARAM lParam)
827 {
828 PDEVINSTDATA DevInstData;
829
830 /* Get pointer to the global setup data */
831 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
832
833 switch (uMsg)
834 {
835 case WM_INITDIALOG:
836 {
837 HWND hwndControl;
838 BOOL DisableableDevice = FALSE;
839
840 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
841 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
842
843 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
844 ShowWindow(hwndControl, SW_HIDE);
845 EnableWindow(hwndControl, FALSE);
846
847 /* Set title font */
848 SendDlgItemMessage(
849 hwndDlg,
850 IDC_FINISHTITLE,
851 WM_SETFONT,
852 (WPARAM)DevInstData->hTitleFont,
853 (LPARAM)TRUE);
854
855 /* disable the "do not show this dialog anymore" checkbox
856 if the device cannot be disabled */
857 CanDisableDevice(
858 DevInstData->devInfoData.DevInst,
859 NULL,
860 &DisableableDevice);
861 EnableWindow(
862 GetDlgItem(hwndDlg, IDC_DONOTSHOWDLG),
863 DisableableDevice);
864 break;
865 }
866
867 case WM_NOTIFY:
868 {
869 LPNMHDR lpnm = (LPNMHDR)lParam;
870
871 switch (lpnm->code)
872 {
873 case PSN_SETACTIVE:
874 /* Enable the correct buttons on for the active page */
875 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
876 break;
877
878 case PSN_WIZBACK:
879 /* Handle a Back button click, if necessary */
880 PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_CHSOURCE);
881 return TRUE;
882
883 case PSN_WIZFINISH:
884 {
885 BOOL DisableableDevice = FALSE;
886 BOOL IsStarted = FALSE;
887
888 if (CanDisableDevice(DevInstData->devInfoData.DevInst,
889 NULL,
890 &DisableableDevice) &&
891 DisableableDevice &&
892 IsDeviceStarted(
893 DevInstData->devInfoData.DevInst,
894 NULL,
895 &IsStarted) &&
896 !IsStarted &&
897 SendDlgItemMessage(
898 hwndDlg,
899 IDC_DONOTSHOWDLG,
900 BM_GETCHECK,
901 (WPARAM)0, (LPARAM)0) == BST_CHECKED)
902 {
903 /* disable the device */
904 StartDevice(
905 DevInstData->hDevInfo,
906 &DevInstData->devInfoData,
907 FALSE,
908 0,
909 NULL);
910 }
911 break;
912 }
913
914 default:
915 break;
916 }
917 break;
918 }
919
920 default:
921 break;
922 }
923
924 return FALSE;
925 }
926
927 static INT_PTR CALLBACK
928 InstallFailedDlgProc(
929 IN HWND hwndDlg,
930 IN UINT uMsg,
931 IN WPARAM wParam,
932 IN LPARAM lParam)
933 {
934 PDEVINSTDATA DevInstData;
935
936 /* Retrieve pointer to the global setup data */
937 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
938
939 switch (uMsg)
940 {
941 case WM_INITDIALOG:
942 {
943 HWND hwndControl;
944
945 /* Get pointer to the global setup data */
946 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
947 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
948
949 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
950 ShowWindow(hwndControl, SW_HIDE);
951 EnableWindow(hwndControl, FALSE);
952
953 SendDlgItemMessage(
954 hwndDlg,
955 IDC_DEVICE,
956 WM_SETTEXT,
957 0,
958 (LPARAM)DevInstData->drvInfoData.Description);
959
960 /* Set title font */
961 SendDlgItemMessage(
962 hwndDlg,
963 IDC_FINISHTITLE,
964 WM_SETFONT,
965 (WPARAM)DevInstData->hTitleFont,
966 (LPARAM)TRUE);
967 break;
968 }
969
970 case WM_NOTIFY:
971 {
972 LPNMHDR lpnm = (LPNMHDR)lParam;
973
974 switch (lpnm->code)
975 {
976 case PSN_SETACTIVE:
977 /* Enable the correct buttons on for the active page */
978 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
979 break;
980
981 case PSN_WIZBACK:
982 /* Handle a Back button click, if necessary */
983 break;
984
985 case PSN_WIZFINISH:
986 /* Handle a Finish button click, if necessary */
987 break;
988
989 default:
990 break;
991 }
992 break;
993 }
994
995 default:
996 break;
997 }
998
999 return FALSE;
1000 }
1001
1002 static INT_PTR CALLBACK
1003 FinishDlgProc(
1004 IN HWND hwndDlg,
1005 IN UINT uMsg,
1006 IN WPARAM wParam,
1007 IN LPARAM lParam)
1008 {
1009 PDEVINSTDATA DevInstData;
1010
1011 /* Retrieve pointer to the global setup data */
1012 DevInstData = (PDEVINSTDATA)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
1013
1014 switch (uMsg)
1015 {
1016 case WM_INITDIALOG:
1017 {
1018 HWND hwndControl;
1019
1020 /* Get pointer to the global setup data */
1021 DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1022 SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
1023
1024 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
1025 ShowWindow(hwndControl, SW_HIDE);
1026 EnableWindow(hwndControl, FALSE);
1027
1028 SendDlgItemMessage(
1029 hwndDlg,
1030 IDC_DEVICE,
1031 WM_SETTEXT,
1032 0,
1033 (LPARAM)DevInstData->drvInfoData.Description);
1034
1035 /* Set title font */
1036 SendDlgItemMessage(
1037 hwndDlg,
1038 IDC_FINISHTITLE,
1039 WM_SETFONT,
1040 (WPARAM)DevInstData->hTitleFont,
1041 (LPARAM)TRUE);
1042 break;
1043 }
1044
1045 case WM_NOTIFY:
1046 {
1047 LPNMHDR lpnm = (LPNMHDR)lParam;
1048
1049 switch (lpnm->code)
1050 {
1051 case PSN_SETACTIVE:
1052 /* Enable the correct buttons on for the active page */
1053 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
1054 break;
1055
1056 case PSN_WIZBACK:
1057 /* Handle a Back button click, if necessary */
1058 break;
1059
1060 case PSN_WIZFINISH:
1061 /* Handle a Finish button click, if necessary */
1062 break;
1063
1064 default:
1065 break;
1066 }
1067 break;
1068 }
1069
1070 default:
1071 break;
1072 }
1073
1074 return FALSE;
1075 }
1076
1077 static HFONT
1078 CreateTitleFont(VOID)
1079 {
1080 NONCLIENTMETRICS ncm;
1081 LOGFONT LogFont;
1082 HDC hdc;
1083 INT FontSize;
1084 HFONT hFont;
1085
1086 ncm.cbSize = sizeof(NONCLIENTMETRICS);
1087 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
1088
1089 LogFont = ncm.lfMessageFont;
1090 LogFont.lfWeight = FW_BOLD;
1091 _tcscpy(LogFont.lfFaceName, _T("MS Shell Dlg"));
1092
1093 hdc = GetDC(NULL);
1094 FontSize = 12;
1095 LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72;
1096 hFont = CreateFontIndirect(&LogFont);
1097 ReleaseDC(NULL, hdc);
1098
1099 return hFont;
1100 }
1101
1102 BOOL
1103 DisplayWizard(
1104 IN PDEVINSTDATA DevInstData,
1105 IN HWND hwndParent,
1106 IN UINT startPage)
1107 {
1108 PROPSHEETHEADER psh;
1109 HPROPSHEETPAGE ahpsp[IDD_FINISHPAGE + 1];
1110 PROPSHEETPAGE psp;
1111
1112 /* Create the Welcome page */
1113 ZeroMemory(&psp, sizeof(PROPSHEETPAGE));
1114 psp.dwSize = sizeof(PROPSHEETPAGE);
1115 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
1116 psp.hInstance = hDllInstance;
1117 psp.lParam = (LPARAM)DevInstData;
1118 psp.pfnDlgProc = WelcomeDlgProc;
1119 psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE);
1120 ahpsp[IDD_WELCOMEPAGE] = CreatePropertySheetPage(&psp);
1121
1122 /* Create the Select Source page */
1123 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1124 psp.pfnDlgProc = CHSourceDlgProc;
1125 psp.pszTemplate = MAKEINTRESOURCE(IDD_CHSOURCE);
1126 ahpsp[IDD_CHSOURCE] = CreatePropertySheetPage(&psp);
1127
1128 /* Create the Search driver page */
1129 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1130 psp.pfnDlgProc = SearchDrvDlgProc;
1131 psp.pszTemplate = MAKEINTRESOURCE(IDD_SEARCHDRV);
1132 ahpsp[IDD_SEARCHDRV] = CreatePropertySheetPage(&psp);
1133
1134 /* Create the Install driver page */
1135 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1136 psp.pfnDlgProc = InstallDrvDlgProc;
1137 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLDRV);
1138 ahpsp[IDD_INSTALLDRV] = CreatePropertySheetPage(&psp);
1139
1140 /* Create the No driver page */
1141 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
1142 psp.pfnDlgProc = NoDriverDlgProc;
1143 psp.pszTemplate = MAKEINTRESOURCE(IDD_NODRIVER);
1144 ahpsp[IDD_NODRIVER] = CreatePropertySheetPage(&psp);
1145
1146 /* Create the Install failed page */
1147 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
1148 psp.pfnDlgProc = InstallFailedDlgProc;
1149 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFAILED);
1150 ahpsp[IDD_INSTALLFAILED] = CreatePropertySheetPage(&psp);
1151
1152 /* Create the Finish page */
1153 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
1154 psp.pfnDlgProc = FinishDlgProc;
1155 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE);
1156 ahpsp[IDD_FINISHPAGE] = CreatePropertySheetPage(&psp);
1157
1158 /* Create the property sheet */
1159 psh.dwSize = sizeof(PROPSHEETHEADER);
1160 psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
1161 psh.hInstance = hDllInstance;
1162 psh.hwndParent = hwndParent;
1163 psh.nPages = IDD_FINISHPAGE + 1;
1164 psh.nStartPage = startPage;
1165 psh.phpage = ahpsp;
1166 psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
1167 psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
1168
1169 /* Create title font */
1170 DevInstData->hTitleFont = CreateTitleFont();
1171
1172 /* Display the wizard */
1173 PropertySheet(&psh);
1174
1175 DeleteObject(DevInstData->hTitleFont);
1176
1177 return TRUE;
1178 }