fixed refreshing the advanced properties on device changes
Modified: trunk/reactos/lib/devmgr/advprop.c

Modified: trunk/reactos/lib/devmgr/advprop.c
--- trunk/reactos/lib/devmgr/advprop.c	2005-12-01 20:12:00 UTC (rev 19804)
+++ trunk/reactos/lib/devmgr/advprop.c	2005-12-01 20:31:49 UTC (rev 19805)
@@ -30,6 +30,8 @@
 #define NDEBUG
 #include <debug.h>
 
+#define DPN_DEVICEUPDATE    (WM_USER + 0x1000)
+
 typedef INT_PTR (WINAPI *PPROPERTYSHEETW)(LPCPROPSHEETHEADERW);
 typedef HPROPSHEETPAGE (WINAPI *PCREATEPROPERTYSHEETPAGEW)(LPCPROPSHEETPAGEW);
 typedef BOOL (WINAPI *PDESTROYPROPERTYSHEETPAGE)(HPROPSHEETPAGE);
@@ -43,12 +45,27 @@
 
 typedef struct _DEVADVPROP_INFO
 {
+    HWND hWndGeneralPage;
+    HWND hWndParent;
+    WNDPROC ParentOldWndProc;
+
     HDEVINFO DeviceInfoSet;
-    PSP_DEVINFO_DATA DeviceInfoData;
+    SP_DEVINFO_DATA DeviceInfoData;
+    HANDLE hMachine;
+    LPCWSTR lpMachineName;
+
     HINSTANCE hComCtl32;
-    HANDLE hMachine;
-    BOOL CanDisable;
-    BOOL DeviceEnabled;
+
+    DWORD PropertySheetType;
+    DWORD nDevPropSheets;
+    HPROPSHEETPAGE *DevPropSheets;
+
+    BOOL FreeDevPropSheets : 1;
+    BOOL CanDisable : 1;
+    BOOL DeviceEnabled : 1;
+    BOOL DeviceUsageChanged : 1;
+    BOOL CreatedDevInfoSet : 1;
+
     WCHAR szDevName[255];
     WCHAR szTemp[255];
     WCHAR szDeviceID[1];
@@ -155,63 +172,193 @@
 ApplyGeneralSettings(IN HWND hwndDlg,
                      IN PDEVADVPROP_INFO dap)
 {
-    DEVENABLEACTION SelectedUsageAction;
+    if (dap->DeviceUsageChanged)
+    {
+        DEVENABLEACTION SelectedUsageAction;
 
-    SelectedUsageAction = GetSelectedUsageAction(GetDlgItem(hwndDlg,
-                                                            IDC_DEVUSAGE));
-    if (SelectedUsageAction != DEA_UNKNOWN)
-    {
-        switch (SelectedUsageAction)
+        SelectedUsageAction = GetSelectedUsageAction(GetDlgItem(hwndDlg,
+                                                                IDC_DEVUSAGE));
+        if (SelectedUsageAction != DEA_UNKNOWN)
         {
-            case DEA_ENABLE:
-                if (!dap->DeviceEnabled)
-                {
-                    /* FIXME - enable device */
-                }
-                break;
+            switch (SelectedUsageAction)
+            {
+                case DEA_ENABLE:
+                    if (!dap->DeviceEnabled)
+                    {
+                        /* FIXME - enable device */
+                    }
+                    break;
 
-            case DEA_DISABLE:
-                if (dap->DeviceEnabled)
-                {
-                    /* FIXME - disable device */
-                }
-                break;
+                case DEA_DISABLE:
+                    if (dap->DeviceEnabled)
+                    {
+                        /* FIXME - disable device */
+                    }
+                    break;
 
-            default:
-                break;
+                default:
+                    break;
+            }
         }
+    }
 
-        /* disable the apply button */
-        PropSheet_UnChanged(GetParent(hwndDlg),
-                            hwndDlg);
-    }
+    /* disable the apply button */
+    PropSheet_UnChanged(GetParent(hwndDlg),
+                        hwndDlg);
+    dap->DeviceUsageChanged = FALSE;
 }
 
 
 static VOID
 UpdateDevInfo(IN HWND hwndDlg,
               IN PDEVADVPROP_INFO dap,
-              IN BOOL ReOpenDevice)
+              IN BOOL ReOpen)
 {
     HICON hIcon;
     HWND hDevUsage;
+    HWND hPropSheetDlg;
+    BOOL bFlag;
+    DWORD i;
 
-    if (ReOpenDevice)
+    hPropSheetDlg = GetParent(hwndDlg);
+
+    if (ReOpen)
     {
-        /* note, we ignore the fact that SetupDiOpenDeviceInfo could fail here,
-           in case of failure we're still going to query information from it even
-           though those calls are going to fail. But they return readable strings
-           even in that case. */
-        SetupDiOpenDeviceInfo(dap->DeviceInfoSet,
-                              dap->szDeviceID,
-                              hwndDlg,
-                              0,
-                              dap->DeviceInfoData);
+        PROPSHEETHEADER psh;
+        HDEVINFO hOldDevInfo;
+
+        /* switch to the General page */
+        PropSheet_SetCurSelByID(hPropSheetDlg,
+                                IDD_DEVICEGENERAL);
+
+        /* remove and destroy the existing device property sheet pages */
+        for (i = 0;
+             i != dap->nDevPropSheets;
+             i++)
+        {
+            PropSheet_RemovePage(hPropSheetDlg,
+                                 -1,
+                                 dap->DevPropSheets[i]);
+        }
+
+        if (dap->FreeDevPropSheets)
+        {
+            /* don't free the array if it's the one allocated in
+               DisplayDeviceAdvancedProperties */
+            HeapFree(GetProcessHeap(),
+                     0,
+                     dap->DevPropSheets);
+
+            dap->FreeDevPropSheets = FALSE;
+        }
+
+        dap->DevPropSheets = NULL;
+        dap->nDevPropSheets = 0;
+
+        /* create a new device info set and re-open the device */
+        hOldDevInfo = dap->DeviceInfoSet;
+        dap->DeviceInfoSet = SetupDiCreateDeviceInfoListEx(NULL,
+                                                           hwndDlg,
+                                                           dap->lpMachineName,
+                                                           NULL);
+        if (dap->DeviceInfoSet != INVALID_HANDLE_VALUE)
+        {
+            if (SetupDiOpenDeviceInfo(dap->DeviceInfoSet,
+                                      dap->szDeviceID,
+                                      hwndDlg,
+                                      0,
+                                      &dap->DeviceInfoData))
+            {
+                if (dap->CreatedDevInfoSet)
+                {
+                    SetupDiDestroyDeviceInfoList(hOldDevInfo);
+                }
+
+                dap->CreatedDevInfoSet = TRUE;
+            }
+            else
+            {
+                SetupDiDestroyDeviceInfoList(dap->DeviceInfoSet);
+                dap->DeviceInfoSet = INVALID_HANDLE_VALUE;
+                dap->CreatedDevInfoSet = FALSE;
+            }
+        }
+        else
+        {
+            /* oops, something went wrong, restore the old device info set */
+            dap->DeviceInfoSet = hOldDevInfo;
+
+            if (dap->DeviceInfoSet != INVALID_HANDLE_VALUE)
+            {
+                SetupDiOpenDeviceInfo(dap->DeviceInfoSet,
+                                      dap->szDeviceID,
+                                      hwndDlg,
+                                      0,
+                                      &dap->DeviceInfoData);
+            }
+        }
+
+        /* find out how many new device property sheets to add.
+           fake a PROPSHEETHEADER structure, we don't plan to
+           call PropertySheet again!*/
+        psh.dwSize = sizeof(PROPSHEETHEADER);
+        psh.dwFlags = 0;
+        psh.nPages = 0;
+
+        if (!SetupDiGetClassDevPropertySheets(dap->DeviceInfoSet,
+                                              &dap->DeviceInfoData,
+                                              &psh,
+                                              0,
+                                              &dap->nDevPropSheets,
+                                              dap->PropertySheetType) &&
+            dap->nDevPropSheets != 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+        {
+            dap->DevPropSheets = HeapAlloc(GetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           dap->nDevPropSheets * sizeof(HPROPSHEETPAGE));
+            if (dap->DevPropSheets != NULL)
+            {
+                psh.phpage = dap->DevPropSheets;
+
+                /* query the new property sheet pages to add */
+                if (SetupDiGetClassDevPropertySheets(dap->DeviceInfoSet,
+                                                     &dap->DeviceInfoData,
+                                                     &psh,
+                                                     dap->nDevPropSheets,
+                                                     NULL,
+                                                     dap->PropertySheetType))
+                {
+                    /* add the property sheets */
+
+                    for (i = 0;
+                         i != dap->nDevPropSheets;
+                         i++)
+                    {
+                        PropSheet_AddPage(hPropSheetDlg,
+                                          dap->DevPropSheets[i]);
+                    }
+
+                    dap->FreeDevPropSheets = TRUE;
+                }
+                else
+                {
+                    /* cleanup, we were unable to get the device property sheets */
+                    HeapFree(GetProcessHeap(),
+                             0,
+                             dap->DevPropSheets);
+
+                    dap->nDevPropSheets = 0;
+                    dap->DevPropSheets = NULL;
+                }
+            }
+            else
+                dap->nDevPropSheets = 0;
+        }
     }
 
     /* get the device name */
     if (GetDeviceDescriptionString(dap->DeviceInfoSet,
-                                   dap->DeviceInfoData,
+                                   &dap->DeviceInfoData,
                                    dap->szDevName,
                                    sizeof(dap->szDevName) / sizeof(dap->szDevName[0])))
     {
@@ -221,7 +368,7 @@
     }
 
     /* set the device image */
-    if (SetupDiLoadClassIcon(&dap->DeviceInfoData->ClassGuid,
+    if (SetupDiLoadClassIcon(&dap->DeviceInfoData.ClassGuid,
                              &hIcon,
                              NULL))
     {
@@ -242,7 +389,7 @@
                    dap->szDevName);
 
     /* set the device type edit control text */
-    if (GetDeviceTypeString(dap->DeviceInfoData,
+    if (GetDeviceTypeString(&dap->DeviceInfoData,
                             dap->szTemp,
                             sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -253,7 +400,7 @@
 
     /* set the device manufacturer edit control text */
     if (GetDeviceManufacturerString(dap->DeviceInfoSet,
-                                    dap->DeviceInfoData,
+                                    &dap->DeviceInfoData,
                                     dap->szTemp,
                                     sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -263,7 +410,7 @@
     }
 
     /* set the device location edit control text */
-    if (GetDeviceLocationString(dap->DeviceInfoData->DevInst,
+    if (GetDeviceLocationString(dap->DeviceInfoData.DevInst,
                                 dap->szTemp,
                                 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -273,7 +420,7 @@
     }
 
     /* set the device status edit control text */
-    if (GetDeviceStatusString(dap->DeviceInfoData->DevInst,
+    if (GetDeviceStatusString(dap->DeviceInfoData.DevInst,
                               dap->hMachine,
                               dap->szTemp,
                               sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
@@ -287,18 +434,24 @@
     hDevUsage = GetDlgItem(hwndDlg,
                            IDC_DEVUSAGE);
 
-    if (!CanDisableDevice(dap->DeviceInfoData->DevInst,
-                          dap->hMachine,
-                          &dap->CanDisable))
+    dap->CanDisable = FALSE;
+    dap->DeviceEnabled = FALSE;
+
+    if (dap->DeviceInfoSet != INVALID_HANDLE_VALUE)
     {
-        dap->CanDisable = FALSE;
-    }
+        if (CanDisableDevice(dap->DeviceInfoData.DevInst,
+                             dap->hMachine,
+                             &bFlag))
+        {
+            dap->CanDisable = bFlag;
+        }
 
-    if (!IsDeviceEnabled(dap->DeviceInfoData->DevInst,
-                         dap->hMachine,
-                         &dap->DeviceEnabled))
-    {
-        dap->DeviceEnabled = FALSE;
+        if (IsDeviceEnabled(dap->DeviceInfoData.DevInst,
+                            dap->hMachine,
+                            &bFlag))
+        {
+            dap->DeviceEnabled = bFlag;
+        }
     }
 
     /* enable/disable the device usage controls */
@@ -321,11 +474,55 @@
     }
 
     /* finally, disable the apply button */
-    PropSheet_UnChanged(GetParent(hwndDlg),
+    PropSheet_UnChanged(hPropSheetDlg,
                         hwndDlg);
+    dap->DeviceUsageChanged = FALSE;
 }
 
 
+static LRESULT
+CALLBACK
+DlgParentSubWndProc(IN HWND hwnd,
+                    IN UINT uMsg,
+                    IN WPARAM wParam,
+                    IN LPARAM lParam)
+{
+    PDEVADVPROP_INFO dap;
+
+    dap = (PDEVADVPROP_INFO)GetProp(hwnd,
+                                    L"DevMgrDevChangeSub");
+    if (dap != NULL)
+    {
+        if (uMsg == WM_DEVICECHANGE && !IsWindowVisible(dap->hWndGeneralPage))
+        {
+            SendMessage(dap->hWndGeneralPage,
+                        WM_DEVICECHANGE,
+                        wParam,
+                        lParam);
+        }
+
+        /* pass the message the the old window proc */
+        return CallWindowProc(dap->ParentOldWndProc,
+                              hwnd,
+                              uMsg,
+                              wParam,
+                              lParam);
+    }
+    else
+    {
+        /* this is not a good idea if the subclassed window was an ansi
+           window, but we failed finding out the previous window proc
+           so we can't use CallWindowProc. This should rarely - if ever -
+           happen. */
+
+        return DefWindowProc(hwnd,
+                             uMsg,
+                             wParam,
+                             lParam);
+    }
+}
+
+
 static INT_PTR
 CALLBACK
 AdvPropGeneralDlgProc(IN HWND hwndDlg,
@@ -353,6 +550,7 @@
                         {
                             PropSheet_Changed(GetParent(hwndDlg),
                                               hwndDlg);
+                            dap->DeviceUsageChanged = TRUE;
                         }
                         break;
                     }
@@ -378,10 +576,34 @@
                 dap = (PDEVADVPROP_INFO)((LPPROPSHEETPAGE)lParam)->lParam;
                 if (dap != NULL)
                 {
+                    HWND hWndParent;
+
+                    dap->hWndGeneralPage = hwndDlg;
+
                     SetWindowLongPtr(hwndDlg,
                                      DWL_USER,
                                      (DWORD_PTR)dap);
 
+                    /* subclass the parent window to always receive
+                       WM_DEVICECHANGE messages */
+                    hWndParent = GetParent(hwndDlg);
+                    if (hWndParent != NULL)
+                    {
+                        /* subclass the parent window. This is not safe
+                           if the parent window belongs to another thread! */
+                        dap->ParentOldWndProc = (WNDPROC)SetWindowLongPtr(hWndParent,
+                                                                          GWLP_WNDPROC,
+                                                                          (LONG_PTR)DlgParentSubWndProc);
+
+                        if (dap->ParentOldWndProc != NULL &&
+                            SetProp(hWndParent,
+                                    L"DevMgrDevChangeSub",
+                                    (HANDLE)dap))
+                        {
+                            dap->hWndParent = hWndParent;
+                        }
+                    }
+
                     UpdateDevInfo(hwndDlg,
                                   dap,
                                   FALSE);
@@ -403,6 +625,14 @@
             {
                 HICON hDevIcon;
 
+                /* restore the old window proc of the subclassed parent window */
+                if (dap->hWndParent != NULL && dap->ParentOldWndProc != NULL)
+                {
+                    SetWindowLongPtr(dap->hWndParent,
+                                     GWLP_WNDPROC,
+                                     (LONG_PTR)dap->ParentOldWndProc);
+                }
+
                 /* destroy the device icon */
                 hDevIcon = (HICON)SendDlgItemMessage(hwndDlg,
                                                      IDC_DEVICON,
@@ -437,7 +667,6 @@
     PCREATEPROPERTYSHEETPAGEW pCreatePropertySheetPageW;
     PDESTROYPROPERTYSHEETPAGE pDestroyPropertySheetPage;
     PDEVADVPROP_INFO DevAdvPropInfo;
-    DWORD PropertySheetType;
     HANDLE hMachine = NULL;
     DWORD DevIdSize = 0;
     INT_PTR Ret = -1;
@@ -469,7 +698,7 @@
                                        0,
                                        &DevIdSize))
         {
-            DPRINT1("SetupDiGetDeviceInterfaceDetail unexpectedly returned TRUE!\n");
+            DPRINT1("SetupDiGetDeviceInstanceId unexpectedly returned TRUE!\n");
             return -1;
         }
 
@@ -496,7 +725,7 @@
     /* create the internal structure associated with the "General",
        "Driver", ... pages */
     DevAdvPropInfo = HeapAlloc(GetProcessHeap(),
-                               0,
+                               HEAP_ZERO_MEMORY,
                                FIELD_OFFSET(DEVADVPROP_INFO,
                                             szDeviceID) +
                                    (DevIdSize * sizeof(WCHAR)));
@@ -517,29 +746,36 @@
             goto Cleanup;
         }
     }
+    else
+    {
+        /* copy the device instance id supplied by the caller */
+        wcscpy(DevAdvPropInfo->szDeviceID,
+               lpDeviceID);
+    }
 
     DevAdvPropInfo->DeviceInfoSet = DeviceInfoSet;
-    DevAdvPropInfo->DeviceInfoData = DeviceInfoData;
-    DevAdvPropInfo->hComCtl32 = hComCtl32;
+    DevAdvPropInfo->DeviceInfoData = *DeviceInfoData;
     DevAdvPropInfo->hMachine = hMachine;
+    DevAdvPropInfo->lpMachineName = lpMachineName;
     DevAdvPropInfo->szDevName[0] = L'\0';
+    DevAdvPropInfo->hComCtl32 = hComCtl32;
 
     psh.dwSize = sizeof(PROPSHEETHEADER);
     psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW;
     psh.hwndParent = hWndParent;
     psh.pszCaption = DevAdvPropInfo->szDevName;
 
-    PropertySheetType = lpMachineName != NULL ?
-                            DIGCDP_FLAG_REMOTE_ADVANCED :
-                            DIGCDP_FLAG_ADVANCED;
+    DevAdvPropInfo->PropertySheetType = lpMachineName != NULL ?
+                                            DIGCDP_FLAG_REMOTE_ADVANCED :
+                                            DIGCDP_FLAG_ADVANCED;
 
     /* find out how many property sheets we need */
     if (SetupDiGetClassDevPropertySheets(DeviceInfoSet,
-                                         DeviceInfoData,
+                                         &DevAdvPropInfo->DeviceInfoData,
                                          &psh,
                                          0,
                                          &nPropSheets,
-                                         PropertySheetType) &&
+                                         DevAdvPropInfo->PropertySheetType) &&
         nPropSheets != 0)
     {
         DPRINT1("SetupDiGetClassDevPropertySheets unexpectedly returned TRUE!\n");
@@ -572,15 +808,19 @@
         psh.nPages++;
     }
 
+    DevAdvPropInfo->nDevPropSheets = nPropSheets;
+
     if (nPropSheets != 0)
     {
+        DevAdvPropInfo->DevPropSheets = psh.phpage + psh.nPages;
+
         /* create the device property sheets */
         if (!SetupDiGetClassDevPropertySheets(DeviceInfoSet,
-                                              DeviceInfoData,
+                                              &DevAdvPropInfo->DeviceInfoData,
                                               &psh,
                                               nPropSheets + psh.nPages,
                                               NULL,
-                                              PropertySheetType))
+                                              DevAdvPropInfo->PropertySheetType))
         {
             goto Cleanup;
         }
@@ -600,25 +840,49 @@
 
 Cleanup:
         /* in case of failure the property sheets must be destroyed */
-        for (i = 0;
-             i < psh.nPages;
-             i++)
+        if (psh.phpage != NULL)
         {
-            if (psh.phpage[i] != NULL)
+            for (i = 0;
+                 i < psh.nPages;
+                 i++)
             {
-                pDestroyPropertySheetPage(psh.phpage[i]);
+                if (psh.phpage[i] != NULL)
+                {
+                    pDestroyPropertySheetPage(psh.phpage[i]);
+                }
             }
         }
     }
 
-    HeapFree(GetProcessHeap(),
-             0,
-             psh.phpage);
+    if (DevAdvPropInfo != NULL)
+    {
+        if (DevAdvPropInfo->FreeDevPropSheets)
+        {
+            /* don't free the array if it's the one allocated in
+               DisplayDeviceAdvancedProperties */
+            HeapFree(GetProcessHeap(),
+                     0,
+                     DevAdvPropInfo->DevPropSheets);
+        }
 
-    HeapFree(GetProcessHeap(),
-             0,
-             DevAdvPropInfo);
+        if (DevAdvPropInfo->CreatedDevInfoSet)
+        {
+            /* close the device info set in case a new one was created */
+            SetupDiDestroyDeviceInfoList(DevAdvPropInfo->DeviceInfoSet);
+        }
 
+        HeapFree(GetProcessHeap(),
+                 0,
+                 DevAdvPropInfo);
+    }
+
+    if (psh.phpage != NULL)
+    {
+        HeapFree(GetProcessHeap(),
+                 0,
+                 psh.phpage);
+    }
+
     if (hMachine != NULL)
     {
         CM_Disconnect_Machine(hMachine);
@@ -665,19 +929,10 @@
     hComCtl32 = LoadAndInitComctl32();
     if (hComCtl32 != NULL)
     {
-        if (lpMachineName != NULL)
-        {
-            hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
-                                                     hWndParent,
-                                                     lpMachineName,
-                                                     NULL);
-        }
-        else
-        {
-            hDevInfo = SetupDiCreateDeviceInfoList(NULL,
-                                                   hWndParent);
-        }
-
+        hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
+                                                 hWndParent,
+                                                 lpMachineName,
+                                                 NULL);
         if (hDevInfo != INVALID_HANDLE_VALUE)
         {
             DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);