xref: /reactos/dll/win32/devmgr/properties/hwpage.cpp (revision 9393fc32)
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 /*
20  * PROJECT:         ReactOS devmgr.dll
21  * FILE:            lib/devmgr/hwpage.c
22  * PURPOSE:         ReactOS Device Manager
23  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
24  * UPDATE HISTORY:
25  *      04-04-2004  Created
26  */
27 
28 #include "precomp.h"
29 #include "properties.h"
30 #include "resource.h"
31 
32 
33 typedef struct _HWDEVINFO
34 {
35     struct _HWCLASSDEVINFO *ClassDevInfo;
36     SP_DEVINFO_DATA DevInfoData;
37     BOOL HideDevice;
38 } HWDEVINFO, *PHWDEVINFO;
39 
40 typedef struct _HWCLASSDEVINFO
41 {
42     GUID Guid;
43     HDEVINFO hDevInfo;
44     INT ImageIndex;
45     INT ItemCount;
46     PHWDEVINFO HwDevInfo;
47 } HWCLASSDEVINFO, *PHWCLASSDEVINFO;
48 
49 typedef struct _HARDWARE_PAGE_DATA
50 {
51     HWND hWnd;
52     HWND hWndDevList;
53     HINSTANCE hComCtl32; /* only save this to keep track of the references */
54     INT DevListViewHeight;
55     SP_CLASSIMAGELIST_DATA ClassImageListData;
56     HWPAGE_DISPLAYMODE DisplayMode;
57 
58     /* parent window subclass info */
59     WNDPROC ParentOldWndProc;
60     HWND hWndParent;
61 
62     UINT NumberOfGuids;
63     HWCLASSDEVINFO ClassDevInfo[1];
64     /* struct may be dynamically expanded here! */
65 } HARDWARE_PAGE_DATA, *PHARDWARE_PAGE_DATA;
66 
67 #define CX_TYPECOLUMN_WIDTH 80
68 
69 static VOID
70 InitializeDevicesList(IN PHARDWARE_PAGE_DATA hpd)
71 {
72     LVCOLUMN lvc;
73     RECT rcClient;
74     WCHAR szColName[255];
75     int iCol = 0;
76 
77     /* set the list view style */
78     (void)ListView_SetExtendedListViewStyle(hpd->hWndDevList,
79                                             LVS_EX_FULLROWSELECT);
80 
81     /* set the list view image list */
82     if (hpd->ClassImageListData.ImageList != NULL)
83     {
84         (void)ListView_SetImageList(hpd->hWndDevList,
85                                     hpd->ClassImageListData.ImageList,
86                                     LVSIL_SMALL);
87     }
88 
89     GetClientRect(hpd->hWndDevList,
90                   &rcClient);
91 
92     /* add the list view columns */
93     lvc.mask = LVCF_TEXT | LVCF_WIDTH;
94     lvc.fmt = LVCFMT_LEFT;
95     lvc.pszText = szColName;
96 
97     if (LoadString(hDllInstance,
98                    IDS_NAME,
99                    szColName,
100                    sizeof(szColName) / sizeof(szColName[0])))
101     {
102         lvc.cx = rcClient.right - CX_TYPECOLUMN_WIDTH -
103                  GetSystemMetrics(SM_CXVSCROLL);
104         (void)ListView_InsertColumn(hpd->hWndDevList,
105                                     iCol++,
106                                     &lvc);
107     }
108     if (LoadString(hDllInstance,
109                    IDS_TYPE,
110                    szColName,
111                    sizeof(szColName) / sizeof(szColName[0])))
112     {
113         lvc.cx = CX_TYPECOLUMN_WIDTH;
114         (void)ListView_InsertColumn(hpd->hWndDevList,
115                                     iCol++,
116                                     &lvc);
117     }
118 }
119 
120 
121 static BOOL
122 DisplaySelectedDeviceProperties(IN PHARDWARE_PAGE_DATA hpd)
123 {
124     PHWDEVINFO HwDevInfo;
125     SP_DEVINFO_DATA DevInfoData;
126     BOOL Ret = FALSE;
127 
128     HwDevInfo = (PHWDEVINFO)ListViewGetSelectedItemData(hpd->hWndDevList);
129     if (HwDevInfo != NULL)
130     {
131         /* make a copy of the SP_DEVINFO_DATA structure on the stack, it may
132            become invalid in case the devices are updated */
133         DevInfoData = HwDevInfo->DevInfoData;
134 
135         /* display the advanced properties */
136         Ret = DisplayDeviceAdvancedProperties(hpd->hWnd,
137                                               NULL,
138                                               HwDevInfo->ClassDevInfo->hDevInfo,
139                                               &DevInfoData,
140                                               hpd->hComCtl32,
141                                               NULL,
142                                               0) != -1;
143     }
144 
145     return Ret;
146 }
147 
148 
149 static VOID
150 UpdateControlStates(IN PHARDWARE_PAGE_DATA hpd)
151 {
152     PHWDEVINFO HwDevInfo;
153     HWND hBtnTroubleShoot, hBtnProperties;
154 
155     hBtnTroubleShoot = GetDlgItem(hpd->hWnd,
156                                   IDC_TROUBLESHOOT);
157     hBtnProperties = GetDlgItem(hpd->hWnd,
158                                 IDC_PROPERTIES);
159 
160     HwDevInfo = (PHWDEVINFO)ListViewGetSelectedItemData(hpd->hWndDevList);
161     if (HwDevInfo != NULL)
162     {
163         /* update static controls */
164         WCHAR szBuffer[256];
165         LPWSTR szFormatted = NULL;
166 
167         /* get the manufacturer string */
168         if (GetDeviceManufacturerString(HwDevInfo->ClassDevInfo->hDevInfo,
169                                         &HwDevInfo->DevInfoData,
170                                         szBuffer,
171                                         sizeof(szBuffer) / sizeof(szBuffer[0])) &&
172             LoadAndFormatString(hDllInstance,
173                                 IDS_MANUFACTURER,
174                                 &szFormatted,
175                                 szBuffer) != 0)
176         {
177             SetDlgItemText(hpd->hWnd,
178                            IDC_MANUFACTURER,
179                            szFormatted);
180             LocalFree((HLOCAL)szFormatted);
181         }
182 
183         /* get the location string */
184         if (GetDeviceLocationString(HwDevInfo->ClassDevInfo->hDevInfo,
185                                     &HwDevInfo->DevInfoData,
186                                     0,
187                                     szBuffer,
188                                     sizeof(szBuffer) / sizeof(szBuffer[0])) &&
189             LoadAndFormatString(hDllInstance,
190                                 IDS_LOCATION,
191                                 &szFormatted,
192                                 szBuffer) != 0)
193         {
194             SetDlgItemText(hpd->hWnd,
195                            IDC_LOCATION,
196                            szFormatted);
197             LocalFree((HLOCAL)szFormatted);
198         }
199 
200         if (GetDeviceStatusString(HwDevInfo->DevInfoData.DevInst,
201                                   NULL,
202                                   szBuffer,
203                                   sizeof(szBuffer) / sizeof(szBuffer[0])) &&
204             LoadAndFormatString(hDllInstance,
205                                 IDS_STATUS,
206                                 &szFormatted,
207                                 szBuffer) != 0)
208         {
209             SetDlgItemText(hpd->hWnd,
210                            IDC_STATUS,
211                            szFormatted);
212             LocalFree((HLOCAL)szFormatted);
213         }
214     }
215     else
216     {
217         /* clear static controls */
218         SetDlgItemText(hpd->hWnd,
219                        IDC_MANUFACTURER,
220                        NULL);
221         SetDlgItemText(hpd->hWnd,
222                        IDC_LOCATION,
223                        NULL);
224         SetDlgItemText(hpd->hWnd,
225                        IDC_STATUS,
226                        NULL);
227     }
228 
229     EnableWindow(hBtnTroubleShoot,
230                  HwDevInfo != NULL);
231     EnableWindow(hBtnProperties,
232                  HwDevInfo != NULL);
233 }
234 
235 
236 static VOID
237 FreeDevicesList(IN PHARDWARE_PAGE_DATA hpd)
238 {
239     PHWCLASSDEVINFO ClassDevInfo, LastClassDevInfo;
240 
241     ClassDevInfo = hpd->ClassDevInfo;
242     LastClassDevInfo = ClassDevInfo + hpd->NumberOfGuids;
243 
244     /* free the device info set handles and structures */
245     while (ClassDevInfo != LastClassDevInfo)
246     {
247         if (ClassDevInfo->hDevInfo != INVALID_HANDLE_VALUE)
248         {
249             SetupDiDestroyDeviceInfoList(ClassDevInfo->hDevInfo);
250             ClassDevInfo->hDevInfo = INVALID_HANDLE_VALUE;
251         }
252 
253         ClassDevInfo->ItemCount = 0;
254         ClassDevInfo->ImageIndex = 0;
255 
256         if (ClassDevInfo->HwDevInfo != NULL)
257         {
258             HeapFree(GetProcessHeap(),
259                      0,
260                      ClassDevInfo->HwDevInfo);
261             ClassDevInfo->HwDevInfo = NULL;
262         }
263 
264         ClassDevInfo++;
265     }
266 }
267 
268 
269 static VOID
270 BuildDevicesList(IN PHARDWARE_PAGE_DATA hpd)
271 {
272     PHWCLASSDEVINFO ClassDevInfo, LastClassDevInfo;
273     SP_DEVINFO_DATA DevInfoData;
274 
275     DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
276 
277     ClassDevInfo = hpd->ClassDevInfo;
278     LastClassDevInfo = ClassDevInfo + hpd->NumberOfGuids;
279 
280     while (ClassDevInfo != LastClassDevInfo)
281     {
282         ClassDevInfo->ImageIndex = -1;
283 
284         /* open a class device handle for the GUID we're processing */
285         ClassDevInfo->hDevInfo = SetupDiGetClassDevs(&ClassDevInfo->Guid,
286                                                      NULL,
287                                                      hpd->hWnd,
288                                                      DIGCF_PRESENT | DIGCF_PROFILE);
289         if (ClassDevInfo->hDevInfo != INVALID_HANDLE_VALUE)
290         {
291             DWORD MemberIndex = 0;
292 
293             SetupDiGetClassImageIndex(&hpd->ClassImageListData,
294                                       &ClassDevInfo->Guid,
295                                       &ClassDevInfo->ImageIndex);
296 
297             /* enumerate all devices in the class */
298             while (SetupDiEnumDeviceInfo(ClassDevInfo->hDevInfo,
299                                          MemberIndex++,
300                                          &DevInfoData))
301             {
302                 BOOL HideDevice = FALSE;
303 
304                 if (ClassDevInfo->HwDevInfo != NULL)
305                 {
306                     PHWDEVINFO HwNewDevInfo = (PHWDEVINFO)HeapReAlloc(GetProcessHeap(),
307                                                                       0,
308                                                                       ClassDevInfo->HwDevInfo,
309                                                                       (ClassDevInfo->ItemCount + 1) *
310                                                                           sizeof(HWDEVINFO));
311                     if (HwNewDevInfo != NULL)
312                     {
313                         ClassDevInfo->HwDevInfo = HwNewDevInfo;
314                     }
315                     else
316                     {
317                         ERR("Unable to allocate memory for %d SP_DEVINFO_DATA structures!\n",
318                             ClassDevInfo->ItemCount + 1);
319                         break;
320                     }
321                 }
322                 else
323                 {
324                     ClassDevInfo->HwDevInfo = (PHWDEVINFO)HeapAlloc(GetProcessHeap(),
325                                                                     0,
326                                                                     sizeof(HWDEVINFO));
327                     if (ClassDevInfo->HwDevInfo == NULL)
328                     {
329                         ERR("Unable to allocate memory for a SP_DEVINFO_DATA structures!\n");
330                         break;
331                     }
332                 }
333 
334                 /* Find out if the device should be hidden by default */
335                 IsDeviceHidden(DevInfoData.DevInst,
336                                NULL,
337                                &HideDevice);
338 
339                 /* save all information for the current device */
340                 ClassDevInfo->HwDevInfo[ClassDevInfo->ItemCount].ClassDevInfo = ClassDevInfo;
341                 ClassDevInfo->HwDevInfo[ClassDevInfo->ItemCount].DevInfoData = DevInfoData;
342                 ClassDevInfo->HwDevInfo[ClassDevInfo->ItemCount++].HideDevice = HideDevice;
343             }
344         }
345 
346         ClassDevInfo++;
347     }
348 }
349 
350 
351 static BOOL
352 DeviceIdMatch(IN HDEVINFO DeviceInfoSet,
353               IN PSP_DEVINFO_DATA DeviceInfoData,
354               IN LPCWSTR lpDeviceId)
355 {
356     DWORD DevIdLen;
357     LPWSTR lpQueriedDeviceId;
358     BOOL Ret = FALSE;
359 
360     if (!SetupDiGetDeviceInstanceId(DeviceInfoSet,
361                                     DeviceInfoData,
362                                     NULL,
363                                     0,
364                                     &DevIdLen) &&
365         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
366     {
367         if (DevIdLen == wcslen(lpDeviceId) + 1)
368         {
369             lpQueriedDeviceId = (LPWSTR)HeapAlloc(GetProcessHeap(),
370                                                   0,
371                                                   DevIdLen * sizeof(WCHAR));
372             if (lpQueriedDeviceId != NULL)
373             {
374                 if (SetupDiGetDeviceInstanceId(DeviceInfoSet,
375                                                DeviceInfoData,
376                                                lpQueriedDeviceId,
377                                                DevIdLen,
378                                                NULL))
379                 {
380                     Ret = (wcscmp(lpDeviceId,
381                                   lpQueriedDeviceId) == 0);
382                 }
383 
384                 HeapFree(GetProcessHeap(),
385                          0,
386                          lpQueriedDeviceId);
387             }
388         }
389     }
390 
391     return Ret;
392 }
393 
394 
395 static VOID
396 FillDevicesListViewControl(IN PHARDWARE_PAGE_DATA hpd,
397                            IN LPCWSTR lpSelectDeviceId  OPTIONAL,
398                            IN GUID *SelectedClassGuid  OPTIONAL)
399 {
400     PHWCLASSDEVINFO ClassDevInfo, LastClassDevInfo;
401     PHWDEVINFO HwDevInfo, LastHwDevInfo;
402     WCHAR szBuffer[255];
403     BOOL SelectedInClass;
404     INT ItemCount = 0;
405 
406     BuildDevicesList(hpd);
407 
408     ClassDevInfo = hpd->ClassDevInfo;
409     LastClassDevInfo = ClassDevInfo + hpd->NumberOfGuids;
410 
411     while (ClassDevInfo != LastClassDevInfo)
412     {
413         if (ClassDevInfo->HwDevInfo != NULL)
414         {
415             HwDevInfo = ClassDevInfo->HwDevInfo;
416             LastHwDevInfo = HwDevInfo + ClassDevInfo->ItemCount;
417 
418             SelectedInClass = (SelectedClassGuid != NULL &&
419                                IsEqualGUID(*SelectedClassGuid,
420                                            ClassDevInfo->Guid));
421             while (HwDevInfo != LastHwDevInfo)
422             {
423                 INT iItem;
424                 LVITEM li = {0};
425 
426                 /* get the device name */
427                 if (!HwDevInfo->HideDevice &&
428                     GetDeviceDescriptionString(ClassDevInfo->hDevInfo,
429                                                &HwDevInfo->DevInfoData,
430                                                szBuffer,
431                                                sizeof(szBuffer) / sizeof(szBuffer[0])))
432                 {
433                     li.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT | LVIF_IMAGE;
434                     li.iItem = ItemCount;
435                     if ((ItemCount == 0 && lpSelectDeviceId == NULL) ||
436                         (SelectedInClass &&
437                          DeviceIdMatch(ClassDevInfo->hDevInfo,
438                                        &HwDevInfo->DevInfoData,
439                                        lpSelectDeviceId)))
440                     {
441                         li.state = LVIS_SELECTED;
442                     }
443                     li.stateMask = LVIS_SELECTED;
444                     li.pszText = szBuffer;
445                     li.iImage = ClassDevInfo->ImageIndex;
446                     li.lParam = (LPARAM)HwDevInfo;
447 
448                     iItem = ListView_InsertItem(hpd->hWndDevList,
449                                                 &li);
450                     if (iItem != -1)
451                     {
452                         ItemCount++;
453 
454                         /* get the device type for the second column */
455                         if (GetDeviceTypeString(&HwDevInfo->DevInfoData,
456                                                 szBuffer,
457                                                 sizeof(szBuffer) / sizeof(szBuffer[0])))
458                         {
459                             li.mask = LVIF_TEXT;
460                             li.iItem = iItem;
461                             li.iSubItem = 1;
462 
463                             (void)ListView_SetItem(hpd->hWndDevList,
464                                                    &li);
465                         }
466                     }
467                 }
468 
469                 HwDevInfo++;
470             }
471         }
472 
473         ClassDevInfo++;
474     }
475 
476     /* update the controls */
477     UpdateControlStates(hpd);
478 }
479 
480 
481 static VOID
482 UpdateDevicesListViewControl(IN PHARDWARE_PAGE_DATA hpd)
483 {
484     PHWDEVINFO HwDevInfo;
485     GUID SelectedClassGuid = {0};
486     LPWSTR lpDeviceId = NULL;
487 
488     /* if a device currently is selected, remember the device id so we can
489        select the device after the update if still present */
490     HwDevInfo = (PHWDEVINFO)ListViewGetSelectedItemData(hpd->hWndDevList);
491     if (HwDevInfo != NULL)
492     {
493         DWORD DevIdLen;
494         if (!SetupDiGetDeviceInstanceId(HwDevInfo->ClassDevInfo->hDevInfo,
495                                         &HwDevInfo->DevInfoData,
496                                         NULL,
497                                         0,
498                                         &DevIdLen) &&
499             GetLastError() == ERROR_INSUFFICIENT_BUFFER)
500         {
501             SelectedClassGuid = HwDevInfo->DevInfoData.ClassGuid;
502             lpDeviceId = (LPWSTR)HeapAlloc(GetProcessHeap(),
503                                            0,
504                                            DevIdLen * sizeof(WCHAR));
505             if (lpDeviceId != NULL &&
506                 !SetupDiGetDeviceInstanceId(HwDevInfo->ClassDevInfo->hDevInfo,
507                                             &HwDevInfo->DevInfoData,
508                                             lpDeviceId,
509                                             DevIdLen,
510                                             NULL))
511             {
512                 HeapFree(GetProcessHeap(),
513                          0,
514                          lpDeviceId);
515                 lpDeviceId = NULL;
516             }
517         }
518     }
519 
520     /* clear the devices list view control */
521     (void)ListView_DeleteAllItems(hpd->hWndDevList);
522 
523     /* free the device list */
524     FreeDevicesList(hpd);
525 
526     /* build rebuild the device list and fill the list box again */
527     FillDevicesListViewControl(hpd,
528                                lpDeviceId,
529                                (lpDeviceId != NULL ?
530                                     &SelectedClassGuid :
531                                     NULL));
532 
533     if (lpDeviceId != NULL)
534     {
535         HeapFree(GetProcessHeap(),
536                  0,
537                  lpDeviceId);
538     }
539 }
540 
541 
542 static LRESULT
543 CALLBACK
544 ParentSubWndProc(IN HWND hwnd,
545                  IN UINT uMsg,
546                  IN WPARAM wParam,
547                  IN LPARAM lParam)
548 {
549     PHARDWARE_PAGE_DATA hpd;
550 
551     hpd = (PHARDWARE_PAGE_DATA)GetProp(hwnd,
552                                        L"DevMgrSubClassInfo");
553     if (hpd != NULL)
554     {
555         if (uMsg == WM_SIZE)
556         {
557             /* resize the hardware page */
558             SetWindowPos(hpd->hWnd,
559                          NULL,
560                          0,
561                          0,
562                          LOWORD(lParam),
563                          HIWORD(lParam),
564                          SWP_NOZORDER);
565         }
566         else if (uMsg == WM_DEVICECHANGE && IsWindowVisible(hpd->hWnd))
567         {
568             /* forward a WM_DEVICECHANGE message to the hardware
569                page which wouldn't get the message itself as it is
570                a child window */
571             SendMessage(hpd->hWnd,
572                         WM_DEVICECHANGE,
573                         wParam,
574                         lParam);
575         }
576 
577         /* pass the message the the old window proc */
578         return CallWindowProc(hpd->ParentOldWndProc,
579                               hwnd,
580                               uMsg,
581                               wParam,
582                               lParam);
583     }
584     else
585     {
586         /* this is not a good idea if the subclassed window was an ansi
587            window, but we failed finding out the previous window proc
588            so we can't use CallWindowProc. This should rarely - if ever -
589            happen. */
590 
591         return DefWindowProc(hwnd,
592                              uMsg,
593                              wParam,
594                              lParam);
595     }
596 }
597 
598 
599 static VOID
600 HardwareDlgResize(IN PHARDWARE_PAGE_DATA hpd,
601                   IN INT cx,
602                   IN INT cy)
603 {
604     HDWP dwp;
605     HWND hControl, hButton;
606     INT Width, x, y;
607     RECT rc, rcButton;
608     POINT pt = {0};
609     POINT ptMargin = {0};
610     POINT ptMarginGroup = {0};
611 
612     /* use left margin of the IDC_DEVICES label as the right
613        margin of all controls outside the group box */
614     hControl = GetDlgItem(hpd->hWnd,
615                           IDC_DEVICES);
616     GetWindowRect(hControl,
617                   &rc);
618     MapWindowPoints(hControl,
619                     hpd->hWnd,
620                     &ptMargin,
621                     1);
622 
623     Width = cx - (2 * ptMargin.x);
624 
625     if ((dwp = BeginDeferWindowPos(8)))
626     {
627         /* rc already has the window rect of IDC_DEVICES! */
628         if (!(dwp = DeferWindowPos(dwp,
629                                    hControl,
630                                    NULL,
631                                    0,
632                                    0,
633                                    Width,
634                                    rc.bottom - rc.top,
635                                    SWP_NOMOVE | SWP_NOZORDER)))
636         {
637             return;
638         }
639 
640         /* resize the devices list view control */
641         GetWindowRect(hpd->hWndDevList,
642                       &rc);
643         MapWindowPoints(hpd->hWndDevList,
644                         hpd->hWnd,
645                         &pt,
646                         1);
647         y = pt.y + hpd->DevListViewHeight + ptMargin.y;
648         if (!(dwp = DeferWindowPos(dwp,
649                                    hpd->hWndDevList,
650                                    NULL,
651                                    0,
652                                    0,
653                                    Width,
654                                    hpd->DevListViewHeight,
655                                    SWP_NOMOVE | SWP_NOZORDER)))
656         {
657             return;
658         }
659 
660         /* resize the group box control */
661         hControl = GetDlgItem(hpd->hWnd,
662                               IDC_PROPERTIESGROUP);
663         GetWindowRect(hControl,
664                       &rc);
665         if (!(dwp = DeferWindowPos(dwp,
666                                    hControl,
667                                    NULL,
668                                    ptMargin.x,
669                                    y,
670                                    Width,
671                                    cy - y - ptMargin.y,
672                                    SWP_NOZORDER)))
673         {
674             return;
675         }
676 
677         /* use left margin of the IDC_MANUFACTURER label as the right
678            margin of all controls inside the group box */
679         hControl = GetDlgItem(hpd->hWnd,
680                               IDC_MANUFACTURER);
681         GetWindowRect(hControl,
682                       &rc);
683         MapWindowPoints(hControl,
684                         hpd->hWnd,
685                         &ptMarginGroup,
686                         1);
687 
688         ptMarginGroup.y = ptMargin.y * 2;
689         Width = cx - (2 * ptMarginGroup.x);
690         y += ptMarginGroup.y;
691         if (!(dwp = DeferWindowPos(dwp,
692                                    hControl,
693                                    NULL,
694                                    ptMarginGroup.x,
695                                    y,
696                                    Width,
697                                    rc.bottom - rc.top,
698                                    SWP_NOZORDER)))
699         {
700             return;
701         }
702         y += rc.bottom - rc.top + (ptMargin.y / 2);
703 
704         /* resize the IDC_LOCATION label */
705         hControl = GetDlgItem(hpd->hWnd,
706                               IDC_LOCATION);
707         GetWindowRect(hControl,
708                       &rc);
709         if (!(dwp = DeferWindowPos(dwp,
710                                    hControl,
711                                    NULL,
712                                    ptMarginGroup.x,
713                                    y,
714                                    Width,
715                                    rc.bottom - rc.top,
716                                    SWP_NOZORDER)))
717         {
718             return;
719         }
720         y += rc.bottom - rc.top + (ptMargin.y / 2);
721 
722         /* measure the size of the buttons */
723         hButton = GetDlgItem(hpd->hWnd,
724                              IDC_PROPERTIES);
725         GetWindowRect(hButton,
726                       &rcButton);
727 
728         /* resize the IDC_STATUS label */
729         hControl = GetDlgItem(hpd->hWnd,
730                               IDC_STATUS);
731         GetWindowRect(hControl,
732                       &rc);
733         if (!(dwp = DeferWindowPos(dwp,
734                                    hControl,
735                                    NULL,
736                                    ptMarginGroup.x,
737                                    y,
738                                    Width,
739                                    cy - y - (3 * ptMargin.y) -
740                                        (rcButton.bottom - rcButton.top),
741                                    SWP_NOZORDER)))
742         {
743             return;
744         }
745 
746         /* move the IDC_PROPERTIES button */
747         y = cy - (2 * ptMargin.y) - (rcButton.bottom - rcButton.top);
748         x = cx - ptMarginGroup.x - (rcButton.right - rcButton.left);
749         if (!(dwp = DeferWindowPos(dwp,
750                                    hButton,
751                                    NULL,
752                                    x,
753                                    y,
754                                    0,
755                                    0,
756                                    SWP_NOSIZE | SWP_NOZORDER)))
757         {
758             return;
759         }
760 
761         /* move the IDC_TROUBLESHOOT button */
762         hButton = GetDlgItem(hpd->hWnd,
763                              IDC_TROUBLESHOOT);
764         GetWindowRect(hButton,
765                       &rcButton);
766         x -= (ptMargin.x / 2) + (rcButton.right - rcButton.left);
767         if (!(dwp = DeferWindowPos(dwp,
768                                    hButton,
769                                    NULL,
770                                    x,
771                                    y,
772                                    0,
773                                    0,
774                                    SWP_NOSIZE | SWP_NOZORDER)))
775         {
776             return;
777         }
778 
779         EndDeferWindowPos(dwp);
780     }
781 }
782 
783 
784 static VOID
785 EnableTroubleShoot(PHARDWARE_PAGE_DATA hpd,
786                    BOOL Enable)
787 {
788     HWND hBtnTroubleShoot = GetDlgItem(hpd->hWnd,
789                                        IDC_TROUBLESHOOT);
790 
791     ShowWindow(hBtnTroubleShoot,
792                Enable ? SW_SHOW : SW_HIDE);
793 }
794 
795 
796 static INT_PTR
797 CALLBACK
798 HardwareDlgProc(IN HWND hwndDlg,
799                 IN UINT uMsg,
800                 IN WPARAM wParam,
801                 IN LPARAM lParam)
802 {
803     PHARDWARE_PAGE_DATA hpd;
804     INT_PTR Ret = FALSE;
805 
806     hpd = (PHARDWARE_PAGE_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
807 
808     if (hpd != NULL || uMsg == WM_INITDIALOG)
809     {
810         switch (uMsg)
811         {
812             case WM_NOTIFY:
813             {
814                 NMHDR *pnmh = (NMHDR*)lParam;
815                 if (pnmh->hwndFrom == hpd->hWndDevList)
816                 {
817                     switch (pnmh->code)
818                     {
819                         case LVN_ITEMCHANGED:
820                         {
821                             LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
822 
823                             if ((pnmv->uChanged & LVIF_STATE) &&
824                                 ((pnmv->uOldState & (LVIS_FOCUSED | LVIS_SELECTED)) ||
825                                  (pnmv->uNewState & (LVIS_FOCUSED | LVIS_SELECTED))))
826                             {
827                                 UpdateControlStates(hpd);
828                             }
829                             break;
830                         }
831 
832                         case NM_DBLCLK:
833                         {
834                             DisplaySelectedDeviceProperties(hpd);
835                             break;
836                         }
837                     }
838                 }
839                 break;
840             }
841 
842             case WM_COMMAND:
843             {
844                 switch (LOWORD(wParam))
845                 {
846                     case IDC_TROUBLESHOOT:
847                     {
848                         /* FIXME - start the help using the command in the window text */
849                         break;
850                     }
851 
852                     case IDC_PROPERTIES:
853                     {
854                         DisplaySelectedDeviceProperties(hpd);
855                         break;
856                     }
857                 }
858                 break;
859             }
860 
861             case WM_SIZE:
862                 HardwareDlgResize(hpd,
863                                   (INT)LOWORD(lParam),
864                                   (INT)HIWORD(lParam));
865                 break;
866 
867             case WM_SETTEXT:
868             {
869                 LPCWSTR szWndText = (LPCWSTR)lParam;
870                 EnableTroubleShoot(hpd,
871                                    (szWndText != NULL && szWndText[0] != L'\0'));
872                 break;
873             }
874 
875             case WM_DEVICECHANGE:
876             {
877                 /* FIXME - don't call UpdateDevicesListViewControl for all events */
878                 UpdateDevicesListViewControl(hpd);
879                 Ret = TRUE;
880                 break;
881             }
882 
883             case WM_INITDIALOG:
884             {
885                 hpd = (PHARDWARE_PAGE_DATA)lParam;
886                 if (hpd != NULL)
887                 {
888                     HWND hWndParent;
889 
890                     hpd->hWnd = hwndDlg;
891                     SetWindowLongPtr(hwndDlg, DWLP_USER, (DWORD_PTR)hpd);
892 
893                     hpd->ClassImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
894 
895                     SetupDiGetClassImageList(&hpd->ClassImageListData);
896 
897                     /* calculate the size of the devices list view control */
898                     hpd->hWndDevList = GetDlgItem(hwndDlg,
899                                                   IDC_LV_DEVICES);
900                     if (hpd->hWndDevList != NULL)
901                     {
902                         RECT rcClient;
903                         GetClientRect(hpd->hWndDevList,
904                                       &rcClient);
905                         hpd->DevListViewHeight = rcClient.bottom;
906 
907                         if (hpd->DisplayMode == HWPD_LARGELIST)
908                         {
909                             hpd->DevListViewHeight = (hpd->DevListViewHeight * 3) / 2;
910                         }
911                     }
912 
913                     /* subclass the parent window */
914                     hWndParent = GetAncestor(hwndDlg,
915                                              GA_PARENT);
916                     if (hWndParent != NULL)
917                     {
918                         RECT rcClient;
919 
920                         if (GetClientRect(hWndParent,
921                                           &rcClient) &&
922                             SetWindowPos(hwndDlg,
923                                          NULL,
924                                          0,
925                                          0,
926                                          rcClient.right,
927                                          rcClient.bottom,
928                                          SWP_NOZORDER))
929                         {
930                             /* subclass the parent window. This is not safe
931                                if the parent window belongs to another thread! */
932                             hpd->ParentOldWndProc = (WNDPROC)SetWindowLongPtr(hWndParent,
933                                                                               GWLP_WNDPROC,
934                                                                               (LONG_PTR)ParentSubWndProc);
935 
936                             if (hpd->ParentOldWndProc != NULL &&
937                                 SetProp(hWndParent,
938                                         L"DevMgrSubClassInfo",
939                                         (HANDLE)hpd))
940                             {
941                                 hpd->hWndParent = hWndParent;
942                             }
943                         }
944                     }
945 
946                     /* initialize the devices list view control */
947                     InitializeDevicesList(hpd);
948 
949                     /* fill the devices list view control */
950                     FillDevicesListViewControl(hpd,
951                                                NULL,
952                                                NULL);
953 
954                     /* decide whether to show or hide the troubleshoot button */
955                     EnableTroubleShoot(hpd,
956                                        GetWindowTextLength(hwndDlg) != 0);
957                 }
958                 Ret = TRUE;
959                 break;
960             }
961 
962             case WM_DESTROY:
963             {
964                 /* zero hpd pointer in window data, because it can be used later (WM_DESTROY has not to be last message) */
965                 SetWindowLongPtr(hwndDlg, DWLP_USER, (DWORD_PTR)NULL);
966 
967                 /* free devices list */
968                 FreeDevicesList(hpd);
969 
970                 /* restore the old window proc of the subclassed parent window */
971                 if (hpd->hWndParent != NULL && hpd->ParentOldWndProc != NULL)
972                 {
973                     SetWindowLongPtr(hpd->hWndParent,
974                                      GWLP_WNDPROC,
975                                      (LONG_PTR)hpd->ParentOldWndProc);
976                 }
977 
978                 if (hpd->ClassImageListData.ImageList != NULL)
979                 {
980                     SetupDiDestroyClassImageList(&hpd->ClassImageListData);
981                 }
982 
983                 /* free the reference to comctl32 */
984                 FreeLibrary(hpd->hComCtl32);
985                 hpd->hComCtl32 = NULL;
986 
987                 /* free the allocated resources */
988                 HeapFree(GetProcessHeap(),
989                          0,
990                          hpd);
991                 break;
992             }
993         }
994     }
995 
996     return Ret;
997 }
998 
999 
1000 /***************************************************************************
1001  * NAME                                                         EXPORTED
1002  *      DeviceCreateHardwarePageEx
1003  *
1004  * DESCRIPTION
1005  *   Creates a hardware page
1006  *
1007  * ARGUMENTS
1008  *   hWndParent:     Handle to the parent window
1009  *   lpGuids:        An array of guids of devices that are to be listed
1010  *   uNumberOfGuids: Numbers of guids in the Guids array
1011  *   DisplayMode:    Sets the size of the device list view control
1012  *
1013  * RETURN VALUE
1014  *   Returns the handle of the hardware page window that has been created or
1015  *   NULL if it failed.
1016  *
1017  * @implemented
1018  */
1019 HWND
1020 WINAPI
1021 DeviceCreateHardwarePageEx(IN HWND hWndParent,
1022                            IN LPGUID lpGuids,
1023                            IN UINT uNumberOfGuids,
1024                            IN HWPAGE_DISPLAYMODE DisplayMode)
1025 {
1026     PHARDWARE_PAGE_DATA hpd;
1027 
1028     /* allocate the HARDWARE_PAGE_DATA structure. Make sure it is
1029        zeroed because the initialization code assumes that in
1030        failure cases! */
1031     hpd = (PHARDWARE_PAGE_DATA)HeapAlloc(GetProcessHeap(),
1032                                          HEAP_ZERO_MEMORY,
1033                                          FIELD_OFFSET(HARDWARE_PAGE_DATA,
1034                                                       ClassDevInfo[uNumberOfGuids]));
1035     if (hpd != NULL)
1036     {
1037         HWND hWnd;
1038         UINT i;
1039 
1040         hpd->DisplayMode = ((DisplayMode > HWPD_MAX) ? HWPD_STANDARDLIST : DisplayMode);
1041 
1042         /* initialize the HARDWARE_PAGE_DATA structure */
1043         hpd->NumberOfGuids = uNumberOfGuids;
1044         for (i = 0;
1045              i < uNumberOfGuids;
1046              i++)
1047         {
1048             hpd->ClassDevInfo[i].hDevInfo = INVALID_HANDLE_VALUE;
1049             hpd->ClassDevInfo[i].Guid = lpGuids[i];
1050         }
1051 
1052         /* load comctl32.dll dynamically */
1053         hpd->hComCtl32 = LoadAndInitComctl32();
1054         if (hpd->hComCtl32 == NULL)
1055         {
1056             goto Cleanup;
1057         }
1058 
1059         /* create the dialog */
1060         hWnd = CreateDialogParam(hDllInstance,
1061                                  MAKEINTRESOURCE(IDD_HARDWARE),
1062                                  hWndParent,
1063                                  HardwareDlgProc,
1064                                  (LPARAM)hpd);
1065         if (hWnd != NULL)
1066         {
1067             return hWnd;
1068         }
1069         else
1070         {
1071 Cleanup:
1072             /* oops, something went wrong... */
1073             if (hpd->hComCtl32 != NULL)
1074             {
1075                 FreeLibrary(hpd->hComCtl32);
1076             }
1077 
1078             HeapFree(GetProcessHeap(),
1079                      0,
1080                      hpd);
1081         }
1082     }
1083 
1084     return NULL;
1085 }
1086 
1087 
1088 /***************************************************************************
1089  * NAME                                                         EXPORTED
1090  *      DeviceCreateHardwarePage
1091  *
1092  * DESCRIPTION
1093  *   Creates a hardware page
1094  *
1095  * ARGUMENTS
1096  *   hWndParent: Handle to the parent window
1097  *   lpGuid:     Guid of the device
1098  *
1099  * RETURN VALUE
1100  *   Returns the handle of the hardware page window that has been created or
1101  *   NULL if it failed.
1102  *
1103  * @implemented
1104  */
1105 HWND
1106 WINAPI
1107 DeviceCreateHardwarePage(IN HWND hWndParent,
1108                          IN LPGUID lpGuid)
1109 {
1110     return DeviceCreateHardwarePageEx(hWndParent,
1111                                       lpGuid,
1112                                       1,
1113                                       HWPD_LARGELIST);
1114 }
1115