mirror of https://github.com/lianthony/NT4.0
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.
3740 lines
123 KiB
3740 lines
123 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
devwiz.c
|
|
|
|
Abstract:
|
|
|
|
Device Installer functions for install wizard support.
|
|
|
|
Author:
|
|
|
|
Lonny McMichael (lonnym) 22-Sep-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "setupntp.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 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
|
|
);
|
|
|
|
BOOL
|
|
CALLBACK
|
|
SelectDeviceDlgProc(
|
|
IN HWND hwndDlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
);
|
|
|
|
VOID
|
|
InitSelectDeviceDlg(
|
|
IN HWND hwndDlg,
|
|
IN OUT PSP_DIALOGDATA lpdd
|
|
);
|
|
|
|
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 PSP_DIALOGDATA lpdd
|
|
);
|
|
|
|
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
|
|
);
|
|
|
|
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
|
|
_CRTAPI1
|
|
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 PSP_DIALOGDATA lpdd,
|
|
IN BOOL Enable
|
|
);
|
|
|
|
VOID
|
|
SetDlgText(
|
|
IN HWND hwndDlg,
|
|
IN INT iControl,
|
|
IN UINT nStartString,
|
|
IN UINT nEndString
|
|
);
|
|
|
|
#define SDT_MAX_TEXT 1000 // Max SetDlgText() combined text size
|
|
|
|
|
|
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
|
|
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;
|
|
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(DWORD)];
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)pspBuffer;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
|
|
switch(PageType) {
|
|
|
|
case SPWPT_SELECTDEVICE :
|
|
|
|
Page->pszTemplate = MAKEINTRESOURCE(IDD_DYNAWIZ_SELECTDEV_PAGE);
|
|
Page->pfnDlgProc = SelectDeviceDlgProc;
|
|
Page->pfnCallback = SelectDevicePropSheetPageProc;
|
|
break;
|
|
|
|
default :
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
goto clean0;
|
|
|
|
} 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;
|
|
goto clean0;
|
|
}
|
|
|
|
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;
|
|
goto clean0;
|
|
}
|
|
|
|
Page->dwSize = sizeof(pspBuffer);
|
|
Page->dwFlags = PSP_DEFAULT | PSP_USECALLBACK;
|
|
Page->hInstance = MyDllModuleHandle;
|
|
|
|
Page->lParam = (LPARAM)DeviceInfoSet;
|
|
|
|
*((PDWORD)(&(pspBuffer[sizeof(PROPSHEETPAGE)]))) = (DWORD)WizPageObject;
|
|
|
|
if(!(hPage = CreatePropertySheetPage(Page))) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
clean0: ; // nothing to do.
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
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;
|
|
DWORD Err;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
Err = NO_ERROR;
|
|
|
|
try {
|
|
|
|
if(pDeviceInfoSet->SelectedDevInfoElem) {
|
|
|
|
if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
pDeviceInfoSet->SelectedDevInfoElem,
|
|
DeviceInfoData))) {
|
|
Err = ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
} else {
|
|
Err = ERROR_NO_DEVICE_SELECTED;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
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;
|
|
DWORD Err;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
Err = NO_ERROR;
|
|
|
|
try {
|
|
|
|
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL)) {
|
|
pDeviceInfoSet->SelectedDevInfoElem = DevInfoElem;
|
|
} else {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR, otherwise, it is
|
|
an 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;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
);
|
|
|
|
clean0: ; // nothing to do.
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
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;
|
|
DWORD 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 in a DWORD at the end of the
|
|
// ppsp structure. Retrieve this now, and look for it in the
|
|
// devinfo set's list of wizard objects.
|
|
//
|
|
WizObjectId = *((PDWORD)(&(((PBYTE)ppsp)[sizeof(PROPSHEETPAGE)])));
|
|
|
|
for(CurWizObject = pDeviceInfoSet->WizPageList, PrevWizObject = NULL;
|
|
CurWizObject;
|
|
PrevWizObject = CurWizObject, CurWizObject = CurWizObject->Next) {
|
|
|
|
if(WizObjectId = (DWORD)CurWizObject) {
|
|
//
|
|
// We found our object.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!CurWizObject) {
|
|
ret = FALSE;
|
|
goto clean0;
|
|
}
|
|
|
|
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;
|
|
goto clean0;
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
clean0: ; // nothing to do
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
ret = FALSE;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
BOOL
|
|
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;
|
|
|
|
if(uMsg == WM_INITDIALOG) {
|
|
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)lParam;
|
|
|
|
//
|
|
// Retrieve a pointer to the device wizard data associated with
|
|
// this wizard page.
|
|
//
|
|
ndwData = GetNewDevWizDataFromPsPage(Page);
|
|
SetWindowLong(hwndDlg, DWL_USER, (LONG)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_AWAITING_CLASSLIST), SW_HIDE);
|
|
|
|
InitSelectDeviceDlg(hwndDlg, &(ndwData->ddData));
|
|
|
|
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)GetWindowLong(hwndDlg, DWL_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 class list
|
|
// radio button.
|
|
//
|
|
if(!wParam) {
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), 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_AWAITING_CLASSLIST), SW_HIDE);
|
|
|
|
if(FillInDeviceList(hwndDlg, &(ndwData->ddData)) == NO_ERROR) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
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
|
|
);
|
|
|
|
//
|
|
// Re-select the "Show compatible devices" radio button, and disable
|
|
// the 'Show all devices" radio button.
|
|
//
|
|
ndwData->ddData.ListType = IDC_NDW_PICKDEV_SHOWCOMPAT;
|
|
CheckRadioButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT,
|
|
IDC_NDW_PICKDEV_SHOWALL,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT
|
|
);
|
|
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), FALSE);
|
|
//
|
|
// We also must unhide the compatible driver list controls, and re-enable
|
|
// the OK button.
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_AWAITING_CLASSLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
|
|
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 :
|
|
//
|
|
// The user clicked the "Have Disk" button. Pass this off to
|
|
// HandleSelectOEM(). If we get back success, then we are done, and
|
|
// can end the dialog.
|
|
//
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_NONE;
|
|
|
|
if(HandleSelectOEM(hwndDlg, &(ndwData->ddData)) == NO_ERROR) {
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
} else {
|
|
//
|
|
// The OEM selection was not made, so we'll just continue as though
|
|
// nothing had happened. Re-enable the dialog controls.
|
|
//
|
|
ToggleDialogControls(hwndDlg, &(ndwData->ddData), TRUE);
|
|
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))) {
|
|
//
|
|
// 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);
|
|
} else {
|
|
|
|
ndwData->ddData.AuxThreadRunning = TRUE;
|
|
|
|
//
|
|
// If we're currently in the class driver list view, then disable
|
|
// the OK button, since the user can't select a class driver yet.
|
|
//
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
}
|
|
}
|
|
|
|
if(!(ndwData->ddData.AuxThreadRunning)) {
|
|
//
|
|
// We couldn't start the class driver search thread. Disable the
|
|
// "Show all devices" radio button (and select the "Show compatible
|
|
// devices" button, if it's not already selected).
|
|
//
|
|
if(ndwData->ddData.ListType != IDC_NDW_PICKDEV_SHOWCOMPAT) {
|
|
|
|
ndwData->ddData.ListType = IDC_NDW_PICKDEV_SHOWCOMPAT;
|
|
CheckRadioButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT,
|
|
IDC_NDW_PICKDEV_SHOWALL,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT
|
|
);
|
|
}
|
|
|
|
ndwData->ddData.flags |= DD_FLAG_CLASSLIST_FAILED;
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch(LOWORD(wParam)) {
|
|
|
|
case IDC_NDW_PICKDEV_SHOWCOMPAT :
|
|
case IDC_NDW_PICKDEV_SHOWALL :
|
|
|
|
if((HIWORD(wParam) == BN_CLICKED) &&
|
|
IsWindowVisible(GetDlgItem(hwndDlg, LOWORD(wParam))) &&
|
|
!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
|
|
//
|
|
// Due to focus/click weirdness in USER, pre-click
|
|
// the button and unclick if needed.
|
|
//
|
|
CheckRadioButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT,
|
|
IDC_NDW_PICKDEV_SHOWALL,
|
|
LOWORD(wParam)
|
|
);
|
|
ndwData->ddData.ListType = (INT)LOWORD(wParam);
|
|
//
|
|
// 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.ListType == IDC_NDW_PICKDEV_SHOWCOMPAT) {
|
|
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_AWAITING_CLASSLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
|
|
//
|
|
// 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_MODELSLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_AWAITING_CLASSLIST), SW_SHOW);
|
|
|
|
//
|
|
// Disable the OK button, because the user can't select a class driver
|
|
// yet.
|
|
//
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
|
|
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
|
|
// aren't isn't a time waiting to pounce and destroy our list!
|
|
//
|
|
if((ndwData->ddData.ListType == IDC_NDW_PICKDEV_SHOWCOMPAT) &&
|
|
(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.
|
|
//
|
|
if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
ToggleDialogControls(hwndDlg, &(ndwData->ddData), 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));
|
|
|
|
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;
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
|
|
if(HandleSelectOEM(hwndDlg, &(ndwData->ddData)) == 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 if(ndwData->ddData.flags & DD_FLAG_IS_DIALOGBOX) {
|
|
//
|
|
// The user didn't make an OEM selection, so we need to re-enable
|
|
// the controls on our dialog.
|
|
//
|
|
ToggleDialogControls(hwndDlg, &(ndwData->ddData), 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));
|
|
|
|
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->ddData), 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;
|
|
|
|
SetCursor(hOldCursor);
|
|
|
|
} else {
|
|
//
|
|
// The auxilliary thread has already returned. We can return
|
|
// success right here.
|
|
//
|
|
SetSelectedDriverNode(&(ndwData->ddData), iCur);
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Tell user to select something
|
|
//
|
|
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));
|
|
|
|
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->ddData), FALSE);
|
|
ndwData->bInit = TRUE;
|
|
if(ndwData->idTimer) {
|
|
KillTimer(hwndDlg, SELECTMFG_TIMER_ID);
|
|
ndwData->idTimer = 0;
|
|
}
|
|
ndwData->ddData.PendingAction = PENDING_ACTION_CANCEL;
|
|
|
|
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_PICKDEV2);
|
|
|
|
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 :
|
|
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.
|
|
//
|
|
SetSelectedDriverNode(&(ndwData->ddData), iCur);
|
|
|
|
} 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;
|
|
}
|
|
}
|
|
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;
|
|
|
|
default :
|
|
return(FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
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 TO 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 an ERROR_* code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDRIVER_NODE DriverNodeHead, CurDriverNode;
|
|
DWORD DriverNodeType;
|
|
LONG MfgNameId;
|
|
INT i;
|
|
LPTSTR lpszMfg;
|
|
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;
|
|
LPGUID ClassGuid;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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->ListType == IDC_NDW_PICKDEV_SHOWALL) {
|
|
DriverNodeHead = DevInfoElem->ClassDriverHead;
|
|
DriverNodeType = SPDIT_CLASSDRIVER;
|
|
} else {
|
|
DriverNodeHead = DevInfoElem->CompatDriverHead;
|
|
DriverNodeType = SPDIT_COMPATDRIVER;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We better not be trying to display a compatible driver list if
|
|
// we don't have a devinfo element!
|
|
//
|
|
MYASSERT(lpdd->ListType == IDC_NDW_PICKDEV_SHOWALL);
|
|
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
DriverNodeHead = pDeviceInfoSet->ClassDriverHead;
|
|
DriverNodeType = SPDIT_CLASSDRIVER;
|
|
|
|
if(pDeviceInfoSet->HasClassGuid) {
|
|
ClassGuid = &(pDeviceInfoSet->ClassGuid);
|
|
} else {
|
|
//
|
|
// Cast away const-ness of this global GUID, since we only use
|
|
// LPGUID type (in fact, there is no 'LPCGUID' type defined).
|
|
//
|
|
ClassGuid = (LPGUID)&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_MFGLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MODELSLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_HIDE);
|
|
|
|
//
|
|
// Show the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the Models string
|
|
//
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL, GET_CI_SELSTRINGS(dipb, ListLabel));
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
szBuf[0] = TEXT('\0');
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL, szBuf);
|
|
}
|
|
|
|
//
|
|
// Use the single listbox view for the driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST);
|
|
}
|
|
|
|
Err = ERROR_DI_BAD_PATH;
|
|
goto clean0;
|
|
}
|
|
|
|
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"
|
|
//
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_GENERIC_DEVNAME,
|
|
szBuf,
|
|
SIZECHARS(szBuf)
|
|
);
|
|
}
|
|
|
|
if(lpdd->ListType == IDC_NDW_PICKDEV_SHOWALL) {
|
|
//
|
|
// Show class list.
|
|
//
|
|
if(LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLSTR1,
|
|
szMessage,
|
|
SIZECHARS(szMessage))) {
|
|
wsprintf(szText, szMessage, szBuf);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Show compatible list.
|
|
//
|
|
if(LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLSTR0,
|
|
szMessage,
|
|
SIZECHARS(szMessage))) {
|
|
wsprintf(szText, szMessage, szBuf);
|
|
}
|
|
lpszText = szText + lstrlen(szText);
|
|
if(LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLCLASS,
|
|
szMessage,
|
|
SIZECHARS(szMessage))) {
|
|
wsprintf(lpszText, szMessage, szBuf);
|
|
}
|
|
}
|
|
|
|
if(dipb->DriverPath != -1) {
|
|
|
|
lpszText = szText + lstrlen(szText);
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLOEM1,
|
|
lpszText,
|
|
SIZECHARS(szText) - lstrlen(szText)
|
|
);
|
|
|
|
} else if(dipb->Flags & DI_SHOWOEM) {
|
|
lpszText = szText + lstrlen(szText);
|
|
LoadString(MyDllModuleHandle,
|
|
IDS_INSTALLOEM,
|
|
lpszText,
|
|
SIZECHARS(szText) - lstrlen(szText)
|
|
);
|
|
}
|
|
|
|
SetDlgItemText(hwndDlg, IDC_NDW_TEXT, szText);
|
|
}
|
|
|
|
if((lpdd->ListType == IDC_NDW_PICKDEV_SHOWALL) && (dipb->Flags & DI_MULTMFGS)) {
|
|
//
|
|
// Hide the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_HIDE);
|
|
|
|
//
|
|
// Show the Multiple MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MODELSLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the colunm heading for the Driver list
|
|
//
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_MODELSLABEL, GET_CI_SELSTRINGS(dipb, ListLabel));
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
szBuf[0] = TEXT('\0');
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_MODELSLABEL, szBuf);
|
|
}
|
|
|
|
//
|
|
// Use the 2nd listbox of the Manufacturers/Models view for the driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST);
|
|
|
|
//
|
|
// No redraw for faster insert
|
|
//
|
|
SendMessage(lpdd->hwndMfgList, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
//
|
|
// 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
|
|
//
|
|
if(CurDriverNode->Flags & DNF_EXCLUDEFROMLIST) {
|
|
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);
|
|
|
|
} 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);
|
|
|
|
ListView_EnsureVisible(lpdd->hwndMfgList,
|
|
ListView_GetNextItem(lpdd->hwndMfgList, -1, LVNI_SELECTED),
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Hide the mult mfg controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MFGLIST), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_MODELSLABEL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), SW_HIDE);
|
|
|
|
//
|
|
// Show the Single MFG controls
|
|
//
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), SW_SHOW);
|
|
|
|
//
|
|
// Set the Models string
|
|
//
|
|
if(USE_CI_SELSTRINGS(dipb)) {
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL, GET_CI_SELSTRINGS(dipb, ListLabel));
|
|
} else {
|
|
if(!(LoadString(MyDllModuleHandle,
|
|
IDS_NDWSEL_MODELSLABEL,
|
|
szBuf,
|
|
SIZECHARS(szBuf)))) {
|
|
szBuf[0] = TEXT('\0');
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_MODELSLABEL, szBuf);
|
|
}
|
|
|
|
//
|
|
// Use the single listbox view for the driver list.
|
|
//
|
|
lpdd->hwndDrvList = GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST);
|
|
|
|
ShowListForMfg(lpdd, pDeviceInfoSet, dipb, DriverNodeHead, -1);
|
|
}
|
|
|
|
clean0: ; // nothing to do
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
|
|
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;
|
|
LV_ITEM lviItem;
|
|
LV_FINDINFO lvfiFind;
|
|
LONG MfgNameId = -1;
|
|
TCHAR szTemp[LINE_LEN];
|
|
|
|
//
|
|
// 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);
|
|
|
|
//
|
|
// Clean out the List.
|
|
//
|
|
ListView_DeleteAllItems(lpdd->hwndDrvList);
|
|
|
|
if(!DriverNode) {
|
|
|
|
lviItem.mask = LVIF_PARAM;
|
|
lviItem.iItem = iMfg;
|
|
lviItem.iSubItem = 0;
|
|
if(!ListView_GetItem(lpdd->hwndMfgList, &lviItem) ||
|
|
!(DriverNode = GetDriverNodeFromLParam(DeviceInfoSet, lpdd, lviItem.lParam))) {
|
|
|
|
return;
|
|
}
|
|
MfgNameId = DriverNode->MfgName;
|
|
}
|
|
|
|
lviItem.mask = LVIF_TEXT | LVIF_PARAM;
|
|
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_EXCLUDEFROMLIST) {
|
|
continue;
|
|
}
|
|
|
|
if(DriverNode->Flags & DNF_DUPDESC) {
|
|
|
|
lstrcpy(szTemp,
|
|
pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->DevDescriptionDisplayName)
|
|
);
|
|
lstrcat(szTemp, pszSpaceLparen);
|
|
if(DriverNode->ProviderDisplayName != -1) {
|
|
lstrcat(szTemp,
|
|
pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->ProviderDisplayName)
|
|
);
|
|
}
|
|
lstrcat(szTemp, pszRparen);
|
|
|
|
lviItem.pszText = szTemp;
|
|
} else {
|
|
lviItem.pszText = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->DevDescriptionDisplayName
|
|
);
|
|
}
|
|
|
|
lviItem.lParam = (LPARAM)DriverNode;
|
|
|
|
if(ListView_InsertItem(lpdd->hwndDrvList, &lviItem) != -1) {
|
|
lviItem.iItem++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
);
|
|
|
|
//
|
|
// 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);
|
|
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;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
VOID
|
|
InitSelectDeviceDlg(
|
|
IN HWND hwndDlg,
|
|
IN OUT PSP_DIALOGDATA lpdd
|
|
)
|
|
/*++
|
|
|
|
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
|
|
|
|
lpdd - Supplies the address of a dialog data buffer that is
|
|
initialized with information concerning this dialog.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
PDEVINSTALL_PARAM_BLOCK dipb;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
DWORD DriverType = SPDIT_CLASSDRIVER;
|
|
DWORD Err;
|
|
INT ShowWhat;
|
|
LPGUID ClassGuid;
|
|
HICON hicon;
|
|
LV_COLUMN lvcCol;
|
|
BOOL SpawnClassDriverSearch = FALSE;
|
|
PCLASSDRV_THREAD_CONTEXT ClassDrvThreadContext;
|
|
|
|
if(!lpdd->hwndMfgList) {
|
|
//
|
|
// 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().
|
|
//
|
|
|
|
//
|
|
// Insert a ListView column for each of the listboxes.
|
|
//
|
|
lvcCol.mask = 0;
|
|
|
|
ListView_InsertColumn(lpdd->hwndMfgList, 0, &lvcCol);
|
|
ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_DRVLIST), 0, &lvcCol);
|
|
ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_ONEMFG_DRVLIST), 0, &lvcCol);
|
|
}
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// If we're in a stand-alone dialogbox, then we want to start out with the
|
|
// compatible driver list, otherwise, we want the class driver list.
|
|
//
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
DriverType = SPDIT_COMPATDRIVER;
|
|
}
|
|
|
|
} else {
|
|
dipb = &(pDeviceInfoSet->InstallParamBlock);
|
|
if(pDeviceInfoSet->HasClassGuid) {
|
|
ClassGuid = &(pDeviceInfoSet->ClassGuid);
|
|
} else {
|
|
//
|
|
// Cast away const-ness of this global GUID, since we only use
|
|
// LPGUID type (in fact, there is no 'LPCGUID' type defined).
|
|
//
|
|
ClassGuid = (LPGUID)&GUID_DEVCLASS_UNKNOWN;
|
|
}
|
|
|
|
if(pDeviceInfoSet->InstallParamBlock.FlagsEx & DI_FLAGSEX_DIDINFOLIST) {
|
|
lpdd->bKeeplpClassDrvList = TRUE;
|
|
}
|
|
|
|
if(pDeviceInfoSet->SelectedClassDriver) {
|
|
lpdd->bKeeplpSelectedDrv = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get/set class icon
|
|
//
|
|
SetupDiLoadClassIcon(ClassGuid, &hicon, &(lpdd->iBitmap));
|
|
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 {
|
|
PropSheet_SetTitle(GetParent(hwndDlg), PSH_DEFAULT, GET_CI_SELSTRINGS(dipb, Title));
|
|
}
|
|
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);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT)); // Potentially slow operations ahead!
|
|
|
|
if(DriverType == SPDIT_COMPATDRIVER) {
|
|
|
|
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) {
|
|
//
|
|
// Either the class driver list is the first (only) list we need (e.g., Wizard),
|
|
// or we couldn't find any compatible drivers, so we fall back on the class driver
|
|
// list. In either 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.
|
|
//
|
|
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.
|
|
//
|
|
if((dipb->FlagsEx & DI_FLAGSEX_AUTOSELECTRANK0) &&
|
|
!(DevInfoElem->CompatDriverHead->Rank)) {
|
|
//
|
|
// We shouldn't be here if we're doing a wizard...
|
|
//
|
|
MYASSERT(lpdd->flags & DD_FLAG_IS_DIALOGBOX);
|
|
|
|
DevInfoElem->SelectedDriver = DevInfoElem->CompatDriverHead;
|
|
DevInfoElem->SelectedDriverType = SPDIT_COMPATDRIVER;
|
|
|
|
//
|
|
// No need to spawn a class driver search thread.
|
|
//
|
|
SpawnClassDriverSearch = FALSE;
|
|
|
|
EndDialog(hwndDlg, NO_ERROR);
|
|
goto clean0;
|
|
}
|
|
lpdd->ListType = IDC_NDW_PICKDEV_SHOWCOMPAT;
|
|
CheckRadioButton(hwndDlg,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT,
|
|
IDC_NDW_PICKDEV_SHOWALL,
|
|
IDC_NDW_PICKDEV_SHOWCOMPAT
|
|
);
|
|
} else {
|
|
//
|
|
// There is no compatible list, so hide the radio buttons.
|
|
//
|
|
lpdd->ListType = IDC_NDW_PICKDEV_SHOWALL;
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWCOMPAT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), 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
|
|
);
|
|
}
|
|
|
|
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);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// 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
|
|
);
|
|
}
|
|
|
|
|
|
clean0: ; // nothing to do
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// If we're doing the dialog box version, then an exception should cause us to
|
|
// terminate the dialog and return an error. Note that presently, the dialog
|
|
// case is the only time we'll try to spawn off a driver search thread, so we're
|
|
// safe in resetting that flag here as well.
|
|
//
|
|
if(lpdd->flags & DD_FLAG_IS_DIALOGBOX) {
|
|
EndDialog(hwndDlg, ERROR_INVALID_DATA);
|
|
SpawnClassDriverSearch = FALSE;
|
|
}
|
|
}
|
|
|
|
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)))) {
|
|
EndDialog(hwndDlg, ERROR_NOT_ENOUGH_MEMORY);
|
|
} else {
|
|
//
|
|
// 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
|
|
//
|
|
MyFree(ClassDrvThreadContext);
|
|
EndDialog(hwndDlg, ERROR_NOT_ENOUGH_MEMORY);
|
|
} else {
|
|
lpdd->AuxThreadRunning = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SetDlgText(
|
|
IN HWND hwndDlg,
|
|
IN INT iControl,
|
|
IN UINT nStartString,
|
|
IN UINT nEndString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine concatenates a number of string resources and does a
|
|
SetWindowText() for a dialog text control.
|
|
|
|
Arguments:
|
|
|
|
hwndDlg - Handle to dialog window
|
|
|
|
iControl - Dialog control ID to receive text
|
|
|
|
nStartString - ID of first string resource to concatenate
|
|
|
|
nEndString - ID of last string resource to concatenate
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Remarks:
|
|
|
|
String IDs must be consecutive.
|
|
|
|
--*/
|
|
{
|
|
TCHAR StringBuffer[SDT_MAX_TEXT];
|
|
UINT i;
|
|
INT Len = 0;
|
|
|
|
for(i = nStartString;
|
|
((i <= nEndString) && (Len < (SDT_MAX_TEXT - 1)));
|
|
i++)
|
|
{
|
|
Len += LoadString(MyDllModuleHandle,
|
|
i,
|
|
StringBuffer + Len,
|
|
SDT_MAX_TEXT - Len
|
|
);
|
|
}
|
|
|
|
if(!Len) {
|
|
StringBuffer[0] = TEXT('\0');
|
|
}
|
|
|
|
SetDlgItemText(hwndDlg, iControl, StringBuffer);
|
|
}
|
|
|
|
|
|
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->ListType == IDC_NDW_PICKDEV_SHOWALL) ? DevInfoElem->ClassDriverHead
|
|
: DevInfoElem->CompatDriverHead;
|
|
} else {
|
|
MYASSERT(lpdd->ListType == IDC_NDW_PICKDEV_SHOWALL);
|
|
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;
|
|
|
|
iwd = &(ndwData->InstallData);
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
try {
|
|
|
|
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, lpdd);
|
|
} 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
|
|
);
|
|
}
|
|
|
|
if(SetupDiCallClassInstaller(DIF_SELECTDEVICE,
|
|
lpdd->DevInfoSet,
|
|
DevInfoElem ? &DeviceInfoData : NULL)) {
|
|
CiErr = NO_ERROR;
|
|
} else {
|
|
CiErr = GetLastError();
|
|
}
|
|
|
|
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 :
|
|
|
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_DYNAWIZ_ANALYZEDEV_PAGE);
|
|
break;
|
|
|
|
//
|
|
// Class installer wants us to do default.
|
|
//
|
|
case ERROR_DI_DO_DEFAULT :
|
|
|
|
InitSelectDeviceDlg(hwndDlg, lpdd);
|
|
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, lpdd);
|
|
|
|
} else {
|
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_DYNAWIZ_SELECTCLASS_PAGE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
b = FALSE;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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) {
|
|
DevInfoElem->SelectedDriver = DriverNode;
|
|
if(DriverNode) {
|
|
DevInfoElem->SelectedDriverType = (lpdd->ListType == IDC_NDW_PICKDEV_SHOWALL)
|
|
? SPDIT_CLASSDRIVER
|
|
: SPDIT_COMPATDRIVER;
|
|
} else {
|
|
DevInfoElem->SelectedDriverType = SPDIT_NODRIVER;
|
|
}
|
|
} else {
|
|
pDeviceInfoSet->SelectedClassDriver = DriverNode;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleSelectOEM(
|
|
IN HWND hwndDlg,
|
|
IN OUT PSP_DIALOGDATA lpdd
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
lpdd - Supplies the address of a dialog data structure that contains information
|
|
used in device selection.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise, it is an ERROR_* code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
PDEVINFO_ELEM DevInfoElem;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
DWORD Err;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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) {
|
|
//
|
|
// Fill in the list to select from
|
|
//
|
|
lpdd->ListType = IDC_NDW_PICKDEV_SHOWALL;
|
|
FillInDeviceList(hwndDlg, lpdd);
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = GetLastError();
|
|
//
|
|
// Access the pDeviceInfoSet variable so that the compiler will respect our statement
|
|
// ordering w.r.t. this value.
|
|
//
|
|
pDeviceInfoSet = pDeviceInfoSet;
|
|
}
|
|
|
|
if(pDeviceInfoSet) {
|
|
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;
|
|
DWORD WizObjectId;
|
|
PWIZPAGE_OBJECT CurWizObject = NULL;
|
|
|
|
//
|
|
// Access the device info set handle stored in the propsheetpage's lParam.
|
|
//
|
|
if(pDeviceInfoSet = AccessDeviceInfoSet((HDEVINFO)(ppsp->lParam))) {
|
|
|
|
try {
|
|
//
|
|
// The ObjectID (pointer, actually) for the corresponding wizard
|
|
// object for this page is stored in a DWORD at the end of the
|
|
// ppsp structure. Retrieve this now, and look for it in the
|
|
// devinfo set's list of wizard objects.
|
|
//
|
|
WizObjectId = *((PDWORD)(&(((PBYTE)ppsp)[sizeof(PROPSHEETPAGE)])));
|
|
|
|
for(CurWizObject = pDeviceInfoSet->WizPageList;
|
|
CurWizObject;
|
|
CurWizObject = CurWizObject->Next) {
|
|
|
|
if(WizObjectId = (DWORD)CurWizObject) {
|
|
//
|
|
// We found our object.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
return CurWizObject ? CurWizObject->ndwData : NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupDiSelectDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Default hander 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;
|
|
DWORD Err;
|
|
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(DWORD)];
|
|
LPPROPSHEETPAGE Page = (LPPROPSHEETPAGE)pspBuffer;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
Err = NO_ERROR;
|
|
|
|
try {
|
|
//
|
|
// 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;
|
|
goto clean0;
|
|
}
|
|
|
|
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;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Then we are to select a driver for a particular device.
|
|
//
|
|
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL))) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
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;
|
|
|
|
*((PDWORD)(&(pspBuffer[sizeof(PROPSHEETPAGE)]))) = (DWORD)&WizPageObject;
|
|
|
|
//
|
|
// Release the lock, so other stuff can happen while this dialog is up.
|
|
//
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
pDeviceInfoSet = NULL;
|
|
|
|
Err = DialogBoxParam(MyDllModuleHandle,
|
|
MAKEINTRESOURCE(DLG_DEVINSTALL),
|
|
dipb->hwndParent,
|
|
SelectDeviceDlgProc,
|
|
(LPARAM)Page
|
|
);
|
|
|
|
//
|
|
// Re-acquire the devinfo set lock.
|
|
//
|
|
pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
//
|
|
// 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
|
|
);
|
|
}
|
|
|
|
clean0: ; // nothing to do.
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
//
|
|
// Access the pDeviceInfoSet variable so that the compiler will respect
|
|
// the statement ordering in the try clause.
|
|
//
|
|
pDeviceInfoSet = pDeviceInfoSet;
|
|
}
|
|
|
|
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;
|
|
|
|
if((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALLOWEXCLUDEDDRVS) &&
|
|
DevInfoElem->CompatDriverCount) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
for(CurDriverNode = DevInfoElem->CompatDriverHead;
|
|
CurDriverNode;
|
|
CurDriverNode = CurDriverNode->Next) {
|
|
|
|
if(!(CurDriverNode->Flags & DNF_EXCLUDEFROMLIST)) {
|
|
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;
|
|
|
|
lpdd = &(ndwData->ddData);
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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->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);
|
|
DevInfoElem->CompatDriverHead = DevInfoElem->CompatDriverTail = NULL;
|
|
DevInfoElem->CompatDriverCount = 0;
|
|
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->InstallParamBlock.Flags &= ~(DI_DIDCLASS | DI_MULTMFGS);
|
|
pDeviceInfoSet->InstallParamBlock.FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
|
|
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;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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(EXCEPTION_EXECUTE_HANDLER) {
|
|
ret = -1;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
VOID
|
|
_CRTAPI1
|
|
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;
|
|
BOOL b;
|
|
DWORD Err;
|
|
|
|
if(b = SetupDiBuildDriverInfoList(ClassDrvThreadContext->DeviceInfoSet,
|
|
&(ClassDrvThreadContext->DeviceInfoData),
|
|
SPDIT_CLASSDRIVER)) {
|
|
Err = NO_ERROR;
|
|
} else {
|
|
Err = GetLastError();
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do.
|
|
}
|
|
|
|
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;
|
|
|
|
pDeviceInfoSet = AccessDeviceInfoSet(lpdd->DevInfoSet);
|
|
MYASSERT(pDeviceInfoSet);
|
|
|
|
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(EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// What to do, what to do???
|
|
// We'll just invalidate the DeviceInfoData structure.
|
|
//
|
|
DeviceInfoData->cbSize = 0;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
}
|
|
|
|
|
|
VOID
|
|
ToggleDialogControls(
|
|
IN HWND hwndDlg,
|
|
IN PSP_DIALOGDATA lpdd,
|
|
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
|
|
|
|
lpdd - Supplies the address of the dialog data for this dialog.
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only be calling this for the dialog box version.
|
|
//
|
|
MYASSERT(lpdd->flags & DD_FLAG_IS_DIALOGBOX);
|
|
|
|
//
|
|
// If we're enabling controls, make sure we only enable the "Show all devices" radio
|
|
// button if we successfully built a class list.
|
|
//
|
|
if(Enable) {
|
|
if(!(lpdd->flags & DD_FLAG_CLASSLIST_FAILED)) {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), TRUE);
|
|
}
|
|
} else {
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWALL), FALSE);
|
|
}
|
|
|
|
EnableWindow(lpdd->hwndDrvList, Enable);
|
|
EnableWindow(lpdd->hwndMfgList, Enable);
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_SHOWCOMPAT), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_NDW_PICKDEV_HAVEDISK), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), Enable);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), Enable);
|
|
}
|
|
|