display the "Reinstall Driver" button in case the installation previously failed
[reactos.git] / reactos / lib / devmgr / advprop.c
1 /*
2 * ReactOS Device Manager Applet
3 * Copyright (C) 2004 - 2005 ReactOS Team
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 /* $Id: hwpage.c 19599 2005-11-26 02:12:58Z weiden $
20 *
21 * PROJECT: ReactOS devmgr.dll
22 * FILE: lib/devmgr/advprop.c
23 * PURPOSE: ReactOS Device Manager
24 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
25 * UPDATE HISTORY:
26 * 04-04-2004 Created
27 */
28 #include <precomp.h>
29
30 #define NDEBUG
31 #include <debug.h>
32
33 typedef INT_PTR (WINAPI *PPROPERTYSHEETW)(LPCPROPSHEETHEADERW);
34 typedef HPROPSHEETPAGE (WINAPI *PCREATEPROPERTYSHEETPAGEW)(LPCPROPSHEETPAGEW);
35 typedef BOOL (WINAPI *PDESTROYPROPERTYSHEETPAGE)(HPROPSHEETPAGE);
36
37 typedef enum
38 {
39 DEA_DISABLE = 0,
40 DEA_ENABLE,
41 DEA_UNKNOWN
42 } DEVENABLEACTION;
43
44 typedef struct _DEVADVPROP_INFO
45 {
46 HWND hWndGeneralPage;
47 HWND hWndParent;
48 WNDPROC ParentOldWndProc;
49
50 HDEVINFO DeviceInfoSet;
51 SP_DEVINFO_DATA DeviceInfoData;
52 HDEVINFO CurrentDeviceInfoSet;
53 SP_DEVINFO_DATA CurrentDeviceInfoData;
54 DEVINST ParentDevInst;
55 HMACHINE hMachine;
56 LPCWSTR lpMachineName;
57
58 HINSTANCE hComCtl32;
59
60 DWORD PropertySheetType;
61 DWORD nDevPropSheets;
62 HPROPSHEETPAGE *DevPropSheets;
63
64 BOOL FreeDevPropSheets : 1;
65 BOOL CanDisable : 1;
66 BOOL DeviceEnabled : 1;
67 BOOL DeviceUsageChanged : 1;
68 BOOL CloseDevInst : 1;
69 BOOL IsAdmin : 1;
70 BOOL DoDefaultDevAction : 1;
71
72 WCHAR szDevName[255];
73 WCHAR szTemp[255];
74 WCHAR szDeviceID[1];
75 /* struct may be dynamically expanded here! */
76 } DEVADVPROP_INFO, *PDEVADVPROP_INFO;
77
78
79 static VOID
80 InitDevUsageActions(IN HWND hwndDlg,
81 IN HWND hComboBox,
82 IN PDEVADVPROP_INFO dap)
83 {
84 INT Index;
85 UINT i;
86 struct
87 {
88 UINT szId;
89 DEVENABLEACTION Action;
90 } Actions[] =
91 {
92 {IDS_ENABLEDEVICE, DEA_ENABLE},
93 {IDS_DISABLEDEVICE, DEA_DISABLE},
94 };
95
96 for (i = 0;
97 i != sizeof(Actions) / sizeof(Actions[0]);
98 i++)
99 {
100 /* fill in the device usage combo box */
101 if (LoadString(hDllInstance,
102 Actions[i].szId,
103 dap->szTemp,
104 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
105 {
106 Index = (INT)SendMessage(hComboBox,
107 CB_ADDSTRING,
108 0,
109 (LPARAM)dap->szTemp);
110 if (Index != CB_ERR)
111 {
112 SendMessage(hComboBox,
113 CB_SETITEMDATA,
114 (WPARAM)Index,
115 (LPARAM)Actions[i].Action);
116
117 switch (Actions[i].Action)
118 {
119 case DEA_ENABLE:
120 if (dap->DeviceEnabled)
121 {
122 SendMessage(hComboBox,
123 CB_SETCURSEL,
124 (WPARAM)Index,
125 0);
126 }
127 break;
128
129 case DEA_DISABLE:
130 if (!dap->DeviceEnabled)
131 {
132 SendMessage(hComboBox,
133 CB_SETCURSEL,
134 (WPARAM)Index,
135 0);
136 }
137 break;
138
139 default:
140 break;
141 }
142 }
143 }
144 }
145 }
146
147
148 static DEVENABLEACTION
149 GetSelectedUsageAction(IN HWND hComboBox)
150 {
151 INT Index;
152 DEVENABLEACTION Ret = DEA_UNKNOWN;
153
154 Index = (INT)SendMessage(hComboBox,
155 CB_GETCURSEL,
156 0,
157 0);
158 if (Index != CB_ERR)
159 {
160 INT iRet = SendMessage(hComboBox,
161 CB_GETITEMDATA,
162 (WPARAM)Index,
163 0);
164 if (iRet != CB_ERR && iRet < (INT)DEA_UNKNOWN)
165 {
166 Ret = (DEVENABLEACTION)iRet;
167 }
168 }
169
170 return Ret;
171 }
172
173
174 static VOID
175 ApplyGeneralSettings(IN HWND hwndDlg,
176 IN PDEVADVPROP_INFO dap)
177 {
178 if (dap->DeviceUsageChanged && dap->IsAdmin)
179 {
180 DEVENABLEACTION SelectedUsageAction;
181
182 SelectedUsageAction = GetSelectedUsageAction(GetDlgItem(hwndDlg,
183 IDC_DEVUSAGE));
184 if (SelectedUsageAction != DEA_UNKNOWN)
185 {
186 switch (SelectedUsageAction)
187 {
188 case DEA_ENABLE:
189 if (!dap->DeviceEnabled)
190 {
191 /* FIXME - enable device */
192 }
193 break;
194
195 case DEA_DISABLE:
196 if (dap->DeviceEnabled)
197 {
198 /* FIXME - disable device */
199 }
200 break;
201
202 default:
203 break;
204 }
205 }
206 }
207
208 /* disable the apply button */
209 PropSheet_UnChanged(GetParent(hwndDlg),
210 hwndDlg);
211 dap->DeviceUsageChanged = FALSE;
212 }
213
214
215 static VOID
216 UpdateDevInfo(IN HWND hwndDlg,
217 IN PDEVADVPROP_INFO dap,
218 IN BOOL ReOpen)
219 {
220 HICON hIcon;
221 HWND hDevUsage, hPropSheetDlg, hDevProbBtn;
222 CONFIGRET cr;
223 ULONG Status, ProblemNumber;
224 UINT TroubleShootStrId = IDS_TROUBLESHOOTDEV;
225 BOOL bFlag;
226 DWORD i;
227 HDEVINFO DeviceInfoSet = NULL;
228 PSP_DEVINFO_DATA DeviceInfoData = NULL;
229
230 hPropSheetDlg = GetParent(hwndDlg);
231
232 if (ReOpen)
233 {
234 PROPSHEETHEADER psh;
235
236 /* switch to the General page */
237 PropSheet_SetCurSelByID(hPropSheetDlg,
238 IDD_DEVICEGENERAL);
239
240 /* remove and destroy the existing device property sheet pages */
241 for (i = 0;
242 i != dap->nDevPropSheets;
243 i++)
244 {
245 PropSheet_RemovePage(hPropSheetDlg,
246 -1,
247 dap->DevPropSheets[i]);
248 }
249
250 if (dap->FreeDevPropSheets)
251 {
252 /* don't free the array if it's the one allocated in
253 DisplayDeviceAdvancedProperties */
254 HeapFree(GetProcessHeap(),
255 0,
256 dap->DevPropSheets);
257
258 dap->FreeDevPropSheets = FALSE;
259 }
260
261 dap->DevPropSheets = NULL;
262 dap->nDevPropSheets = 0;
263
264 /* create a new device info set and re-open the device */
265 if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
266 {
267 SetupDiDestroyDeviceInfoList(dap->CurrentDeviceInfoSet);
268 }
269
270 dap->ParentDevInst = 0;
271 dap->CurrentDeviceInfoSet = SetupDiCreateDeviceInfoListEx(NULL,
272 hwndDlg,
273 dap->lpMachineName,
274 NULL);
275 if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
276 {
277 if (SetupDiOpenDeviceInfo(dap->CurrentDeviceInfoSet,
278 dap->szDeviceID,
279 hwndDlg,
280 0,
281 &dap->CurrentDeviceInfoData))
282 {
283 if (dap->CloseDevInst)
284 {
285 SetupDiDestroyDeviceInfoList(dap->DeviceInfoSet);
286 }
287
288 dap->CloseDevInst = TRUE;
289 dap->DeviceInfoSet = dap->CurrentDeviceInfoSet;
290 dap->DeviceInfoData = dap->CurrentDeviceInfoData;
291 dap->CurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
292 }
293 else
294 goto GetParentNode;
295 }
296 else
297 {
298 GetParentNode:
299 /* get the parent node from the initial devinst */
300 CM_Get_Parent_Ex(&dap->ParentDevInst,
301 dap->DeviceInfoData.DevInst,
302 0,
303 dap->hMachine);
304 }
305
306 if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
307 {
308 DeviceInfoSet = dap->CurrentDeviceInfoSet;
309 DeviceInfoData = &dap->CurrentDeviceInfoData;
310 }
311 else
312 {
313 DeviceInfoSet = dap->DeviceInfoSet;
314 DeviceInfoData = &dap->DeviceInfoData;
315 }
316
317 /* find out how many new device property sheets to add.
318 fake a PROPSHEETHEADER structure, we don't plan to
319 call PropertySheet again!*/
320 psh.dwSize = sizeof(PROPSHEETHEADER);
321 psh.dwFlags = 0;
322 psh.nPages = 0;
323
324 if (!SetupDiGetClassDevPropertySheets(DeviceInfoSet,
325 DeviceInfoData,
326 &psh,
327 0,
328 &dap->nDevPropSheets,
329 dap->PropertySheetType) &&
330 dap->nDevPropSheets != 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
331 {
332 dap->DevPropSheets = HeapAlloc(GetProcessHeap(),
333 HEAP_ZERO_MEMORY,
334 dap->nDevPropSheets * sizeof(HPROPSHEETPAGE));
335 if (dap->DevPropSheets != NULL)
336 {
337 psh.phpage = dap->DevPropSheets;
338
339 /* query the new property sheet pages to add */
340 if (SetupDiGetClassDevPropertySheets(DeviceInfoSet,
341 DeviceInfoData,
342 &psh,
343 dap->nDevPropSheets,
344 NULL,
345 dap->PropertySheetType))
346 {
347 /* add the property sheets */
348
349 for (i = 0;
350 i != dap->nDevPropSheets;
351 i++)
352 {
353 PropSheet_AddPage(hPropSheetDlg,
354 dap->DevPropSheets[i]);
355 }
356
357 dap->FreeDevPropSheets = TRUE;
358 }
359 else
360 {
361 /* cleanup, we were unable to get the device property sheets */
362 HeapFree(GetProcessHeap(),
363 0,
364 dap->DevPropSheets);
365
366 dap->nDevPropSheets = 0;
367 dap->DevPropSheets = NULL;
368 }
369 }
370 else
371 dap->nDevPropSheets = 0;
372 }
373 }
374 else
375 {
376 if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
377 {
378 DeviceInfoSet = dap->CurrentDeviceInfoSet;
379 DeviceInfoData = &dap->CurrentDeviceInfoData;
380 }
381 else
382 {
383 DeviceInfoSet = dap->DeviceInfoSet;
384 DeviceInfoData = &dap->DeviceInfoData;
385 }
386 }
387
388 /* get the device name */
389 if (GetDeviceDescriptionString(DeviceInfoSet,
390 DeviceInfoData,
391 dap->szDevName,
392 sizeof(dap->szDevName) / sizeof(dap->szDevName[0])))
393 {
394 PropSheet_SetTitle(GetParent(hwndDlg),
395 PSH_PROPTITLE,
396 dap->szDevName);
397 }
398
399 /* set the device image */
400 if (SetupDiLoadClassIcon(&DeviceInfoData->ClassGuid,
401 &hIcon,
402 NULL))
403 {
404 HICON hOldIcon = (HICON)SendDlgItemMessage(hwndDlg,
405 IDC_DEVICON,
406 STM_SETICON,
407 (WPARAM)hIcon,
408 0);
409 if (hOldIcon != NULL)
410 {
411 DestroyIcon(hOldIcon);
412 }
413 }
414
415 /* set the device name edit control text */
416 SetDlgItemText(hwndDlg,
417 IDC_DEVNAME,
418 dap->szDevName);
419
420 /* set the device type edit control text */
421 if (GetDeviceTypeString(DeviceInfoData,
422 dap->szTemp,
423 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
424 {
425 SetDlgItemText(hwndDlg,
426 IDC_DEVTYPE,
427 dap->szTemp);
428 }
429
430 /* set the device manufacturer edit control text */
431 if (GetDeviceManufacturerString(DeviceInfoSet,
432 DeviceInfoData,
433 dap->szTemp,
434 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
435 {
436 SetDlgItemText(hwndDlg,
437 IDC_DEVMANUFACTURER,
438 dap->szTemp);
439 }
440
441 /* set the device location edit control text */
442 if (GetDeviceLocationString(DeviceInfoData->DevInst,
443 dap->ParentDevInst,
444 dap->szTemp,
445 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
446 {
447 SetDlgItemText(hwndDlg,
448 IDC_DEVLOCATION,
449 dap->szTemp);
450 }
451
452 /* set the device status edit control text */
453 if (GetDeviceStatusString(DeviceInfoData->DevInst,
454 dap->hMachine,
455 dap->szTemp,
456 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
457 {
458 SetDlgItemText(hwndDlg,
459 IDC_DEVSTATUS,
460 dap->szTemp);
461 }
462
463 /* set the device troubleshoot button text and disable it if necessary */
464 hDevProbBtn = GetDlgItem(hwndDlg,
465 IDC_DEVPROBLEM);
466 EnableWindow(hDevProbBtn,
467 dap->IsAdmin);
468 cr = CM_Get_DevNode_Status_Ex(&Status,
469 &ProblemNumber,
470 DeviceInfoData->DevInst,
471 0,
472 dap->hMachine);
473 if (cr == CR_SUCCESS && (Status & DN_HAS_PROBLEM))
474 {
475 switch (ProblemNumber)
476 {
477 case CM_PROB_DISABLED:
478 TroubleShootStrId = IDS_ENABLEDEV;
479 break;
480
481 case CM_PROB_FAILED_INSTALL:
482 TroubleShootStrId = IDS_REINSTALLDRV;
483 break;
484 }
485 }
486
487 if (LoadString(hDllInstance,
488 TroubleShootStrId,
489 dap->szTemp,
490 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])) != 0)
491 {
492 SetWindowText(hDevProbBtn,
493 dap->szTemp);
494 }
495
496 /* check if the device can be enabled/disabled */
497 hDevUsage = GetDlgItem(hwndDlg,
498 IDC_DEVUSAGE);
499
500 dap->CanDisable = FALSE;
501 dap->DeviceEnabled = FALSE;
502
503 if (CanDisableDevice(DeviceInfoData->DevInst,
504 dap->hMachine,
505 &bFlag))
506 {
507 dap->CanDisable = bFlag;
508 }
509
510 if (IsDeviceEnabled(DeviceInfoData->DevInst,
511 dap->hMachine,
512 &bFlag))
513 {
514 dap->DeviceEnabled = bFlag;
515 }
516
517 /* enable/disable the device usage controls */
518 EnableWindow(GetDlgItem(hwndDlg,
519 IDC_DEVUSAGELABEL),
520 dap->CanDisable && dap->IsAdmin);
521 EnableWindow(hDevUsage,
522 dap->CanDisable && dap->IsAdmin);
523
524 /* clear the combobox */
525 SendMessage(hDevUsage,
526 CB_RESETCONTENT,
527 0,
528 0);
529 if (dap->CanDisable)
530 {
531 InitDevUsageActions(hwndDlg,
532 hDevUsage,
533 dap);
534 }
535
536 /* finally, disable the apply button */
537 PropSheet_UnChanged(hPropSheetDlg,
538 hwndDlg);
539 dap->DeviceUsageChanged = FALSE;
540 }
541
542
543 static LRESULT
544 CALLBACK
545 DlgParentSubWndProc(IN HWND hwnd,
546 IN UINT uMsg,
547 IN WPARAM wParam,
548 IN LPARAM lParam)
549 {
550 PDEVADVPROP_INFO dap;
551
552 dap = (PDEVADVPROP_INFO)GetProp(hwnd,
553 L"DevMgrDevChangeSub");
554 if (dap != NULL)
555 {
556 if (uMsg == WM_DEVICECHANGE && !IsWindowVisible(dap->hWndGeneralPage))
557 {
558 SendMessage(dap->hWndGeneralPage,
559 WM_DEVICECHANGE,
560 wParam,
561 lParam);
562 }
563
564 /* pass the message the the old window proc */
565 return CallWindowProc(dap->ParentOldWndProc,
566 hwnd,
567 uMsg,
568 wParam,
569 lParam);
570 }
571 else
572 {
573 /* this is not a good idea if the subclassed window was an ansi
574 window, but we failed finding out the previous window proc
575 so we can't use CallWindowProc. This should rarely - if ever -
576 happen. */
577
578 return DefWindowProc(hwnd,
579 uMsg,
580 wParam,
581 lParam);
582 }
583 }
584
585
586 static INT_PTR
587 CALLBACK
588 AdvPropGeneralDlgProc(IN HWND hwndDlg,
589 IN UINT uMsg,
590 IN WPARAM wParam,
591 IN LPARAM lParam)
592 {
593 PDEVADVPROP_INFO dap;
594 INT_PTR Ret = FALSE;
595
596 dap = (PDEVADVPROP_INFO)GetWindowLongPtr(hwndDlg,
597 DWL_USER);
598
599 if (dap != NULL || uMsg == WM_INITDIALOG)
600 {
601 switch (uMsg)
602 {
603 case WM_COMMAND:
604 {
605 switch (LOWORD(wParam))
606 {
607 case IDC_DEVUSAGE:
608 {
609 if (HIWORD(wParam) == CBN_SELCHANGE)
610 {
611 PropSheet_Changed(GetParent(hwndDlg),
612 hwndDlg);
613 dap->DeviceUsageChanged = TRUE;
614 }
615 break;
616 }
617
618 case IDC_DEVPROBLEM:
619 {
620 if (dap->IsAdmin)
621 {
622 /* display the device problem wizard */
623 ShowDeviceProblemWizard(hwndDlg,
624 dap->DeviceInfoSet,
625 &dap->DeviceInfoData,
626 dap->hMachine);
627 }
628 break;
629 }
630 }
631 break;
632 }
633
634 case WM_NOTIFY:
635 {
636 NMHDR *hdr = (NMHDR*)lParam;
637 switch (hdr->code)
638 {
639 case PSN_APPLY:
640 ApplyGeneralSettings(hwndDlg,
641 dap);
642 break;
643 }
644 break;
645 }
646
647 case WM_INITDIALOG:
648 {
649 dap = (PDEVADVPROP_INFO)((LPPROPSHEETPAGE)lParam)->lParam;
650 if (dap != NULL)
651 {
652 HWND hWndParent;
653
654 dap->hWndGeneralPage = hwndDlg;
655
656 SetWindowLongPtr(hwndDlg,
657 DWL_USER,
658 (DWORD_PTR)dap);
659
660 /* subclass the parent window to always receive
661 WM_DEVICECHANGE messages */
662 hWndParent = GetParent(hwndDlg);
663 if (hWndParent != NULL)
664 {
665 /* subclass the parent window. This is not safe
666 if the parent window belongs to another thread! */
667 dap->ParentOldWndProc = (WNDPROC)SetWindowLongPtr(hWndParent,
668 GWLP_WNDPROC,
669 (LONG_PTR)DlgParentSubWndProc);
670
671 if (dap->ParentOldWndProc != NULL &&
672 SetProp(hWndParent,
673 L"DevMgrDevChangeSub",
674 (HANDLE)dap))
675 {
676 dap->hWndParent = hWndParent;
677 }
678 }
679
680 UpdateDevInfo(hwndDlg,
681 dap,
682 FALSE);
683 }
684 Ret = TRUE;
685 break;
686 }
687
688 case WM_DEVICECHANGE:
689 {
690 /* FIXME - don't call UpdateDevInfo for all events */
691 UpdateDevInfo(hwndDlg,
692 dap,
693 TRUE);
694 Ret = TRUE;
695 break;
696 }
697
698 case WM_DESTROY:
699 {
700 HICON hDevIcon;
701
702 /* restore the old window proc of the subclassed parent window */
703 if (dap->hWndParent != NULL && dap->ParentOldWndProc != NULL)
704 {
705 SetWindowLongPtr(dap->hWndParent,
706 GWLP_WNDPROC,
707 (LONG_PTR)dap->ParentOldWndProc);
708 }
709
710 /* destroy the device icon */
711 hDevIcon = (HICON)SendDlgItemMessage(hwndDlg,
712 IDC_DEVICON,
713 STM_GETICON,
714 0,
715 0);
716 if (hDevIcon != NULL)
717 {
718 DestroyIcon(hDevIcon);
719 }
720 break;
721 }
722 }
723 }
724
725 return Ret;
726 }
727
728
729 INT_PTR
730 DisplayDeviceAdvancedProperties(IN HWND hWndParent,
731 IN LPCWSTR lpDeviceID OPTIONAL,
732 IN HDEVINFO DeviceInfoSet,
733 IN PSP_DEVINFO_DATA DeviceInfoData,
734 IN HINSTANCE hComCtl32,
735 IN LPCWSTR lpMachineName,
736 IN DWORD dwFlags)
737 {
738 PROPSHEETHEADER psh = {0};
739 PROPSHEETPAGE pspGeneral = {0};
740 DWORD nPropSheets = 0;
741 PPROPERTYSHEETW pPropertySheetW;
742 PCREATEPROPERTYSHEETPAGEW pCreatePropertySheetPageW;
743 PDESTROYPROPERTYSHEETPAGE pDestroyPropertySheetPage;
744 PDEVADVPROP_INFO DevAdvPropInfo;
745 HMACHINE hMachine = NULL;
746 DWORD DevIdSize = 0;
747 INT_PTR Ret = -1;
748
749 /* we don't want to statically link against comctl32, so find the
750 functions we need dynamically */
751 pPropertySheetW =
752 (PPROPERTYSHEETW)GetProcAddress(hComCtl32,
753 "PropertySheetW");
754 pCreatePropertySheetPageW =
755 (PCREATEPROPERTYSHEETPAGEW)GetProcAddress(hComCtl32,
756 "CreatePropertySheetPageW");
757 pDestroyPropertySheetPage =
758 (PDESTROYPROPERTYSHEETPAGE)GetProcAddress(hComCtl32,
759 "DestroyPropertySheetPage");
760 if (pPropertySheetW == NULL ||
761 pCreatePropertySheetPageW == NULL ||
762 pDestroyPropertySheetPage == NULL)
763 {
764 return -1;
765 }
766
767 if (lpDeviceID == NULL)
768 {
769 /* find out how much size is needed for the device id */
770 if (SetupDiGetDeviceInstanceId(DeviceInfoSet,
771 DeviceInfoData,
772 NULL,
773 0,
774 &DevIdSize))
775 {
776 DPRINT1("SetupDiGetDeviceInstanceId unexpectedly returned TRUE!\n");
777 return -1;
778 }
779
780 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
781 {
782 return -1;
783 }
784 }
785 else
786 {
787 DevIdSize = (DWORD)wcslen(lpDeviceID) + 1;
788 }
789
790 if (lpMachineName != NULL)
791 {
792 CONFIGRET cr = CM_Connect_Machine(lpMachineName,
793 &hMachine);
794 if (cr != CR_SUCCESS)
795 {
796 return -1;
797 }
798 }
799
800 /* create the internal structure associated with the "General",
801 "Driver", ... pages */
802 DevAdvPropInfo = HeapAlloc(GetProcessHeap(),
803 HEAP_ZERO_MEMORY,
804 FIELD_OFFSET(DEVADVPROP_INFO,
805 szDeviceID) +
806 (DevIdSize * sizeof(WCHAR)));
807 if (DevAdvPropInfo == NULL)
808 {
809 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
810 goto Cleanup;
811 }
812
813 if (lpDeviceID == NULL)
814 {
815 /* read the device instance id */
816 if (!SetupDiGetDeviceInstanceId(DeviceInfoSet,
817 DeviceInfoData,
818 DevAdvPropInfo->szDeviceID,
819 DevIdSize,
820 NULL))
821 {
822 goto Cleanup;
823 }
824 }
825 else
826 {
827 /* copy the device instance id supplied by the caller */
828 wcscpy(DevAdvPropInfo->szDeviceID,
829 lpDeviceID);
830 }
831
832 DevAdvPropInfo->DeviceInfoSet = DeviceInfoSet;
833 DevAdvPropInfo->DeviceInfoData = *DeviceInfoData;
834 DevAdvPropInfo->CurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
835 DevAdvPropInfo->CurrentDeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
836
837 DevAdvPropInfo->hMachine = hMachine;
838 DevAdvPropInfo->lpMachineName = lpMachineName;
839 DevAdvPropInfo->szDevName[0] = L'\0';
840 DevAdvPropInfo->hComCtl32 = hComCtl32;
841
842 DevAdvPropInfo->IsAdmin = IsUserAdmin();
843 DevAdvPropInfo->DoDefaultDevAction = ((dwFlags & DPF_DEVICE_STATUS_ACTION) != 0);
844
845 psh.dwSize = sizeof(PROPSHEETHEADER);
846 psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW;
847 psh.hwndParent = hWndParent;
848 psh.pszCaption = DevAdvPropInfo->szDevName;
849
850 DevAdvPropInfo->PropertySheetType = lpMachineName != NULL ?
851 DIGCDP_FLAG_REMOTE_ADVANCED :
852 DIGCDP_FLAG_ADVANCED;
853
854 /* find out how many property sheets we need */
855 if (SetupDiGetClassDevPropertySheets(DeviceInfoSet,
856 &DevAdvPropInfo->DeviceInfoData,
857 &psh,
858 0,
859 &nPropSheets,
860 DevAdvPropInfo->PropertySheetType) &&
861 nPropSheets != 0)
862 {
863 DPRINT1("SetupDiGetClassDevPropertySheets unexpectedly returned TRUE!\n");
864 goto Cleanup;
865 }
866
867 if (nPropSheets != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
868 {
869 goto Cleanup;
870 }
871
872 psh.phpage = HeapAlloc(GetProcessHeap(),
873 HEAP_ZERO_MEMORY,
874 (nPropSheets + 1) * sizeof(HPROPSHEETPAGE));
875 if (psh.phpage == NULL)
876 {
877 goto Cleanup;
878 }
879
880 /* add the "General" property sheet */
881 pspGeneral.dwSize = sizeof(PROPSHEETPAGE);
882 pspGeneral.dwFlags = PSP_DEFAULT;
883 pspGeneral.hInstance = hDllInstance;
884 pspGeneral.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_DEVICEGENERAL);
885 pspGeneral.pfnDlgProc = AdvPropGeneralDlgProc;
886 pspGeneral.lParam = (LPARAM)DevAdvPropInfo;
887 psh.phpage[0] = pCreatePropertySheetPageW(&pspGeneral);
888 if (psh.phpage[0] != NULL)
889 {
890 psh.nPages++;
891 }
892
893 DevAdvPropInfo->nDevPropSheets = nPropSheets;
894
895 if (nPropSheets != 0)
896 {
897 DevAdvPropInfo->DevPropSheets = psh.phpage + psh.nPages;
898
899 /* create the device property sheets */
900 if (!SetupDiGetClassDevPropertySheets(DeviceInfoSet,
901 &DevAdvPropInfo->DeviceInfoData,
902 &psh,
903 nPropSheets + psh.nPages,
904 NULL,
905 DevAdvPropInfo->PropertySheetType))
906 {
907 goto Cleanup;
908 }
909 }
910
911 /* FIXME - add the "Driver" property sheet if necessary */
912
913 if (psh.nPages != 0)
914 {
915 Ret = pPropertySheetW(&psh);
916
917 /* NOTE: no need to destroy the property sheets anymore! */
918 }
919 else
920 {
921 UINT i;
922
923 Cleanup:
924 /* in case of failure the property sheets must be destroyed */
925 if (psh.phpage != NULL)
926 {
927 for (i = 0;
928 i < psh.nPages;
929 i++)
930 {
931 if (psh.phpage[i] != NULL)
932 {
933 pDestroyPropertySheetPage(psh.phpage[i]);
934 }
935 }
936 }
937 }
938
939 if (DevAdvPropInfo != NULL)
940 {
941 if (DevAdvPropInfo->FreeDevPropSheets)
942 {
943 /* don't free the array if it's the one allocated in
944 DisplayDeviceAdvancedProperties */
945 HeapFree(GetProcessHeap(),
946 0,
947 DevAdvPropInfo->DevPropSheets);
948 }
949
950 if (DevAdvPropInfo->CloseDevInst)
951 {
952 /* close the device info set in case a new one was created */
953 SetupDiDestroyDeviceInfoList(DevAdvPropInfo->DeviceInfoSet);
954 }
955
956 if (DevAdvPropInfo->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
957 {
958 SetupDiDestroyDeviceInfoList(DevAdvPropInfo->CurrentDeviceInfoSet);
959 }
960
961 HeapFree(GetProcessHeap(),
962 0,
963 DevAdvPropInfo);
964 }
965
966 if (psh.phpage != NULL)
967 {
968 HeapFree(GetProcessHeap(),
969 0,
970 psh.phpage);
971 }
972
973 if (hMachine != NULL)
974 {
975 CM_Disconnect_Machine(hMachine);
976 }
977
978 return Ret;
979 }
980
981
982 /***************************************************************************
983 * NAME EXPORTED
984 * DeviceAdvancedPropertiesW
985 *
986 * DESCRIPTION
987 * Invokes the device properties dialog, this version may add some property pages
988 * for some devices
989 *
990 * ARGUMENTS
991 * hWndParent: Handle to the parent window
992 * lpMachineName: Machine Name, NULL is the local machine
993 * lpDeviceID: Specifies the device whose properties are to be shown
994 *
995 * RETURN VALUE
996 * Always returns -1, a call to GetLastError returns 0 if successful
997 *
998 * @implemented
999 */
1000 INT_PTR
1001 WINAPI
1002 DeviceAdvancedPropertiesW(IN HWND hWndParent OPTIONAL,
1003 IN LPCWSTR lpMachineName OPTIONAL,
1004 IN LPCWSTR lpDeviceID)
1005 {
1006 HDEVINFO hDevInfo;
1007 SP_DEVINFO_DATA DevInfoData;
1008 HINSTANCE hComCtl32;
1009 INT_PTR Ret = -1;
1010
1011 if (lpDeviceID == NULL)
1012 {
1013 SetLastError(ERROR_INVALID_PARAMETER);
1014 return FALSE;
1015 }
1016
1017 /* dynamically load comctl32 */
1018 hComCtl32 = LoadAndInitComctl32();
1019 if (hComCtl32 != NULL)
1020 {
1021 hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
1022 hWndParent,
1023 lpMachineName,
1024 NULL);
1025 if (hDevInfo != INVALID_HANDLE_VALUE)
1026 {
1027 DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1028 if (SetupDiOpenDeviceInfo(hDevInfo,
1029 lpDeviceID,
1030 hWndParent,
1031 0,
1032 &DevInfoData))
1033 {
1034 Ret = DisplayDeviceAdvancedProperties(hWndParent,
1035 lpDeviceID,
1036 hDevInfo,
1037 &DevInfoData,
1038 hComCtl32,
1039 lpMachineName,
1040 0);
1041 }
1042
1043 SetupDiDestroyDeviceInfoList(hDevInfo);
1044 }
1045
1046 FreeLibrary(hComCtl32);
1047 }
1048
1049 return Ret;
1050 }
1051
1052
1053 /***************************************************************************
1054 * NAME EXPORTED
1055 * DeviceAdvancedPropertiesA
1056 *
1057 * DESCRIPTION
1058 * Invokes the device properties dialog, this version may add some property pages
1059 * for some devices
1060 *
1061 * ARGUMENTS
1062 * hWndParent: Handle to the parent window
1063 * lpMachineName: Machine Name, NULL is the local machine
1064 * lpDeviceID: Specifies the device whose properties are to be shown
1065 *
1066 * RETURN VALUE
1067 * Always returns -1, a call to GetLastError returns 0 if successful
1068 *
1069 * @implemented
1070 */
1071 INT_PTR
1072 WINAPI
1073 DeviceAdvancedPropertiesA(IN HWND hWndParent OPTIONAL,
1074 IN LPCSTR lpMachineName OPTIONAL,
1075 IN LPCSTR lpDeviceID)
1076 {
1077 LPWSTR lpMachineNameW = NULL;
1078 LPWSTR lpDeviceIDW = NULL;
1079 INT_PTR Ret = -1;
1080
1081 if (lpMachineName != NULL)
1082 {
1083 if (!(lpMachineNameW = ConvertMultiByteToUnicode(lpMachineName,
1084 CP_ACP)))
1085 {
1086 goto Cleanup;
1087 }
1088 }
1089 if (lpDeviceID != NULL)
1090 {
1091 if (!(lpDeviceIDW = ConvertMultiByteToUnicode(lpDeviceID,
1092 CP_ACP)))
1093 {
1094 goto Cleanup;
1095 }
1096 }
1097
1098 Ret = DeviceAdvancedPropertiesW(hWndParent,
1099 lpMachineNameW,
1100 lpDeviceIDW);
1101
1102 Cleanup:
1103 if (lpMachineNameW != NULL)
1104 {
1105 HeapFree(GetProcessHeap(),
1106 0,
1107 lpMachineNameW);
1108 }
1109 if (lpDeviceIDW != NULL)
1110 {
1111 HeapFree(GetProcessHeap(),
1112 0,
1113 lpDeviceIDW);
1114 }
1115
1116 return Ret;
1117 }
1118
1119
1120 /***************************************************************************
1121 * NAME EXPORTED
1122 * DevicePropertiesExA
1123 *
1124 * DESCRIPTION
1125 * Invokes the extended device properties dialog
1126 *
1127 * ARGUMENTS
1128 * hWndParent: Handle to the parent window
1129 * lpMachineName: Machine Name, NULL is the local machine
1130 * lpDeviceID: Specifies the device whose properties are to be shown, optional if
1131 * bShowDevMgr is nonzero
1132 * dwFlags: This parameter can be a combination of the following flags:
1133 * * DPF_DEVICE_STATUS_ACTION: Only valid if bShowDevMgr, causes
1134 * the default device status action button
1135 * to be clicked (Troubleshoot, Enable
1136 * Device, etc)
1137 * bShowDevMgr: If non-zero it displays the device manager instead of
1138 * the advanced device property dialog
1139 *
1140 * RETURN VALUE
1141 * 1: if bShowDevMgr is non-zero and no error occured
1142 * -1: a call to GetLastError returns 0 if successful
1143 *
1144 * @implemented
1145 */
1146 INT_PTR
1147 WINAPI
1148 DevicePropertiesExA(IN HWND hWndParent OPTIONAL,
1149 IN LPCSTR lpMachineName OPTIONAL,
1150 IN LPCSTR lpDeviceID OPTIONAL,
1151 IN DWORD dwFlags OPTIONAL,
1152 IN BOOL bShowDevMgr)
1153 {
1154 LPWSTR lpMachineNameW = NULL;
1155 LPWSTR lpDeviceIDW = NULL;
1156 INT_PTR Ret = -1;
1157
1158 if (lpMachineName != NULL)
1159 {
1160 if (!(lpMachineNameW = ConvertMultiByteToUnicode(lpMachineName,
1161 CP_ACP)))
1162 {
1163 goto Cleanup;
1164 }
1165 }
1166 if (lpDeviceID != NULL)
1167 {
1168 if (!(lpDeviceIDW = ConvertMultiByteToUnicode(lpDeviceID,
1169 CP_ACP)))
1170 {
1171 goto Cleanup;
1172 }
1173 }
1174
1175 Ret = DevicePropertiesExW(hWndParent,
1176 lpMachineNameW,
1177 lpDeviceIDW,
1178 dwFlags,
1179 bShowDevMgr);
1180
1181 Cleanup:
1182 if (lpMachineNameW != NULL)
1183 {
1184 HeapFree(GetProcessHeap(),
1185 0,
1186 lpMachineNameW);
1187 }
1188 if (lpDeviceIDW != NULL)
1189 {
1190 HeapFree(GetProcessHeap(),
1191 0,
1192 lpDeviceIDW);
1193 }
1194
1195 return Ret;
1196 }
1197
1198
1199 /***************************************************************************
1200 * NAME EXPORTED
1201 * DevicePropertiesExW
1202 *
1203 * DESCRIPTION
1204 * Invokes the extended device properties dialog
1205 *
1206 * ARGUMENTS
1207 * hWndParent: Handle to the parent window
1208 * lpMachineName: Machine Name, NULL is the local machine
1209 * lpDeviceID: Specifies the device whose properties are to be shown, optional if
1210 * bShowDevMgr is nonzero
1211 * dwFlags: This parameter can be a combination of the following flags:
1212 * * DPF_DEVICE_STATUS_ACTION: Only valid if bShowDevMgr, causes
1213 * the default device status action button
1214 * to be clicked (Troubleshoot, Enable
1215 * Device, etc)
1216 * bShowDevMgr: If non-zero it displays the device manager instead of
1217 * the advanced device property dialog
1218 *
1219 * RETURN VALUE
1220 * 1: if bShowDevMgr is non-zero and no error occured
1221 * -1: a call to GetLastError returns 0 if successful
1222 *
1223 * @unimplemented
1224 */
1225 INT_PTR
1226 WINAPI
1227 DevicePropertiesExW(IN HWND hWndParent OPTIONAL,
1228 IN LPCWSTR lpMachineName OPTIONAL,
1229 IN LPCWSTR lpDeviceID OPTIONAL,
1230 IN DWORD dwFlags OPTIONAL,
1231 IN BOOL bShowDevMgr)
1232 {
1233 INT_PTR Ret = -1;
1234
1235 if (dwFlags & ~(DPF_UNKNOWN | DPF_DEVICE_STATUS_ACTION))
1236 {
1237 DPRINT1("DevPropertiesExW: Invalid flags: 0x%x\n",
1238 dwFlags & ~(DPF_UNKNOWN | DPF_DEVICE_STATUS_ACTION));
1239 SetLastError(ERROR_INVALID_FLAGS);
1240 return -1;
1241 }
1242
1243 if (bShowDevMgr)
1244 {
1245 DPRINT("DevPropertiesExW doesn't support bShowDevMgr!\n");
1246 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1247 }
1248 else
1249 {
1250 HDEVINFO hDevInfo;
1251 SP_DEVINFO_DATA DevInfoData;
1252 HINSTANCE hComCtl32;
1253
1254 if (lpDeviceID == NULL)
1255 {
1256 SetLastError(ERROR_INVALID_PARAMETER);
1257 return -1;
1258 }
1259
1260 /* dynamically load comctl32 */
1261 hComCtl32 = LoadAndInitComctl32();
1262 if (hComCtl32 != NULL)
1263 {
1264 hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
1265 hWndParent,
1266 lpMachineName,
1267 NULL);
1268 if (hDevInfo != INVALID_HANDLE_VALUE)
1269 {
1270 DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1271 if (SetupDiOpenDeviceInfo(hDevInfo,
1272 lpDeviceID,
1273 hWndParent,
1274 0,
1275 &DevInfoData))
1276 {
1277 Ret = DisplayDeviceAdvancedProperties(hWndParent,
1278 lpDeviceID,
1279 hDevInfo,
1280 &DevInfoData,
1281 hComCtl32,
1282 lpMachineName,
1283 dwFlags);
1284 }
1285
1286 SetupDiDestroyDeviceInfoList(hDevInfo);
1287 }
1288
1289 FreeLibrary(hComCtl32);
1290 }
1291 }
1292
1293 return Ret;
1294 }