You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6112 lines
220 KiB
6112 lines
220 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
devwiz.c
|
|
|
|
Abstract:
|
|
|
|
Device Installer functions for install wizard support.
|
|
|
|
Author:
|
|
|
|
Lonny McMichael (lonnym) 22-Sep-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Define some macros to make the code a little cleaner.
|
|
//
|
|
|
|
//
|
|
// BOOL
|
|
// USE_CI_SELSTRINGS(
|
|
// IN PDEVINSTALL_PARAM_BLOCK p
|
|
// );
|
|
//
|
|
// This macro checks all the appropriate values to determine whether the
|
|
// class-installer provided strings may be used in the wizard.
|
|
//
|
|
#define USE_CI_SELSTRINGS(p) \
|
|
\
|
|
(((((p)->Flags) & (DI_USECI_SELECTSTRINGS | DI_CLASSINSTALLPARAMS)) == \
|
|
(DI_USECI_SELECTSTRINGS | DI_CLASSINSTALLPARAMS)) && \
|
|
(((p)->ClassInstallHeader->InstallFunction) == DIF_SELECTDEVICE))
|
|
|
|
//
|
|
// PTSTR
|
|
// GET_CI_SELSTRING(
|
|
// IN PDEVINSTALL_PARAM_BLOCK p,
|
|
// IN <FieldName> f
|
|
// );
|
|
//
|
|
// This macro retrieves a pointer to the specified string in a
|
|
// SP_SELECTDEVICE_PARAMS structure.
|
|
//
|
|
#define GET_CI_SELSTRINGS(p, f) \
|
|
\
|
|
(((PSP_SELECTDEVICE_PARAMS)((p)->ClassInstallHeader))->f)
|
|
|
|
//
|
|
// Definitions for timer used in device selection listboxes.
|
|
//
|
|
#define SELECTMFG_TIMER_ID 1
|
|
#define SELECTMFG_TIMER_DELAY 250
|
|
|
|
//
|
|
// Define a message sent from our auxilliary class driver search thread.
|
|
//
|
|
#define WMX_CLASSDRVLIST_DONE (WM_USER+131)
|
|
#define WMX_NO_DRIVERS_IN_LIST (WM_USER+132)
|
|
|
|
//
|
|
// HELP ID's
|
|
//
|
|
static const DWORD SelectDeviceShowAllHelpIDs[]=
|
|
{
|
|
IDC_NDW_PICKDEV_MFGLIST, IDH_DEVMGR_SELECTDEVICE_MANUFACTURER,
|
|
IDC_NDW_PICKDEV_ONEMFG_DRVLIST, IDH_DEVMGR_SELECTDEVICE_MODEL,
|
|
IDC_NDW_PICKDEV_DRVLIST, IDH_DEVMGR_SELECTDEVICE_MODEL,
|
|
IDC_NDW_STATUS_TEXT, IDH_NOHELP,
|
|
IDC_CLASSICON, IDH_NOHELP,
|
|
IDC_NDW_PICKDEV_COMPAT, IDH_DEVMGR_SELECTDEVICE_SHOWCOMPATIBLE,
|
|
IDC_NDW_PICKDEV_WINDOWSUPDATE, IDH_DEVMGR_SELECTDEVICE_WINDOWSUPDATE,
|
|
IDC_NDW_PICKDEV_HAVEDISK, IDH_DEVMGR_SELECTDEVICE_HAVEDISK,
|
|
IDC_NDW_TEXT, IDH_NOHELP,
|
|
IDD_DEVINSLINE, IDH_NOHELP,
|
|
0, 0
|
|
};
|
|
|
|
static const DWORD SelectDeviceShowSimilarHelpIDs[]=
|
|
{
|
|
IDC_NDW_PICKDEV_MFGLIST, IDH_DEVMGR_SELECTDEVICE_MANUFACTURER,
|
|
IDC_NDW_PICKDEV_ONEMFG_DRVLIST, IDH_DEVMGR_SELECTDEVICE_MODEL,
|
|
IDC_NDW_PICKDEV_DRVLIST, IDH_DEVMGR_SELECTDEVICE_MODEL,
|
|
IDC_NDW_STATUS_TEXT, IDH_NOHELP,
|
|
IDC_CLASSICON, IDH_NOHELP,
|
|
IDC_NDW_PICKDEV_COMPAT, IDH_DEVMGR_SELECTDEVICE_SHOWCOMPATIBLE,
|
|
IDC_NDW_PICKDEV_WINDOWSUPDATE, IDH_DEVMGR_SELECTDEVICE_WINDOWSUPDATE,
|
|
IDC_NDW_PICKDEV_HAVEDISK, IDH_DEVMGR_SELECTDEVICE_HAVEDISK,
|
|
IDC_NDW_TEXT, IDH_NOHELP,
|
|
IDD_DEVINSLINE, IDH_NOHELP,
|
|
0, 0
|
|
};
|
|
|
|
#define SELECTDEVICE_HELP TEXT("devmgr.hlp")
|
|
|
|
//
|
|
// Define structure containing class driver search context that is passed to
|
|
// an auxilliary thread while a Select Device dialog is displayed.
|
|
//
|
|
typedef struct _CLASSDRV_THREAD_CONTEXT {
|
|
|
|
HDEVINFO DeviceInfoSet;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
|
|
HWND NotificationWindow;
|
|
|
|
} CLASSDRV_THREAD_CONTEXT, *PCLASSDRV_THREAD_CONTEXT;
|
|
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
DWORD
|
|
pSetupCreateNewDevWizData(
|
|
IN PSP_INSTALLWIZARD_DATA InstallWizardData,
|
|
OUT PNEWDEVWIZ_DATA *NewDeviceWizardData
|
|
);
|
|
|
|
UINT
|
|
CALLBACK
|
|
SelectDevicePropSheetPageProc(
|
|
IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN LPPROPSHEETPAGE ppsp
|
|
);
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
SelectDeviceDlgProc(
|
|
IN HWND hwndDlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
InitSelectDeviceDlg(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
VOID
|
|
_OnSysColorChange(
|
|
HWND hWnd,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
OnSetActive(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
DWORD
|
|
HandleSelectOEM(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
DWORD
|
|
HandleWindowsUpdate(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
DWORD
|
|
FillInDeviceList(
|
|
IN HWND hwndDlg,
|
|
IN PSP_DIALOGDATA lpdd
|
|
);
|
|
|
|
VOID
|
|
ShowListForMfg(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDEVINSTALL_PARAM_BLOCK InstallParamBlock,
|
|
IN PDRIVER_NODE DriverNode, OPTIONAL
|
|
IN INT iMfg
|
|
);
|
|
|
|
VOID
|
|
LockAndShowListForMfg(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iMfg
|
|
);
|
|
|
|
PDRIVER_NODE
|
|
GetDriverNodeFromLParam(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
pSetupIsSelectedHardwareIdValid(
|
|
IN HWND hWnd,
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iCur
|
|
);
|
|
|
|
VOID
|
|
SetSelectedDriverNode(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iCur
|
|
);
|
|
|
|
BOOL
|
|
bNoDevsToShow(
|
|
IN PDEVINFO_ELEM DevInfoElem
|
|
);
|
|
|
|
PNEWDEVWIZ_DATA
|
|
GetNewDevWizDataFromPsPage(
|
|
LPPROPSHEETPAGE ppsp
|
|
);
|
|
|
|
LONG
|
|
GetCurDesc(
|
|
IN PSP_DIALOGDATA lpdd
|
|
);
|
|
|
|
VOID
|
|
OnCancel(
|
|
IN PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
VOID
|
|
__cdecl
|
|
ClassDriverSearchThread(
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOL
|
|
pSetupIsClassDriverListBuilt(
|
|
IN PSP_DIALOGDATA lpdd
|
|
);
|
|
|
|
VOID
|
|
pSetupDevInfoDataFromDialogData(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData
|
|
);
|
|
|
|
VOID
|
|
ToggleDialogControls(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData,
|
|
IN BOOL Enable
|
|
);
|
|
|
|
void
|
|
CleanupDriverLists(
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
);
|
|
|
|
BOOL
|
|
CDMIsInternetAvailable(
|
|
void
|
|
);
|
|
|
|
|
|
HPROPSHEETPAGE
|
|
WINAPI
|
|
SetupDiGetWizardPage(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
|
|
IN PSP_INSTALLWIZARD_DATA InstallWizardData,
|
|
IN DWORD PageType,
|
|
IN DWORD Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves a handle to one of the Setup API-provided wizard
|
|
pages, for an application to include in its own wizard.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the handle of the device information set to
|
|
retrieve a wizard page for.
|
|
|
|
DeviceInfoData - Optionally, supplies the address of a device information
|
|
element with which the wizard page will be associated. This parameter
|
|
is only used if the flags parameter includes DIWP_FLAG_USE_DEVINFO_DATA.
|
|
If that flag is set, and if this parameter is not specified, then the
|
|
wizard page will be associated with the global class driver list.
|
|
|
|
InstallWizardData - Supplies the address of a PSP_INSTALLWIZARD_DATA
|
|
structure containing parameters to be used by this wizard page. The
|
|
cbSize field must be set to the size of the structure, in bytes, or the
|
|
structure will be considered invalid.
|
|
|
|
PageType - Supplies an ordinal indicating the type of wizard page to be
|
|
retreived. May be one of the following values:
|
|
|
|
SPWPT_SELECTDEVICE - Retrieve a select device wizard page.
|
|
|
|
Flags - Supplies flags that specify how the wizard page is to be created.
|
|
May be a combination of the following values:
|
|
|
|
SPWP_USE_DEVINFO_DATA - Use the device information element specified
|
|
by DeviceInfoData, or use the global class
|
|
driver list if DeviceInfoData is not supplied.
|
|
If this flag is not supplied, the wizard page
|
|
will act upon the currently selected device
|
|
(as selected by SetupDiSetSelectedDevice), or
|
|
upon the global class driver list if no device
|
|
is selected.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is the handle to the requested
|
|
wizard page.
|
|
|
|
If the function fails, the return value is NULL. To get extended error
|
|
information, call GetLastError.
|
|
|
|
Remarks:
|
|
|
|
A device information set may not be destroyed as long as there are any
|
|
active wizard pages using it. In addition, if the wizard page is
|
|
associated with a particular device information element, then that element
|
|
will not be deletable as long as it is being used by a wizard page.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
DWORD Err = NO_ERROR;
|
|
HPROPSHEETPAGE hPage = NULL;
|
|
PNEWDEVWIZ_DATA ndwData = NULL;
|
|
PWIZPAGE_OBJECT WizPageObject = NULL;
|
|
//
|
|
// Store the address of the corresponding wizard object at the
|
|
// end of the PROPSHEETPAGE buffer.
|
|
//
|
|
BYTE pspBuffer[sizeof(PROPSHEETPAGE) + sizeof(PVOID)];
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)pspBuffer;
|
|
|
|
try {
|
|
//
|
|
// Make sure we're running interactively.
|
|
//
|
|
if(GlobalSetupFlags & (PSPGF_NONINTERACTIVE|PSPGF_UNATTENDED_SETUP)) {
|
|
Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
|
|
leave;
|
|
}
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
Err = ERROR_INVALID_HANDLE;
|
|
leave;
|
|
}
|
|
|
|
switch(PageType) {
|
|
|
|
case SPWPT_SELECTDEVICE :
|
|
|
|
Page->pszTemplate = MAKEINTRESOURCE(IDD_DYNAWIZ_SELECTDEV_PAGE);
|
|
Page->pfnDlgProc = SelectDeviceDlgProc;
|
|
Page->pfnCallback = SelectDevicePropSheetPageProc;
|
|
|
|
Page->pszHeaderTitle = MAKEINTRESOURCE(IDS_NDW_SELECTDEVICE);
|
|
Page->pszHeaderSubTitle = NULL;
|
|
|
|
break;
|
|
|
|
default :
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Validate the supplied InstallWizardData structure, and create a
|
|
// private storage buffer for internal use by the wizard page.
|
|
//
|
|
if((Err = pSetupCreateNewDevWizData(InstallWizardData, &ndwData)) != NO_ERROR) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Store the device information set handle in the dialogdata structure
|
|
// embedded in the New Device Wizard buffer.
|
|
//
|
|
ndwData->ddData.DevInfoSet = DeviceInfoSet;
|
|
|
|
//
|
|
// If the caller specified the SPWP_USE_DEVINFO_DATA flag, then store information
|
|
// in the dialog data structure about the specified devinfo element (if supplied).
|
|
//
|
|
if(Flags & SPWP_USE_DEVINFO_DATA) {
|
|
if(DeviceInfoData) {
|
|
//
|
|
// Verify that the specified device information element is a valid one.
|
|
//
|
|
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL))) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
|
|
} else if(DevInfoElem->DiElemFlags & DIE_IS_LOCKED) {
|
|
//
|
|
// Device information element cannot be explicitly used by more than
|
|
// one wizard page at a time.
|
|
//
|
|
Err = ERROR_DEVINFO_DATA_LOCKED;
|
|
leave;
|
|
}
|
|
|
|
DevInfoElem->DiElemFlags |= DIE_IS_LOCKED;
|
|
ndwData->ddData.DevInfoElem = DevInfoElem;
|
|
}
|
|
ndwData->ddData.flags = DD_FLAG_USE_DEVINFO_ELEM;
|
|
}
|
|
|
|
//
|
|
// We've successfully created and initialized the devwiz data structure.
|
|
// Now create a wizpage object so we can keep track of it.
|
|
//
|
|
if(WizPageObject = MyMalloc(sizeof(WIZPAGE_OBJECT))) {
|
|
WizPageObject->RefCount = 0;
|
|
WizPageObject->ndwData = ndwData;
|
|
//
|
|
// Insert this new object into the devinfo set's wizard object list.
|
|
//
|
|
WizPageObject->Next = pDeviceInfoSet->WizPageList;
|
|
pDeviceInfoSet->WizPageList = WizPageObject;
|
|
|
|
} else {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
leave;
|
|
}
|
|
|
|
Page->dwSize = sizeof(pspBuffer);
|
|
|
|
Page->dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | PSP_USECALLBACK | PSP_USEFUSIONCONTEXT;
|
|
Page->hActCtx = NULL;
|
|
|
|
Page->hInstance = MyDllModuleHandle;
|
|
|
|
Page->lParam = (LPARAM)DeviceInfoSet;
|
|
|
|
*((PVOID *)(&(pspBuffer[sizeof(PROPSHEETPAGE)]))) = WizPageObject;
|
|
|
|
if(!(hPage = CreatePropertySheetPage(Page))) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
if(Err != NO_ERROR) {
|
|
if(ndwData) {
|
|
MyFree(ndwData);
|
|
}
|
|
if(WizPageObject) {
|
|
MyFree(WizPageObject);
|
|
}
|
|
}
|
|
|
|
SetLastError(Err);
|
|
return hPage;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupDiGetSelectedDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the currently-selected device for the specified
|
|
device information set. This is typically used during an installation
|
|
wizard.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a handle to the device information set for
|
|
which the selected device is to be retrieved.
|
|
|
|
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure
|
|
that receives the currently-selected device. If there is no device
|
|
currently selected, then the routine will fail, and GetLastError
|
|
will return ERROR_NO_DEVICE_SELECTED.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE.
|
|
|
|
If the function fails, the return value is FALSE. To get extended error
|
|
information, call GetLastError.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
|
|
DWORD Err = NO_ERROR;
|
|
|
|
try {
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
Err = ERROR_INVALID_HANDLE;
|
|
leave;
|
|
}
|
|
|
|
if(pDeviceInfoSet->SelectedDevInfoElem) {
|
|
|
|
if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
pDeviceInfoSet->SelectedDevInfoElem,
|
|
DeviceInfoData))) {
|
|
Err = ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
} else {
|
|
Err = ERROR_NO_DEVICE_SELECTED;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
SetLastError(Err);
|
|
return (Err == NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupDiSetSelectedDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the specified device information element to be the
|
|
currently selected member of a device information set. This is typically
|
|
used during an installation wizard.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a handle to the device information set for
|
|
which the selected device is to be set.
|
|
|
|
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure
|
|
specifying the device information element to be selected.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE.
|
|
|
|
If the function fails, the return value is FALSE. To get extended error
|
|
information, call GetLastError.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
|
|
DWORD Err = NO_ERROR;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
|
|
try {
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
Err = ERROR_INVALID_HANDLE;
|
|
leave;
|
|
}
|
|
|
|
DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL
|
|
);
|
|
if(DevInfoElem) {
|
|
pDeviceInfoSet->SelectedDevInfoElem = DevInfoElem;
|
|
} else {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
SetLastError(Err);
|
|
return (Err == NO_ERROR);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupCreateNewDevWizData(
|
|
IN PSP_INSTALLWIZARD_DATA InstallWizardData,
|
|
OUT PNEWDEVWIZ_DATA *NewDeviceWizardData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates an InstallWizardData buffer, then allocates and
|
|
fills in a NEWDEVWIZ_DATA buffer based on information supplied therein.
|
|
|
|
Arguments:
|
|
|
|
InstallWizardData - Supplies the address of an installation wizard data
|
|
structure to be validated and used in building the private buffer.
|
|
|
|
NewDeviceWizardData - Supplies the address of a variable that receives a
|
|
pointer to the newly-allocated install wizard data buffer. This buffer
|
|
will not be modified unless the NEWDEVWIZ_DATA buffer was successfully
|
|
built.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR, otherwise, it is
|
|
a Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PNEWDEVWIZ_DATA ndwData = NULL;
|
|
DWORD Err = NO_ERROR;
|
|
|
|
if((InstallWizardData->ClassInstallHeader.cbSize != sizeof(SP_CLASSINSTALL_HEADER)) ||
|
|
(InstallWizardData->ClassInstallHeader.InstallFunction != DIF_INSTALLWIZARD)) {
|
|
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// The dynamic page entries are currently ignored, as are the Private
|
|
// fields. Also, the hwndWizardDlg is not validated.
|
|
//
|
|
|
|
try {
|
|
|
|
if(ndwData = MyMalloc(sizeof(NEWDEVWIZ_DATA))) {
|
|
ZeroMemory(ndwData, sizeof(NEWDEVWIZ_DATA));
|
|
} else {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Initialize the Current Description string table index in the dialog
|
|
// data to -1, so that it will get updated when the wizard page is
|
|
// first entered.
|
|
//
|
|
ndwData->ddData.iCurDesc = -1;
|
|
|
|
//
|
|
// Copy the installwizard data.
|
|
//
|
|
CopyMemory(&(ndwData->InstallData),
|
|
InstallWizardData,
|
|
sizeof(SP_INSTALLWIZARD_DATA)
|
|
);
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if((Err != NO_ERROR) && ndwData) {
|
|
MyFree(ndwData);
|
|
} else {
|
|
*NewDeviceWizardData = ndwData;
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
UINT
|
|
CALLBACK
|
|
SelectDevicePropSheetPageProc(
|
|
IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN LPPROPSHEETPAGE ppsp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the Select Device wizard page is created or
|
|
destroyed.
|
|
|
|
Arguments:
|
|
|
|
hwnd - Reserved
|
|
|
|
uMsg - Action flag, either PSPCB_CREATE or PSPCB_RELEASE
|
|
|
|
ppsp - Supplies the address of the PROPSHEETPAGE structure being created or
|
|
destroyed.
|
|
|
|
Return Value:
|
|
|
|
If uMsg is PSPCB_CREATE, then return non-zero to allow the page to be
|
|
created, or zero to prevent it.
|
|
|
|
if uMsg is PSPCB_RELEASE, the return value is ignored.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
UINT ret;
|
|
PVOID WizObjectId;
|
|
PWIZPAGE_OBJECT CurWizObject, PrevWizObject;
|
|
|
|
//
|
|
// Access the device info set handle stored in the propsheetpage's lParam.
|
|
//
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet((HDEVINFO)(ppsp->lParam)))) {
|
|
return FALSE;
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
try {
|
|
//
|
|
// The ObjectID (pointer, actually) for the corresponding wizard
|
|
// object for this page is stored at the end of the ppsp structure.
|
|
// Retrieve this now, and look for it in the devinfo set's list of
|
|
// wizard objects.
|
|
//
|
|
WizObjectId = *((PVOID *)(&(((PBYTE)ppsp)[sizeof(PROPSHEETPAGE)])));
|
|
|
|
for(CurWizObject = pDeviceInfoSet->WizPageList, PrevWizObject = NULL;
|
|
CurWizObject;
|
|
PrevWizObject = CurWizObject, CurWizObject = CurWizObject->Next) {
|
|
|
|
if(WizObjectId == CurWizObject) {
|
|
//
|
|
// We found our object.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!CurWizObject) {
|
|
ret = FALSE;
|
|
leave;
|
|
}
|
|
|
|
switch(uMsg) {
|
|
|
|
case PSPCB_CREATE :
|
|
//
|
|
// Fail the create if we've already been created once
|
|
// (hopefully, this will never happen).
|
|
//
|
|
if(CurWizObject->RefCount) {
|
|
ret = FALSE;
|
|
leave;
|
|
} else {
|
|
CurWizObject->RefCount++;
|
|
}
|
|
break;
|
|
|
|
case PSPCB_RELEASE :
|
|
//
|
|
// Decrement the wizard object refcount. If it goes to zero
|
|
// (or if it already was zero because we never got a
|
|
// PSPCB_CREATE message), then remove the object from the
|
|
// linked list, and free all associated memory.
|
|
//
|
|
if(CurWizObject->RefCount) {
|
|
CurWizObject->RefCount--;
|
|
}
|
|
|
|
MYASSERT(!CurWizObject->RefCount);
|
|
|
|
if(!CurWizObject->RefCount) {
|
|
//
|
|
// Remove the object from the object list.
|
|
//
|
|
if(PrevWizObject) {
|
|
PrevWizObject->Next = CurWizObject->Next;
|
|
} else {
|
|
pDeviceInfoSet->WizPageList = CurWizObject->Next;
|
|
}
|
|
|
|
//
|
|
// If this wizard object was explicitly tied to a
|
|
// particular device information element, then unlock that
|
|
// element now.
|
|
//
|
|
if((CurWizObject->ndwData->ddData.flags & DD_FLAG_USE_DEVINFO_ELEM) &&
|
|
(DevInfoElem = CurWizObject->ndwData->ddData.DevInfoElem)) {
|
|
|
|
MYASSERT(DevInfoElem->DiElemFlags & DIE_IS_LOCKED);
|
|
|
|
DevInfoElem->DiElemFlags ^= DIE_IS_LOCKED;
|
|
}
|
|
|
|
MyFree(CurWizObject->ndwData);
|
|
MyFree(CurWizObject);
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
ret = FALSE;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
SelectDeviceDlgProc(
|
|
IN HWND hwndDlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dialog proc for the Select Device wizard page.
|
|
|
|
--*/
|
|
{
|
|
INT iCur;
|
|
HICON hicon;
|
|
PNEWDEVWIZ_DATA ndwData;
|
|
PSP_INSTALLWIZARD_DATA iwd;
|
|
LV_ITEM lvItem;
|
|
TCHAR TempString[LINE_LEN];
|
|
PCLASSDRV_THREAD_CONTEXT ClassDrvThreadContext;
|
|
HCURSOR hOldCursor;
|
|
OSVERSIONINFOEX osVersionInfoEx;
|
|
DWORD Err;
|
|
|
|
if(uMsg == WM_INITDIALOG) {
|
|
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)lParam;
|
|
|
|
//
|
|
// Retrieve a pointer to the device wizard data associated with
|
|
// this wizard page.
|
|
//
|
|
ndwData = GetNewDevWizDataFromPsPage(Page);
|
|
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)ndwData);
|
|
|
|
if(ndwData) {
|
|
ndwData->bInit = TRUE;
|
|
ndwData->idTimer = 0;
|
|
ndwData->bInit = FALSE;
|
|
} else {
|
|
//
|
|
// This is really bad--we can't simply call EndDialog() since we
|
|
// don't know whether we're a dialog or a wizard page. This should
|
|
// never happen.
|
|
//
|
|
return TRUE; // we didn't set the focus
|
|
}
|
|
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
//
|
|
// For the stand-alone dialog box version, we initialize here.
|
|
//
|
|
ndwData->bInit = TRUE; // Still doing some init stuff
|
|
|
|
//
|
|
// Make sure our "waiting for class list" static text control is
|
|
// hidden!
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
|
|
if(!InitSelectDeviceDlg(hwndDlg, ndwData)) {
|
|
//
|
|
// We don't have any items displayed so ask the user if they
|
|
// want to go directly to Have Disk, or just cancel.
|
|
//
|
|
PostMessage(hwndDlg, WMX_NO_DRIVERS_IN_LIST, 0, 0);
|
|
}
|
|
|
|
ndwData->bInit = FALSE; // Done with init stuff
|
|
|
|
return FALSE; // we already set the focus.
|
|
|
|
} else {
|
|
return TRUE; // we didn't set the focus
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// For the small set of messages that we get before WM_INITDIALOG, we
|
|
// won't have a devwizdata pointer!
|
|
//
|
|
if(ndwData = (PNEWDEVWIZ_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER)) {
|
|
iwd = &(ndwData->InstallData);
|
|
} else {
|
|
//
|
|
// If we haven't gotten a WM_INITDIALOG message yet, or if for some
|
|
// reason we weren't able to retrieve the ndwData pointer when we
|
|
// did, then we simply return FALSE for all messages.
|
|
//
|
|
// (If we ever need to process messages before WM_INITDIALOG (e.g.,
|
|
// set font), then we'll need to modify this approach.)
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch(uMsg) {
|
|
|
|
case WMX_CLASSDRVLIST_DONE :
|
|
|
|
MYASSERT(ndwData->ddData.AuxThreadRunning);
|
|
ndwData->ddData.AuxThreadRunning = FALSE;
|
|
|
|
//
|
|
// wParam is a boolean indicating the result of the class driver
|
|
// search.
|
|
//
|
|
// lParam is NO_ERROR upon success, or a Win32 error code
|
|
// indicating cause of failure.
|
|
//
|
|
switch(ndwData->ddData.PendingAction) {
|
|
|
|
case PENDING_ACTION_NONE :
|
|
//
|
|
// Then the thread has completed, but the user is still
|
|
// mulling over the choices on the compatible driver list.
|
|
// If the class driver list was successfully built, then
|
|
// there's nothing to do here. If it failed for some
|
|
// reason (highly unlikely), then we (silently) disable the
|
|
// 'show compatible devices' check box.
|
|
if(!wParam) {
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), FALSE);
|
|
}
|
|
break;
|
|
|
|
case PENDING_ACTION_SELDONE :
|
|
//
|
|
// In this case, we don't care what happened in the other
|
|
// thread. The user has made their selection, and we're
|
|
// ready to return success.
|
|
//
|
|
SetSelectedDriverNode(&(ndwData->ddData),
|
|
ndwData->ddData.CurSelectionForSuccess
|
|
);
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
break;
|
|
|
|
case PENDING_ACTION_SHOWCLASS :
|
|
//
|
|
// Then we've been waiting on the class driver search to
|
|
// complete, so that we can show the list. Hopefully, the
|
|
// search was successful. If not, we'll give the user a
|
|
// popup saying that the list could not be shown, and then
|
|
// leave them in the compatible list view (with the class
|
|
// list radio button now disabled).
|
|
//
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
|
|
if(wParam) {
|
|
//
|
|
// The class driver list was built successfully.
|
|
//
|
|
if(ndwData->ddData.CurSelectionForSuccess != LB_ERR) {
|
|
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = ndwData->ddData.CurSelectionForSuccess;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.pszText = TempString;
|
|
lvItem.cchTextMax = SIZECHARS(TempString);
|
|
|
|
if(ListView_GetItem((ndwData->ddData).hwndDrvList, &lvItem)) {
|
|
//
|
|
// Now retrieve the (case-insensitive) string
|
|
// ID of this string, and store it as the
|
|
// current description ID.
|
|
//
|
|
(ndwData->ddData).iCurDesc = LookUpStringInDevInfoSet(
|
|
(ndwData->ddData).DevInfoSet,
|
|
TempString,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
|
|
if(FillInDeviceList(hwndDlg, &(ndwData->ddData)) == NO_ERROR) {
|
|
//
|
|
// Enable the OK/Next button
|
|
//
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list, and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Inform the user that the class driver search failed.
|
|
//
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_SELECT_DEVICE,
|
|
TempString,
|
|
SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
|
|
FormatMessageBox(MyDllModuleHandle,
|
|
hwndDlg,
|
|
MSG_NO_CLASSDRVLIST_ERROR,
|
|
TempString,
|
|
MB_OK | MB_TASKMODAL
|
|
);
|
|
|
|
//
|
|
// Check the 'Show compatible devices' check box and then
|
|
// gray it out since the user cannot uncheck it since we
|
|
// don't have a class list.
|
|
//
|
|
CheckDlgButton(hwndDlg, IDC_NDW_PICKDEV_COMPAT, BST_CHECKED);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), FALSE);
|
|
ndwData->ddData.bShowCompat = TRUE;
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
|
|
//
|
|
// We also must unhide the compatible driver list controls,
|
|
// and re-enable the OK button.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list, and are in
|
|
// express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PENDING_ACTION_CANCEL :
|
|
//
|
|
// This is an easy one. No matter what happened in the
|
|
// other thread, we simply want to clean up and return.
|
|
//
|
|
OnCancel(ndwData);
|
|
EndDialog(hwndDlg, ERROR_CANCELLED);
|
|
break;
|
|
|
|
case PENDING_ACTION_OEM :
|
|
case PENDING_ACTION_WINDOWSUPDATE:
|
|
|
|
if(ndwData->ddData.PendingAction == PENDING_ACTION_OEM) {
|
|
|
|
Err = HandleSelectOEM(hwndDlg, ndwData);
|
|
|
|
if (Err == NO_ERROR) {
|
|
//
|
|
// The class installer picked a driver for the
|
|
// user automatically, so just end the dialog or
|
|
// go to the next wizard page.
|
|
//
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
} else {
|
|
iwd->Flags |= NDW_INSTALLFLAG_CI_PICKED_OEM;
|
|
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
|
|
}
|
|
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// Call HandleWindowsUpdate. If this API succeeds
|
|
// then it will fill in the existing list control with
|
|
// the Windows Update drivers.
|
|
//
|
|
Err = HandleWindowsUpdate(hwndDlg, ndwData);
|
|
}
|
|
|
|
ToggleDialogControls(hwndDlg, ndwData, TRUE);
|
|
|
|
if ((Err == NO_ERROR) ||
|
|
(Err == ERROR_DI_DO_DEFAULT)) {
|
|
//
|
|
// If HandleSelectOEM or HandleWindowsUpdate returned
|
|
// NO_ERROR or ERROR_DI_DO_DEFAULT then they have
|
|
// updated the existing listbox of drivers, so there
|
|
// is no need to fire up the class driver list thread
|
|
// again.
|
|
//
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
} else {
|
|
//
|
|
// HandleSelectOEM or HandleWindowsUpdate returned some
|
|
// other error, so the dialog needs to be put back to
|
|
// the state it was in before the user pressed the
|
|
// button. This includes starting up the class driver
|
|
// search thread again, if needed.
|
|
//
|
|
if(ndwData->ddData.bShowCompat) {
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
} else {
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_SHOWCLASS;
|
|
}
|
|
|
|
ndwData->bInit = FALSE;
|
|
|
|
//
|
|
// We got here by aborting the class driver search.
|
|
// Since we may need it after all, we must re-start the
|
|
// search (unless the auxilliary thread happened to
|
|
// have already finished before we sent it the abort
|
|
// request).
|
|
//
|
|
if(!(ndwData->ddData.flags & DD_FLAG_CLASSLIST_FAILED) &&
|
|
!pSetupIsClassDriverListBuilt(&(ndwData->ddData)))
|
|
{
|
|
//
|
|
// Allocate a context structure to pass to the
|
|
// auxilliary thread (the auxilliary thread will
|
|
// take care of freeing the memory).
|
|
//
|
|
if(ClassDrvThreadContext = MyMalloc(sizeof(CLASSDRV_THREAD_CONTEXT))) {
|
|
|
|
try {
|
|
//
|
|
// Fill in the context structure, and fire
|
|
// off the thread.
|
|
//
|
|
ClassDrvThreadContext->DeviceInfoSet =
|
|
ndwData->ddData.DevInfoSet;
|
|
|
|
//
|
|
// SP_DEVINFO_DATA can only be retrieved
|
|
// whilst the device information set is
|
|
// locked.
|
|
//
|
|
pSetupDevInfoDataFromDialogData(
|
|
&(ndwData->ddData),
|
|
&(ClassDrvThreadContext->DeviceInfoData)
|
|
);
|
|
|
|
ClassDrvThreadContext->NotificationWindow = hwndDlg;
|
|
|
|
if(_beginthread(ClassDriverSearchThread, 0, ClassDrvThreadContext) == -1) {
|
|
MyFree(ClassDrvThreadContext);
|
|
ClassDrvThreadContext = NULL;
|
|
} else {
|
|
//
|
|
// Our class driver search context has
|
|
// been officially handed off to the
|
|
// thread we just created. Reset our
|
|
// pointer so we won't try to free this
|
|
// later.
|
|
//
|
|
ClassDrvThreadContext = NULL;
|
|
|
|
ndwData->ddData.AuxThreadRunning = TRUE;
|
|
|
|
//
|
|
// If we're currently in the class
|
|
// driver list view, then disable the
|
|
// OK/Next button, since the user can't
|
|
// select a class driver yet.
|
|
//
|
|
if(!ndwData->ddData.bShowCompat) {
|
|
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped
|
|
// the Class list, and are
|
|
// in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
|
|
if(ClassDrvThreadContext) {
|
|
MyFree(ClassDrvThreadContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!(ndwData->ddData.AuxThreadRunning)) {
|
|
//
|
|
// We couldn't start the class driver search
|
|
// thread. Check and disable the 'Show
|
|
// compatible devices' check box.
|
|
//
|
|
if(!ndwData->ddData.bShowCompat) {
|
|
CheckDlgButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_COMPAT,
|
|
BST_CHECKED
|
|
);
|
|
}
|
|
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case WMX_NO_DRIVERS_IN_LIST: {
|
|
|
|
TCHAR Title[LINE_LEN];
|
|
|
|
if(!LoadString(MyDllModuleHandle, IDS_SELECT_DEVICE, Title, SIZECHARS(Title))) {
|
|
Title[0]=TEXT('\0');
|
|
}
|
|
if(!LoadString(MyDllModuleHandle, IDS_NDW_NODRIVERS_WARNING, TempString, SIZECHARS(TempString))) {
|
|
TempString[0]=TEXT('\0');
|
|
}
|
|
|
|
if(IDOK == MessageBox(hwndDlg, TempString, Title, MB_OKCANCEL | MB_ICONEXCLAMATION)) {
|
|
PostMessage(hwndDlg, WM_COMMAND, IDC_NDW_PICKDEV_HAVEDISK, 0);
|
|
} else {
|
|
PostMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// This should never happen. But just to be on the safe side,
|
|
// if it does, we'll cancel the search. We _will not_ however,
|
|
// wait for the WMX_CLASSDRVLIST_DONE message, to signal that
|
|
// the thread has terminated. This should be OK, since the
|
|
// worst that can happen is that it will try to send a message
|
|
// to a window that no longer exists.
|
|
//
|
|
SetupDiCancelDriverInfoSearch(ndwData->ddData.DevInfoSet);
|
|
}
|
|
|
|
if(ndwData->idTimer) {
|
|
ndwData->bInit = TRUE;
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
}
|
|
|
|
if(hicon = (HICON)SendDlgItemMessage(hwndDlg, IDC_CLASSICON, STM_GETICON, 0, 0)) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
|
|
if(hicon = (HICON)SendDlgItemMessage(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON, STM_GETICON, 0, 0)) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
|
|
if(ndwData->ddData.hImageList) {
|
|
ImageList_Destroy(ndwData->ddData.hImageList);
|
|
}
|
|
|
|
if(ndwData->ddData.hFontNormal) {
|
|
DeleteObject(ndwData->ddData.hFontNormal);
|
|
}
|
|
|
|
if(ndwData->ddData.hFontBold) {
|
|
DeleteObject(ndwData->ddData.hFontBold);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch(LOWORD(wParam)) {
|
|
|
|
case IDC_NDW_PICKDEV_COMPAT:
|
|
|
|
if((HIWORD(wParam) == BN_CLICKED) &&
|
|
IsWindowVisible(GetDlgItem(hwndDlg, LOWORD(wParam)))) {
|
|
|
|
ndwData->ddData.bShowCompat = IsDlgButtonChecked(hwndDlg, IDC_NDW_PICKDEV_COMPAT);
|
|
|
|
//
|
|
// Update the current description ID in the dialog data
|
|
// so that the same device will be highlighted when we
|
|
// switch from one view to the other.
|
|
//
|
|
iCur = (int)ListView_GetNextItem((ndwData->ddData).hwndDrvList,
|
|
-1,
|
|
LVNI_SELECTED
|
|
);
|
|
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// There are two possibilities here:
|
|
//
|
|
// 1. The user was looking at the compatible driver
|
|
// list, and then decided to look at the class
|
|
// driver list, which we're not done building
|
|
// yet. In that case, hide the compatible
|
|
// driver listbox, and unhide our "waiting for
|
|
// class list" static text control.
|
|
//
|
|
// 2. The user switched to the class driver list
|
|
// view, saw that we were still working on it,
|
|
// and then decided to switch back to the
|
|
// compatible list. In that case, we simply
|
|
// need to re-hide the "waiting for class list"
|
|
// static text control, and show the compatible
|
|
// driver listbox again. In this case, we don't
|
|
// want to attempt to re-initialize the listbox,
|
|
// as that will require acquiring the HDEVINFO
|
|
// lock, and we will hang.
|
|
//
|
|
if(ndwData->ddData.bShowCompat) {
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK), SW_SHOW);
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class
|
|
// list, and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We no longer have a pending action.
|
|
//
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
|
|
} else {
|
|
//
|
|
// Temporarily hide the compatible driver
|
|
// listbox, and unhide the "waiting for class
|
|
// list" static text control.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_SHOW);
|
|
|
|
if(!LoadString(MyDllModuleHandle, IDS_NDW_RETRIEVING_LIST, TempString, SIZECHARS(TempString))) {
|
|
TempString[0]=TEXT('\0');
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_STATUS_TEXT, TempString);
|
|
|
|
//
|
|
// Disable the OK/Next button, because the user
|
|
// can't select a class driver yet.
|
|
//
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class
|
|
// list, and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
|
|
}
|
|
}
|
|
|
|
MYASSERT(ndwData->ddData.PendingAction == PENDING_ACTION_NONE);
|
|
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_SHOWCLASS;
|
|
ndwData->ddData.CurSelectionForSuccess = iCur;
|
|
}
|
|
|
|
} else {
|
|
|
|
if(iCur != LB_ERR) {
|
|
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = iCur;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.pszText = TempString;
|
|
lvItem.cchTextMax = SIZECHARS(TempString);
|
|
|
|
if(ListView_GetItem((ndwData->ddData).hwndDrvList, &lvItem)) {
|
|
//
|
|
// Now retrieve the (case-insensitive)
|
|
// string ID of this string, and store it
|
|
// as the current description ID.
|
|
//
|
|
(ndwData->ddData).iCurDesc =
|
|
LookUpStringInDevInfoSet((ndwData->ddData).DevInfoSet,
|
|
TempString,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
FillInDeviceList(hwndDlg, &(ndwData->ddData));
|
|
|
|
//
|
|
// If we just filled in the compatible driver list,
|
|
// then make sure there isn't a timer waiting to
|
|
// pounce and destroy our list!
|
|
//
|
|
if((ndwData->ddData.bShowCompat) &&
|
|
(ndwData->idTimer)) {
|
|
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_NDW_PICKDEV_HAVEDISK :
|
|
//
|
|
// If we're doing a dialog box, then pressing "Have Disk"
|
|
// will popup another Select Device dialog. Disable all
|
|
// controls on this one first, to avoid user confusion.
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, FALSE);
|
|
|
|
//
|
|
// If HandleSelectOEM returns success, we are done, and can
|
|
// either end the dialog, or proceed to the next wizard
|
|
// page.
|
|
//
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// The auxilliary thread is still running. Set our
|
|
// cursor to an hourglass, and set our pending action
|
|
// to be OEM Select while we wait for the thread to
|
|
// respond to our cancel request.
|
|
//
|
|
MYASSERT((ndwData->ddData.PendingAction == PENDING_ACTION_NONE) ||
|
|
(ndwData->ddData.PendingAction == PENDING_ACTION_SHOWCLASS));
|
|
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
try {
|
|
|
|
SetupDiCancelDriverInfoSearch(ndwData->ddData.DevInfoSet);
|
|
//
|
|
// Disable all dialog controls, so that no other
|
|
// button may be pressed until we respond to this
|
|
// pending action. Also, kill the timer, so that
|
|
// it doesn't fire in the meantime.
|
|
//
|
|
ndwData->bInit = TRUE;
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_OEM;
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
|
|
if(HandleSelectOEM(hwndDlg, ndwData) == NO_ERROR) {
|
|
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
} else {
|
|
iwd->Flags |= NDW_INSTALLFLAG_CI_PICKED_OEM;
|
|
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// The user didn't make an OEM selection, so we
|
|
// need to re-enable the controls on our dialog or
|
|
// wizard.
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, TRUE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_NDW_PICKDEV_WINDOWSUPDATE:
|
|
//
|
|
// If we're doing a dialog box, then pressing "Have Disk"
|
|
// will popup another Select Device dialog. Disable all
|
|
// controls on this one first, to avoid user confusion.
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, FALSE);
|
|
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// The auxilliary thread is still running. Set our
|
|
// cursor to an hourglass, and set our pending action
|
|
// to be Windows Update Select while we wait for the
|
|
// thread to respond to our cancel request.
|
|
//
|
|
MYASSERT((ndwData->ddData.PendingAction == PENDING_ACTION_NONE) ||
|
|
(ndwData->ddData.PendingAction == PENDING_ACTION_SHOWCLASS));
|
|
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
try {
|
|
|
|
SetupDiCancelDriverInfoSearch(ndwData->ddData.DevInfoSet);
|
|
//
|
|
// Disable all dialog controls, so that no other
|
|
// button may be pressed until we respond to this
|
|
// pending action. Also, kill the timer, so that
|
|
// it doesn't fire in the meantime.
|
|
//
|
|
ndwData->bInit = TRUE;
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_WINDOWSUPDATE;
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
//
|
|
// Call Windows Update to get an updated list of drivers
|
|
// to populate our listview control with, and then
|
|
// reenable the dialog controls.
|
|
//
|
|
HandleWindowsUpdate(hwndDlg, ndwData);
|
|
ToggleDialogControls(hwndDlg, ndwData, TRUE);
|
|
}
|
|
break;
|
|
|
|
case IDOK :
|
|
HandleOK:
|
|
iCur = (int)ListView_GetNextItem((ndwData->ddData).hwndDrvList,
|
|
-1,
|
|
LVNI_SELECTED
|
|
);
|
|
if(iCur != LB_ERR) {
|
|
//
|
|
// We have retrieved a valid selection from our listbox.
|
|
//
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// The auxilliary thread is still running. Set our
|
|
// cursor to an hourglass, while we wait for the
|
|
// thread to terminate.
|
|
//
|
|
MYASSERT((ndwData->ddData.PendingAction == PENDING_ACTION_NONE) ||
|
|
(ndwData->ddData.PendingAction == PENDING_ACTION_SHOWCLASS));
|
|
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
try {
|
|
|
|
SetupDiCancelDriverInfoSearch(ndwData->ddData.DevInfoSet);
|
|
//
|
|
// Disable all dialog controls, so that no
|
|
// other button may be pressed until we respond
|
|
// to this pending action. Also, kill the
|
|
// timer, so that it doesn't fire in the
|
|
// meantime.
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, FALSE);
|
|
ndwData->bInit = TRUE;
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_SELDONE;
|
|
ndwData->ddData.CurSelectionForSuccess = iCur;
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
//
|
|
// The auxilliary thread has already returned. We
|
|
// can return success right here.
|
|
//
|
|
SetSelectedDriverNode(&(ndwData->ddData), iCur);
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If the list box is empty, then just leave. We will
|
|
// treat this just like the user canceled.
|
|
//
|
|
if(0 == ListView_GetItemCount((ndwData->ddData).hwndDrvList)) {
|
|
|
|
PostMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0);
|
|
|
|
} else {
|
|
//
|
|
// Tell user to select something since there are
|
|
// items in the list
|
|
//
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_SELECT_DEVICE,
|
|
TempString,
|
|
SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
|
|
FormatMessageBox(MyDllModuleHandle,
|
|
hwndDlg,
|
|
MSG_SELECTDEVICE_ERROR,
|
|
TempString,
|
|
MB_OK | MB_ICONEXCLAMATION
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL :
|
|
|
|
if(ndwData->ddData.AuxThreadRunning) {
|
|
//
|
|
// The auxilliary thread is running, so we have to ask
|
|
// it to cancel, and set our pending action to do the
|
|
// cancel upon the thread's termination notification.
|
|
//
|
|
MYASSERT((ndwData->ddData.PendingAction == PENDING_ACTION_NONE) ||
|
|
(ndwData->ddData.PendingAction == PENDING_ACTION_SHOWCLASS));
|
|
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
try {
|
|
|
|
SetupDiCancelDriverInfoSearch(ndwData->ddData.DevInfoSet);
|
|
//
|
|
// Disable all dialog controls, so that no other
|
|
// button may be pressed until we respond to this
|
|
// pending action. Also, kill the timer, so that
|
|
// it doesn't fire in the meantime.
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, FALSE);
|
|
ndwData->bInit = TRUE;
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_CANCEL;
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
//
|
|
// The auxilliary thread isn't running, so we can
|
|
// return right here.
|
|
//
|
|
OnCancel(ndwData);
|
|
EndDialog(hwndDlg, ERROR_CANCELLED);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY :
|
|
|
|
switch(((LPNMHDR)lParam)->code) {
|
|
|
|
case PSN_SETACTIVE :
|
|
//
|
|
// Init the text in set active since a class installer has
|
|
// the option of replacing it.
|
|
//
|
|
SetDlgText(hwndDlg, IDC_NDW_TEXT, IDS_NDW_PICKDEV1, IDS_NDW_PICKDEV1);
|
|
|
|
ndwData->bInit = TRUE; // Still doing some init stuff
|
|
|
|
if(!OnSetActive(hwndDlg, ndwData)) {
|
|
SetDlgMsgResult(hwndDlg, uMsg, -1);
|
|
}
|
|
|
|
ndwData->bInit = FALSE; // Done with init stuff
|
|
break;
|
|
|
|
case PSN_WIZBACK :
|
|
CleanupDriverLists(ndwData);
|
|
|
|
if(iwd->DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED) {
|
|
SetDlgMsgResult(hwndDlg, uMsg, IDD_DYNAWIZ_SELECT_PREVPAGE);
|
|
} else {
|
|
SetDlgMsgResult(hwndDlg, uMsg, IDD_DYNAWIZ_SELECTCLASS_PAGE);
|
|
}
|
|
break;
|
|
|
|
case PSN_WIZNEXT :
|
|
if(!(iwd->Flags & NDW_INSTALLFLAG_CI_PICKED_OEM)) {
|
|
|
|
iCur = (int)ListView_GetNextItem((ndwData->ddData).hwndDrvList,
|
|
-1,
|
|
LVNI_SELECTED
|
|
);
|
|
if(iCur != LB_ERR) {
|
|
//
|
|
// We have retrieved a valid selection from our
|
|
// listbox.
|
|
//
|
|
if (pSetupIsSelectedHardwareIdValid(hwndDlg, &(ndwData->ddData), iCur)) {
|
|
SetSelectedDriverNode(&(ndwData->ddData), iCur);
|
|
} else {
|
|
SetDlgMsgResult(hwndDlg, uMsg, (LRESULT)-1);
|
|
break;
|
|
}
|
|
|
|
} else { // Invalid Listview selection
|
|
//
|
|
// Fail the call and end the case
|
|
//
|
|
SetDlgMsgResult(hwndDlg, uMsg, (LRESULT)-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the current description in the dialog data so
|
|
// that we'll hi-lite the correct selection if the user
|
|
// comes back to this page.
|
|
//
|
|
(ndwData->ddData).iCurDesc = GetCurDesc(&(ndwData->ddData));
|
|
|
|
if(iwd->DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED) {
|
|
SetDlgMsgResult(hwndDlg, uMsg, IDD_DYNAWIZ_SELECT_NEXTPAGE);
|
|
} else {
|
|
SetDlgMsgResult(hwndDlg, uMsg, IDD_DYNAWIZ_ANALYZEDEV_PAGE);
|
|
}
|
|
break;
|
|
|
|
case LVN_ITEMCHANGED :
|
|
//
|
|
// If the idFrom is the MFG list, then update the Drv list.
|
|
//
|
|
if(((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_MFGLIST) && !ndwData->bInit) {
|
|
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
}
|
|
|
|
ndwData->idTimer = SetTimer(hwndDlg,
|
|
SELECTMFG_TIMER_ID,
|
|
SELECTMFG_TIMER_DELAY,
|
|
NULL
|
|
);
|
|
|
|
if(ndwData->idTimer == 0) {
|
|
goto SelectMfgItemNow;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the idFrom is either of the model lists then update
|
|
// the digital signature icon and text.
|
|
//
|
|
if(((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_ONEMFG_DRVLIST) ||
|
|
((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_DRVLIST)) {
|
|
|
|
LVITEM lviItem;
|
|
int iImage = -1;
|
|
|
|
hicon = NULL;
|
|
|
|
iCur = (int)ListView_GetNextItem((ndwData->ddData).hwndDrvList,
|
|
-1,
|
|
LVNI_SELECTED
|
|
);
|
|
|
|
if(iCur != -1) {
|
|
//
|
|
// We have retrieved a valid selection from our
|
|
// listbox.
|
|
//
|
|
lviItem.mask = LVIF_IMAGE;
|
|
lviItem.iItem = iCur;
|
|
lviItem.iSubItem = 0;
|
|
|
|
if(ListView_GetItem((ndwData->ddData).hwndDrvList, &lviItem)) {
|
|
iImage = lviItem.iImage;
|
|
}
|
|
}
|
|
|
|
if(iImage != -1) {
|
|
//
|
|
// Enable the OK/Next button, because there's
|
|
// currently an item selected in the model list.
|
|
//
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list,
|
|
// and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
|
|
}
|
|
}
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), SW_SHOW);
|
|
|
|
//
|
|
// Don't show the link if we are in GUI setup
|
|
// because help center is not installed yet and so
|
|
// clicking on the link won't do anything.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK),
|
|
GuiSetupInProgress ? SW_HIDE : SW_SHOW);
|
|
|
|
hicon = NULL;
|
|
|
|
try {
|
|
|
|
if(iImage == IMAGE_ICON_SIGNED) {
|
|
//
|
|
// Load the digital signature icon and text
|
|
//
|
|
hicon = LoadImage(MyDllModuleHandle,
|
|
MAKEINTRESOURCE(IDI_SIGNED),
|
|
IMAGE_ICON,
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
0
|
|
);
|
|
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_DRIVER_IS_SIGNED,
|
|
TempString,
|
|
SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
|
|
if((ndwData->ddData).hFontNormal) {
|
|
SetWindowFont(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), (ndwData->ddData).hFontNormal, TRUE);
|
|
}
|
|
} else if(iImage == IMAGE_ICON_AUTHENTICODE_SIGNED) {
|
|
//
|
|
// Load the digital signature icon and text
|
|
//
|
|
hicon = LoadImage(MyDllModuleHandle,
|
|
MAKEINTRESOURCE(IDI_CERT),
|
|
IMAGE_ICON,
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
0
|
|
);
|
|
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_DRIVER_AUTHENTICODE_SIGNED,
|
|
TempString,
|
|
SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
|
|
if((ndwData->ddData).hFontNormal) {
|
|
SetWindowFont(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), (ndwData->ddData).hFontNormal, TRUE);
|
|
}
|
|
} else {
|
|
//
|
|
// Load the warning icon and text
|
|
//
|
|
hicon = LoadImage(MyDllModuleHandle,
|
|
MAKEINTRESOURCE(IDI_WARN),
|
|
IMAGE_ICON,
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
0
|
|
);
|
|
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_DRIVER_NOT_SIGNED,
|
|
TempString,
|
|
SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
|
|
if((ndwData->ddData).hFontBold) {
|
|
SetWindowFont(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), (ndwData->ddData).hFontBold, TRUE);
|
|
}
|
|
}
|
|
|
|
if(hicon) {
|
|
hicon = (HICON)SendDlgItemMessage(hwndDlg,
|
|
IDC_NDW_PICKDEV_SIGNED_ICON,
|
|
STM_SETICON,
|
|
(WPARAM)hicon,
|
|
0L
|
|
);
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if(hicon) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT, TempString);
|
|
|
|
} else {
|
|
//
|
|
// Nothing is selected so hide the icon and the
|
|
// text.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK), SW_HIDE);
|
|
|
|
//
|
|
// Disable the OK/Next button, because the user
|
|
// can't select a class driver yet.
|
|
//
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list,
|
|
// and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case NM_RETURN:
|
|
case NM_CLICK:
|
|
if((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_SIGNED_LINK) {
|
|
//
|
|
// We need to know if this is a server machine or a
|
|
// workstation machine since there are different help
|
|
// topic structures for the different products.
|
|
//
|
|
ZeroMemory(&osVersionInfoEx, sizeof(osVersionInfoEx));
|
|
osVersionInfoEx.dwOSVersionInfoSize = sizeof(osVersionInfoEx);
|
|
if(!GetVersionEx((LPOSVERSIONINFO)&osVersionInfoEx)) {
|
|
//
|
|
// If GetVersionEx fails then assume this is a
|
|
// workstation machine.
|
|
//
|
|
osVersionInfoEx.wProductType = VER_NT_WORKSTATION;
|
|
}
|
|
|
|
ShellExecute(hwndDlg,
|
|
TEXT("open"),
|
|
TEXT("HELPCTR.EXE"),
|
|
(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
|
|
? TEXT("HELPCTR.EXE -url hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm")
|
|
: TEXT("HELPCTR.EXE -url hcp://services/subsite?node=Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm"),
|
|
NULL,
|
|
SW_SHOWNORMAL
|
|
);
|
|
}
|
|
break;
|
|
|
|
case NM_DBLCLK :
|
|
if(((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_DRVLIST) ||
|
|
((((LPNMHDR)lParam)->idFrom) == IDC_NDW_PICKDEV_ONEMFG_DRVLIST)) {
|
|
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
goto HandleOK;
|
|
} else {
|
|
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_TIMER :
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
|
|
SelectMfgItemNow:
|
|
iCur = ListView_GetNextItem((ndwData->ddData).hwndMfgList,
|
|
-1,
|
|
LVNI_SELECTED
|
|
);
|
|
if(iCur != -1) {
|
|
|
|
RECT rcTo, rcFrom;
|
|
|
|
ListView_EnsureVisible((ndwData->ddData).hwndMfgList, iCur, FALSE);
|
|
UpdateWindow((ndwData->ddData).hwndMfgList);
|
|
|
|
GetWindowRect((ndwData->ddData).hwndDrvList, &rcTo);
|
|
MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcTo, 2);
|
|
|
|
ListView_GetItemRect((ndwData->ddData).hwndMfgList,
|
|
iCur,
|
|
&rcFrom,
|
|
LVIR_LABEL
|
|
);
|
|
MapWindowPoints((ndwData->ddData).hwndMfgList,
|
|
hwndDlg,
|
|
(LPPOINT)&rcFrom,
|
|
2
|
|
);
|
|
|
|
DrawAnimatedRects(hwndDlg, IDANI_OPEN, &rcFrom, &rcTo);
|
|
LockAndShowListForMfg(&(ndwData->ddData), iCur);
|
|
}
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE :
|
|
_OnSysColorChange(hwndDlg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_HELP: // F1
|
|
WinHelp(((LPHELPINFO)lParam)->hItemHandle,
|
|
SELECTDEVICE_HELP,
|
|
HELP_WM_HELP,
|
|
(ndwData->ddData.flags & DD_FLAG_SHOWSIMILARDRIVERS) ?
|
|
(ULONG_PTR)SelectDeviceShowSimilarHelpIDs :
|
|
(ULONG_PTR)SelectDeviceShowAllHelpIDs
|
|
);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU: // right mouse click
|
|
WinHelp((HWND)wParam,
|
|
SELECTDEVICE_HELP,
|
|
HELP_CONTEXTMENU,
|
|
(ndwData->ddData.flags & DD_FLAG_SHOWSIMILARDRIVERS) ?
|
|
(ULONG_PTR)SelectDeviceShowSimilarHelpIDs :
|
|
(ULONG_PTR)SelectDeviceShowAllHelpIDs
|
|
);
|
|
break;
|
|
|
|
default :
|
|
|
|
if(!g_uQueryCancelAutoPlay) {
|
|
g_uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
|
|
}
|
|
|
|
if(uMsg == g_uQueryCancelAutoPlay) {
|
|
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, 1 );
|
|
return 1; // cancel auto-play
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT CALLBACK
|
|
DriverNodeCompareProc(
|
|
LPARAM lParam1,
|
|
LPARAM lParam2,
|
|
LPARAM lParamSort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the callback for the list control sorting that is done when
|
|
ListView_SortItems is called.
|
|
|
|
The sorting this routine does is that if a DriverNode has the
|
|
DNF_INF_IS_SIGNED flag then it is considered better than a DriverNode that
|
|
does not have this flag. If both DriverNodes have, or don't have, this
|
|
flag then a simple string compare is done.
|
|
|
|
Return Value:
|
|
|
|
-1 if lParam1 is better than lParam2 (should be higher in the ListControl
|
|
+1 if lParam2 is better than lParam1 (should be higher in the ListControl
|
|
0 if lParam1 and lParam2 are the same
|
|
|
|
--*/
|
|
{
|
|
if((((PDRIVER_NODE)lParam1)->Flags & DNF_INF_IS_SIGNED) &&
|
|
!(((PDRIVER_NODE)lParam2)->Flags & DNF_INF_IS_SIGNED)) {
|
|
|
|
return -1;
|
|
}
|
|
|
|
if((((PDRIVER_NODE)lParam2)->Flags & DNF_INF_IS_SIGNED) &&
|
|
!(((PDRIVER_NODE)lParam1)->Flags & DNF_INF_IS_SIGNED)) {
|
|
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// At this point both driver nodes are signed or both are not signed, so
|
|
// compare based on their description.
|
|
//
|
|
return (lstrcmpi(pStringTableStringFromId(((PDEVICE_INFO_SET)lParamSort)->StringTable,
|
|
((PDRIVER_NODE)lParam1)->DevDescriptionDisplayName),
|
|
pStringTableStringFromId(((PDEVICE_INFO_SET)lParamSort)->StringTable,
|
|
((PDRIVER_NODE)lParam2)->DevDescriptionDisplayName)));
|
|
}
|
|
|
|
|
|
VOID
|
|
_OnSysColorChange(
|
|
HWND hWnd,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine notifies all child windows of the specified window when there
|
|
is a system color change.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HWND hChildWnd;
|
|
|
|
hChildWnd = GetWindow(hWnd, GW_CHILD);
|
|
|
|
while(hChildWnd != NULL) {
|
|
SendMessage(hChildWnd, WM_SYSCOLORCHANGE, wParam, lParam);
|
|
hChildWnd = GetWindow(hChildWnd, GW_HWNDNEXT);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
FillInDeviceList(
|
|
IN HWND hwndDlg,
|
|
IN PSP_DIALOGDATA lpdd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the dialog to have the appropriate description strings.
|
|
It also alternates dialog between showing the manufacturer "double list"
|
|
and the single list. This is done by showing/hiding overlapping listview.
|
|
|
|
NOTE: DO NOT CALL THIS ROUTINE WHILE ANOTHER THREAD IS BUSY BUILDING A
|
|
CLASS DRIVER LIST. WE WILL HANG HERE UNTIL THE OTHER THREAD COMPLETES!!!!
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Supplies the handle of the dialog window.
|
|
|
|
lpdd - Supplies the address of a dialog data buffer containing parameters
|
|
to be used in filling in the device list.
|
|
|
|
Return Value:
|
|
|
|
If success, the return value is NO_ERROR, otherwise, it is a Win32 code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDRIVER_NODE DriverNodeHead, CurDriverNode;
|
|
DWORD DriverNodeType;
|
|
LONG MfgNameId;
|
|
INT i;
|
|
LPTSTR lpszMfg;
|
|
LV_COLUMN lvcCol;
|
|
LV_ITEM lviItem;
|
|
BOOL bDidDrvList = FALSE;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
DWORD Err = NO_ERROR;
|
|
TCHAR szBuf[LINE_LEN];
|
|
TCHAR szMessage[MAX_INSTRUCTION_LEN];
|
|
TCHAR szText[SDT_MAX_TEXT];
|
|
LPTSTR lpszText;
|
|
size_t szTextSize;
|
|
CONST GUID *ClassGuid;
|
|
HRESULT hr;
|
|
BOOL TurnRedrawBackOn = FALSE;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
ClassGuid = &(DevInfoElem->ClassGuid);
|
|
|
|
if(lpdd->bShowCompat) {
|
|
DriverNodeHead = DevInfoElem->CompatDriverHead;
|
|
DriverNodeType = SPDIT_COMPATDRIVER;
|
|
} else {
|
|
DriverNodeHead = DevInfoElem->ClassDriverHead;
|
|
DriverNodeType = SPDIT_CLASSDRIVER;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We better not be trying to display a compatible driver list if
|
|
// we don't have a devinfo element!
|
|
//
|
|
MYASSERT(!lpdd->bShowCompat);
|
|
|
|
//
|
|
// Since we don't have any compatible drivers to show, we will hide
|
|
// the show compatible checkbox.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), SW_HIDE);
|
|
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
DriverNodeHead = pDeviceInfoSet->ClassDriverHead;
|
|
DriverNodeType = SPDIT_CLASSDRIVER;
|
|
|
|
if(pDeviceInfoSet->HasClassGuid) {
|
|
ClassGuid = &(pDeviceInfoSet->ClassGuid);
|
|
} else {
|
|
ClassGuid = &GUID_DEVCLASS_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
if(!DriverNodeHead) {
|
|
|
|
if(!(lpdd->flags & DD_FLAG_IS_DIALOGBOX)) {
|
|
//
|
|
// We can't just go away, so we have to do something useful.
|
|
// For now, simply display the UI as if we had a single-Mfg
|
|
// list, except that the list is empty.
|
|
//
|
|
// Hide the mult mfg controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_HIDE);
|
|
|
|
//
|
|
// Show the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the Models string
|
|
//
|
|
lvcCol.mask = LVCF_FMT | LVCF_TEXT;
|
|
lvcCol.fmt = LVCFMT_LEFT;
|
|
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
lvcCol.pszText = GET_CI_SELSTRINGS(dipb, ListLabel);
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
*szBuf = TEXT('\0');
|
|
}
|
|
lvcCol.pszText = szBuf;
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
}
|
|
|
|
//
|
|
// Use the single listbox view for the driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST);
|
|
|
|
ListView_DeleteAllItems(lpdd->hwndDrvList);
|
|
}
|
|
|
|
Err = ERROR_DI_BAD_PATH;
|
|
leave;
|
|
}
|
|
|
|
if((lpdd->flags & DD_FLAG_IS_DIALOGBOX) && !USE_CI_SELSTRINGS(dipb)) {
|
|
//
|
|
// If a class installer didn't supply strings for us to use in this
|
|
// dialogbox, then retrieve the instruction text to be used.
|
|
//
|
|
// First, get the class description to use for the dialog text.
|
|
//
|
|
if(!SetupDiGetClassDescription(ClassGuid, szBuf, SIZECHARS(szBuf), NULL)) {
|
|
//
|
|
// Fall back to the generic description "device"
|
|
//
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_GENERIC_DEVNAME,
|
|
szBuf,
|
|
SIZECHARS(szBuf))) {
|
|
*szBuf = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
if(!lpdd->bShowCompat) {
|
|
//
|
|
// Show class list.
|
|
//
|
|
hr = HRESULT_FROM_WIN32(GLE_FN_CALL(0,
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLSTR1,
|
|
szMessage,
|
|
SIZECHARS(szMessage))
|
|
)
|
|
);
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
hr = StringCchPrintfEx(szText,
|
|
SIZECHARS(szText),
|
|
&lpszText,
|
|
&szTextSize,
|
|
0,
|
|
szMessage,
|
|
szBuf
|
|
);
|
|
|
|
MYASSERT(SUCCEEDED(hr));
|
|
|
|
} else {
|
|
*szText = TEXT('\0');
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Show compatible list.
|
|
//
|
|
hr = HRESULT_FROM_WIN32(GLE_FN_CALL(0,
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLSTR0,
|
|
szMessage,
|
|
SIZECHARS(szMessage))
|
|
)
|
|
);
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
hr = StringCchPrintfEx(szText,
|
|
SIZECHARS(szText),
|
|
&lpszText,
|
|
&szTextSize,
|
|
0,
|
|
szMessage,
|
|
szBuf
|
|
);
|
|
|
|
MYASSERT(SUCCEEDED(hr));
|
|
|
|
} else {
|
|
*szText = TEXT('\0');
|
|
}
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
hr = HRESULT_FROM_WIN32(GLE_FN_CALL(0,
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLCLASS,
|
|
lpszText,
|
|
szTextSize)
|
|
)
|
|
);
|
|
if(SUCCEEDED(hr)) {
|
|
//
|
|
// Update our pointer to the end of the string, and the
|
|
// variable indicating the space remaining in the
|
|
// szText buffer.
|
|
//
|
|
hr = StringCchLength(szText, SIZECHARS(szText), &szTextSize);
|
|
|
|
MYASSERT(SUCCEEDED(hr));
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
lpszText = szText + szTextSize;
|
|
szTextSize = SIZECHARS(szText) - szTextSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
if(dipb->DriverPath != -1) {
|
|
//
|
|
// Inform the user that the list they're seeing represents
|
|
// only what was found in the location they pointed us at.
|
|
//
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLOEM1,
|
|
lpszText,
|
|
szTextSize
|
|
);
|
|
|
|
} else if (dipb->Flags & DI_SHOWOEM) {
|
|
//
|
|
// Tell the user they can click "Have Disk".
|
|
//
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLOEM,
|
|
lpszText,
|
|
szTextSize
|
|
);
|
|
}
|
|
}
|
|
|
|
SetDlgItemText(hwndDlg, IDC_NDW_TEXT, szText);
|
|
}
|
|
|
|
if((!lpdd->bShowCompat) && (dipb->Flags & DI_MULTMFGS)) {
|
|
//
|
|
// Hide the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
|
|
//
|
|
// Show the Multiple MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the colunm heading for the Driver list
|
|
//
|
|
lvcCol.mask = LVCF_FMT | LVCF_TEXT;
|
|
lvcCol.fmt = LVCFMT_LEFT;
|
|
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
lvcCol.pszText = GET_CI_SELSTRINGS(dipb, ListLabel);
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), 0, &lvcCol);
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
*szBuf = TEXT('\0');
|
|
}
|
|
lvcCol.pszText = szBuf;
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), 0, &lvcCol);
|
|
}
|
|
|
|
//
|
|
// Use the 2nd listbox of the Manufacturers/Models view for the
|
|
// driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST);
|
|
|
|
ListView_SetExtendedListViewStyle(lpdd->hwndDrvList, LVS_EX_LABELTIP);
|
|
|
|
if(lpdd->hImageList) {
|
|
ListView_SetImageList(lpdd->hwndDrvList, lpdd->hImageList, LVSIL_SMALL);
|
|
}
|
|
|
|
//
|
|
// No redraw for faster insert
|
|
//
|
|
SendMessage(lpdd->hwndMfgList, WM_SETREDRAW, FALSE, 0L);
|
|
TurnRedrawBackOn = TRUE;
|
|
|
|
//
|
|
// Clean out the MFG list before filling it.
|
|
//
|
|
ListView_DeleteAllItems(lpdd->hwndMfgList);
|
|
|
|
lviItem.mask = LVIF_TEXT | LVIF_PARAM;
|
|
lviItem.iItem = 0;
|
|
lviItem.iSubItem = 0;
|
|
|
|
//
|
|
// Setup the Column Header
|
|
//
|
|
MfgNameId = -1;
|
|
|
|
for(CurDriverNode = DriverNodeHead; CurDriverNode; CurDriverNode = CurDriverNode->Next) {
|
|
//
|
|
// Skip this driver node if it is to be excluded of if it is an
|
|
// old INET driver or if it is a BAD driver.
|
|
//
|
|
if((CurDriverNode->Flags & DNF_OLD_INET_DRIVER) ||
|
|
(CurDriverNode->Flags & DNF_BAD_DRIVER) ||
|
|
((CurDriverNode->Flags & DNF_EXCLUDEFROMLIST) &&
|
|
!(dipb->FlagsEx & DI_FLAGSEX_ALLOWEXCLUDEDDRVS))) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if((MfgNameId == -1) || (MfgNameId != CurDriverNode->MfgName)) {
|
|
|
|
MfgNameId = CurDriverNode->MfgName;
|
|
|
|
MYASSERT(CurDriverNode->MfgDisplayName != -1);
|
|
lpszMfg = pStringTableStringFromId(pDeviceInfoSet->StringTable,
|
|
CurDriverNode->MfgDisplayName
|
|
);
|
|
lviItem.pszText = lpszMfg;
|
|
lviItem.lParam = (LPARAM)CurDriverNode;
|
|
i = ListView_InsertItem(lpdd->hwndMfgList, &lviItem);
|
|
}
|
|
|
|
//
|
|
// If this driver node is the selected one, preselect here.
|
|
//
|
|
if(lpdd->iCurDesc == CurDriverNode->DevDescription) {
|
|
ListView_SetItemState(lpdd->hwndMfgList,
|
|
i,
|
|
(LVIS_SELECTED|LVIS_FOCUSED),
|
|
(LVIS_SELECTED|LVIS_FOCUSED)
|
|
);
|
|
ShowListForMfg(lpdd, pDeviceInfoSet, dipb, NULL, i);
|
|
bDidDrvList = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resize the Column
|
|
//
|
|
ListView_SetColumnWidth(lpdd->hwndMfgList, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
//
|
|
// If we did not expand one of the MFGs by default, then
|
|
// expand the First MFG.
|
|
//
|
|
if(!bDidDrvList) {
|
|
|
|
ListView_SetItemState(lpdd->hwndMfgList,
|
|
0,
|
|
(LVIS_SELECTED|LVIS_FOCUSED),
|
|
(LVIS_SELECTED|LVIS_FOCUSED)
|
|
);
|
|
ShowListForMfg(lpdd, pDeviceInfoSet, dipb, NULL, 0);
|
|
|
|
SendMessage(lpdd->hwndMfgList, WM_SETREDRAW, TRUE, 0L);
|
|
TurnRedrawBackOn = FALSE;
|
|
|
|
} else {
|
|
//
|
|
// We must set redraw back to true before sending the
|
|
// LVM_ENSUREVISIBLE message, or otherwise, the listbox item
|
|
// may only be partially exposed.
|
|
//
|
|
SendMessage(lpdd->hwndMfgList, WM_SETREDRAW, TRUE, 0L);
|
|
TurnRedrawBackOn = FALSE;
|
|
|
|
ListView_EnsureVisible(lpdd->hwndMfgList,
|
|
ListView_GetNextItem(lpdd->hwndMfgList, -1, LVNI_SELECTED),
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Hide the mult mfg controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
|
|
//
|
|
// Show the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the Models string
|
|
//
|
|
lvcCol.mask = LVCF_FMT | LVCF_TEXT;
|
|
lvcCol.fmt = LVCFMT_LEFT;
|
|
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
lvcCol.pszText = GET_CI_SELSTRINGS(dipb, ListLabel);
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
*szBuf = TEXT('\0');
|
|
}
|
|
lvcCol.pszText = szBuf;
|
|
ListView_SetColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
}
|
|
|
|
//
|
|
// Use the single listbox view for the driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST);
|
|
|
|
if (lpdd->hImageList) {
|
|
ListView_SetImageList(lpdd->hwndDrvList, lpdd->hImageList, LVSIL_SMALL);
|
|
}
|
|
|
|
ShowListForMfg(lpdd, pDeviceInfoSet, dipb, DriverNodeHead, -1);
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
|
|
if(TurnRedrawBackOn) {
|
|
SendMessage(lpdd->hwndMfgList, WM_SETREDRAW, TRUE, 0L);
|
|
}
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
VOID
|
|
ShowListForMfg(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDEVINSTALL_PARAM_BLOCK InstallParamBlock,
|
|
IN PDRIVER_NODE DriverNode, OPTIONAL
|
|
IN INT iMfg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds the driver description list.
|
|
THE LOCK MUST ALREADY BE ACQUIRED BEFORE CALLING THIS ROUTINE!
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data buffer containing parameters
|
|
to be used in filling in the driver description list.
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set
|
|
structure for which the driver description list is to be built.
|
|
|
|
InstallParamBlock - Supplies the address of a device installation parameter
|
|
block that controls how the list is displayed.
|
|
|
|
DriverNode - Optionally, supplies a pointer to the first node in a driver
|
|
node list to traverse, adding to the list for each node. If DriverNode
|
|
is not specified, then the list is to be built based on a particular
|
|
manufacturer, whose index in the Manufacturer list is given by iMfg.
|
|
|
|
iMfg - Supplies the index within the Manufacturer list that the driver
|
|
description list is to be based on. This parameter is ignored if a
|
|
DriverNode is specified.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
INT i = -1;
|
|
LV_ITEM lviItem;
|
|
LV_FINDINFO lvfiFind;
|
|
LONG MfgNameId = -1;
|
|
TCHAR szTemp[LINE_LEN];
|
|
SYSTEMTIME SystemTime;
|
|
TCHAR FormatString[LINE_LEN];
|
|
HRESULT hr;
|
|
PTSTR StringEnd;
|
|
size_t StringBufSize;
|
|
|
|
//
|
|
// Set listview sortascending style based on DI_INF_IS_SORTED flag
|
|
//
|
|
SetWindowLong(lpdd->hwndDrvList,
|
|
GWL_STYLE,
|
|
(GetWindowLong(lpdd->hwndDrvList, GWL_STYLE) & ~(LVS_SORTASCENDING | LVS_SORTDESCENDING)) |
|
|
((InstallParamBlock->Flags & DI_INF_IS_SORTED)
|
|
? 0
|
|
: LVS_SORTASCENDING)
|
|
);
|
|
|
|
SendMessage(lpdd->hwndDrvList, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
try {
|
|
//
|
|
// Clean out the List.
|
|
//
|
|
ListView_DeleteAllItems(lpdd->hwndDrvList);
|
|
|
|
if(!DriverNode) {
|
|
|
|
if(ListView_GetItemCount(lpdd->hwndMfgList) > 0) {
|
|
|
|
lviItem.mask = LVIF_PARAM;
|
|
lviItem.iItem = iMfg;
|
|
lviItem.iSubItem = 0;
|
|
if(!ListView_GetItem(lpdd->hwndMfgList, &lviItem) ||
|
|
!(DriverNode = GetDriverNodeFromLParam(DeviceInfoSet, lpdd, lviItem.lParam))) {
|
|
|
|
leave;
|
|
}
|
|
MfgNameId = DriverNode->MfgName;
|
|
} else {
|
|
//
|
|
// This means that there are no Manufacturers so we just have a empty list.
|
|
//
|
|
leave;
|
|
}
|
|
}
|
|
|
|
lviItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
|
|
lviItem.iItem = 0;
|
|
lviItem.iSubItem = 0;
|
|
|
|
//
|
|
// Add descriptions to the list
|
|
//
|
|
for( ; DriverNode; DriverNode = DriverNode->Next) {
|
|
|
|
if((MfgNameId != -1) && (MfgNameId != DriverNode->MfgName)) {
|
|
//
|
|
// We've gone beyond the manufacturer list--break out of loop.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If this is a special "Don't show me" one, then skip it
|
|
//
|
|
if((DriverNode->Flags & DNF_OLD_INET_DRIVER) ||
|
|
(DriverNode->Flags & DNF_BAD_DRIVER) ||
|
|
((DriverNode->Flags & DNF_EXCLUDEFROMLIST) &&
|
|
!(InstallParamBlock->FlagsEx & DI_FLAGSEX_ALLOWEXCLUDEDDRVS))) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Build the device description string to add to the models list.
|
|
//
|
|
hr = StringCchCopyEx(
|
|
szTemp,
|
|
SIZECHARS(szTemp),
|
|
pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->DevDescriptionDisplayName),
|
|
&StringEnd,
|
|
&StringBufSize,
|
|
0
|
|
);
|
|
|
|
//
|
|
// This shouldn't fail, since the maximum length of a device
|
|
// description name is less than the size of our szTemp buffer!
|
|
//
|
|
MYASSERT(SUCCEEDED(hr));
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
if((DriverNode->Flags & DNF_DUPDESC) &&
|
|
(DriverNode->ProviderDisplayName != -1)) {
|
|
|
|
//
|
|
// For drivers with duplicate descriptions add the provider
|
|
// name in parens.
|
|
//
|
|
|
|
if(GetWindowLong(lpdd->hwndDrvList, GWL_EXSTYLE) & WS_EX_RTLREADING) {
|
|
MYVERIFY(SUCCEEDED(StringCchPrintfEx(StringEnd,
|
|
StringBufSize,
|
|
NULL,
|
|
NULL,
|
|
STRSAFE_IGNORE_NULLS | STRSAFE_NO_TRUNCATION,
|
|
L" \x200E(%s)\x200E",
|
|
pStringTableStringFromId(
|
|
DeviceInfoSet->StringTable,
|
|
DriverNode->ProviderDisplayName)
|
|
)));
|
|
} else {
|
|
MYVERIFY(SUCCEEDED(StringCchPrintfEx(StringEnd,
|
|
StringBufSize,
|
|
NULL,
|
|
NULL,
|
|
STRSAFE_IGNORE_NULLS | STRSAFE_NO_TRUNCATION,
|
|
TEXT(" (%s)"),
|
|
pStringTableStringFromId(
|
|
DeviceInfoSet->StringTable,
|
|
DriverNode->ProviderDisplayName)
|
|
)));
|
|
}
|
|
|
|
} else if(DriverNode->Flags & DNF_DUPPROVIDER) {
|
|
//
|
|
// For drivers with duplicate descriptions and providers,
|
|
// add the driver version and driver date if there is one,
|
|
// in brackets.
|
|
//
|
|
if(DriverNode->DriverVersion != 0) {
|
|
|
|
ULARGE_INTEGER Version;
|
|
|
|
Version.QuadPart = DriverNode->DriverVersion;
|
|
|
|
if(LoadString(MyDllModuleHandle,
|
|
IDS_VERSION,
|
|
FormatString,
|
|
SIZECHARS(FormatString))) {
|
|
|
|
hr = StringCchPrintfEx(StringEnd,
|
|
StringBufSize,
|
|
&StringEnd,
|
|
&StringBufSize,
|
|
STRSAFE_NO_TRUNCATION,
|
|
FormatString,
|
|
HIWORD(Version.HighPart),
|
|
LOWORD(Version.HighPart),
|
|
HIWORD(Version.LowPart),
|
|
LOWORD(Version.LowPart)
|
|
);
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr) &&
|
|
((DriverNode->DriverDate.dwLowDateTime != 0) ||
|
|
(DriverNode->DriverDate.dwHighDateTime != 0))) {
|
|
|
|
if(FileTimeToSystemTime(&(DriverNode->DriverDate), &SystemTime)) {
|
|
if(GetDateFormat(LOCALE_USER_DEFAULT,
|
|
((GetWindowLong(lpdd->hwndDrvList, GWL_EXSTYLE) & WS_EX_RTLREADING)
|
|
? DATE_SHORTDATE | DATE_RTLREADING
|
|
: DATE_SHORTDATE),
|
|
&SystemTime,
|
|
NULL,
|
|
FormatString,
|
|
SIZECHARS(FormatString)) != 0) {
|
|
|
|
MYVERIFY(SUCCEEDED(StringCchPrintfEx(StringEnd,
|
|
StringBufSize,
|
|
NULL,
|
|
NULL,
|
|
STRSAFE_NO_TRUNCATION,
|
|
TEXT(" [%s]"),
|
|
FormatString
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lviItem.pszText = szTemp;
|
|
|
|
lviItem.lParam = (LPARAM)DriverNode;
|
|
|
|
//
|
|
// We have to test for DNF_AUTHENTICODE_SIGNED first since if
|
|
// that flag is set then DNF_INF_IS_SIGNED is also always set.
|
|
//
|
|
if (DriverNode->Flags & DNF_AUTHENTICODE_SIGNED) {
|
|
lviItem.iImage = IMAGE_ICON_AUTHENTICODE_SIGNED;
|
|
} else if (DriverNode->Flags & DNF_INF_IS_SIGNED) {
|
|
lviItem.iImage = IMAGE_ICON_SIGNED;
|
|
} else {
|
|
lviItem.iImage = IMAGE_ICON_NOT_SIGNED;
|
|
}
|
|
|
|
if(ListView_InsertItem(lpdd->hwndDrvList, &lviItem) != -1) {
|
|
lviItem.iItem++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sort the list unless the DI_INF_IS_SORTED flag is set
|
|
//
|
|
if(GetWindowLong(lpdd->hwndDrvList, GWL_STYLE) & LVS_SORTASCENDING) {
|
|
ListView_SortItems(lpdd->hwndDrvList,
|
|
(PFNLVCOMPARE)DriverNodeCompareProc,
|
|
(LPARAM)DeviceInfoSet
|
|
);
|
|
}
|
|
|
|
//
|
|
// Resize the Column
|
|
//
|
|
ListView_SetColumnWidth(lpdd->hwndDrvList, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
//
|
|
// select the current description string
|
|
//
|
|
if(lpdd->iCurDesc == -1) {
|
|
i = 0;
|
|
} else {
|
|
lvfiFind.flags = LVFI_STRING;
|
|
lvfiFind.psz = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
lpdd->iCurDesc
|
|
);
|
|
i = ListView_FindItem(lpdd->hwndDrvList, -1, &lvfiFind);
|
|
if(i == -1) {
|
|
i = 0;
|
|
}
|
|
}
|
|
ListView_SetItemState(lpdd->hwndDrvList,
|
|
i,
|
|
(LVIS_SELECTED|LVIS_FOCUSED),
|
|
(LVIS_SELECTED|LVIS_FOCUSED)
|
|
);
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
i = -1;
|
|
}
|
|
|
|
//
|
|
// We must turn redraw back on before sending the LVM_ENSUREVISIBLE
|
|
// message, or otherwise the item may only be partially visible.
|
|
//
|
|
SendMessage(lpdd->hwndDrvList, WM_SETREDRAW, TRUE, 0L);
|
|
if(i != -1) {
|
|
ListView_EnsureVisible(lpdd->hwndDrvList, i, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LockAndShowListForMfg(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iMfg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a wrapper for ShowListForMfg. It is to be called from
|
|
points where the device information set lock is not already owned (e.g.,
|
|
the dialog prop message loop.
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data buffer containing parameters
|
|
to be used in filling in the driver description list.
|
|
|
|
iMfg - Supplies the index within the Manufacturer list that the driver
|
|
description list is to be based on.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
dipb = DevInfoElem ? &(DevInfoElem->InstallParamBlock)
|
|
: &(pDeviceInfoSet->InstallParamBlock);
|
|
|
|
ShowListForMfg(lpdd,
|
|
pDeviceInfoSet,
|
|
dipb,
|
|
NULL,
|
|
iMfg
|
|
);
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitSelectDeviceDlg(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the select device wizard page. It
|
|
builds the class list if it is needed, shows/hides necessary
|
|
controls based on Flags, and comes up with the right text
|
|
description of what's going on.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Handle to dialog window
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Return Value:
|
|
|
|
TRUE if we have at least one driver (compat or class) displayed.
|
|
FALSE if we do not have any drivers (class and compat) displayed.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
DWORD DriverType = SPDIT_CLASSDRIVER;
|
|
DWORD Err;
|
|
INT ShowWhat;
|
|
CONST GUID *ClassGuid;
|
|
HICON hIcon;
|
|
LV_COLUMN lvcCol;
|
|
BOOL SpawnClassDriverSearch = FALSE;
|
|
PCLASSDRV_THREAD_CONTEXT ClassDrvThreadContext;
|
|
HCURSOR hOldCursor;
|
|
BOOL bRet = TRUE;
|
|
PSP_DIALOGDATA lpdd;
|
|
TCHAR szBuf[LINE_LEN];
|
|
HFONT hfont;
|
|
LOGFONT LogFont;
|
|
|
|
MYASSERT(ndwData);
|
|
lpdd = &(ndwData->ddData);
|
|
MYASSERT(lpdd);
|
|
|
|
if(!lpdd->hwndMfgList) {
|
|
|
|
UINT ImageListFlags = 0;
|
|
|
|
//
|
|
// Then this is the first time we've initialized this dialog (we may
|
|
// hit this routine multiple times in the wizard case, because the user
|
|
// can go back and forth between pages).
|
|
//
|
|
lpdd->hwndMfgList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST);
|
|
//
|
|
// Don't worry--hwndDrvList will be set later in FillInDeviceList().
|
|
//
|
|
|
|
//
|
|
// Create an image list and add the signed and not signed (blank) icons
|
|
// to the list.
|
|
// Note: If the window is in right-to-left reading then we need to OR
|
|
// in the ILC_MIRROR flag so that the icons do NOT get mirrored along
|
|
// with the rest of the UI.
|
|
//
|
|
ImageListFlags = ILC_MASK;
|
|
if(GetWindowLong(hwndDlg, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) {
|
|
ImageListFlags |= ILC_MIRROR;
|
|
}
|
|
|
|
|
|
lpdd->hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
ImageListFlags,
|
|
1,
|
|
1
|
|
);
|
|
|
|
if(lpdd->hImageList) {
|
|
|
|
ImageList_SetBkColor(lpdd->hImageList, GetSysColor(COLOR_WINDOW));
|
|
|
|
if((hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(IDI_BLANK))) != NULL) {
|
|
ImageList_AddIcon(lpdd->hImageList, hIcon);
|
|
}
|
|
|
|
if((hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(IDI_SIGNED))) != NULL) {
|
|
ImageList_AddIcon(lpdd->hImageList, hIcon);
|
|
}
|
|
|
|
if((hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(IDI_CERT))) != NULL) {
|
|
ImageList_AddIcon(lpdd->hImageList, hIcon);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the normal and bold fonts for the digital signing text.
|
|
//
|
|
lpdd->hFontNormal = lpdd->hFontBold = NULL;
|
|
|
|
if((hfont = (HFONT)SendMessage(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), WM_GETFONT, 0, 0)) != NULL) {
|
|
GetObject(hfont, sizeof(LogFont), &LogFont);
|
|
lpdd->hFontNormal = CreateFontIndirect(&LogFont);
|
|
}
|
|
|
|
if((hfont = (HFONT)SendMessage(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), WM_GETFONT, 0, 0)) != NULL) {
|
|
GetObject(hfont, sizeof(LogFont), &LogFont);
|
|
LogFont.lfWeight = FW_BOLD;
|
|
lpdd->hFontBold = CreateFontIndirect(&LogFont);
|
|
}
|
|
|
|
ListView_SetExtendedListViewStyle(lpdd->hwndMfgList, LVS_EX_LABELTIP);
|
|
|
|
//
|
|
// Insert a ListView column for each of the listboxes.
|
|
// Set the text for the Manufacturer label now since it can't be
|
|
// changed by class installers like the model label can.
|
|
//
|
|
lvcCol.mask = 0;
|
|
|
|
ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), 0, &lvcCol);
|
|
ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
|
|
lvcCol.mask = LVCF_FMT | LVCF_TEXT;
|
|
lvcCol.fmt = LVCFMT_LEFT;
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MFGLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
*szBuf = TEXT('\0');
|
|
}
|
|
lvcCol.pszText = szBuf;
|
|
ListView_InsertColumn(lpdd->hwndMfgList, 0, &lvcCol);
|
|
}
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
ClassGuid = &(DevInfoElem->ClassGuid);
|
|
//
|
|
// Fill in a SP_DEVINFO_DATA structure for a later call to
|
|
// SetupDiBuildDriverInfoList.
|
|
//
|
|
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DevInfoData
|
|
);
|
|
//
|
|
// Set flags indicating which driver lists already exist.
|
|
//
|
|
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDCOMPATINFO) {
|
|
lpdd->bKeeplpCompatDrvList = TRUE;
|
|
}
|
|
|
|
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST) {
|
|
lpdd->bKeeplpClassDrvList = TRUE;
|
|
}
|
|
|
|
if(DevInfoElem->SelectedDriver) {
|
|
lpdd->bKeeplpSelectedDrv = TRUE;
|
|
}
|
|
|
|
//
|
|
// We want to start out with the compatible driver list.
|
|
//
|
|
DriverType = SPDIT_COMPATDRIVER;
|
|
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
if(pDeviceInfoSet->HasClassGuid) {
|
|
ClassGuid = &(pDeviceInfoSet->ClassGuid);
|
|
} else {
|
|
ClassGuid = &GUID_DEVCLASS_UNKNOWN;
|
|
}
|
|
|
|
if(pDeviceInfoSet->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST) {
|
|
lpdd->bKeeplpClassDrvList = TRUE;
|
|
}
|
|
|
|
if(pDeviceInfoSet->SelectedClassDriver) {
|
|
lpdd->bKeeplpSelectedDrv = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get/set class icon
|
|
//
|
|
if(IsEqualGUID(ClassGuid, &GUID_NULL)) {
|
|
if(!SetupDiLoadClassIcon(&GUID_DEVCLASS_UNKNOWN, &hIcon, &(lpdd->iBitmap))) {
|
|
hIcon = NULL;
|
|
}
|
|
} else {
|
|
if(!SetupDiLoadClassIcon(ClassGuid, &hIcon, &(lpdd->iBitmap))) {
|
|
hIcon = NULL;
|
|
}
|
|
}
|
|
|
|
if(hIcon) {
|
|
SendDlgItemMessage(hwndDlg, IDC_CLASSICON, STM_SETICON, (WPARAM)hIcon, 0L);
|
|
}
|
|
|
|
//
|
|
// If we are supposed to override the instructions and title with the
|
|
// class installer-provided strings, do it now.
|
|
//
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
SetWindowText(hwndDlg, GET_CI_SELSTRINGS(dipb, Title));
|
|
} else {
|
|
//
|
|
// Set wizard title and subtitle
|
|
//
|
|
PropSheet_SetHeaderTitle(GetParent(hwndDlg),
|
|
PropSheet_HwndToIndex(GetParent(hwndDlg), hwndDlg),
|
|
GET_CI_SELSTRINGS(dipb, Title));
|
|
|
|
PropSheet_SetHeaderSubTitle(GetParent(hwndDlg),
|
|
PropSheet_HwndToIndex(GetParent(hwndDlg), hwndDlg),
|
|
GET_CI_SELSTRINGS(dipb, SubTitle));
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_TEXT, GET_CI_SELSTRINGS(dipb, Instructions));
|
|
}
|
|
|
|
//
|
|
// If we should not allow OEM driver, then hide the HAVE disk button.
|
|
//
|
|
if(!(dipb->Flags & DI_SHOWOEM)) {
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_HAVEDISK), SW_HIDE);
|
|
}
|
|
|
|
//
|
|
// Hide the Windows Update button if we should not search the web.
|
|
//
|
|
if((!(dipb->FlagsEx & DI_FLAGSEX_SHOWWINDOWSUPDATE)) ||
|
|
!CDMIsInternetAvailable()) {
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_WINDOWSUPDATE), SW_HIDE);
|
|
}
|
|
|
|
if(dipb->FlagsEx & DI_FLAGSEX_FILTERSIMILARDRIVERS) {
|
|
lpdd->flags |= DD_FLAG_SHOWSIMILARDRIVERS;
|
|
}
|
|
|
|
//
|
|
// In order to decrease the amount of time the user must wait before
|
|
// they're able to work with the Select Device dialog, we have adopted
|
|
// a 'hybrid' multi-threaded approach. As soon as we get the first
|
|
// displayable list built, then we will return, and build the other
|
|
// list (if necessary) in another thread.
|
|
//
|
|
// We do it this way because it's easier, it maintains the existing
|
|
// external behavior, and because it's easier.
|
|
//
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); // Potentially slow operations ahead!
|
|
|
|
if(DriverType == SPDIT_COMPATDRIVER) {
|
|
//
|
|
// OR in the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS flag so that we
|
|
// don't include old internet drivers in the list that we get back.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if(SetupDiGetDeviceInstallParams(lpdd->DevInfoSet,
|
|
&DevInfoData,
|
|
&DeviceInstallParams)) {
|
|
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS;
|
|
|
|
SetupDiSetDeviceInstallParams(lpdd->DevInfoSet,
|
|
&DevInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
SetupDiBuildDriverInfoList(lpdd->DevInfoSet,
|
|
&DevInfoData,
|
|
SPDIT_COMPATDRIVER
|
|
);
|
|
|
|
//
|
|
// Verify that there are some devices in the list to show.
|
|
//
|
|
if(bNoDevsToShow(DevInfoElem)) {
|
|
if(!lpdd->bKeeplpCompatDrvList) {
|
|
SetupDiDestroyDriverInfoList(lpdd->DevInfoSet, &DevInfoData, SPDIT_COMPATDRIVER);
|
|
}
|
|
DriverType = SPDIT_CLASSDRIVER;
|
|
|
|
} else if(!lpdd->bKeeplpClassDrvList) {
|
|
//
|
|
// We have a list to get our UI up and running, but we don't
|
|
// have a class driver list yet. Set a flag that causes us to
|
|
// spawn a thread for this later.
|
|
//
|
|
SpawnClassDriverSearch = TRUE;
|
|
}
|
|
}
|
|
|
|
if(DriverType == SPDIT_CLASSDRIVER) {
|
|
//
|
|
// We couldn't find any compatible drivers, so we fall back on the
|
|
// class driver list. In this case we have to have this list
|
|
// before continuing. In the future, maybe we'll get fancier and
|
|
// do this in a separate thread, but for now, we just make the user
|
|
// wait.
|
|
//
|
|
|
|
//
|
|
// OR in the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS flag so that we
|
|
// don't include old internet drivers in the list that we get back.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if(SetupDiGetDeviceInstallParams(lpdd->DevInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
&DeviceInstallParams)) {
|
|
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS;
|
|
|
|
SetupDiSetDeviceInstallParams(lpdd->DevInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
SetupDiBuildDriverInfoList(lpdd->DevInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
SPDIT_CLASSDRIVER
|
|
);
|
|
}
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW)); // Done with slow operations.
|
|
|
|
if(DriverType == SPDIT_COMPATDRIVER) {
|
|
//
|
|
// Since we ran this through bNoDevsToShow() above, and it
|
|
// succeeded, we know there's at least one driver in the compatible
|
|
// driver list.
|
|
//
|
|
lpdd->bShowCompat = TRUE;
|
|
CheckDlgButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_COMPAT,
|
|
BST_CHECKED
|
|
);
|
|
} else {
|
|
//
|
|
// There is no compatible list, so hide the radio buttons.
|
|
//
|
|
lpdd->bShowCompat = FALSE;
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), SW_HIDE);
|
|
}
|
|
|
|
//
|
|
// Initial current description. This will be used to set
|
|
// the Default ListView selection.
|
|
//
|
|
if(lpdd->iCurDesc == -1) {
|
|
//
|
|
// If we already have a selected driver for the devinfo set or
|
|
// element, then we'll use that, otherwise, we'll use the devinfo
|
|
// element's description (if applicable).
|
|
//
|
|
if(DevInfoElem) {
|
|
if(DevInfoElem->SelectedDriver) {
|
|
lpdd->iCurDesc = DevInfoElem->SelectedDriver->DevDescription;
|
|
} else {
|
|
|
|
TCHAR TempString[LINE_LEN];
|
|
ULONG TempStringSize;
|
|
|
|
//
|
|
// Use the caller-supplied device description, if there is
|
|
// one. If not, then see if we can retrieve the DeviceDesc
|
|
// registry property.
|
|
//
|
|
TempStringSize = sizeof(TempString);
|
|
|
|
if((DevInfoElem->DeviceDescription == -1) &&
|
|
(CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
|
|
CM_DRP_DEVICEDESC,
|
|
NULL,
|
|
TempString,
|
|
&TempStringSize,
|
|
0) == CR_SUCCESS)) {
|
|
//
|
|
// We were able to retrieve a device description. Now
|
|
// store it (case-insensitive only) in the devinfo
|
|
// element.
|
|
//
|
|
DevInfoElem->DeviceDescription = pStringTableAddString(
|
|
pDeviceInfoSet->StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE,
|
|
NULL,0
|
|
);
|
|
}
|
|
|
|
lpdd->iCurDesc = DevInfoElem->DeviceDescription;
|
|
}
|
|
} else {
|
|
if(pDeviceInfoSet->SelectedClassDriver) {
|
|
lpdd->iCurDesc = pDeviceInfoSet->SelectedClassDriver->DevDescription;
|
|
}
|
|
}
|
|
}
|
|
|
|
Err = FillInDeviceList(hwndDlg, lpdd);
|
|
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
|
|
HWND hLineWnd;
|
|
RECT Rect;
|
|
|
|
//
|
|
// If FillInDeviceList() fails during init time, don't even bring
|
|
// up the dialog.
|
|
//
|
|
if(Err != NO_ERROR) {
|
|
EndDialog(hwndDlg, Err);
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Set the initial focus on the OK button.
|
|
//
|
|
SetFocus(GetDlgItem(hwndDlg, IDOK));
|
|
|
|
//
|
|
// Use the fancy etched frame style for the separator bar in the
|
|
// dialog.
|
|
//
|
|
hLineWnd = GetDlgItem(hwndDlg, IDD_DEVINSLINE);
|
|
SetWindowLong(hLineWnd,
|
|
GWL_EXSTYLE,
|
|
(GetWindowLong(hLineWnd, GWL_EXSTYLE) | WS_EX_STATICEDGE)
|
|
);
|
|
GetClientRect(hLineWnd, &Rect);
|
|
SetWindowPos(hLineWnd,
|
|
HWND_TOP,
|
|
0,
|
|
0,
|
|
Rect.right,
|
|
GetSystemMetrics(SM_CYEDGE),
|
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED
|
|
);
|
|
}
|
|
|
|
//
|
|
// If DriverType is SPDIT_CLASSDRIVER and ListView_GetItemCount returns
|
|
// 0 then we don't have any items to show at all. This will only happen
|
|
// when we search the default Windows INF directory and we do not have
|
|
// any INFs for a device.
|
|
//
|
|
if((DriverType == SPDIT_CLASSDRIVER) &&
|
|
(0 == ListView_GetItemCount(lpdd->hwndDrvList))) {
|
|
|
|
TCHAR TempString[LINE_LEN];
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_SHOW);
|
|
|
|
if(!LoadString(MyDllModuleHandle, IDS_NDW_NO_DRIVERS, TempString, SIZECHARS(TempString))) {
|
|
*TempString = TEXT('\0');
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_STATUS_TEXT, TempString);
|
|
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
} else {
|
|
if(((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list, and are in express
|
|
// mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
|
|
}
|
|
}
|
|
|
|
bRet = FALSE;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
|
|
bRet = FALSE;
|
|
SpawnClassDriverSearch = FALSE;
|
|
|
|
//
|
|
// If we're doing the dialog box version, then an exception should
|
|
// cause us to terminate the dialog and return an error.
|
|
//
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, ERROR_INVALID_DATA);
|
|
}
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
if(SpawnClassDriverSearch) {
|
|
//
|
|
// Allocate a context structure to pass to the auxilliary thread (the
|
|
// auxilliary thread will take care of freeing the memory).
|
|
//
|
|
if(!(ClassDrvThreadContext = MyMalloc(sizeof(CLASSDRV_THREAD_CONTEXT)))) {
|
|
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
|
//
|
|
// Fill in the context structure, and fire off the thread.
|
|
// NOTE: The DevInfoData struct has to have been filled in above
|
|
// for us to have gotten to this point.
|
|
//
|
|
ClassDrvThreadContext->DeviceInfoSet = lpdd->DevInfoSet;
|
|
|
|
CopyMemory(&(ClassDrvThreadContext->DeviceInfoData),
|
|
&DevInfoData,
|
|
sizeof(DevInfoData)
|
|
);
|
|
|
|
ClassDrvThreadContext->NotificationWindow = hwndDlg;
|
|
|
|
if(_beginthread(ClassDriverSearchThread, 0, ClassDrvThreadContext) == -1) {
|
|
//
|
|
// Assume out-of-memory
|
|
//
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Memory "handed off" to other thread--reset the pointer
|
|
// so we won't try to free it below...
|
|
//
|
|
ClassDrvThreadContext = NULL;
|
|
lpdd->AuxThreadRunning = TRUE;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
if(ClassDrvThreadContext) {
|
|
MyFree(ClassDrvThreadContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
PDRIVER_NODE
|
|
GetDriverNodeFromLParam(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine interprets lParam as a pointer to a driver node, and tries to
|
|
find the node in the class driver list for either the selected devinfo
|
|
element, or the set itself. If the lpdd flags field has the
|
|
DD_FLAG_USE_DEVINFO_ELEM bit set, then the lpdd's DevInfoElem will be used
|
|
instead of the currently selected device.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set
|
|
structure to search for the driver node in.
|
|
|
|
lpdd - Supplies the address of a dialog data structure that specifies
|
|
whether the wizard has an explicit association to the global class
|
|
driver list or to a particular device information element, and if so,
|
|
what it's associated with.
|
|
|
|
lParam - Supplies a value which may be the address of a driver node. The
|
|
appropriate linked list of driver nodes is searched to see if one of
|
|
them has this value as its address, and if so, a pointer to that driver
|
|
node is returned.
|
|
|
|
Return Value:
|
|
|
|
If success, the return value is the address of the matching driver node,
|
|
otherwise, it is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE CurDriverNode;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = DeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
CurDriverNode = (lpdd->bShowCompat) ? DevInfoElem->CompatDriverHead
|
|
: DevInfoElem->ClassDriverHead;
|
|
} else {
|
|
MYASSERT(!lpdd->bShowCompat);
|
|
CurDriverNode = DeviceInfoSet->ClassDriverHead;
|
|
}
|
|
|
|
while(CurDriverNode) {
|
|
if(CurDriverNode == (PDRIVER_NODE)lParam) {
|
|
return CurDriverNode;
|
|
} else {
|
|
CurDriverNode = CurDriverNode->Next;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
OnSetActive(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the PSN_SETACTIVE message of the select device wizard
|
|
page.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Supplies the window handle of the wizard dialog page.
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Return Value:
|
|
|
|
If success, the return value is TRUE, otherwise, it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
BOOL b = TRUE;
|
|
PSP_INSTALLWIZARD_DATA iwd;
|
|
PSP_DIALOGDATA lpdd;
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
|
|
MYASSERT(ndwData);
|
|
iwd = &(ndwData->InstallData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
//
|
|
// Make sure our "waiting for class list" static text control is
|
|
// hidden!
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_STATUS_TEXT), SW_HIDE);
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
}
|
|
|
|
//
|
|
// Set the Button State
|
|
//
|
|
if((iwd->Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) &&
|
|
(iwd->Flags & NDW_INSTALLFLAG_EXPRESSINTRO) &&
|
|
!(iwd->DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
//
|
|
// No back if we skipped the Class list, and are in express mode
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
} else {
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
|
|
}
|
|
|
|
//
|
|
// Set the New Class install params.
|
|
// If we are being jumped to by a dyna wiz page,
|
|
// then do not call the class installer
|
|
//
|
|
if(iwd->DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED) {
|
|
InitSelectDeviceDlg(hwndDlg, ndwData);
|
|
} else {
|
|
|
|
BOOL FlagNeedsReset = FALSE;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
DWORD CiErr;
|
|
PDEVINFO_ELEM CurDevInfoElem;
|
|
|
|
//
|
|
// Call the Class Installer
|
|
//
|
|
if(!(dipb->Flags & DI_NODI_DEFAULTACTION)) {
|
|
dipb->Flags |= DI_NODI_DEFAULTACTION;
|
|
FlagNeedsReset = TRUE;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
//
|
|
// Initialize a SP_DEVINFO_DATA buffer to use as an argument to
|
|
// SetupDiCallClassInstaller.
|
|
//
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DeviceInfoData
|
|
);
|
|
}
|
|
|
|
//
|
|
// We need to unlock the HDEVINFO before calling the class
|
|
// installer.
|
|
//
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
pDeviceInfoSet = NULL;
|
|
|
|
CiErr = _SetupDiCallClassInstaller(
|
|
DIF_SELECTDEVICE,
|
|
lpdd->DevInfoSet,
|
|
DevInfoElem ? &DeviceInfoData : NULL,
|
|
CALLCI_LOAD_HELPERS | CALLCI_CALL_HELPERS
|
|
);
|
|
|
|
//
|
|
// Re-acquire the HDEVINFO lock.
|
|
//
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
b = FALSE;
|
|
leave;
|
|
}
|
|
|
|
if(DevInfoElem && !(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM)) {
|
|
//
|
|
// Verify that the class installer didn't do something nasty
|
|
// like delete the currently selected devinfo element!
|
|
//
|
|
for(CurDevInfoElem = pDeviceInfoSet->DeviceInfoHead;
|
|
CurDevInfoElem;
|
|
CurDevInfoElem = CurDevInfoElem->Next) {
|
|
|
|
if(CurDevInfoElem = DevInfoElem) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!CurDevInfoElem) {
|
|
//
|
|
// The class installer deleted the selected devinfo
|
|
// element. Get the newly-selected one, or fall back to
|
|
// the global driver list if none selected.
|
|
//
|
|
if(DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem) {
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
}
|
|
|
|
//
|
|
// Don't need to reset the default action flag.
|
|
//
|
|
FlagNeedsReset = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the DI_NODI_DEFAULTACTION flag if necessary.
|
|
//
|
|
if(FlagNeedsReset) {
|
|
dipb->Flags &= ~DI_NODI_DEFAULTACTION;
|
|
}
|
|
|
|
switch(CiErr) {
|
|
//
|
|
// Class installer did the select, so goto analyze
|
|
//
|
|
case NO_ERROR :
|
|
|
|
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_DYNAWIZ_ANALYZEDEV_PAGE);
|
|
break;
|
|
|
|
//
|
|
// Class installer wants us to do default.
|
|
//
|
|
case ERROR_DI_DO_DEFAULT :
|
|
|
|
InitSelectDeviceDlg(hwndDlg, ndwData);
|
|
break;
|
|
|
|
default :
|
|
//
|
|
// If we are doing an OEM select, and we fail, then
|
|
// we should init after clearing the OEM stuff.
|
|
//
|
|
if(iwd->Flags & NDW_INSTALLFLAG_CI_PICKED_OEM) {
|
|
|
|
iwd->Flags &= ~NDW_INSTALLFLAG_CI_PICKED_OEM;
|
|
|
|
//
|
|
// Destroy the existing class driver list.
|
|
//
|
|
if(DevInfoElem) {
|
|
//
|
|
// Initialize a SP_DEVINFO_DATA buffer to use as an
|
|
// argument to SetupDiDestroyDriverInfoList.
|
|
//
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DeviceInfoData
|
|
);
|
|
}
|
|
|
|
SetupDiDestroyDriverInfoList(lpdd->DevInfoSet,
|
|
DevInfoElem ? &DeviceInfoData : NULL,
|
|
SPDIT_CLASSDRIVER
|
|
);
|
|
|
|
//
|
|
// Make sure the OEM button is shown.
|
|
//
|
|
dipb->Flags |= DI_SHOWOEM;
|
|
|
|
InitSelectDeviceDlg(hwndDlg, ndwData);
|
|
|
|
} else {
|
|
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_DYNAWIZ_SELECTCLASS_PAGE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
b = FALSE;
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupIsSelectedHardwareIdValid(
|
|
IN HWND hWnd,
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iCur
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will check to see if the driver selected by the user is valid.
|
|
If the driver is not valid then we will prompt the user with a message box
|
|
telling them that the driver was not writen for their hardware and ask them
|
|
if they want to continue.
|
|
|
|
A compatible driver is considered valid if one of the Hardware or
|
|
Compatible IDs of the driver node matches a Hardware or Compatible ID of
|
|
the hardware. The only reason we have this check is that class-/
|
|
co-installers can do the list themselves so we just need to make sure they
|
|
didn't stick a driver node into the compatible list that is not really
|
|
compatible.
|
|
|
|
A class driver is considered valid if it has the same Name, section name,
|
|
and INF as a driver in the compatible list. Or if one of the Hardware or
|
|
Compatible IDs of the driver node matches a Hardware or Compatible ID of
|
|
the hardware.
|
|
|
|
Arguments:
|
|
|
|
hwnd - Supplies the window handle of the wizard dialog page.
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
iCur - The index of the selected driver in the list view.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the selected driver is valid, or if the selected driver is NOT
|
|
valid but the user said they still want to install it.
|
|
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDRIVER_NODE DriverNode;
|
|
LV_ITEM lviItem;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
LPTSTR Title = NULL;
|
|
LPTSTR Message = NULL;
|
|
UINT MessageLen;
|
|
BOOL bRet = FALSE;
|
|
LPTSTR IDBuffer = NULL;
|
|
ULONG IDBufferLen;
|
|
PTSTR pID, SelectedDriverHardwareId, SelectedDriverCompatibleId;
|
|
DWORD i, j;
|
|
int FailCount = 0;
|
|
PTSTR StringBuffers = NULL;
|
|
|
|
//
|
|
// If the DD_FLAG_USE_DEVINFO_ELEM flag is not set then we don't have a
|
|
// DevNode to validate the choosen ID against...so just return TRUE.
|
|
//
|
|
MYASSERT(lpdd);
|
|
if(!(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
//
|
|
// Figure out which device we're working with (there'd better be _some_
|
|
// device--it doesn't make sense to ask about compatibility otherwise!
|
|
//
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
MYASSERT(DevInfoElem);
|
|
if(!DevInfoElem) {
|
|
//
|
|
// Return true, since this is analogous to the case where the
|
|
// device has no hardware or compatible IDs.
|
|
//
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// We won't verify the selected driver for certain classes of devices.
|
|
// This is usually because the device is not very Plug and Play and so
|
|
// we can't tell what is a valid driver and what isn't a valid driver.
|
|
// Currently the list of classes that we don't verify are:
|
|
//
|
|
// Monitor
|
|
//
|
|
if(IsEqualGUID(&GUID_DEVCLASS_MONITOR, &(DevInfoElem->ClassGuid))) {
|
|
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
|
|
lviItem.mask = LVIF_PARAM;
|
|
lviItem.iItem = iCur;
|
|
lviItem.iSubItem = 0;
|
|
|
|
if(ListView_GetItem(lpdd->hwndDrvList, &lviItem)) {
|
|
DriverNode = GetDriverNodeFromLParam(pDeviceInfoSet, lpdd, lviItem.lParam);
|
|
} else {
|
|
DriverNode = NULL;
|
|
}
|
|
|
|
if(!DriverNode) {
|
|
MYASSERT(FALSE); // this should never happen.
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// allocate memory required for various strings to reduce stack usage
|
|
//
|
|
StringBuffers = MyMalloc(sizeof(TCHAR)*(LINE_LEN+SDT_MAX_TEXT+REGSTR_VAL_MAX_HCID_LEN));
|
|
|
|
if(StringBuffers) {
|
|
Title = StringBuffers; // [LINE_LEN]
|
|
Message = Title+LINE_LEN; // [SDT_MAX_TEXT]
|
|
IDBuffer = Message+SDT_MAX_TEXT; // [REGSTR_VAL_MAX_HCID_LEN]
|
|
} else {
|
|
leave; // out-of-memory, gotta bail!
|
|
}
|
|
|
|
//
|
|
// Retrieve the HardwareId for the selected driver.
|
|
//
|
|
SelectedDriverHardwareId = pStringTableStringFromId(
|
|
pDeviceInfoSet->StringTable,
|
|
DriverNode->HardwareId
|
|
);
|
|
|
|
for(i=0; i<2; i++) {
|
|
|
|
IDBufferLen = REGSTR_VAL_MAX_HCID_LEN;
|
|
|
|
if(CR_SUCCESS == CM_Get_DevInst_Registry_Property_Ex(
|
|
DevInfoElem->DevInst,
|
|
(i ? CM_DRP_COMPATIBLEIDS
|
|
: CM_DRP_HARDWAREID),
|
|
NULL,
|
|
IDBuffer,
|
|
&IDBufferLen,
|
|
0,
|
|
pDeviceInfoSet->hMachine)) {
|
|
//
|
|
// See if the HardwareID for the selected driver matches
|
|
// any of the Hardware or Compatible IDs for the actual
|
|
// hardware.
|
|
//
|
|
for(pID = IDBuffer; *pID; pID += (lstrlen(pID) + 1)) {
|
|
|
|
if(!lstrcmpi(SelectedDriverHardwareId, pID)) {
|
|
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if any of the Compatible IDs for the selected driver
|
|
// match any of the Hardware or Compatible IDs for the
|
|
// actual hardware
|
|
//
|
|
for(j=0; j < DriverNode->NumCompatIds; j++) {
|
|
|
|
SelectedDriverCompatibleId = pStringTableStringFromId(
|
|
pDeviceInfoSet->StringTable,
|
|
DriverNode->CompatIdList[j]
|
|
);
|
|
|
|
for(pID = IDBuffer; *pID; pID += (lstrlen(pID) + 1)) {
|
|
|
|
if(!lstrcmpi(SelectedDriverCompatibleId, pID)) {
|
|
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
FailCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If FailCount is 2 then CM_Get_DevInst_Registry_Property_Ex
|
|
// failed for both CM_DRP_HARDWAREID and CM_DRP_COMPATIBLEIDS.
|
|
// Since this DevNode does not have any Hardware or Compatible IDs
|
|
// we will let the user install any driver they want on this
|
|
// device. This usually happens only with manually installed
|
|
// devices.
|
|
//
|
|
if(FailCount == 2) {
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// At this point none of the Hardware or Compatible Ids of the
|
|
// selected driver node match any of the Hardware or Compatible Ids
|
|
// of the actual hardware. We will do one last check here if this
|
|
// is a class driver. We will see if this class driver node has
|
|
// the same description, install section, and INF as a driver in
|
|
// the compatible list. The reason for this is that we don't
|
|
// handle duplicate descriptions well for class drivers since we
|
|
// don't compute their Rank. This means that if two driver nodes
|
|
// exist in an INF with the same description we might not have the
|
|
// correct matching one in the class list.
|
|
//
|
|
if(!lpdd->bShowCompat) {
|
|
|
|
PDRIVER_NODE CurDriverNode = NULL;
|
|
|
|
//
|
|
// Enumerate through all of the compatible driver nodes
|
|
//
|
|
for(CurDriverNode = DevInfoElem->CompatDriverHead;
|
|
CurDriverNode;
|
|
CurDriverNode = CurDriverNode->Next) {
|
|
|
|
if((CurDriverNode->InfFileName == DriverNode->InfFileName) &&
|
|
(CurDriverNode->InfSectionName == DriverNode->InfSectionName) &&
|
|
(CurDriverNode->DrvDescription == DriverNode->DrvDescription)) {
|
|
|
|
//
|
|
// We found a node that matches ours in the
|
|
// compatible driver list so that means that this
|
|
// class driver node is good.
|
|
//
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The ID of the driver that the user selected does not match any
|
|
// of the Hardware or Compatible IDs of this device. Warn the user
|
|
// that this is a bad thing and see if they want to continue.
|
|
//
|
|
if(!LoadString(MyDllModuleHandle, IDS_DRIVER_UPDATE_TITLE, Title, LINE_LEN)) {
|
|
*Title = TEXT('\0');
|
|
}
|
|
|
|
MessageLen = LoadString(MyDllModuleHandle, IDS_DRIVER_NOMATCH1, Message, SDT_MAX_TEXT);
|
|
MessageLen += LoadString(MyDllModuleHandle, IDS_DRIVER_NOMATCH2, Message + MessageLen, SDT_MAX_TEXT - MessageLen);
|
|
MessageLen += LoadString(MyDllModuleHandle, IDS_DRIVER_NOMATCH3, Message + MessageLen, SDT_MAX_TEXT - MessageLen);
|
|
|
|
if(MessageLen) {
|
|
|
|
if(IDYES == MessageBox(hWnd,
|
|
Message,
|
|
Title,
|
|
MB_YESNO
|
|
| MB_TASKMODAL
|
|
| MB_ICONEXCLAMATION
|
|
| MB_DEFBUTTON2)) {
|
|
|
|
bRet = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
if(StringBuffers) {
|
|
MyFree(StringBuffers);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
VOID
|
|
SetSelectedDriverNode(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
IN INT iCur
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the selected driver for the currently selected device (or
|
|
global class driver list if no device selected) in the device information
|
|
set referenced in the SP_DIALOGDATA structure. If the
|
|
DD_FLAG_USE_DEVINFO_ELEM flag in the structure is set, then the driver is
|
|
selected for the set or element based on the DevInfoElem pointer instead of
|
|
the currently selected one.
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data structure that contains
|
|
information about the device information set being used.
|
|
|
|
iCur - Supplies the index within the driver listbox window containing the
|
|
driver to be selected.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDRIVER_NODE DriverNode;
|
|
LV_ITEM lviItem;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
TCHAR ClassGuidString[GUID_STRING_LEN];
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
lviItem.mask = LVIF_PARAM;
|
|
lviItem.iItem = iCur;
|
|
lviItem.iSubItem = 0;
|
|
|
|
if(ListView_GetItem(lpdd->hwndDrvList, &lviItem)) {
|
|
DriverNode = GetDriverNodeFromLParam(pDeviceInfoSet, lpdd, lviItem.lParam);
|
|
} else {
|
|
DriverNode = NULL;
|
|
}
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
//
|
|
// If a driver node is being selected, update the device's class to
|
|
// ensure it matches the class of the INF containing the selected
|
|
// driver node.
|
|
//
|
|
if(DriverNode) {
|
|
//
|
|
// Get the INF class GUID for this driver node in string form,
|
|
// because this property is stored as a REG_SZ.
|
|
//
|
|
pSetupStringFromGuid(&(pDeviceInfoSet->GuidTable[DriverNode->GuidIndex]),
|
|
ClassGuidString,
|
|
SIZECHARS(ClassGuidString)
|
|
);
|
|
|
|
//
|
|
// Fill in a SP_DEVINFO_DATA structure for the upcoming call to
|
|
// SetupDiSetDeviceRegistryProperty.
|
|
//
|
|
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DevInfoData
|
|
);
|
|
|
|
if(!SetupDiSetDeviceRegistryProperty(lpdd->DevInfoSet,
|
|
&DevInfoData,
|
|
SPDRP_CLASSGUID,
|
|
(PBYTE)ClassGuidString,
|
|
sizeof(ClassGuidString))) {
|
|
//
|
|
// The class cannot be updated--don't change the selected
|
|
// driver.
|
|
//
|
|
leave;
|
|
}
|
|
}
|
|
|
|
DevInfoElem->SelectedDriver = DriverNode;
|
|
if(DriverNode) {
|
|
DevInfoElem->SelectedDriverType = (lpdd->bShowCompat)
|
|
? SPDIT_COMPATDRIVER
|
|
: SPDIT_CLASSDRIVER;
|
|
} else {
|
|
DevInfoElem->SelectedDriverType = SPDIT_NODRIVER;
|
|
}
|
|
} else {
|
|
pDeviceInfoSet->SelectedClassDriver = DriverNode;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleSelectOEM(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine selects a new device based on a user-supplied path. Calling
|
|
this routine may cause a driver list to get built, which is a potentially
|
|
slow operation.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Supplies the window handle of the select device wizard page.
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error
|
|
code indicating the cause of failure.
|
|
|
|
--*/
|
|
{
|
|
PSP_DIALOGDATA lpdd;
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
DWORD Err;
|
|
|
|
MYASSERT(ndwData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
//
|
|
// If this is for a particular device, then initialize a device
|
|
// information structure to use for SelectOEMDriver.
|
|
//
|
|
if(DevInfoElem) {
|
|
|
|
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DevInfoData
|
|
);
|
|
}
|
|
|
|
//
|
|
// Unlock the device information set before popping up the OEM driver
|
|
// selection UI. Otherwise, our multi-threaded dialog will deadlock.
|
|
//
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
pDeviceInfoSet = NULL;
|
|
|
|
if((Err = SelectOEMDriver(hwndDlg,
|
|
lpdd->DevInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
!(lpdd->flags & DD_FLAG_IS_DIALOGBOX)
|
|
)) == ERROR_DI_DO_DEFAULT) {
|
|
|
|
if(DevInfoElem && DevInfoElem->CompatDriverHead) {
|
|
|
|
lpdd->bShowCompat = TRUE;
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), SW_SHOW);
|
|
|
|
} else {
|
|
|
|
lpdd->bShowCompat = FALSE;
|
|
|
|
//
|
|
// Since we don't have any compatible drivers to show, we will
|
|
// hide the show compatible and show class selection buttons.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), SW_HIDE);
|
|
}
|
|
|
|
CheckDlgButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_COMPAT,
|
|
lpdd->bShowCompat ? BST_CHECKED : BST_UNCHECKED
|
|
);
|
|
|
|
//
|
|
// Enable the UI. We turned it off when the user pressed the Have
|
|
// Disk... button
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, TRUE);
|
|
|
|
//
|
|
// Fill in the list to select from
|
|
//
|
|
FillInDeviceList(hwndDlg, lpdd);
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SelectWindowsUpdateDriver(
|
|
IN HWND hwndParent, OPTIONAL
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
|
|
IN PTSTR CDMPath,
|
|
IN BOOL IsWizard
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the worker routine that actually allows for the selection of a
|
|
Windows Update driver.
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Optionally, supplies the window handle that is to be the
|
|
parent for any selection UI. If this parameter is not supplied, then
|
|
the hwndParent field of the devinfo set or element will be used.
|
|
|
|
DeviceInfoSet - Supplies the handle of the device info set for which a
|
|
Windows Update driver selection is to be performed.
|
|
|
|
DeviceInfoData - Optionally, supplies the address of the device information
|
|
element to select a driver for. If this parameter is not supplied,
|
|
then a Windows Update driver for the global class driver list will be
|
|
selected.
|
|
|
|
If a compatible driver was found for this device, the device
|
|
information element will have its class GUID updated upon return to
|
|
reflect the device's new class.
|
|
|
|
CDMPath - Supplies the directory path where Windows Update downloaded the
|
|
driver package(s) to.
|
|
|
|
IsWizard - Specifies whether this routine is being called in the context of
|
|
a select device wizard page.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error
|
|
code indicating the cause of failure.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem = NULL;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
DWORD Err = NO_ERROR;
|
|
HWND hwndSave;
|
|
LONG DriverPathSave;
|
|
DWORD DriverPathFlagsSave;
|
|
BOOL bRestoreHwnd = FALSE, bRestoreDriverPath = FALSE;
|
|
UINT NewClassDriverCount;
|
|
TCHAR Title[MAX_TITLE_LEN];
|
|
HCURSOR hOldCursor;
|
|
BOOL bDoneWithDrvSearch = TRUE;
|
|
|
|
PDRIVER_NODE lpOrgCompat;
|
|
PDRIVER_NODE lpOrgCompatTail;
|
|
UINT OrgCompatCount;
|
|
PDRIVER_NODE lpOrgClass;
|
|
PDRIVER_NODE lpOrgClassTail;
|
|
UINT OrgClassCount;
|
|
PDRIVER_NODE lpOrgSel;
|
|
PDRIVER_NODE CurDriverNode;
|
|
PDRIVER_NODE DriverNodeHead = NULL;
|
|
DWORD dwOrgSelType;
|
|
DWORD dwOrgFlags;
|
|
DWORD dwOrgFlagsEx;
|
|
BOOL bRestoreDeviceInfo = FALSE;
|
|
LONG DriverPathId;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(DeviceInfoData) {
|
|
//
|
|
// Then we're working with a particular device.
|
|
//
|
|
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL))) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
}
|
|
|
|
//
|
|
// Make this selection window the parent window for Windows Update UI
|
|
//
|
|
if(hwndParent) {
|
|
hwndSave = dipb->hwndParent;
|
|
dipb->hwndParent = hwndParent;
|
|
bRestoreHwnd = TRUE;
|
|
}
|
|
|
|
//
|
|
// Don't assume there is no old path. Save old one and pretend there
|
|
// is no old one in case of cancel.
|
|
//
|
|
DriverPathSave = dipb->DriverPath;
|
|
dipb->DriverPath = -1;
|
|
|
|
//
|
|
// Clear the DI_ENUMSINGLEINF flag, because we're going to be getting
|
|
// a path to a directory, _not_ to an individual INF
|
|
//
|
|
DriverPathFlagsSave = dipb->Flags & DI_ENUMSINGLEINF;
|
|
dipb->Flags &= ~DI_ENUMSINGLEINF;
|
|
bRestoreDriverPath = TRUE;
|
|
|
|
//
|
|
// Set the DriverPath to the path returned by CDM
|
|
//
|
|
if((DriverPathId = pStringTableAddString(
|
|
pDeviceInfoSet->StringTable,
|
|
CDMPath,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE,
|
|
NULL,0)) == -1) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
leave;
|
|
|
|
} else {
|
|
|
|
dipb->DriverPath = DriverPathId;
|
|
|
|
}
|
|
|
|
//
|
|
// Save the Original List info, in case we get
|
|
// an empty list on the Windows Update information.
|
|
//
|
|
// Note: we don't attempt to save/restore our driver enumeration hints
|
|
//
|
|
if(DevInfoElem) {
|
|
lpOrgClass = DevInfoElem->ClassDriverHead;
|
|
lpOrgClassTail = DevInfoElem->ClassDriverTail;
|
|
OrgClassCount = DevInfoElem->ClassDriverCount;
|
|
|
|
lpOrgSel = DevInfoElem->SelectedDriver;
|
|
dwOrgSelType = DevInfoElem->SelectedDriverType;
|
|
} else {
|
|
lpOrgClass = pDeviceInfoSet->ClassDriverHead;
|
|
lpOrgClassTail = pDeviceInfoSet->ClassDriverTail;
|
|
OrgClassCount = pDeviceInfoSet->ClassDriverCount;
|
|
|
|
lpOrgSel = pDeviceInfoSet->SelectedClassDriver;
|
|
dwOrgSelType = lpOrgSel ? SPDIT_CLASSDRIVER : SPDIT_NODRIVER;
|
|
}
|
|
|
|
dwOrgFlags = dipb->Flags;
|
|
dwOrgFlagsEx = dipb->FlagsEx;
|
|
|
|
bRestoreDeviceInfo = TRUE;
|
|
|
|
if(DevInfoElem) {
|
|
DevInfoElem->ClassDriverHead = DevInfoElem->ClassDriverTail = NULL;
|
|
DevInfoElem->ClassDriverCount = 0;
|
|
DevInfoElem->ClassDriverEnumHint = NULL;
|
|
DevInfoElem->ClassDriverEnumHintIndex = INVALID_ENUM_INDEX;
|
|
|
|
DevInfoElem->SelectedDriver = NULL;
|
|
DevInfoElem->SelectedDriverType = SPDIT_NODRIVER;
|
|
} else {
|
|
lpOrgCompat = NULL; // just so we won't ever try to free this list.
|
|
|
|
pDeviceInfoSet->ClassDriverHead = pDeviceInfoSet->ClassDriverTail = NULL;
|
|
pDeviceInfoSet->ClassDriverCount = 0;
|
|
pDeviceInfoSet->ClassDriverEnumHint = NULL;
|
|
pDeviceInfoSet->ClassDriverEnumHintIndex = INVALID_ENUM_INDEX;
|
|
|
|
pDeviceInfoSet->SelectedClassDriver = NULL;
|
|
}
|
|
|
|
dipb->Flags &= ~(DI_DIDCLASS | DI_MULTMFGS);
|
|
dipb->FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST;
|
|
|
|
|
|
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); // Potentially slow operation ahead!
|
|
dipb->FlagsEx |= DI_FLAGSEX_INET_DRIVER;
|
|
bDoneWithDrvSearch = FALSE;
|
|
|
|
Err = GLE_FN_CALL(FALSE,
|
|
SetupDiBuildDriverInfoList(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDIT_CLASSDRIVER)
|
|
);
|
|
|
|
dipb->FlagsEx &= ~DI_FLAGSEX_INET_DRIVER;
|
|
SetCursor(hOldCursor); // done with slow operation.
|
|
bDoneWithDrvSearch = TRUE;
|
|
|
|
if(Err != NO_ERROR) {
|
|
leave;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
NewClassDriverCount = DevInfoElem->ClassDriverCount;
|
|
DriverNodeHead = DevInfoElem->ClassDriverHead;
|
|
} else {
|
|
NewClassDriverCount = pDeviceInfoSet->ClassDriverCount;
|
|
DriverNodeHead = pDeviceInfoSet->ClassDriverHead;
|
|
}
|
|
|
|
if(!NewClassDriverCount) {
|
|
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_SELECT_DEVICE,
|
|
Title,
|
|
SIZECHARS(Title))) {
|
|
*Title = TEXT('\0');
|
|
}
|
|
|
|
FormatMessageBox(MyDllModuleHandle,
|
|
NULL,
|
|
MSG_NO_DEVICEINFO_ERROR,
|
|
Title,
|
|
MB_OK | MB_TASKMODAL
|
|
);
|
|
|
|
Err = ERROR_DI_BAD_PATH;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// If we get to here, then we have at least one class driver--the class
|
|
// driver list head had better point to something!
|
|
//
|
|
MYASSERT(DriverNodeHead);
|
|
|
|
//
|
|
// Assume every driver node in the list is signed. Note that this
|
|
// may not actually be the case, as Windows Update may have given
|
|
// us a "stub" INF whose only use is to build up a list of choices
|
|
// to present to the user. After the user makes a selection, the
|
|
// relevant ID will then be sent to Windows Update to retrieve the
|
|
// real (signed) package.
|
|
//
|
|
for(CurDriverNode=DriverNodeHead;
|
|
CurDriverNode;
|
|
CurDriverNode = CurDriverNode->Next) {
|
|
|
|
CurDriverNode->Flags |= DNF_INF_IS_SIGNED;
|
|
}
|
|
|
|
//
|
|
// We're happy with our new class driver list, so destroy the original
|
|
// list
|
|
//
|
|
if(bRestoreDeviceInfo) {
|
|
DereferenceClassDriverList(pDeviceInfoSet, lpOrgClass);
|
|
bRestoreDeviceInfo = FALSE;
|
|
}
|
|
|
|
//
|
|
// We're happy with the new path, so we don't want to restore the
|
|
// original path.
|
|
//
|
|
bRestoreDriverPath = FALSE;
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
|
|
if(!bDoneWithDrvSearch) {
|
|
dipb->FlagsEx &= ~DI_FLAGSEX_INET_DRIVER;
|
|
SetCursor(hOldCursor);
|
|
}
|
|
}
|
|
|
|
if(bRestoreDeviceInfo || bRestoreHwnd || bRestoreDriverPath) {
|
|
|
|
try {
|
|
//
|
|
// If necessary, restore the original list(s).
|
|
//
|
|
if(bRestoreDeviceInfo) {
|
|
|
|
if(DevInfoElem) {
|
|
|
|
DereferenceClassDriverList(pDeviceInfoSet, DevInfoElem->ClassDriverHead);
|
|
DevInfoElem->ClassDriverHead = lpOrgClass;
|
|
DevInfoElem->ClassDriverTail = lpOrgClassTail;
|
|
DevInfoElem->ClassDriverCount = OrgClassCount;
|
|
|
|
DevInfoElem->SelectedDriver = lpOrgSel;
|
|
DevInfoElem->SelectedDriverType = dwOrgSelType;
|
|
|
|
} else {
|
|
|
|
DereferenceClassDriverList(pDeviceInfoSet, pDeviceInfoSet->ClassDriverHead);
|
|
pDeviceInfoSet->ClassDriverHead = lpOrgClass;
|
|
pDeviceInfoSet->ClassDriverTail = lpOrgClassTail;
|
|
pDeviceInfoSet->ClassDriverCount = OrgClassCount;
|
|
|
|
pDeviceInfoSet->SelectedClassDriver = lpOrgSel;
|
|
}
|
|
|
|
dipb->Flags = dwOrgFlags;
|
|
dipb->FlagsEx = dwOrgFlagsEx;
|
|
}
|
|
|
|
//
|
|
// If the install param block needs its parent hwnd restored, do so
|
|
// now.
|
|
//
|
|
if(bRestoreHwnd) {
|
|
dipb->hwndParent = hwndSave;
|
|
}
|
|
|
|
//
|
|
// Likewise, restore the old driver path if necessary.
|
|
//
|
|
if(bRestoreDriverPath) {
|
|
dipb->DriverPath = DriverPathSave;
|
|
dipb->Flags |= DriverPathFlagsSave;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(),
|
|
ERROR_INVALID_PARAMETER,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleWindowsUpdate(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine selects a new driver based on a list from INF file(s) that get
|
|
downloaded from the Windows Update Internet site.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Supplies the window handle of the select device wizard page.
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error
|
|
code indicating the cause of failure.
|
|
|
|
--*/
|
|
{
|
|
PSP_DIALOGDATA lpdd;
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
SP_WINDOWSUPDATE_PARAMS WindowsUpdateParams;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
TCHAR CDMPath[MAX_PATH];
|
|
ULONG BufferLen;
|
|
DOWNLOADINFO DownloadInfo;
|
|
HMODULE hModCDM = NULL;
|
|
DOWNLOAD_UPDATED_FILES_PROC DownloadUpdateFilesProc;
|
|
DWORD Err;
|
|
TCHAR Title[MAX_TITLE_LEN];
|
|
SPFUSIONINSTANCE spFusionInstance;
|
|
BOOL bLeaveFusionContext = FALSE;
|
|
|
|
MYASSERT(ndwData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
//
|
|
// If this is for a particular device, then initialize a device
|
|
// information structure.
|
|
//
|
|
if(DevInfoElem) {
|
|
|
|
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DevInfoData
|
|
);
|
|
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
}
|
|
|
|
//
|
|
// Call the class installer to get the package ID and so it can open
|
|
// a handle to the Windows Update server.
|
|
//
|
|
ZeroMemory(&WindowsUpdateParams, sizeof(SP_WINDOWSUPDATE_PARAMS));
|
|
WindowsUpdateParams.ClassInstallHeader.InstallFunction = DIF_GETWINDOWSUPDATEINFO;
|
|
WindowsUpdateParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
|
|
Err = DoInstallActionWithParams(DIF_GETWINDOWSUPDATEINFO,
|
|
pDeviceInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
&WindowsUpdateParams.ClassInstallHeader,
|
|
sizeof(SP_WINDOWSUPDATE_PARAMS),
|
|
INSTALLACTION_CALL_CI
|
|
);
|
|
if(Err != NO_ERROR) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// We now have a PackageId and a handle to the Windows Update
|
|
// server.
|
|
//
|
|
|
|
//
|
|
// Fill In the DOWNLOADINFO structure to pass to CDM.DLL
|
|
//
|
|
ZeroMemory(&DownloadInfo, sizeof(DOWNLOADINFO));
|
|
DownloadInfo.dwDownloadInfoSize = sizeof(DOWNLOADINFO);
|
|
DownloadInfo.lpFile = NULL;
|
|
|
|
DownloadInfo.lpHardwareIDs = (LPCWSTR)WindowsUpdateParams.PackageId;
|
|
|
|
DownloadInfo.lpDeviceInstanceID = NULL;
|
|
|
|
GetVersionEx((OSVERSIONINFOW *)&DownloadInfo.OSVersionInfo);
|
|
|
|
//
|
|
// Set dwArchitecture to PROCESSOR_ARCHITECTURE_UNKNOWN, this
|
|
// causes Windows Update to check base on the architecture of the
|
|
// machine itself. You only need to explictly set the value if
|
|
// you want to download drivers for a different architecture than
|
|
// the machine this is running on.
|
|
//
|
|
DownloadInfo.dwArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
|
|
DownloadInfo.dwFlags = 0;
|
|
DownloadInfo.dwClientID = 0;
|
|
DownloadInfo.localid = 0;
|
|
|
|
CDMPath[0] = TEXT('\0');
|
|
|
|
//
|
|
// Dynamically retrieve the CDM function we need to call.
|
|
//
|
|
bLeaveFusionContext = spFusionEnterContext(NULL, &spFusionInstance);
|
|
|
|
Err = GLE_FN_CALL(NULL, hModCDM = LoadLibrary(TEXT("cdm.dll")));
|
|
|
|
if(Err == NO_ERROR) {
|
|
|
|
Err = GLE_FN_CALL(NULL,
|
|
DownloadUpdateFilesProc = (PVOID)GetProcAddress(
|
|
hModCDM,
|
|
"DownloadUpdatedFiles")
|
|
);
|
|
}
|
|
|
|
if(Err == NO_ERROR) {
|
|
|
|
Err = GLE_FN_CALL(FALSE,
|
|
DownloadUpdateFilesProc(WindowsUpdateParams.CDMContext,
|
|
hwndDlg,
|
|
&DownloadInfo,
|
|
CDMPath,
|
|
sizeof(CDMPath),
|
|
&BufferLen)
|
|
);
|
|
|
|
if((Err == NO_ERROR) && !(*CDMPath)) {
|
|
Err = ERROR_DI_BAD_PATH;
|
|
}
|
|
}
|
|
|
|
if(hModCDM) {
|
|
FreeLibrary(hModCDM);
|
|
hModCDM = NULL;
|
|
}
|
|
|
|
if(bLeaveFusionContext) {
|
|
spFusionLeaveContext(&spFusionInstance);
|
|
bLeaveFusionContext = FALSE;
|
|
}
|
|
|
|
if(Err != NO_ERROR) {
|
|
//
|
|
// Pop up an error messagebox informing the user that we didn't
|
|
// get any drivers from Windows Update.
|
|
//
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_SELECT_DEVICE,
|
|
Title,
|
|
SIZECHARS(Title))) {
|
|
*Title = TEXT('\0');
|
|
}
|
|
|
|
FormatMessageBox(MyDllModuleHandle,
|
|
NULL,
|
|
MSG_NO_DEVICEINFO_ERROR,
|
|
Title,
|
|
MB_OK | MB_TASKMODAL
|
|
);
|
|
leave;
|
|
}
|
|
|
|
Err = SelectWindowsUpdateDriver(hwndDlg,
|
|
lpdd->DevInfoSet,
|
|
DevInfoElem ? &DevInfoData : NULL,
|
|
CDMPath,
|
|
!(lpdd->flags & DD_FLAG_IS_DIALOGBOX)
|
|
);
|
|
|
|
if(Err != NO_ERROR) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Fill in the list to select from
|
|
//
|
|
lpdd->bShowCompat = FALSE;
|
|
CheckDlgButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_COMPAT,
|
|
BST_UNCHECKED
|
|
);
|
|
|
|
//
|
|
// Enable the UI. We turned it off when the user pressed the Windows
|
|
// Update... button
|
|
//
|
|
ToggleDialogControls(hwndDlg, ndwData, TRUE);
|
|
|
|
FillInDeviceList(hwndDlg, lpdd);
|
|
|
|
//
|
|
// Since we only show the class list when selecting from Windows
|
|
// Update, hide the selection buttons.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), SW_HIDE);
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
|
|
if(hModCDM) {
|
|
FreeLibrary(hModCDM);
|
|
}
|
|
|
|
if(bLeaveFusionContext) {
|
|
spFusionLeaveContext(&spFusionInstance);
|
|
}
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
PNEWDEVWIZ_DATA
|
|
GetNewDevWizDataFromPsPage(
|
|
LPPROPSHEETPAGE ppsp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves a pointer to a NEWDEVWIZDATA structure to be used by
|
|
a wizard page dialog proc. It is called during the WM_INITDIALOG handling.
|
|
|
|
Arguments:
|
|
|
|
Page - Property sheet page structure for this wizard page.
|
|
|
|
Return Value:
|
|
|
|
If success, a pointer to the structure, NULL otherwise.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PVOID WizObjectId;
|
|
PWIZPAGE_OBJECT CurWizObject = NULL;
|
|
|
|
//
|
|
// Access the device info set handle stored in the propsheetpage's lParam.
|
|
//
|
|
MYASSERT(ppsp);
|
|
if(pDeviceInfoSet = AccessDeviceInfoSet((HDEVINFO)(ppsp->lParam))) {
|
|
|
|
try {
|
|
//
|
|
// The ObjectID (pointer, actually) for the corresponding wizard
|
|
// object for this page is stored at the end of the ppsp structure.
|
|
// Retrieve this now, and look for it in the devinfo set's list of
|
|
// wizard objects.
|
|
//
|
|
WizObjectId = *((PVOID *)(&(((PBYTE)ppsp)[sizeof(PROPSHEETPAGE)])));
|
|
|
|
for(CurWizObject = pDeviceInfoSet->WizPageList;
|
|
CurWizObject;
|
|
CurWizObject = CurWizObject->Next) {
|
|
|
|
if(WizObjectId == CurWizObject) {
|
|
//
|
|
// We found our object.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
CurWizObject = NULL;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
return CurWizObject ? CurWizObject->ndwData : NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupDiSelectDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Default handler for DIF_SELECTDEVICE
|
|
|
|
This routine will handle the UI for allowing a user to select a driver
|
|
for the specified device information set or element. By using the Flags
|
|
field of the installation parameter block struct, the caller can specify
|
|
special handling of the UI, such as allowing selecting from OEM disks.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a handle to the device information set for which a
|
|
driver is to be selected.
|
|
|
|
DeviceInfoData - Optionally, supplies the address of a SP_DEVINFO_DATA
|
|
structure for which a driver is to be selected. If this parameter is
|
|
not specified, then a driver will be selected for the global class
|
|
driver list associated with the device information set itself.
|
|
|
|
This is an IN OUT parameter because the class GUID for the device will
|
|
be updated to reflect the class of the most-compatible driver, if a
|
|
compatible driver list was built.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE.
|
|
If the function fails, the return value is FALSE. To get extended error
|
|
information, call GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
|
|
DWORD Err = NO_ERROR;
|
|
PDEVINFO_ELEM DevInfoElem = NULL;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
WIZPAGE_OBJECT WizPageObject;
|
|
NEWDEVWIZ_DATA ndwData;
|
|
PWIZPAGE_OBJECT CurWizObject, PrevWizObject;
|
|
//
|
|
// Store the address of the corresponding wizard object at the
|
|
// end of the PROPSHEETPAGE buffer.
|
|
//
|
|
BYTE pspBuffer[sizeof(PROPSHEETPAGE) + sizeof(PVOID)];
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)pspBuffer;
|
|
|
|
try {
|
|
//
|
|
// Make sure we're running interactively.
|
|
//
|
|
if(GlobalSetupFlags & (PSPGF_NONINTERACTIVE|PSPGF_UNATTENDED_SETUP)) {
|
|
Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
|
|
leave;
|
|
}
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
Err = ERROR_INVALID_HANDLE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// This routine cannot be called when the lock level is nested
|
|
// (i.e., > 1). This is explicitly disallowed, so that our multi-
|
|
// threaded dialog won't deadlock.
|
|
//
|
|
if(pDeviceInfoSet->LockRefCount > 1) {
|
|
Err = ERROR_DEVINFO_LIST_LOCKED;
|
|
leave;
|
|
}
|
|
|
|
if(DeviceInfoData) {
|
|
//
|
|
// Special check to make sure we aren't being passed a zombie
|
|
// (different from phantom, the zombie devinfo element is one whose
|
|
// corresponding devinst was deleted via SetupDiRemoveDevice, but
|
|
// who lingers on until the caller kills it via
|
|
// SetupDiDeleteDeviceInfo or SetupDiDestroyDeviceInfoList).
|
|
//
|
|
if(!DeviceInfoData->DevInst) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Then we are to select a driver for a particular device.
|
|
//
|
|
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL))) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
dipb = &(DevInfoElem->InstallParamBlock);
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
}
|
|
|
|
ZeroMemory(&ndwData, sizeof(ndwData));
|
|
ndwData.ddData.iCurDesc = -1;
|
|
ndwData.ddData.DevInfoSet = DeviceInfoSet;
|
|
ndwData.ddData.DevInfoElem = DevInfoElem;
|
|
ndwData.ddData.flags = DD_FLAG_USE_DEVINFO_ELEM | DD_FLAG_IS_DIALOGBOX;
|
|
|
|
WizPageObject.RefCount = 1;
|
|
WizPageObject.ndwData = &ndwData;
|
|
//
|
|
// We're safe in placing this stack object in the devinfo set's linked
|
|
// list, since nobody will ever attempt to free it.
|
|
//
|
|
WizPageObject.Next = pDeviceInfoSet->WizPageList;
|
|
pDeviceInfoSet->WizPageList = &WizPageObject;
|
|
|
|
//
|
|
// Since we're using the same code as the Add New Device Wizard, we
|
|
// have to supply a LPROPSHEETPAGE as the lParam to the DialogProc.
|
|
// (All we care about is the lParam field, and the DWORD at the end
|
|
// of the buffer.)
|
|
//
|
|
Page->lParam = (LPARAM)DeviceInfoSet;
|
|
|
|
*((PVOID *)(&(pspBuffer[sizeof(PROPSHEETPAGE)]))) = &WizPageObject;
|
|
|
|
//
|
|
// Release the lock, so other stuff can happen while this dialog is up.
|
|
//
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
pDeviceInfoSet = NULL;
|
|
|
|
Err = (DWORD)DialogBoxParam(MyDllModuleHandle,
|
|
MAKEINTRESOURCE(DLG_DEVINSTALL),
|
|
dipb->hwndParent,
|
|
SelectDeviceDlgProc,
|
|
(LPARAM)Page
|
|
);
|
|
|
|
//
|
|
// Re-acquire the devinfo set lock.
|
|
//
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
// this would imply another thread messed this handle up
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
Err = ERROR_INVALID_HANDLE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Now remove the wizard page object from the devinfo set's list. We
|
|
// can't assume that it's still at the head of the list, since someone
|
|
// else couldn've added another page.
|
|
//
|
|
for(CurWizObject = pDeviceInfoSet->WizPageList, PrevWizObject = NULL;
|
|
CurWizObject;
|
|
PrevWizObject = CurWizObject, CurWizObject = CurWizObject->Next) {
|
|
|
|
if(CurWizObject == &WizPageObject) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MYASSERT(CurWizObject);
|
|
|
|
if(PrevWizObject) {
|
|
PrevWizObject->Next = CurWizObject->Next;
|
|
} else {
|
|
pDeviceInfoSet->WizPageList = CurWizObject->Next;
|
|
}
|
|
|
|
if(DeviceInfoData) {
|
|
//
|
|
// Update the caller's device information element with its
|
|
// (potentially) new class.
|
|
//
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
DeviceInfoData
|
|
);
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
SetLastError(Err);
|
|
return (Err == NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL
|
|
bNoDevsToShow(
|
|
IN PDEVINFO_ELEM DevInfoElem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether or not there are any compatible devices to
|
|
be displayed for the specified devinfo element.
|
|
|
|
Arguments:
|
|
|
|
DevInfoElem - Supplies the address of a devinfo element to check.
|
|
|
|
Return Value:
|
|
|
|
If there are no devices to show, the return value is TRUE.
|
|
If there is at least one device (driver node) without the
|
|
DNF_EXCLUDEFROMLIST flag set, the return value is FALSE.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE CurDriverNode;
|
|
|
|
for(CurDriverNode = DevInfoElem->CompatDriverHead;
|
|
CurDriverNode;
|
|
CurDriverNode = CurDriverNode->Next) {
|
|
|
|
if(!(CurDriverNode->Flags & DNF_OLD_INET_DRIVER) &&
|
|
!(CurDriverNode->Flags & DNF_BAD_DRIVER) &&
|
|
(!(CurDriverNode->Flags & DNF_EXCLUDEFROMLIST) ||
|
|
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALLOWEXCLUDEDDRVS))) {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
OnCancel(
|
|
IN PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is only called in the select device dialog (not wizard) case.
|
|
Its sole purpose is to destroy any driver lists that weren't present before
|
|
SetupDiSelectDevice was called.
|
|
|
|
Arguments:
|
|
|
|
ndwData - Supplies the address of a data structure containing information
|
|
on the driver lists to be (possibly) destroyed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PSP_DIALOGDATA lpdd;
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
DWORD SelectedDriverType = SPDIT_NODRIVER;
|
|
|
|
MYASSERT(ndwData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
|
|
if(lpdd->bKeeplpSelectedDrv) {
|
|
SelectedDriverType = DevInfoElem->SelectedDriverType;
|
|
} else {
|
|
DevInfoElem->SelectedDriver = NULL;
|
|
DevInfoElem->SelectedDriverType = SPDIT_NODRIVER;
|
|
}
|
|
|
|
if((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST) &&
|
|
!lpdd->bKeeplpClassDrvList && (SelectedDriverType != SPDIT_CLASSDRIVER)) {
|
|
|
|
DereferenceClassDriverList(pDeviceInfoSet, DevInfoElem->ClassDriverHead);
|
|
DevInfoElem->ClassDriverHead = DevInfoElem->ClassDriverTail = NULL;
|
|
DevInfoElem->ClassDriverCount = 0;
|
|
DevInfoElem->ClassDriverEnumHint = NULL;
|
|
DevInfoElem->ClassDriverEnumHintIndex = INVALID_ENUM_INDEX;
|
|
DevInfoElem->InstallParamBlock.Flags &= ~(DI_DIDCLASS | DI_MULTMFGS);
|
|
DevInfoElem->InstallParamBlock.FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST;
|
|
}
|
|
|
|
if((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDCOMPATINFO) &&
|
|
!lpdd->bKeeplpCompatDrvList && (SelectedDriverType != SPDIT_COMPATDRIVER)) {
|
|
|
|
DestroyDriverNodes(DevInfoElem->CompatDriverHead, pDeviceInfoSet);
|
|
DevInfoElem->CompatDriverHead = DevInfoElem->CompatDriverTail = NULL;
|
|
DevInfoElem->CompatDriverCount = 0;
|
|
DevInfoElem->CompatDriverEnumHint = NULL;
|
|
DevInfoElem->CompatDriverEnumHintIndex = INVALID_ENUM_INDEX;
|
|
DevInfoElem->InstallParamBlock.Flags &= ~DI_DIDCOMPAT;
|
|
DevInfoElem->InstallParamBlock.FlagsEx &= ~DI_FLAGSEX_DIDCOMPATINFO;
|
|
}
|
|
|
|
} else {
|
|
|
|
if(lpdd->bKeeplpSelectedDrv) {
|
|
if(pDeviceInfoSet->SelectedClassDriver) {
|
|
SelectedDriverType = SPDIT_CLASSDRIVER;
|
|
}
|
|
} else {
|
|
pDeviceInfoSet->SelectedClassDriver = NULL;
|
|
}
|
|
|
|
if((pDeviceInfoSet->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST) &&
|
|
!lpdd->bKeeplpClassDrvList && (SelectedDriverType != SPDIT_CLASSDRIVER)) {
|
|
|
|
DereferenceClassDriverList(pDeviceInfoSet, pDeviceInfoSet->ClassDriverHead);
|
|
pDeviceInfoSet->ClassDriverHead = pDeviceInfoSet->ClassDriverTail = NULL;
|
|
pDeviceInfoSet->ClassDriverCount = 0;
|
|
pDeviceInfoSet->ClassDriverEnumHint = NULL;
|
|
pDeviceInfoSet->ClassDriverEnumHintIndex = INVALID_ENUM_INDEX;
|
|
pDeviceInfoSet->InstallParamBlock.Flags &= ~(DI_DIDCLASS | DI_MULTMFGS);
|
|
pDeviceInfoSet->InstallParamBlock.FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST;
|
|
}
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
LONG
|
|
GetCurDesc(
|
|
IN PSP_DIALOGDATA lpdd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the (case-insensitive) string table index for the
|
|
description of the currently selected driver. This is used to select a
|
|
particular entry in a listview control.
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data structure that contains
|
|
information about the device information set being used.
|
|
|
|
Return Value:
|
|
|
|
The string table ID for the device description, as stored in the currently-
|
|
selected driver node.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
LONG ret;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return -1;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
ret = DevInfoElem->SelectedDriver
|
|
? DevInfoElem->SelectedDriver->DevDescription
|
|
: -1;
|
|
} else {
|
|
ret = pDeviceInfoSet->SelectedClassDriver
|
|
? pDeviceInfoSet->SelectedClassDriver->DevDescription
|
|
: -1;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
ret = -1;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
VOID
|
|
__cdecl
|
|
ClassDriverSearchThread(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Thread entry point to build a class driver list asynchronously to the main
|
|
thread which is displaying a Select Device dialog. This thread will free
|
|
the memory containing its context, so the main thread should not access it
|
|
after passing it to this thread.
|
|
|
|
Arguments:
|
|
|
|
Context - supplies driver search context.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLASSDRV_THREAD_CONTEXT ClassDrvThreadContext = Context;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
BOOL b;
|
|
DWORD Err;
|
|
|
|
//
|
|
// OR in the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS flag so that we don't
|
|
// include old internet drivers in the list that we get back.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if(SetupDiGetDeviceInstallParams(ClassDrvThreadContext->DeviceInfoSet,
|
|
&(ClassDrvThreadContext->DeviceInfoData),
|
|
&DeviceInstallParams))
|
|
{
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS;
|
|
|
|
SetupDiSetDeviceInstallParams(ClassDrvThreadContext->DeviceInfoSet,
|
|
&(ClassDrvThreadContext->DeviceInfoData),
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
Err = GLE_FN_CALL(FALSE,
|
|
b = SetupDiBuildDriverInfoList(
|
|
ClassDrvThreadContext->DeviceInfoSet,
|
|
&(ClassDrvThreadContext->DeviceInfoData),
|
|
SPDIT_CLASSDRIVER)
|
|
);
|
|
|
|
//
|
|
// Now send a message to our notification window informing them of the outcome.
|
|
//
|
|
PostMessage(ClassDrvThreadContext->NotificationWindow,
|
|
WMX_CLASSDRVLIST_DONE,
|
|
(WPARAM)b,
|
|
(LPARAM)Err
|
|
);
|
|
|
|
MyFree(Context);
|
|
|
|
//
|
|
// Done.
|
|
//
|
|
_endthread();
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupIsClassDriverListBuilt(
|
|
IN PSP_DIALOGDATA lpdd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether or not a class driver list has already been
|
|
built for the specified dialog data.
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data buffer that is being queried
|
|
for the presence of a class driver list.
|
|
|
|
Return Value:
|
|
|
|
If a class driver list has already been built, the return value is TRUE,
|
|
otherwise, it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
BOOL b = FALSE;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
b = DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST;
|
|
} else {
|
|
b = pDeviceInfoSet->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST;
|
|
}
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
pSetupDevInfoDataFromDialogData(
|
|
IN PSP_DIALOGDATA lpdd,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DEVINFO_DATA structure based on the device
|
|
information element specified in the supplied dialog data.
|
|
|
|
Arguments:
|
|
|
|
lpdd - Supplies the address of a dialog data buffer that specifies a
|
|
devinfo element to be used in filling in the DeviceInfoData buffer.
|
|
|
|
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure that
|
|
is filled in with information about the devinfo element specified in
|
|
the dialog data.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
//
|
|
// The dialog data had better be referencing a devinfo element!
|
|
//
|
|
MYASSERT(DevInfoElem);
|
|
|
|
DeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, DeviceInfoData);
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
VOID
|
|
CleanupDriverLists(
|
|
IN OUT PNEWDEVWIZ_DATA ndwData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will destroy both the class and compatible driver lists that
|
|
were created while the wizard page was up.
|
|
|
|
Arguments:
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PSP_DIALOGDATA lpdd;
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
|
|
MYASSERT(ndwData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
MYASSERT(lpdd);
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet))) {
|
|
//
|
|
// should never hit this code path
|
|
//
|
|
MYASSERT(pDeviceInfoSet);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
if(lpdd->flags & DD_FLAG_USE_DEVINFO_ELEM) {
|
|
DevInfoElem = lpdd->DevInfoElem;
|
|
} else {
|
|
DevInfoElem = pDeviceInfoSet->SelectedDevInfoElem;
|
|
}
|
|
|
|
if(DevInfoElem) {
|
|
//
|
|
// Initialize a SP_DEVINFO_DATA buffer to use as an argument to
|
|
// SetupDiDestroyDriverInfoList.
|
|
//
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DeviceInfoData
|
|
);
|
|
|
|
//
|
|
// We need to reset the DriverPath so that if we come back into the
|
|
// wizard we will rebuild from the correct directory. Otherwise we
|
|
// will be stuck with the Have Disk... path that the user entered.
|
|
//
|
|
DevInfoElem->InstallParamBlock.DriverPath = -1;
|
|
|
|
} else {
|
|
|
|
pDeviceInfoSet->InstallParamBlock.DriverPath = -1;
|
|
}
|
|
|
|
SetupDiDestroyDriverInfoList(pDeviceInfoSet,
|
|
DevInfoElem ? &DeviceInfoData : NULL,
|
|
SPDIT_COMPATDRIVER
|
|
);
|
|
|
|
SetupDiDestroyDriverInfoList(pDeviceInfoSet,
|
|
DevInfoElem ? &DeviceInfoData : NULL,
|
|
SPDIT_CLASSDRIVER
|
|
);
|
|
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
VOID
|
|
ToggleDialogControls(
|
|
IN HWND hwndDlg,
|
|
IN OUT PNEWDEVWIZ_DATA ndwData,
|
|
IN BOOL Enable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine either enables or disables all controls on a Select Device
|
|
dialog box, depending on the value of Enable.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Supplies the handle of the Select Device dialog
|
|
|
|
ndwData - Supplies the address of a New Device Wizard data block to be used
|
|
during the processing of this message.
|
|
|
|
Enable - If TRUE, then enable all controls (with possible exception of
|
|
"Show all devices" radio button (if class list search failed). If
|
|
FALSE, disable all controls.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we're enabling controls, make sure we only enable the "Show
|
|
// compatible drivers" check box if we successfully built a class list.
|
|
//
|
|
if(Enable) {
|
|
if(!((ndwData->ddData).flags & DD_FLAG_CLASSLIST_FAILED)) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), TRUE);
|
|
} else {
|
|
//
|
|
// The class list failed to build, so we will show the compatible
|
|
// list only. Check and disable the show compatible check box.
|
|
//
|
|
(ndwData->ddData).bShowCompat = TRUE;
|
|
CheckDlgButton(hwndDlg, IDC_NDW_PICKDEV_COMPAT, BST_CHECKED);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), FALSE);
|
|
}
|
|
} else {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_COMPAT), FALSE);
|
|
}
|
|
|
|
EnableWindow((ndwData->ddData).hwndDrvList, Enable);
|
|
EnableWindow((ndwData->ddData).hwndMfgList, Enable);
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_HAVEDISK), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_WINDOWSUPDATE), Enable);
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_ICON), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_TEXT), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SIGNED_LINK), Enable);
|
|
|
|
if ((ndwData->ddData).flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), Enable);
|
|
} else {
|
|
INT WizardFlags = 0;
|
|
|
|
if (Enable) {
|
|
//
|
|
// Don't show the back button if we are in express mode.
|
|
//
|
|
if(!((ndwData->InstallData).Flags & NDW_INSTALLFLAG_SKIPCLASSLIST) ||
|
|
!((ndwData->InstallData).Flags & NDW_INSTALLFLAG_EXPRESSINTRO) ||
|
|
((ndwData->InstallData).DynamicPageFlags & DYNAWIZ_FLAG_PAGESADDED)) {
|
|
|
|
WizardFlags |= PSWIZB_BACK;
|
|
}
|
|
|
|
//
|
|
// Show the Next button if there is at least one driver selected in the
|
|
// list view.
|
|
//
|
|
if(ListView_GetSelectedCount((ndwData->ddData).hwndDrvList) > 0) {
|
|
WizardFlags |= PSWIZB_NEXT;
|
|
}
|
|
}
|
|
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), WizardFlags);
|
|
|
|
EnableWindow(GetDlgItem(GetParent(hwndDlg), IDCANCEL), Enable);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
CDMIsInternetAvailable(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return TRUE or FALSE based on if this machine can get to
|
|
the Internet or not.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if the machine can get to the Internet.
|
|
FALSE if the machine can NOT get to the Internet.
|
|
|
|
--*/
|
|
{
|
|
BOOL IsInternetAvailable = FALSE;
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
DWORD Policy;
|
|
ULONG cbData;
|
|
HMODULE hModCDM = NULL;
|
|
CDM_INTERNET_AVAILABLE_PROC CdmInternetAvailable;
|
|
SPFUSIONINSTANCE spFusionInstance;
|
|
BOOL bLeaveFusionContext = FALSE;
|
|
|
|
try {
|
|
//
|
|
// Check the DontSearchWindowsUpdate DriverSearching policy.
|
|
//
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
TEXT("Software\\Policies\\Microsoft\\Windows\\DriverSearching"),
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
|
|
cbData = sizeof(Policy);
|
|
//
|
|
// Initialize Policy, so we don't pickup random data if
|
|
// actual cbData size < sizeof(DWORD)
|
|
//
|
|
Policy = 0;
|
|
if(ERROR_SUCCESS == RegQueryValueEx(hKey,
|
|
TEXT("DontSearchWindowsUpdate"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&Policy,
|
|
&cbData)) {
|
|
if(Policy) {
|
|
//
|
|
// If the DontSearchWindowsUpdate policy is set then we
|
|
// want to return FALSE.
|
|
//
|
|
leave;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Couldn't open the policy registry key--make sure it's still set
|
|
// to an invalid value so we'll know not to try and close it later.
|
|
//
|
|
hKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
bLeaveFusionContext = spFusionEnterContext(NULL,&spFusionInstance);
|
|
|
|
if((NO_ERROR != GLE_FN_CALL(NULL, hModCDM = LoadLibrary(TEXT("cdm.dll")))) ||
|
|
(NO_ERROR != GLE_FN_CALL(NULL, CdmInternetAvailable = (PVOID)GetProcAddress(hModCDM, "DownloadIsInternetAvailable")))) {
|
|
|
|
leave;
|
|
}
|
|
|
|
IsInternetAvailable = CdmInternetAvailable();
|
|
|
|
} except(pSetupExceptionFilter(GetExceptionCode())) {
|
|
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
|
|
}
|
|
|
|
if(hModCDM) {
|
|
FreeLibrary(hModCDM);
|
|
}
|
|
|
|
if(bLeaveFusionContext) {
|
|
spFusionLeaveContext(&spFusionInstance);
|
|
}
|
|
|
|
if(hKey != INVALID_HANDLE_VALUE) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return IsInternetAvailable;
|
|
}
|
|
|