add the driver details dialog (not fully implemented)
[reactos.git] / reactos / lib / devmgr / advprop.c
index 5f19061..245e51c 100644 (file)
@@ -34,21 +34,42 @@ typedef INT_PTR (WINAPI *PPROPERTYSHEETW)(LPCPROPSHEETHEADERW);
 typedef HPROPSHEETPAGE (WINAPI *PCREATEPROPERTYSHEETPAGEW)(LPCPROPSHEETPAGEW);
 typedef BOOL (WINAPI *PDESTROYPROPERTYSHEETPAGE)(HPROPSHEETPAGE);
 
-typedef enum
-{
-    DEA_DISABLE = 0,
-    DEA_ENABLE,
-    DEA_UNKNOWN
-} DEVENABLEACTION;
-
 typedef struct _DEVADVPROP_INFO
 {
+    HWND hWndGeneralPage;
+    HWND hWndParent;
+    WNDPROC ParentOldWndProc;
+    HICON hDevIcon;
+
     HDEVINFO DeviceInfoSet;
-    PSP_DEVINFO_DATA DeviceInfoData;
+    SP_DEVINFO_DATA DeviceInfoData;
+    HDEVINFO CurrentDeviceInfoSet;
+    SP_DEVINFO_DATA CurrentDeviceInfoData;
+    DEVINST ParentDevInst;
+    HMACHINE hMachine;
+    LPCWSTR lpMachineName;
+
     HINSTANCE hComCtl32;
-    HANDLE hMachine;
-    BOOL CanDisable;
-    BOOL DeviceEnabled;
+    PCREATEPROPERTYSHEETPAGEW pCreatePropertySheetPageW;
+    PDESTROYPROPERTYSHEETPAGE pDestroyPropertySheetPage;
+
+    DWORD PropertySheetType;
+    DWORD nDevPropSheets;
+    HPROPSHEETPAGE *DevPropSheets;
+
+    BOOL FreeDevPropSheets : 1;
+    BOOL CanDisable : 1;
+    BOOL DeviceStarted : 1;
+    BOOL DeviceUsageChanged : 1;
+    BOOL CloseDevInst : 1;
+    BOOL IsAdmin : 1;
+    BOOL DoDefaultDevAction : 1;
+    BOOL PageInitialized : 1;
+    BOOL ShowRemotePages : 1;
+    BOOL HasDriverPage : 1;
+    BOOL HasResourcePage : 1;
+    BOOL HasPowerPage : 1;
+
     WCHAR szDevName[255];
     WCHAR szTemp[255];
     WCHAR szDeviceID[1];
@@ -56,6 +77,340 @@ typedef struct _DEVADVPROP_INFO
 } DEVADVPROP_INFO, *PDEVADVPROP_INFO;
 
 
+typedef struct _ENUMDRIVERFILES_CONTEXT
+{
+    HWND hDriversListView;
+    UINT nCount;
+} ENUMDRIVERFILES_CONTEXT, *PENUMDRIVERFILES_CONTEXT;
+
+#define PM_INITIALIZE (WM_APP + 0x101)
+
+
+static UINT WINAPI
+EnumDeviceDriverFilesCallback(IN PVOID Context,
+                              IN UINT Notification,
+                              IN UINT_PTR Param1,
+                              IN UINT_PTR Param2)
+{
+    LVITEM li;
+    PENUMDRIVERFILES_CONTEXT EnumDriverFilesContext = (PENUMDRIVERFILES_CONTEXT)Context;
+
+    li.mask = LVIF_TEXT | LVIF_STATE;
+    li.iItem = EnumDriverFilesContext->nCount++;
+    li.iSubItem = 0;
+    li.state = (li.iItem == 0 ? LVIS_SELECTED : 0);
+    li.stateMask = LVIS_SELECTED;
+    li.pszText = (LPWSTR)Param1;
+    ListView_InsertItem(EnumDriverFilesContext->hDriversListView,
+                        &li);
+    return NO_ERROR;
+}
+
+
+static VOID
+UpdateDriverDetailsDlg(IN HWND hwndDlg,
+                       IN HWND hDriversListView,
+                       IN PDEVADVPROP_INFO dap)
+{
+    HDEVINFO DeviceInfoSet;
+    PSP_DEVINFO_DATA DeviceInfoData;
+    SP_DRVINFO_DATA DriverInfoData;
+    ENUMDRIVERFILES_CONTEXT EnumDriverFilesContext;
+
+    if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+    {
+        DeviceInfoSet = dap->CurrentDeviceInfoSet;
+        DeviceInfoData = &dap->CurrentDeviceInfoData;
+    }
+    else
+    {
+        DeviceInfoSet = dap->DeviceInfoSet;
+        DeviceInfoData = &dap->DeviceInfoData;
+    }
+
+    /* set the device image */
+    SendDlgItemMessage(hwndDlg,
+                       IDC_DEVICON,
+                       STM_SETICON,
+                       (WPARAM)dap->hDevIcon,
+                       0);
+
+    /* set the device name edit control text */
+    SetDlgItemText(hwndDlg,
+                   IDC_DEVNAME,
+                   dap->szDevName);
+
+    /* fill the driver files list view */
+    EnumDriverFilesContext.hDriversListView = hDriversListView;
+    EnumDriverFilesContext.nCount = 0;
+
+    ListView_DeleteAllItems(EnumDriverFilesContext.hDriversListView);
+    DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
+    if (FindCurrentDriver(DeviceInfoSet,
+                          DeviceInfoData,
+                          &DriverInfoData) &&
+        SetupDiSetSelectedDriver(DeviceInfoSet,
+                                 DeviceInfoData,
+                                 &DriverInfoData))
+    {
+        HSPFILEQ queueHandle;
+
+        queueHandle = SetupOpenFileQueue();
+        if (queueHandle != (HSPFILEQ)INVALID_HANDLE_VALUE)
+        {
+            SP_DEVINSTALL_PARAMS DeviceInstallParams = {0};
+            DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+            if (SetupDiGetDeviceInstallParams(DeviceInfoSet,
+                                              DeviceInfoData,
+                                              &DeviceInstallParams))
+            {
+                DeviceInstallParams.FileQueue = queueHandle;
+                DeviceInstallParams.Flags |= DI_NOVCP;
+
+                if (SetupDiSetDeviceInstallParams(DeviceInfoSet,
+                                                  DeviceInfoData,
+                                                  &DeviceInstallParams) &&
+                    SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
+                                              DeviceInfoSet,
+                                              DeviceInfoData))
+                {
+                    DWORD scanResult;
+                    RECT rcClient;
+                    LVCOLUMN lvc;
+
+                    /* enumerate the driver files */
+                    SetupScanFileQueue(queueHandle,
+                                       SPQ_SCAN_USE_CALLBACK,
+                                       NULL,
+                                       EnumDeviceDriverFilesCallback,
+                                       &EnumDriverFilesContext,
+                                       &scanResult);
+
+                    /* update the list view column width */
+                    GetClientRect(hDriversListView,
+                                  &rcClient);
+                    lvc.mask = LVCF_WIDTH;
+                    lvc.cx = rcClient.right;
+                    ListView_SetColumn(hDriversListView,
+                                       0,
+                                       &lvc);
+                }
+            }
+
+            SetupCloseFileQueue(queueHandle);
+        }
+    }
+}
+
+
+static INT_PTR
+CALLBACK
+DriverDetailsDlgProc(IN HWND hwndDlg,
+                     IN UINT uMsg,
+                     IN WPARAM wParam,
+                     IN LPARAM lParam)
+{
+    PDEVADVPROP_INFO dap;
+    INT_PTR Ret = FALSE;
+
+    dap = (PDEVADVPROP_INFO)GetWindowLongPtr(hwndDlg,
+                                             DWL_USER);
+
+    if (dap != NULL || uMsg == WM_INITDIALOG)
+    {
+        switch (uMsg)
+        {
+            case WM_COMMAND:
+            {
+                switch (LOWORD(wParam))
+                {
+                    case IDOK:
+                    {
+                        EndDialog(hwndDlg,
+                                  IDOK);
+                        break;
+                    }
+                }
+                break;
+            }
+
+            case WM_CLOSE:
+            {
+                EndDialog(hwndDlg,
+                          IDCANCEL);
+                break;
+            }
+
+            case WM_INITDIALOG:
+            {
+                LV_COLUMN lvc;
+                HWND hDriversListView;
+
+                dap = (PDEVADVPROP_INFO)lParam;
+                if (dap != NULL)
+                {
+                    SetWindowLongPtr(hwndDlg,
+                                     DWL_USER,
+                                     (DWORD_PTR)dap);
+
+                    hDriversListView = GetDlgItem(hwndDlg,
+                                                  IDC_DRIVERFILES);
+
+                    /* add a column to the list view control */
+                    lvc.mask = LVCF_FMT | LVCF_WIDTH;
+                    lvc.fmt = LVCFMT_LEFT;
+                    lvc.cx = 0;
+                    ListView_InsertColumn(hDriversListView,
+                                          0,
+                                          &lvc);
+
+                    UpdateDriverDetailsDlg(hwndDlg,
+                                           hDriversListView,
+                                           dap);
+                }
+
+                Ret = TRUE;
+                break;
+            }
+        }
+    }
+
+    return Ret;
+}
+
+
+static VOID
+UpdateDriverDlg(IN HWND hwndDlg,
+                IN PDEVADVPROP_INFO dap)
+{
+    HDEVINFO DeviceInfoSet;
+    PSP_DEVINFO_DATA DeviceInfoData;
+
+    if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+    {
+        DeviceInfoSet = dap->CurrentDeviceInfoSet;
+        DeviceInfoData = &dap->CurrentDeviceInfoData;
+    }
+    else
+    {
+        DeviceInfoSet = dap->DeviceInfoSet;
+        DeviceInfoData = &dap->DeviceInfoData;
+    }
+
+    /* set the device image */
+    SendDlgItemMessage(hwndDlg,
+                       IDC_DEVICON,
+                       STM_SETICON,
+                       (WPARAM)dap->hDevIcon,
+                       0);
+
+    /* set the device name edit control text */
+    SetDlgItemText(hwndDlg,
+                   IDC_DEVNAME,
+                   dap->szDevName);
+
+    /* query the driver provider */
+    if (GetDriverProviderString(DeviceInfoSet,
+                                DeviceInfoData,
+                                dap->szTemp,
+                                sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
+    {
+        SetDlgItemText(hwndDlg,
+                       IDC_DRVPROVIDER,
+                       dap->szTemp);
+    }
+
+    /* query the driver date */
+    if (GetDriverDateString(DeviceInfoSet,
+                            DeviceInfoData,
+                            dap->szTemp,
+                            sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
+    {
+        SetDlgItemText(hwndDlg,
+                       IDC_DRVDATE,
+                       dap->szTemp);
+    }
+
+    /* query the driver version */
+    if (GetDriverVersionString(DeviceInfoSet,
+                               DeviceInfoData,
+                               dap->szTemp,
+                               sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
+    {
+        SetDlgItemText(hwndDlg,
+                       IDC_DRVVERSION,
+                       dap->szTemp);
+    }
+}
+
+
+static INT_PTR
+CALLBACK
+AdvProcDriverDlgProc(IN HWND hwndDlg,
+                     IN UINT uMsg,
+                     IN WPARAM wParam,
+                     IN LPARAM lParam)
+{
+    PDEVADVPROP_INFO dap;
+    INT_PTR Ret = FALSE;
+
+    dap = (PDEVADVPROP_INFO)GetWindowLongPtr(hwndDlg,
+                                             DWL_USER);
+
+    if (dap != NULL || uMsg == WM_INITDIALOG)
+    {
+        switch (uMsg)
+        {
+            case WM_COMMAND:
+            {
+                switch (LOWORD(wParam))
+                {
+                    case IDC_DRIVERDETAILS:
+                    {
+                        DialogBoxParam(hDllInstance,
+                                       MAKEINTRESOURCE(IDD_DRIVERDETAILS),
+                                       hwndDlg,
+                                       DriverDetailsDlgProc,
+                                       (ULONG_PTR)dap);
+                        break;
+                    }
+                }
+                break;
+            }
+
+            case WM_NOTIFY:
+            {
+                NMHDR *hdr = (NMHDR*)lParam;
+                switch (hdr->code)
+                {
+                    case PSN_APPLY:
+                        break;
+                }
+                break;
+            }
+
+            case WM_INITDIALOG:
+            {
+                dap = (PDEVADVPROP_INFO)((LPPROPSHEETPAGE)lParam)->lParam;
+                if (dap != NULL)
+                {
+                    SetWindowLongPtr(hwndDlg,
+                                     DWL_USER,
+                                     (DWORD_PTR)dap);
+
+                    UpdateDriverDlg(hwndDlg,
+                                    dap);
+                }
+                Ret = TRUE;
+                break;
+            }
+        }
+    }
+
+    return Ret;
+}
+
+
 static VOID
 InitDevUsageActions(IN HWND hwndDlg,
                     IN HWND hComboBox,
@@ -63,14 +418,10 @@ InitDevUsageActions(IN HWND hwndDlg,
 {
     INT Index;
     UINT i;
-    struct
+    UINT Actions[] =
     {
-        UINT szId;
-        DEVENABLEACTION Action;
-    } Actions[] =
-    {
-        {IDS_ENABLEDEVICE, DEA_ENABLE},
-        {IDS_DISABLEDEVICE, DEA_DISABLE},
+        IDS_ENABLEDEVICE,
+        IDS_DISABLEDEVICE,
     };
 
     for (i = 0;
@@ -79,7 +430,7 @@ InitDevUsageActions(IN HWND hwndDlg,
     {
         /* fill in the device usage combo box */
         if (LoadString(hDllInstance,
-                       Actions[i].szId,
+                       Actions[i],
                        dap->szTemp,
                        sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
         {
@@ -92,12 +443,12 @@ InitDevUsageActions(IN HWND hwndDlg,
                 SendMessage(hComboBox,
                             CB_SETITEMDATA,
                             (WPARAM)Index,
-                            (LPARAM)Actions[i].Action);
+                            (LPARAM)Actions[i]);
 
-                switch (Actions[i].Action)
+                switch (Actions[i])
                 {
-                    case DEA_ENABLE:
-                        if (dap->DeviceEnabled)
+                    case IDS_ENABLEDEVICE:
+                        if (dap->DeviceStarted)
                         {
                             SendMessage(hComboBox,
                                         CB_SETCURSEL,
@@ -106,8 +457,8 @@ InitDevUsageActions(IN HWND hwndDlg,
                         }
                         break;
 
-                    case DEA_DISABLE:
-                        if (!dap->DeviceEnabled)
+                    case IDS_DISABLEDEVICE:
+                        if (!dap->DeviceStarted)
                         {
                             SendMessage(hComboBox,
                                         CB_SETCURSEL,
@@ -125,11 +476,11 @@ InitDevUsageActions(IN HWND hwndDlg,
 }
 
 
-static DEVENABLEACTION
+static UINT
 GetSelectedUsageAction(IN HWND hComboBox)
 {
     INT Index;
-    DEVENABLEACTION Ret = DEA_UNKNOWN;
+    UINT Ret = 0;
 
     Index = (INT)SendMessage(hComboBox,
                              CB_GETCURSEL,
@@ -141,9 +492,9 @@ GetSelectedUsageAction(IN HWND hComboBox)
                                CB_GETITEMDATA,
                                (WPARAM)Index,
                                0);
-        if (iRet != CB_ERR && iRet < (INT)DEA_UNKNOWN)
+        if (iRet != CB_ERR)
         {
-            Ret = (DEVENABLEACTION)iRet;
+            Ret = (UINT)iRet;
         }
     }
 
@@ -151,29 +502,40 @@ GetSelectedUsageAction(IN HWND hComboBox)
 }
 
 
-static VOID
+static BOOL
 ApplyGeneralSettings(IN HWND hwndDlg,
                      IN PDEVADVPROP_INFO dap)
 {
-    DEVENABLEACTION SelectedUsageAction;
+    BOOL Ret = FALSE;
 
-    SelectedUsageAction = GetSelectedUsageAction(GetDlgItem(hwndDlg,
-                                                            IDC_DEVUSAGE));
-    if (SelectedUsageAction != DEA_UNKNOWN)
+    if (dap->DeviceUsageChanged && dap->IsAdmin && dap->CanDisable)
     {
+        UINT SelectedUsageAction;
+        BOOL NeedReboot = FALSE;
+
+        SelectedUsageAction = GetSelectedUsageAction(GetDlgItem(hwndDlg,
+                                                                IDC_DEVUSAGE));
         switch (SelectedUsageAction)
         {
-            case DEA_ENABLE:
-                if (!dap->DeviceEnabled)
+            case IDS_ENABLEDEVICE:
+                if (!dap->DeviceStarted)
                 {
-                    /* FIXME - enable device */
+                    Ret = EnableDevice(dap->DeviceInfoSet,
+                                       &dap->DeviceInfoData,
+                                       TRUE,
+                                       0,
+                                       &NeedReboot);
                 }
                 break;
 
-            case DEA_DISABLE:
-                if (dap->DeviceEnabled)
+            case IDS_DISABLEDEVICE:
+                if (dap->DeviceStarted)
                 {
-                    /* FIXME - disable device */
+                    Ret = EnableDevice(dap->DeviceInfoSet,
+                                       &dap->DeviceInfoData,
+                                       FALSE,
+                                       0,
+                                       &NeedReboot);
                 }
                 break;
 
@@ -181,60 +543,212 @@ ApplyGeneralSettings(IN HWND hwndDlg,
                 break;
         }
 
-        /* disable the apply button */
-        PropSheet_UnChanged(GetParent(hwndDlg),
-                            hwndDlg);
+        if (Ret)
+        {
+            if (NeedReboot)
+            {
+                /* make PropertySheet() return PSM_REBOOTSYSTEM */
+                PropSheet_RebootSystem(hwndDlg);
+            }
+        }
+        else
+        {
+            /* FIXME - display an error message */
+            DPRINT1("Failed to enable/disable device! LastError: %d\n",
+                    GetLastError());
+        }
     }
+    else
+        Ret = !dap->DeviceUsageChanged;
+
+    /* disable the apply button */
+    PropSheet_UnChanged(GetParent(hwndDlg),
+                        hwndDlg);
+    dap->DeviceUsageChanged = FALSE;
+    return Ret;
 }
 
 
 static VOID
 UpdateDevInfo(IN HWND hwndDlg,
               IN PDEVADVPROP_INFO dap,
-              IN BOOL ReOpenDevice)
+              IN BOOL ReOpen)
 {
-    HICON hIcon;
-    HWND hDevUsage;
+    HWND hDevUsage, hPropSheetDlg, hDevProbBtn;
+    CONFIGRET cr;
+    ULONG Status, ProblemNumber;
+    SP_DEVINSTALL_PARAMS_W InstallParams;
+    UINT TroubleShootStrId = IDS_TROUBLESHOOTDEV;
+    BOOL bFlag, bDevActionAvailable = TRUE;
+    BOOL bDrvInstalled = FALSE;
+    DWORD iPage;
+    HDEVINFO DeviceInfoSet = NULL;
+    PSP_DEVINFO_DATA DeviceInfoData = NULL;
+    PROPSHEETHEADER psh;
+    DWORD nDriverPages = 0;
+
+    hPropSheetDlg = GetParent(hwndDlg);
+
+    if (dap->PageInitialized)
+    {
+        /* switch to the General page */
+        PropSheet_SetCurSelByID(hPropSheetDlg,
+                                IDD_DEVICEGENERAL);
 
-    if (ReOpenDevice)
+        /* remove and destroy the existing device property sheet pages */
+        if (dap->DevPropSheets != NULL)
+        {
+            for (iPage = 0;
+                 iPage != dap->nDevPropSheets;
+                 iPage++)
+            {
+                if (dap->DevPropSheets[iPage] != NULL)
+                {
+                    PropSheet_RemovePage(hPropSheetDlg,
+                                         -1,
+                                         dap->DevPropSheets[iPage]);
+                }
+            }
+        }
+    }
+
+    iPage = 0;
+
+    if (dap->FreeDevPropSheets)
     {
-        /* 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);
+        /* 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;
+
+    if (ReOpen)
+    {
+        /* create a new device info set and re-open the device */
+        if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+        {
+            SetupDiDestroyDeviceInfoList(dap->CurrentDeviceInfoSet);
+        }
+
+        dap->ParentDevInst = 0;
+        dap->CurrentDeviceInfoSet = SetupDiCreateDeviceInfoListEx(NULL,
+                                                                  hwndDlg,
+                                                                  dap->lpMachineName,
+                                                                  NULL);
+        if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+        {
+            if (SetupDiOpenDeviceInfo(dap->CurrentDeviceInfoSet,
+                                      dap->szDeviceID,
+                                      hwndDlg,
+                                      0,
+                                      &dap->CurrentDeviceInfoData))
+            {
+                if (dap->CloseDevInst)
+                {
+                    SetupDiDestroyDeviceInfoList(dap->DeviceInfoSet);
+                }
+
+                dap->CloseDevInst = TRUE;
+                dap->DeviceInfoSet = dap->CurrentDeviceInfoSet;
+                dap->DeviceInfoData = dap->CurrentDeviceInfoData;
+                dap->CurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
+            }
+            else
+                goto GetParentNode;
+        }
+        else
+        {
+GetParentNode:
+            /* get the parent node from the initial devinst */
+            CM_Get_Parent_Ex(&dap->ParentDevInst,
+                             dap->DeviceInfoData.DevInst,
+                             0,
+                             dap->hMachine);
+        }
+
+        if (dap->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+        {
+            DeviceInfoSet = dap->CurrentDeviceInfoSet;
+            DeviceInfoData = &dap->CurrentDeviceInfoData;
+        }
+        else
+        {
+            DeviceInfoSet = dap->DeviceInfoSet;
+            DeviceInfoData = &dap->DeviceInfoData;
+        }
+    }
+    else
+    {
+        DeviceInfoSet = dap->DeviceInfoSet;
+        DeviceInfoData = &dap->DeviceInfoData;
+    }
+
+    dap->HasDriverPage = FALSE;
+    dap->HasResourcePage = FALSE;
+    dap->HasPowerPage = FALSE;
+    if (IsDriverInstalled(DeviceInfoData->DevInst,
+                          dap->hMachine,
+                          &bDrvInstalled) &&
+        bDrvInstalled)
+    {
+        if (SetupDiCallClassInstaller((dap->ShowRemotePages ?
+                                           DIF_ADDREMOTEPROPERTYPAGE_ADVANCED :
+                                           DIF_ADDPROPERTYPAGE_ADVANCED),
+                                      DeviceInfoSet,
+                                      DeviceInfoData))
+        {
+            /* get install params */
+            InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+            if (!SetupDiGetDeviceInstallParamsW(DeviceInfoSet,
+                                                DeviceInfoData,
+                                                &InstallParams))
+            {
+                /* zero the flags */
+                InstallParams.Flags = 0;
+            }
+
+            dap->HasDriverPage = !(InstallParams.Flags & DI_DRIVERPAGE_ADDED);
+            dap->HasResourcePage = !(InstallParams.Flags & DI_RESOURCEPAGE_ADDED);
+            dap->HasPowerPage = !(InstallParams.Flags & DI_FLAGSEX_POWERPAGE_ADDED);
+        }
+    }
+
+    /* get the device icon */
+    if (dap->hDevIcon != NULL)
+    {
+        DestroyIcon(dap->hDevIcon);
+        dap->hDevIcon = NULL;
+    }
+    if (!SetupDiLoadClassIcon(&DeviceInfoData->ClassGuid,
+                              &dap->hDevIcon,
+                              NULL))
+    {
+        dap->hDevIcon = NULL;
     }
 
     /* get the device name */
-    if (GetDeviceDescriptionString(dap->DeviceInfoSet,
-                                   dap->DeviceInfoData,
+    if (GetDeviceDescriptionString(DeviceInfoSet,
+                                   DeviceInfoData,
                                    dap->szDevName,
                                    sizeof(dap->szDevName) / sizeof(dap->szDevName[0])))
     {
-        PropSheet_SetTitle(GetParent(hwndDlg),
+        PropSheet_SetTitle(hPropSheetDlg,
                            PSH_PROPTITLE,
                            dap->szDevName);
     }
 
     /* set the device image */
-    if (SetupDiLoadClassIcon(&dap->DeviceInfoData->ClassGuid,
-                             &hIcon,
-                             NULL))
-    {
-        HICON hOldIcon = (HICON)SendDlgItemMessage(hwndDlg,
-                                                   IDC_DEVICON,
-                                                   STM_SETICON,
-                                                   (WPARAM)hIcon,
-                                                   0);
-        if (hOldIcon != NULL)
-        {
-            DestroyIcon(hOldIcon);
-        }
-    }
+    SendDlgItemMessage(hwndDlg,
+                       IDC_DEVICON,
+                       STM_SETICON,
+                       (WPARAM)dap->hDevIcon,
+                       0);
 
     /* set the device name edit control text */
     SetDlgItemText(hwndDlg,
@@ -242,7 +756,7 @@ UpdateDevInfo(IN HWND hwndDlg,
                    dap->szDevName);
 
     /* set the device type edit control text */
-    if (GetDeviceTypeString(dap->DeviceInfoData,
+    if (GetDeviceTypeString(DeviceInfoData,
                             dap->szTemp,
                             sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -252,8 +766,8 @@ UpdateDevInfo(IN HWND hwndDlg,
     }
 
     /* set the device manufacturer edit control text */
-    if (GetDeviceManufacturerString(dap->DeviceInfoSet,
-                                    dap->DeviceInfoData,
+    if (GetDeviceManufacturerString(DeviceInfoSet,
+                                    DeviceInfoData,
                                     dap->szTemp,
                                     sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -263,7 +777,8 @@ UpdateDevInfo(IN HWND hwndDlg,
     }
 
     /* set the device location edit control text */
-    if (GetDeviceLocationString(dap->DeviceInfoData->DevInst,
+    if (GetDeviceLocationString(DeviceInfoData->DevInst,
+                                dap->ParentDevInst,
                                 dap->szTemp,
                                 sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
     {
@@ -272,41 +787,170 @@ UpdateDevInfo(IN HWND hwndDlg,
                        dap->szTemp);
     }
 
-    /* set the device status edit control text */
-    if (GetDeviceStatusString(dap->DeviceInfoData->DevInst,
-                              dap->hMachine,
-                              dap->szTemp,
-                              sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
+    /* set the device status edit control text */
+    if (GetDeviceStatusString(DeviceInfoData->DevInst,
+                              dap->hMachine,
+                              dap->szTemp,
+                              sizeof(dap->szTemp) / sizeof(dap->szTemp[0])))
+    {
+        SetDlgItemText(hwndDlg,
+                       IDC_DEVSTATUS,
+                       dap->szTemp);
+    }
+
+    /* set the device troubleshoot button text and disable it if necessary */
+    hDevProbBtn = GetDlgItem(hwndDlg,
+                             IDC_DEVPROBLEM);
+    cr = CM_Get_DevNode_Status_Ex(&Status,
+                                  &ProblemNumber,
+                                  DeviceInfoData->DevInst,
+                                  0,
+                                  dap->hMachine);
+    if (cr == CR_SUCCESS && (Status & DN_HAS_PROBLEM))
+    {
+        switch (ProblemNumber)
+        {
+            case CM_PROB_DEVLOADER_FAILED:
+            {
+                /* FIXME - only if it's not a root bus devloader,
+                           disable the button otherwise */
+                TroubleShootStrId = IDS_UPDATEDRV;
+                break;
+            }
+
+            case CM_PROB_OUT_OF_MEMORY:
+            case CM_PROB_ENTRY_IS_WRONG_TYPE:
+            case CM_PROB_LACKED_ARBITRATOR:
+            case CM_PROB_FAILED_START:
+            case CM_PROB_LIAR:
+            case CM_PROB_UNKNOWN_RESOURCE:
+            {
+                TroubleShootStrId = IDS_UPDATEDRV;
+                break;
+            }
+
+            case CM_PROB_BOOT_CONFIG_CONFLICT:
+            case CM_PROB_NORMAL_CONFLICT:
+            case CM_PROB_REENUMERATION:
+            {
+                /* FIXME - Troubleshoot conflict */
+                break;
+            }
+
+            case CM_PROB_FAILED_FILTER:
+            case CM_PROB_REINSTALL:
+            case CM_PROB_FAILED_INSTALL:
+            {
+                TroubleShootStrId = IDS_REINSTALLDRV;
+                break;
+            }
+
+            case CM_PROB_DEVLOADER_NOT_FOUND:
+            {
+                /* FIXME - 4 cases:
+                   1) if it's a missing system devloader:
+                      - disable the button (Reinstall Driver)
+                   2) if it's not a system devloader but still missing:
+                      - Reinstall Driver
+                   3) if it's not a system devloader but the file can be found:
+                      - Update Driver
+                   4) if it's a missing or empty software key
+                      - Update Driver
+                 */
+                break;
+            }
+
+            case CM_PROB_INVALID_DATA:
+            case CM_PROB_PARTIAL_LOG_CONF:
+            case CM_PROB_NO_VALID_LOG_CONF:
+            case CM_PROB_HARDWARE_DISABLED:
+            case CM_PROB_CANT_SHARE_IRQ:
+            case CM_PROB_TRANSLATION_FAILED:
+            case CM_PROB_SYSTEM_SHUTDOWN:
+            case CM_PROB_PHANTOM:
+                bDevActionAvailable = FALSE;
+                break;
+
+            case CM_PROB_NOT_VERIFIED:
+            case CM_PROB_DEVICE_NOT_THERE:
+                /* FIXME - search hardware */
+                break;
+
+            case CM_PROB_NEED_RESTART:
+            case CM_PROB_WILL_BE_REMOVED:
+            case CM_PROB_MOVED:
+            case CM_PROB_TOO_EARLY:
+            case CM_PROB_DISABLED_SERVICE:
+                TroubleShootStrId = IDS_REBOOT;
+                break;
+
+            case CM_PROB_REGISTRY:
+                /* FIXME - check registry? */
+                break;
+
+            case CM_PROB_DISABLED:
+                /* if device was disabled by the user: */
+                TroubleShootStrId = IDS_ENABLEDEV;
+                /* FIXME - otherwise disable button because the device was
+                           disabled by the system*/
+                break;
+
+            case CM_PROB_DEVLOADER_NOT_READY:
+                /* FIXME - if it's a graphics adapter:
+                           - if it's a a secondary adapter and the main adapter
+                             couldn't be found
+                             - disable  button
+                           - else
+                             - Properties
+                         - else
+                           - Update driver
+                 */
+                break;
+
+            case CM_PROB_FAILED_ADD:
+                TroubleShootStrId = IDS_PROPERTIES;
+                break;
+        }
+    }
+
+    if (LoadString(hDllInstance,
+                   TroubleShootStrId,
+                   dap->szTemp,
+                   sizeof(dap->szTemp) / sizeof(dap->szTemp[0])) != 0)
     {
-        SetDlgItemText(hwndDlg,
-                       IDC_DEVSTATUS,
-                       dap->szTemp);
+        SetWindowText(hDevProbBtn,
+                      dap->szTemp);
     }
+    EnableWindow(hDevProbBtn,
+                 dap->IsAdmin && bDevActionAvailable);
 
     /* check if the device can be enabled/disabled */
     hDevUsage = GetDlgItem(hwndDlg,
                            IDC_DEVUSAGE);
 
-    if (!CanDisableDevice(dap->DeviceInfoData->DevInst,
-                          dap->hMachine,
-                          &dap->CanDisable))
+    dap->CanDisable = FALSE;
+    dap->DeviceStarted = FALSE;
+
+    if (CanDisableDevice(DeviceInfoData->DevInst,
+                         dap->hMachine,
+                         &bFlag))
     {
-        dap->CanDisable = FALSE;
+        dap->CanDisable = bFlag;
     }
 
-    if (!IsDeviceEnabled(dap->DeviceInfoData->DevInst,
-                         dap->hMachine,
-                         &dap->DeviceEnabled))
+    if (IsDeviceStarted(DeviceInfoData->DevInst,
+                        dap->hMachine,
+                        &bFlag))
     {
-        dap->DeviceEnabled = FALSE;
+        dap->DeviceStarted = bFlag;
     }
 
     /* enable/disable the device usage controls */
     EnableWindow(GetDlgItem(hwndDlg,
                             IDC_DEVUSAGELABEL),
-                 dap->CanDisable);
+                 dap->CanDisable && dap->IsAdmin);
     EnableWindow(hDevUsage,
-                 dap->CanDisable);
+                 dap->CanDisable && dap->IsAdmin);
 
     /* clear the combobox */
     SendMessage(hDevUsage,
@@ -320,9 +964,152 @@ UpdateDevInfo(IN HWND hwndDlg,
                             dap);
     }
 
+    /* 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;
+
+    /* get the number of device property sheets for the device */
+    if (!SetupDiGetClassDevPropertySheets(DeviceInfoSet,
+                                          DeviceInfoData,
+                                          &psh,
+                                          0,
+                                          &nDriverPages,
+                                          dap->PropertySheetType) &&
+        nDriverPages != 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+    {
+        dap->nDevPropSheets += nDriverPages;
+    }
+    else
+    {
+        nDriverPages = 0;
+    }
+
+    /* include the driver page */
+    if (dap->HasDriverPage)
+        dap->nDevPropSheets++;
+
+    /* add the device property sheets */
+    if (dap->nDevPropSheets != 0)
+    {
+        dap->DevPropSheets = HeapAlloc(GetProcessHeap(),
+                                       HEAP_ZERO_MEMORY,
+                                       dap->nDevPropSheets * sizeof(HPROPSHEETPAGE));
+        if (dap->DevPropSheets != NULL)
+        {
+            if (nDriverPages != 0)
+            {
+                psh.phpage = dap->DevPropSheets;
+
+                /* query the device property sheet pages to add */
+                if (SetupDiGetClassDevPropertySheets(DeviceInfoSet,
+                                                     DeviceInfoData,
+                                                     &psh,
+                                                     dap->nDevPropSheets,
+                                                     NULL,
+                                                     dap->PropertySheetType))
+                {
+                    /* add the property sheets */
+                    for (iPage = 0;
+                         iPage != nDriverPages;
+                         iPage++)
+                    {
+                        PropSheet_AddPage(hPropSheetDlg,
+                                          dap->DevPropSheets[iPage]);
+                    }
+
+                    dap->FreeDevPropSheets = TRUE;
+                }
+                else
+                {
+                    /* cleanup, we were unable to get the device property sheets */
+                    iPage = nDriverPages;
+                    dap->nDevPropSheets -= nDriverPages;
+                    nDriverPages = 0;
+                }
+            }
+            else
+                iPage = 0;
+
+            /* add the driver page if necessary */
+            if (dap->HasDriverPage)
+            {
+                PROPSHEETPAGE pspDriver = {0};
+                pspDriver.dwSize = sizeof(PROPSHEETPAGE);
+                pspDriver.dwFlags = PSP_DEFAULT;
+                pspDriver.hInstance = hDllInstance;
+                pspDriver.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_DEVICEDRIVER);
+                pspDriver.pfnDlgProc = AdvProcDriverDlgProc;
+                pspDriver.lParam = (LPARAM)dap;
+                dap->DevPropSheets[iPage] = dap->pCreatePropertySheetPageW(&pspDriver);
+                if (dap->DevPropSheets[iPage] != NULL)
+                {
+                    if (PropSheet_AddPage(hPropSheetDlg,
+                                          dap->DevPropSheets[iPage]))
+                    {
+                        iPage++;
+                    }
+                    else
+                    {
+                        dap->pDestroyPropertySheetPage(dap->DevPropSheets[iPage]);
+                        dap->DevPropSheets[iPage] = NULL;
+                    }
+                }
+            }
+        }
+        else
+            dap->nDevPropSheets = 0;
+    }
+
     /* 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);
+    }
 }
 
 
@@ -353,6 +1140,20 @@ AdvPropGeneralDlgProc(IN HWND hwndDlg,
                         {
                             PropSheet_Changed(GetParent(hwndDlg),
                                               hwndDlg);
+                            dap->DeviceUsageChanged = TRUE;
+                        }
+                        break;
+                    }
+
+                    case IDC_DEVPROBLEM:
+                    {
+                        if (dap->IsAdmin)
+                        {
+                            /* display the device problem wizard */
+                            ShowDeviceProblemWizard(hwndDlg,
+                                                    dap->DeviceInfoSet,
+                                                    &dap->DeviceInfoData,
+                                                    dap->hMachine);
                         }
                         break;
                     }
@@ -378,13 +1179,40 @@ AdvPropGeneralDlgProc(IN HWND hwndDlg,
                 dap = (PDEVADVPROP_INFO)((LPPROPSHEETPAGE)lParam)->lParam;
                 if (dap != NULL)
                 {
+                    HWND hWndParent;
+
+                    dap->hWndGeneralPage = hwndDlg;
+
                     SetWindowLongPtr(hwndDlg,
                                      DWL_USER,
                                      (DWORD_PTR)dap);
 
-                    UpdateDevInfo(hwndDlg,
-                                  dap,
-                                  FALSE);
+                    /* 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;
+                        }
+                    }
+
+                    /* do not call UpdateDevInfo directly in here because it modifies
+                       the pages of the property sheet! */
+                    PostMessage(hwndDlg,
+                                PM_INITIALIZE,
+                                0,
+                                0);
                 }
                 Ret = TRUE;
                 break;
@@ -392,26 +1220,35 @@ AdvPropGeneralDlgProc(IN HWND hwndDlg,
 
             case WM_DEVICECHANGE:
             {
-                /* FIXME - don't call UpdateDevInfo in all events */
+                /* FIXME - don't call UpdateDevInfo for all events */
                 UpdateDevInfo(hwndDlg,
                               dap,
                               TRUE);
+                Ret = TRUE;
+                break;
+            }
+
+            case PM_INITIALIZE:
+            {
+                UpdateDevInfo(hwndDlg,
+                              dap,
+                              FALSE);
+                dap->PageInitialized = TRUE;
                 break;
             }
 
             case WM_DESTROY:
             {
-                HICON hDevIcon;
-
-                /* destroy the device icon */
-                hDevIcon = (HICON)SendDlgItemMessage(hwndDlg,
-                                                     IDC_DEVICON,
-                                                     STM_GETICON,
-                                                     0,
-                                                     0);
-                if (hDevIcon != NULL)
+                /* restore the old window proc of the subclassed parent window */
+                if (dap->hWndParent != NULL && dap->ParentOldWndProc != NULL)
                 {
-                    DestroyIcon(hDevIcon);
+                    if (SetWindowLongPtr(dap->hWndParent,
+                                         GWLP_WNDPROC,
+                                         (LONG_PTR)dap->ParentOldWndProc) == (LONG_PTR)DlgParentSubWndProc)
+                    {
+                        RemoveProp(dap->hWndParent,
+                                   L"DevMgrDevChangeSub");
+                    }
                 }
                 break;
             }
@@ -428,17 +1265,16 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
                                 IN HDEVINFO DeviceInfoSet,
                                 IN PSP_DEVINFO_DATA DeviceInfoData,
                                 IN HINSTANCE hComCtl32,
-                                IN LPCWSTR lpMachineName)
+                                IN LPCWSTR lpMachineName,
+                                IN DWORD dwFlags)
 {
     PROPSHEETHEADER psh = {0};
     PROPSHEETPAGE pspGeneral = {0};
-    DWORD nPropSheets = 0;
     PPROPERTYSHEETW pPropertySheetW;
     PCREATEPROPERTYSHEETPAGEW pCreatePropertySheetPageW;
     PDESTROYPROPERTYSHEETPAGE pDestroyPropertySheetPage;
     PDEVADVPROP_INFO DevAdvPropInfo;
-    DWORD PropertySheetType;
-    HANDLE hMachine = NULL;
+    HMACHINE hMachine = NULL;
     DWORD DevIdSize = 0;
     INT_PTR Ret = -1;
 
@@ -469,7 +1305,7 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
                                        0,
                                        &DevIdSize))
         {
-            DPRINT1("SetupDiGetDeviceInterfaceDetail unexpectedly returned TRUE!\n");
+            DPRINT1("SetupDiGetDeviceInstanceId unexpectedly returned TRUE!\n");
             return -1;
         }
 
@@ -483,7 +1319,7 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
         DevIdSize = (DWORD)wcslen(lpDeviceID) + 1;
     }
 
-    if (lpMachineName != NULL)
+    if (lpMachineName != NULL && lpMachineName[0] != L'\0')
     {
         CONFIGRET cr = CM_Connect_Machine(lpMachineName,
                                           &hMachine);
@@ -496,12 +1332,13 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
     /* 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)));
     if (DevAdvPropInfo == NULL)
     {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         goto Cleanup;
     }
 
@@ -517,43 +1354,41 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
             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->CurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
+    DevAdvPropInfo->CurrentDeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+    DevAdvPropInfo->ShowRemotePages = (lpMachineName != NULL && lpMachineName[0] != L'\0');
     DevAdvPropInfo->hMachine = hMachine;
+    DevAdvPropInfo->lpMachineName = lpMachineName;
     DevAdvPropInfo->szDevName[0] = L'\0';
+    DevAdvPropInfo->hComCtl32 = hComCtl32;
+    DevAdvPropInfo->pCreatePropertySheetPageW = pCreatePropertySheetPageW;
+    DevAdvPropInfo->pDestroyPropertySheetPage = pDestroyPropertySheetPage;
+
+    DevAdvPropInfo->IsAdmin = IsUserAdmin();
+    DevAdvPropInfo->DoDefaultDevAction = ((dwFlags & DPF_DEVICE_STATUS_ACTION) != 0);
 
     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;
-
-    /* find out how many property sheets we need */
-    if (SetupDiGetClassDevPropertySheets(DeviceInfoSet,
-                                         DeviceInfoData,
-                                         &psh,
-                                         0,
-                                         &nPropSheets,
-                                         PropertySheetType) &&
-        nPropSheets != 0)
-    {
-        DPRINT1("SetupDiGetClassDevPropertySheets unexpectedly returned TRUE!\n");
-        goto Cleanup;
-    }
-
-    if (nPropSheets != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
-    {
-        goto Cleanup;
-    }
+    DevAdvPropInfo->PropertySheetType = DevAdvPropInfo->ShowRemotePages ?
+                                            DIGCDP_FLAG_REMOTE_ADVANCED :
+                                            DIGCDP_FLAG_ADVANCED;
 
     psh.phpage = HeapAlloc(GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
-                           (nPropSheets + 1) * sizeof(HPROPSHEETPAGE));
+                           1 * sizeof(HPROPSHEETPAGE));
     if (psh.phpage == NULL)
     {
         goto Cleanup;
@@ -566,27 +1401,13 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
     pspGeneral.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_DEVICEGENERAL);
     pspGeneral.pfnDlgProc = AdvPropGeneralDlgProc;
     pspGeneral.lParam = (LPARAM)DevAdvPropInfo;
-    psh.phpage[0] = pCreatePropertySheetPageW(&pspGeneral);
-    if (psh.phpage[0] != NULL)
+    psh.phpage[psh.nPages] = pCreatePropertySheetPageW(&pspGeneral);
+    if (psh.phpage[psh.nPages] != NULL)
     {
         psh.nPages++;
     }
 
-    if (nPropSheets != 0)
-    {
-        /* create the device property sheets */
-        if (!SetupDiGetClassDevPropertySheets(DeviceInfoSet,
-                                              DeviceInfoData,
-                                              &psh,
-                                              nPropSheets + psh.nPages,
-                                              NULL,
-                                              PropertySheetType))
-        {
-            goto Cleanup;
-        }
-    }
-
-    /* FIXME - add the "Driver" property sheet if necessary */
+    DevAdvPropInfo->nDevPropSheets = psh.nPages;
 
     if (psh.nPages != 0)
     {
@@ -600,24 +1421,58 @@ DisplayDeviceAdvancedProperties(IN HWND hWndParent,
 
 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);
+        }
+
+        if (DevAdvPropInfo->CloseDevInst)
+        {
+            /* close the device info set in case a new one was created */
+            SetupDiDestroyDeviceInfoList(DevAdvPropInfo->DeviceInfoSet);
+        }
+
+        if (DevAdvPropInfo->CurrentDeviceInfoSet != INVALID_HANDLE_VALUE)
+        {
+            SetupDiDestroyDeviceInfoList(DevAdvPropInfo->CurrentDeviceInfoSet);
+        }
+
+        if (DevAdvPropInfo->hDevIcon != NULL)
+        {
+            DestroyIcon(DevAdvPropInfo->hDevIcon);
+        }
+
+        HeapFree(GetProcessHeap(),
+                 0,
+                 DevAdvPropInfo);
+    }
 
-    HeapFree(GetProcessHeap(),
-             0,
-             DevAdvPropInfo);
+    if (psh.phpage != NULL)
+    {
+        HeapFree(GetProcessHeap(),
+                 0,
+                 psh.phpage);
+    }
 
     if (hMachine != NULL)
     {
@@ -642,42 +1497,35 @@ Cleanup:
  *   lpDeviceID:    Specifies the device whose properties are to be shown
  *
  * RETURN VALUE
- *   -1: if errors occured
- *
- * REVISIONS
- *
- * NOTE
+ *   Always returns -1, a call to GetLastError returns 0 if successful
  *
  * @implemented
  */
 INT_PTR
 WINAPI
-DeviceAdvancedPropertiesW(HWND hWndParent,
-                          LPCWSTR lpMachineName,
-                          LPCWSTR lpDeviceID)
+DeviceAdvancedPropertiesW(IN HWND hWndParent  OPTIONAL,
+                          IN LPCWSTR lpMachineName  OPTIONAL,
+                          IN LPCWSTR lpDeviceID)
 {
     HDEVINFO hDevInfo;
     SP_DEVINFO_DATA DevInfoData;
     HINSTANCE hComCtl32;
     INT_PTR Ret = -1;
 
+    if (lpDeviceID == NULL)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
     /* dynamically load comctl32 */
     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);
@@ -692,7 +1540,8 @@ DeviceAdvancedPropertiesW(HWND hWndParent,
                                                       hDevInfo,
                                                       &DevInfoData,
                                                       hComCtl32,
-                                                      lpMachineName);
+                                                      lpMachineName,
+                                                      0);
             }
 
             SetupDiDestroyDeviceInfoList(hDevInfo);
@@ -719,19 +1568,15 @@ DeviceAdvancedPropertiesW(HWND hWndParent,
  *   lpDeviceID:    Specifies the device whose properties are to be shown
  *
  * RETURN VALUE
- *   -1: if errors occured
- *
- * REVISIONS
- *
- * NOTE
+ *   Always returns -1, a call to GetLastError returns 0 if successful
  *
  * @implemented
  */
 INT_PTR
 WINAPI
-DeviceAdvancedPropertiesA(HWND hWndParent,
-                          LPCSTR lpMachineName,
-                          LPCSTR lpDeviceID)
+DeviceAdvancedPropertiesA(IN HWND hWndParent  OPTIONAL,
+                          IN LPCSTR lpMachineName  OPTIONAL,
+                          IN LPCSTR lpDeviceID)
 {
     LPWSTR lpMachineNameW = NULL;
     LPWSTR lpDeviceIDW = NULL;
@@ -774,3 +1619,180 @@ Cleanup:
 
     return Ret;
 }
+
+
+/***************************************************************************
+ * NAME                                                         EXPORTED
+ *      DevicePropertiesExA
+ *
+ * DESCRIPTION
+ *   Invokes the extended device properties dialog
+ *
+ * ARGUMENTS
+ *   hWndParent:    Handle to the parent window
+ *   lpMachineName: Machine Name, NULL is the local machine
+ *   lpDeviceID:    Specifies the device whose properties are to be shown, optional if
+ *                  bShowDevMgr is nonzero
+ *   dwFlags:       This parameter can be a combination of the following flags:
+ *                  * DPF_DEVICE_STATUS_ACTION: Only valid if bShowDevMgr, causes
+ *                                              the default device status action button
+ *                                              to be clicked (Troubleshoot, Enable
+ *                                              Device, etc)
+ *   bShowDevMgr:   If non-zero it displays the device manager instead of
+ *                  the advanced device property dialog
+ *
+ * RETURN VALUE
+ *   1:  if bShowDevMgr is non-zero and no error occured
+ *   -1: a call to GetLastError returns 0 if successful
+ *
+ * @implemented
+ */
+INT_PTR
+WINAPI
+DevicePropertiesExA(IN HWND hWndParent  OPTIONAL,
+                    IN LPCSTR lpMachineName  OPTIONAL,
+                    IN LPCSTR lpDeviceID  OPTIONAL,
+                    IN DWORD dwFlags  OPTIONAL,
+                    IN BOOL bShowDevMgr)
+{
+    LPWSTR lpMachineNameW = NULL;
+    LPWSTR lpDeviceIDW = NULL;
+    INT_PTR Ret = -1;
+
+    if (lpMachineName != NULL)
+    {
+        if (!(lpMachineNameW = ConvertMultiByteToUnicode(lpMachineName,
+                                                         CP_ACP)))
+        {
+            goto Cleanup;
+        }
+    }
+    if (lpDeviceID != NULL)
+    {
+        if (!(lpDeviceIDW = ConvertMultiByteToUnicode(lpDeviceID,
+                                                      CP_ACP)))
+        {
+            goto Cleanup;
+        }
+    }
+
+    Ret = DevicePropertiesExW(hWndParent,
+                              lpMachineNameW,
+                              lpDeviceIDW,
+                              dwFlags,
+                              bShowDevMgr);
+
+Cleanup:
+    if (lpMachineNameW != NULL)
+    {
+        HeapFree(GetProcessHeap(),
+                 0,
+                 lpMachineNameW);
+    }
+    if (lpDeviceIDW != NULL)
+    {
+        HeapFree(GetProcessHeap(),
+                 0,
+                 lpDeviceIDW);
+    }
+
+    return Ret;
+}
+
+
+/***************************************************************************
+ * NAME                                                         EXPORTED
+ *      DevicePropertiesExW
+ *
+ * DESCRIPTION
+ *   Invokes the extended device properties dialog
+ *
+ * ARGUMENTS
+ *   hWndParent:    Handle to the parent window
+ *   lpMachineName: Machine Name, NULL is the local machine
+ *   lpDeviceID:    Specifies the device whose properties are to be shown, optional if
+ *                  bShowDevMgr is nonzero
+ *   dwFlags:       This parameter can be a combination of the following flags:
+ *                  * DPF_DEVICE_STATUS_ACTION: Only valid if bShowDevMgr, causes
+ *                                              the default device status action button
+ *                                              to be clicked (Troubleshoot, Enable
+ *                                              Device, etc)
+ *   bShowDevMgr:   If non-zero it displays the device manager instead of
+ *                  the advanced device property dialog
+ *
+ * RETURN VALUE
+ *   1:  if bShowDevMgr is non-zero and no error occured
+ *   -1: a call to GetLastError returns 0 if successful
+ *
+ * @unimplemented
+ */
+INT_PTR
+WINAPI
+DevicePropertiesExW(IN HWND hWndParent  OPTIONAL,
+                    IN LPCWSTR lpMachineName  OPTIONAL,
+                    IN LPCWSTR lpDeviceID  OPTIONAL,
+                    IN DWORD dwFlags  OPTIONAL,
+                    IN BOOL bShowDevMgr)
+{
+    INT_PTR Ret = -1;
+
+    if (dwFlags & ~(DPF_UNKNOWN | DPF_DEVICE_STATUS_ACTION))
+    {
+        DPRINT1("DevPropertiesExW: Invalid flags: 0x%x\n",
+                dwFlags & ~(DPF_UNKNOWN | DPF_DEVICE_STATUS_ACTION));
+        SetLastError(ERROR_INVALID_FLAGS);
+        return -1;
+    }
+
+    if (bShowDevMgr)
+    {
+        DPRINT("DevPropertiesExW doesn't support bShowDevMgr!\n");
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    }
+    else
+    {
+        HDEVINFO hDevInfo;
+        SP_DEVINFO_DATA DevInfoData;
+        HINSTANCE hComCtl32;
+
+        if (lpDeviceID == NULL)
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return -1;
+        }
+
+        /* dynamically load comctl32 */
+        hComCtl32 = LoadAndInitComctl32();
+        if (hComCtl32 != NULL)
+        {
+            hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
+                                                     hWndParent,
+                                                     lpMachineName,
+                                                     NULL);
+            if (hDevInfo != INVALID_HANDLE_VALUE)
+            {
+                DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+                if (SetupDiOpenDeviceInfo(hDevInfo,
+                                          lpDeviceID,
+                                          hWndParent,
+                                          0,
+                                          &DevInfoData))
+                {
+                    Ret = DisplayDeviceAdvancedProperties(hWndParent,
+                                                          lpDeviceID,
+                                                          hDevInfo,
+                                                          &DevInfoData,
+                                                          hComCtl32,
+                                                          lpMachineName,
+                                                          dwFlags);
+                }
+
+                SetupDiDestroyDeviceInfoList(hDevInfo);
+            }
+
+            FreeLibrary(hComCtl32);
+        }
+    }
+
+    return Ret;
+}