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.
1317 lines
41 KiB
1317 lines
41 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
devmgr.c
|
|
|
|
Abstract:
|
|
|
|
Routines for displaying device installation dialogs.
|
|
|
|
Author:
|
|
|
|
Paula Tomlinson (paulat) 7-Feb-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "setupp.h"
|
|
#include <setupapi.h>
|
|
#include <cfgmgr32.h>
|
|
#include <string.h>
|
|
#include <devguid.h>
|
|
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
|
|
//
|
|
// Global Definitions
|
|
//
|
|
#define PNP_DLG_WAIT 1500
|
|
#define WUM_DI_INIT (WM_USER+279)
|
|
#define HELP_FILE TEXT("SYS2.HLP")
|
|
|
|
#define SELECT_DOINSTALL 0x00000000
|
|
#define SELECT_CANCEL 0x00000001
|
|
#define SELECT_DONE 0x00000002
|
|
#define SELECT_INSTALLNULLDRIVER 0x00000003
|
|
|
|
|
|
|
|
typedef struct DEVINSTALL_DLG_DATA_s {
|
|
HWND hDlg;
|
|
LPCWSTR szDeviceId[MAX_DEVICE_ID_LEN];
|
|
HDEVINFO hDevInfo;
|
|
PSP_DEVINFO_DATA pDeviceInfoData;
|
|
RECT DlgRect;
|
|
DWORD Timer;
|
|
} DEVINSTALL_DLG_DATA, *PDEVINSTALL_DLG_DATA;
|
|
|
|
|
|
|
|
typedef struct DRIVER_LIST_DATA_s {
|
|
HDEVINFO hDevInfo;
|
|
PSP_DEVINFO_DATA pDeviceInfoData;
|
|
} DRIVER_LIST_DATA, *PDRIVER_LIST_DATA;
|
|
|
|
|
|
|
|
//
|
|
// global data
|
|
//
|
|
static int aProfileIds[] = {
|
|
#if 0
|
|
// IDD_HWP_PROFILES, (IDH_HWPROFILE + IDD_HWP_PROFILES),
|
|
// IDD_HWP_PROPERTIES, (IDH_HWPROFILE + IDD_HWP_PROPERTIES),
|
|
IDRETRY, IDH_SYSTEM_DMCONFIG_RETRY,
|
|
IDIGNORE, IDH_SYSTEM_DMCONFIG_IGNORE,
|
|
IDC_NEWDEV_DEFAULTDRV, IDH_NHF_WINDOWS,
|
|
IDC_NEWDEV_OEMDRV, IDH_NHF_DISK,
|
|
IDC_NEWDEV_NODRV, IDH_NHF_NODRIVER,
|
|
IDC_NEWDEV_SELECTDRV, IDH_NHF_SIMILAR,
|
|
#endif
|
|
0, 0
|
|
};
|
|
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
|
|
DWORD
|
|
DevInstallThread(
|
|
IN PVOID ThreadParam
|
|
);
|
|
|
|
LRESULT CALLBACK
|
|
DevInstallDlgProc(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
DWORD
|
|
DmConfigureDevInst(
|
|
IN PDEVINSTALL_DLG_DATA pDevDlgData
|
|
);
|
|
|
|
DWORD
|
|
BuildDriverListThread(
|
|
IN PVOID ThreadParam
|
|
);
|
|
|
|
BOOL
|
|
MakeDialogInteractive(
|
|
IN HWND hDlg,
|
|
IN LPCTSTR pszDeviceId,
|
|
IN HDEVINFO hDevInfo,
|
|
IN PSP_DEVINFO_DATA pDeviceInfoData,
|
|
IN PRECT pDlgRect
|
|
);
|
|
|
|
BOOL
|
|
GetClassGuidStringForInf(
|
|
IN PCTSTR InfFileName,
|
|
OUT PTSTR InfClassGuidString,
|
|
IN DWORD InfClassGuidStringSize
|
|
);
|
|
|
|
BOOL
|
|
InstallDev(
|
|
IN HWND hwndParent,
|
|
IN HDEVINFO hDevInfo,
|
|
IN PSP_DEVINFO_DATA pDeviceInfoData
|
|
);
|
|
|
|
ULONG
|
|
SelectDeviceClass(
|
|
IN OUT PDEVINSTALL_DLG_DATA pData
|
|
);
|
|
|
|
LRESULT CALLBACK
|
|
PickClassDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
InstallNullDriver(
|
|
IN PDEVINSTALL_DLG_DATA pData
|
|
);
|
|
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
CONST WCHAR szUnknownClassGuid[] = L"{4D36E97E-E325-11CE-BFC1-08002BE10318}";
|
|
|
|
|
|
|
|
BOOL
|
|
DevInstallW(
|
|
HDEVINFO hDevInfo,
|
|
PSP_DEVINFO_DATA pDeviceInfoData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the user-mode pnp manager (server-side)
|
|
invokes it via rundll32. This will happen whenever new hardware is
|
|
detected while umpnpmgr is initializing. The DevInstall routine is
|
|
called once for each devnode that is marked as needing to be
|
|
configured or installed, as a separate rundll32 process. umpnpmgr
|
|
waits for DevInstall to return before invoking it again for the next
|
|
new devnode.
|
|
|
|
Arguments:
|
|
|
|
hWnd Main window of rundll32 process
|
|
|
|
hInst
|
|
|
|
lpszCmdLine
|
|
|
|
CmdShow
|
|
|
|
|
|
Return Value:
|
|
|
|
Return the version number, with the major version in the high byte and
|
|
the minor version number in the low byte.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// NOTE - this status value is to indicate whether a device was
|
|
// successfully installed or not. If this routine returns a
|
|
// value of TRUE, then the caller determines whether to put
|
|
// up a reboot prompt. So if the user cancels, or for whatever
|
|
// reason we do not install the device, this routine should return
|
|
// a value of FALSE.
|
|
//
|
|
BOOL Status;
|
|
|
|
try {
|
|
//
|
|
// determine whether the calling process is member of
|
|
// Administrators localgroup
|
|
//
|
|
if (!IsUserAdmin()) {
|
|
|
|
WCHAR szMsg[MAX_PATH], szCaption[MAX_PATH];
|
|
|
|
if (LoadString(MyModuleHandle,
|
|
IDS_NEWDEVFOUND_NOTADMIN,
|
|
szMsg,
|
|
MAX_PATH)) {
|
|
|
|
if (LoadString(MyModuleHandle,
|
|
IDS_NEWDEVFOUND_CAPTION,
|
|
szCaption,
|
|
MAX_PATH)) {
|
|
|
|
MessageBox(NULL,
|
|
szMsg,
|
|
szCaption,
|
|
MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
}
|
|
|
|
Status = FALSE; // device not installed
|
|
}
|
|
else {
|
|
|
|
HANDLE hThread = NULL;
|
|
DWORD ThreadId;
|
|
DEVINSTALL_DLG_DATA DevDlgData;
|
|
|
|
//
|
|
// initialize data that is later accessed by the dialog box proc
|
|
//
|
|
SetupDiGetDeviceInstanceId(hDevInfo,
|
|
pDeviceInfoData,
|
|
(LPTSTR)DevDlgData.szDeviceId,
|
|
MAX_DEVICE_ID_LEN,
|
|
NULL);
|
|
|
|
DevDlgData.hDevInfo = hDevInfo;
|
|
DevDlgData.pDeviceInfoData = pDeviceInfoData;
|
|
|
|
|
|
Status = DialogBoxParam(MyModuleHandle,
|
|
MAKEINTRESOURCE(IDD_DEVINSTALL),
|
|
NULL,
|
|
DevInstallDlgProc,
|
|
(LPARAM)&DevDlgData);
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = FALSE;
|
|
// log an event
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // DevInstall
|
|
|
|
|
|
|
|
|
|
LRESULT CALLBACK
|
|
DevInstallDlgProc(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
PDEVINSTALL_DLG_DATA pDevDlgData = NULL;
|
|
BOOL Status = TRUE;
|
|
HICON hIcon = NULL;
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
WCHAR szString[MAX_PATH];
|
|
RECT rcOK, rcInstructions;
|
|
int dy;
|
|
ULONG ulSize;
|
|
|
|
|
|
//
|
|
// Initialize the rest of the DevDlgData fields.
|
|
//
|
|
pDevDlgData = (PDEVINSTALL_DLG_DATA)lParam;
|
|
|
|
pDevDlgData->Timer = GetTickCount();
|
|
pDevDlgData->hDlg = hDlg;
|
|
|
|
SetWindowLong(hDlg, DWL_USER, lParam);
|
|
|
|
//
|
|
// Hide all the controls.
|
|
//
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_DEFAULTDRV), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_OEMDRV), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_SELECTDRV), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_NODRV), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDOK), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_HIDE);
|
|
|
|
//
|
|
// Reduce the height by the difference between the two
|
|
//
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_NEWDEV_INSTRUCTIONS),&rcInstructions);
|
|
GetWindowRect(GetDlgItem(hDlg, IDOK),&rcOK);
|
|
|
|
dy = rcOK.bottom - rcInstructions.bottom;
|
|
|
|
GetWindowRect(hDlg, &(pDevDlgData->DlgRect));
|
|
|
|
SetWindowPos(hDlg, HWND_TOP,
|
|
pDevDlgData->DlgRect.left,
|
|
pDevDlgData->DlgRect.top,
|
|
pDevDlgData->DlgRect.right - pDevDlgData->DlgRect.left,
|
|
(pDevDlgData->DlgRect.bottom - pDevDlgData->DlgRect.top) - dy,
|
|
SWP_SHOWWINDOW);
|
|
|
|
//
|
|
// Set the description field
|
|
//
|
|
ulSize = MAX_PATH * sizeof(TCHAR);
|
|
Status = CM_Get_DevNode_Registry_Property(pDevDlgData->pDeviceInfoData->DevInst,
|
|
CM_DRP_DEVICEDESC,
|
|
NULL,
|
|
szString,
|
|
&ulSize,
|
|
0);
|
|
|
|
if (Status != CR_SUCCESS || *szString == 0x0) {
|
|
//
|
|
// No known description yet so display "Searching..." for now
|
|
//
|
|
LoadString(MyModuleHandle, IDS_SEARCHING, szString, MAX_PATH);
|
|
}
|
|
|
|
SetDlgItemText(hDlg, IDC_NEWDEV_DESCRIPTION, szString);
|
|
|
|
|
|
//
|
|
// Set the instructions to "Please Wait"
|
|
//
|
|
if (LoadString(MyModuleHandle, IDS_NEWDEVFOUND_WAIT,
|
|
szString, MAX_PATH)) {
|
|
|
|
SetDlgItemText(hDlg, IDC_NEWDEV_INSTRUCTIONS, szString);
|
|
}
|
|
|
|
UpdateWindow(GetDlgItem(hDlg, IDC_NEWDEV_CLASSICON));
|
|
PostMessage(hDlg, WUM_DI_INIT, 0, (LPARAM)lParam);
|
|
SetFocus(hDlg);
|
|
return FALSE; // focus has been set
|
|
}
|
|
|
|
|
|
case WUM_DI_INIT: {
|
|
|
|
HANDLE hThread;
|
|
DWORD ThreadId;
|
|
|
|
//
|
|
// Configure the new device
|
|
//
|
|
pDevDlgData = (PDEVINSTALL_DLG_DATA)(LONG)GetWindowLong(hDlg, DWL_USER);
|
|
|
|
//
|
|
// Run the install/configuration code in a separate
|
|
// thread so that the dialog box can continue to
|
|
// repaint, etc. No buttons will be active on the
|
|
// dialog box until this thread routine makes the
|
|
// dialog box interactive.
|
|
//
|
|
if ((hThread = CreateThread(NULL,
|
|
0,
|
|
DmConfigureDevInst,
|
|
pDevDlgData,
|
|
0,
|
|
&ThreadId))) {
|
|
CloseHandle(hThread);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case WM_DESTROY:
|
|
hIcon = (HICON)LOWORD(SendDlgItemMessage(hDlg,
|
|
IDC_NEWDEV_CLASSICON, STM_GETICON, 0, 0L));
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
break;
|
|
|
|
|
|
case WM_CLOSE:
|
|
SendMessage (hDlg, WM_COMMAND, IDCANCEL, 0L);
|
|
break;
|
|
|
|
case WM_HELP: // F1
|
|
WinHelp(((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
|
|
HELP_WM_HELP, (DWORD)(LPTSTR)aProfileIds);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU: // right mouse click
|
|
WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
|
|
(DWORD)(LPTSTR)aProfileIds);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch(LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
//
|
|
// retrieve private data from window long (stored there
|
|
// during WM_INITDIALOG)
|
|
//
|
|
pDevDlgData = (PDEVINSTALL_DLG_DATA)(LONG)GetWindowLong(
|
|
hDlg, DWL_USER);
|
|
|
|
//
|
|
// use default driver
|
|
//
|
|
if (IsDlgButtonChecked(hDlg, IDC_NEWDEV_DEFAULTDRV)) {
|
|
|
|
Status = InstallDev(hDlg, pDevDlgData->hDevInfo, pDevDlgData->pDeviceInfoData);
|
|
}
|
|
//
|
|
// user has a driver disk
|
|
//
|
|
else if (IsDlgButtonChecked(hDlg, IDC_NEWDEV_OEMDRV)) {
|
|
|
|
if(Status = SetupDiSelectOEMDrv(hDlg,
|
|
pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData)) {
|
|
|
|
Status = InstallDev(hDlg, pDevDlgData->hDevInfo, pDevDlgData->pDeviceInfoData);
|
|
}
|
|
|
|
}
|
|
//
|
|
// allow user to select from a list of drivers
|
|
//
|
|
else if (IsDlgButtonChecked(hDlg, IDC_NEWDEV_SELECTDRV)) {
|
|
|
|
ULONG SelectState;
|
|
|
|
SelectState = SelectDeviceClass(pDevDlgData);
|
|
|
|
if (SelectState == SELECT_INSTALLNULLDRIVER) {
|
|
InstallNullDriver(pDevDlgData);
|
|
Status = FALSE; // "real" device not installed
|
|
|
|
} else if (SelectState == SELECT_DOINSTALL) {
|
|
|
|
if((Status = SetupDiCallClassInstaller(DIF_SELECTDEVICE,
|
|
pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData))) {
|
|
|
|
Status = InstallDev(hDlg, pDevDlgData->hDevInfo, pDevDlgData->pDeviceInfoData);
|
|
} else {
|
|
//
|
|
// This means no INFs found for this device type.
|
|
//
|
|
if (GetLastError() == ERROR_DI_BAD_PATH) {
|
|
Status = SetupDiSelectOEMDrv(hDlg,
|
|
pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData);
|
|
}
|
|
}
|
|
|
|
} else if (SelectState == SELECT_CANCEL) {
|
|
//
|
|
// user cancelled, don't fall through and dismiss
|
|
// dialog box
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// install null driver
|
|
//
|
|
else if (IsDlgButtonChecked(hDlg, IDC_NEWDEV_NODRV)) {
|
|
InstallNullDriver(pDevDlgData);
|
|
Status = FALSE; // "real" device not installed
|
|
}
|
|
|
|
//
|
|
// If the user canceled, just return to the dialog without
|
|
// ending.
|
|
//
|
|
// BUGBUG - On Windows 95, if error other than user cancel,
|
|
// do null driver
|
|
//
|
|
if (!Status) {
|
|
if (GetLastError() == ERROR_CANCELLED) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
EndDialog(hDlg, Status);
|
|
free(pDevDlgData);
|
|
return TRUE;
|
|
|
|
|
|
case IDCANCEL:
|
|
pDevDlgData = (PDEVINSTALL_DLG_DATA)(LONG)GetWindowLong(
|
|
hDlg, DWL_USER);
|
|
EndDialog(hDlg, FALSE); // device not installed
|
|
break;
|
|
|
|
#if 0
|
|
case IDD_HELP:
|
|
WinHelp(hDlg, "WINDOWS.HLP>PROC4", HELP_CONTEXT, IDH_NHF_HELP);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // DevInstallDlgProc
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
DmConfigureDevInst(
|
|
IN PDEVINSTALL_DLG_DATA pDevDlgData
|
|
)
|
|
{
|
|
BOOL Status = TRUE;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINSTALL_PARAMS DriverInstallParams;
|
|
BOOL DoSilentInstall;
|
|
HICON hIcon;
|
|
// HANDLE hThread;
|
|
// DWORD ThreadId;
|
|
// DRIVER_LIST_DATA DriverListData;
|
|
|
|
//
|
|
// Retrieve the device installation parameters so that we can set the flags specified
|
|
// by the caller.
|
|
// (In the original Win95 code the 'DiFlags' field of pDevDlgData was OR'ed in at this point.)
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if(SetupDiGetDeviceInstallParams(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DeviceInstallParams)) {
|
|
|
|
DeviceInstallParams.hwndParent = pDevDlgData->hDlg;
|
|
DeviceInstallParams.Flags |= DI_SHOWOEM;
|
|
SetupDiSetDeviceInstallParams(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DeviceInstallParams);
|
|
}
|
|
|
|
//
|
|
// Build the compatible driver list.
|
|
//
|
|
SetupDiBuildDriverInfoList(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
SPDIT_COMPATDRIVER);
|
|
|
|
#if 0 // No need to do spawn a thread for this--we're already in a separate thread.
|
|
|
|
DriverListData.hDevInfo = pDevDlgData->hDevInfo;
|
|
DriverListData.pDeviceInfoData = pDevDlgData->pDeviceInfoData;
|
|
|
|
if ((hThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
BuildDriverListThread,
|
|
&DriverListData,
|
|
0,
|
|
&ThreadId))) {
|
|
|
|
WaitForSingleObject(hThread, INFINITE);
|
|
CloseHandle(hThread);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Now attempt to retrieve the first driver node. If there are none, or if the first one
|
|
// isn't a rank-0 match, then we gotta bug the user about this.
|
|
//
|
|
DoSilentInstall = FALSE;
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if(SetupDiEnumDriverInfo(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
SPDIT_COMPATDRIVER,
|
|
0,
|
|
&DriverInfoData)) {
|
|
//
|
|
// We know the class type now, set the Description text and the icon
|
|
// in the device install dialog box
|
|
//
|
|
SetDlgItemText(DeviceInstallParams.hwndParent,
|
|
IDC_NEWDEV_DESCRIPTION,
|
|
DriverInfoData.Description);
|
|
|
|
if (SetupDiLoadClassIcon(&(pDevDlgData->pDeviceInfoData->ClassGuid),
|
|
&hIcon,
|
|
NULL)) {
|
|
|
|
if ((hIcon = (HICON)LOWORD(SendDlgItemMessage(
|
|
DeviceInstallParams.hwndParent,
|
|
IDC_NEWDEV_CLASSICON,
|
|
STM_SETICON,
|
|
(WPARAM)hIcon,
|
|
0L)))) {
|
|
DestroyIcon(hIcon);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have at least one driver node--select for later use.
|
|
//
|
|
SetupDiSetSelectedDriver(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DriverInfoData);
|
|
|
|
//
|
|
// Check to see if this driver node is a rank-0 match (in which case a silent
|
|
// install is in order).
|
|
//
|
|
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
|
|
if(SetupDiGetDriverInstallParams(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInstallParams)) {
|
|
|
|
if(!(DriverInstallParams.Rank)) {
|
|
DoSilentInstall = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(DoSilentInstall) {
|
|
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if(SetupDiGetDeviceInstallParams(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DeviceInstallParams)) {
|
|
DeviceInstallParams.Flags |= DI_QUIETINSTALL;
|
|
SetupDiSetDeviceInstallParams(pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&DeviceInstallParams);
|
|
}
|
|
|
|
//
|
|
// The informative portion of the dialog box is already being displayed
|
|
// at this point so just finish the install...
|
|
//
|
|
InstallDev(pDevDlgData->hDlg, pDevDlgData->hDevInfo, pDevDlgData->pDeviceInfoData);
|
|
Status = FALSE; // close the dialog, we're done
|
|
EndDialog(pDevDlgData->hDlg, TRUE);
|
|
|
|
} else {
|
|
//
|
|
// Expand the dialog to include its interactive portion (radio button
|
|
// installation options), based on which radio button the user chooses,
|
|
// do the appropriate installation...
|
|
//
|
|
MakeDialogInteractive(pDevDlgData->hDlg,
|
|
(LPCTSTR)pDevDlgData->szDeviceId,
|
|
pDevDlgData->hDevInfo,
|
|
pDevDlgData->pDeviceInfoData,
|
|
&(pDevDlgData->DlgRect));
|
|
}
|
|
|
|
|
|
if (Status == FALSE) {
|
|
EndDialog(pDevDlgData->hDlg, TRUE);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // DmConfigureDevInst
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
BuildDriverListThread(
|
|
IN PVOID ThreadParam
|
|
)
|
|
{
|
|
PDRIVER_LIST_DATA pDriverListData = (PDRIVER_LIST_DATA)ThreadParam;
|
|
|
|
SetupDiBuildDriverInfoList(pDriverListData->hDevInfo,
|
|
pDriverListData->pDeviceInfoData,
|
|
SPDIT_COMPATDRIVER);
|
|
|
|
return TRUE;
|
|
|
|
} // BuildDriverListThread
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
MakeDialogInteractive(
|
|
IN HWND hDlg,
|
|
IN LPCTSTR pszDeviceId,
|
|
IN HDEVINFO hDevInfo,
|
|
IN PSP_DEVINFO_DATA pDeviceInfoData,
|
|
IN PRECT pDlgRect
|
|
)
|
|
{
|
|
WCHAR szString[MAX_PATH];
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
LOG_CONF LogConfig;
|
|
|
|
UNREFERENCED_PARAMETER(pszDeviceId);
|
|
|
|
//
|
|
// enable all the controls
|
|
//
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_DEFAULTDRV), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_OEMDRV), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_SELECTDRV), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NEWDEV_NODRV), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
|
|
|
|
//
|
|
// Expand the dialog to its full size
|
|
//
|
|
SetWindowPos(hDlg,
|
|
HWND_TOP,
|
|
pDlgRect->left,
|
|
pDlgRect->top,
|
|
pDlgRect->right - pDlgRect->left,
|
|
pDlgRect->bottom - pDlgRect->top,
|
|
SWP_SHOWWINDOW);
|
|
|
|
|
|
//
|
|
// set the instruction text to the interactive case
|
|
//
|
|
if (LoadString(MyModuleHandle, IDS_NEWDEVFOUND_NOAUTO,
|
|
szString, MAX_PATH)) {
|
|
|
|
SetDlgItemText(hDlg, IDC_NEWDEV_INSTRUCTIONS, szString);
|
|
}
|
|
|
|
//
|
|
// Determine if we have a Compatible Driver, and set the default radio button
|
|
//
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if(!SetupDiEnumDriverInfo(hDevInfo,
|
|
pDeviceInfoData,
|
|
SPDIT_COMPATDRIVER,
|
|
0,
|
|
&DriverInfoData)) {
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_NEWDEV_DEFAULTDRV), FALSE);
|
|
CheckRadioButton(hDlg, IDC_NEWDEV_OEMDRV, IDC_NEWDEV_NODRV, IDC_NEWDEV_OEMDRV);
|
|
SetFocus(GetDlgItem(hDlg, IDC_NEWDEV_OEMDRV));
|
|
|
|
} else {
|
|
CheckRadioButton(hDlg, IDC_NEWDEV_DEFAULTDRV, IDC_NEWDEV_NODRV, IDC_NEWDEV_DEFAULTDRV);
|
|
SetFocus(GetDlgItem(hDlg, IDC_NEWDEV_DEFAULTDRV));
|
|
}
|
|
|
|
#if 0 // The following Win95 logic may be necessary at some point...
|
|
//
|
|
// See if this device has a boot config. If it does, then disable the cancel button, since we
|
|
// must install the NULL driver at a minimum.
|
|
// BUG # 18380 [DJM]
|
|
//
|
|
if (CM_Get_First_Log_Conf(&LogConfig, pDeviceInfoData->DevInst, BOOT_LOG_CONF) == CR_SUCCESS) {
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
|
|
EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_DISABLED);
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
|
|
} // DisplayInteractiveInstallDialog
|
|
|
|
|
|
|
|
BOOL
|
|
GetClassGuidForInf(
|
|
IN PCTSTR InfFileName,
|
|
OUT LPGUID ClassGuid
|
|
)
|
|
{
|
|
TCHAR ClassName[MAX_CLASS_NAME_LEN];
|
|
DWORD NumGuids;
|
|
|
|
if(!SetupDiGetINFClass(InfFileName,
|
|
ClassGuid,
|
|
ClassName,
|
|
SIZECHARS(ClassName),
|
|
NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if(pSetupIsGuidNull(ClassGuid)) {
|
|
//
|
|
// Then we need to retrieve the GUID associated with the INF's class name.
|
|
// (If this class name isn't installed (i.e., has no corresponding GUID),
|
|
// or if it matches with multiple GUIDs, then we abort.
|
|
//
|
|
if(!SetupDiClassGuidsFromName(ClassName, ClassGuid, 1, &NumGuids) || !NumGuids) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
InstallDev(
|
|
IN HWND hwndParent,
|
|
IN HDEVINFO hDevInfo,
|
|
IN PSP_DEVINFO_DATA pDeviceInfoData
|
|
)
|
|
{
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
|
|
TCHAR InfClassGuidString[MAX_GUID_STRING_LEN];
|
|
GUID ClassGuid;
|
|
LPGUID ClassGuidList;
|
|
DWORD ClassGuidListSize, i;
|
|
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if(SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &DriverInfoData)) {
|
|
//
|
|
// Get details on this driver node, so that we can examine the INF that this
|
|
// node came from.
|
|
//
|
|
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
|
|
if(!SetupDiGetDriverInfoDetail(hDevInfo,
|
|
pDeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInfoDetailData,
|
|
sizeof(DriverInfoDetailData),
|
|
NULL)
|
|
&& (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(!GetClassGuidForInf(DriverInfoDetailData.InfFileName, &ClassGuid)) {
|
|
//
|
|
// If we can't find out what the class GUID is for this INF, we're dead.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Retrieve a list of all installed classes, to see if this driver node's class
|
|
// has previously been installed.
|
|
//
|
|
ClassGuidListSize = 10;
|
|
|
|
while(TRUE) {
|
|
if(!(ClassGuidList = MyMalloc(ClassGuidListSize * sizeof(GUID)))) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if(SetupDiBuildClassInfoList(0, ClassGuidList, ClassGuidListSize, &ClassGuidListSize)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Free the current buffer before checking the cause of the failure.
|
|
//
|
|
MyFree(ClassGuidList);
|
|
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < ClassGuidListSize; i++) {
|
|
if(IsEqualGUID(&(ClassGuidList[i]), &ClassGuid)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MyFree(ClassGuidList);
|
|
|
|
if((i == ClassGuidListSize) ||
|
|
(IsEqualGUID(&ClassGuid, &GUID_DEVCLASS_NET)
|
|
&& InfIsFromOemLocation(DriverInfoDetailData.InfFileName))) {
|
|
|
|
//
|
|
// Then this class hasn't been installed yet, or it's an OEM
|
|
// network INF that might want to install its own class
|
|
// installer--install the class installer now.
|
|
//
|
|
if(!SetupDiInstallClass(hwndParent,
|
|
DriverInfoDetailData.InfFileName,
|
|
0,
|
|
NULL) && (i == ClassGuidListSize)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now make sure that the class of this device is the same as the class
|
|
// of the selected driver node.
|
|
//
|
|
if(!IsEqualGUID(&ClassGuid, &(pDeviceInfoData->ClassGuid))) {
|
|
|
|
pSetupStringFromGuid(&ClassGuid, InfClassGuidString, SIZECHARS(InfClassGuidString));
|
|
|
|
SetupDiSetDeviceRegistryProperty(hDevInfo,
|
|
pDeviceInfoData,
|
|
SPDRP_CLASSGUID,
|
|
(PBYTE)InfClassGuidString,
|
|
sizeof(InfClassGuidString)
|
|
);
|
|
}
|
|
|
|
} else if(pSetupIsGuidNull(&(pDeviceInfoData->ClassGuid))) {
|
|
//
|
|
// No selected driver, and no associated class--use "Unknown" class.
|
|
//
|
|
SetupDiSetDeviceRegistryProperty(hDevInfo,
|
|
pDeviceInfoData,
|
|
SPDRP_CLASSGUID,
|
|
(PBYTE)szUnknownClassGuid,
|
|
sizeof(szUnknownClassGuid)
|
|
);
|
|
}
|
|
|
|
return SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
|
|
hDevInfo,
|
|
pDeviceInfoData
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
SelectDeviceClass(
|
|
IN OUT PDEVINSTALL_DLG_DATA pData
|
|
)
|
|
{
|
|
return (ULONG)DialogBoxParam(MyModuleHandle,
|
|
MAKEINTRESOURCE(IDD_SELECTCLASS),
|
|
pData->hDlg,
|
|
PickClassDlgProc,
|
|
(LPARAM)pData);
|
|
|
|
} // SelectDeviceClass
|
|
|
|
|
|
|
|
LRESULT CALLBACK
|
|
PickClassDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
HWND hList = GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST);
|
|
static LPGUID pClassGuidList = NULL;
|
|
static int SelectedClass = 0;
|
|
static BOOL bNetClassFound = FALSE;
|
|
static SP_CLASSIMAGELIST_DATA ClassImageList;
|
|
LPGUID pClassGuid = NULL;
|
|
PDEVINSTALL_DLG_DATA pData = NULL;
|
|
BOOL IsRealClassInstaller;
|
|
|
|
|
|
switch (wMsg) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
ULONG ulCount = 32, i;
|
|
LV_COLUMN lvcCol;
|
|
LV_ITEM lviItem;
|
|
int iMiniIconListIndex;
|
|
TCHAR szDescription[MAX_PATH];
|
|
HWND hList = GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST);
|
|
|
|
SetWindowLong(hDlg, DWL_USER, lParam);
|
|
|
|
// NOTE - Windows 95 has some special PCMCIA code here
|
|
|
|
//
|
|
// Get the Class Icon Image Lists.
|
|
//
|
|
ClassImageList.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
|
|
ClassImageList.Reserved = 0;
|
|
|
|
if (SetupDiGetClassImageList(&ClassImageList)) {
|
|
ListView_SetImageList(GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST),
|
|
ClassImageList.ImageList,
|
|
LVSIL_SMALL);
|
|
}
|
|
|
|
SendMessage(hList, WM_SETREDRAW, FALSE, 0L);
|
|
SendMessage(hList, LB_RESETCONTENT, 0, 0L);
|
|
|
|
//
|
|
// Clear the Class List and init selected class to 0
|
|
//
|
|
ListView_DeleteAllItems(hList);
|
|
|
|
// Insert a column for the class list
|
|
lvcCol.mask = LVCF_FMT | LVCF_WIDTH;
|
|
lvcCol.fmt = LVCFMT_LEFT;
|
|
lvcCol.iSubItem = 0;
|
|
ListView_InsertColumn(hList, 0, (LV_COLUMN FAR *)&lvcCol);
|
|
|
|
lviItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
|
|
lviItem.iItem = -1;
|
|
lviItem.iSubItem = 0;
|
|
|
|
//
|
|
// retrieve a list of known class guids
|
|
//
|
|
pClassGuidList = MyMalloc(sizeof(GUID) * ulCount);
|
|
if (pClassGuidList == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetupDiBuildClassInfoList(0,
|
|
pClassGuidList,
|
|
ulCount,
|
|
&ulCount)) {
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
pClassGuidList = MyRealloc(pClassGuidList, sizeof(GUID) * ulCount);
|
|
if (pClassGuidList == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetupDiBuildClassInfoList(0,
|
|
pClassGuidList,
|
|
ulCount,
|
|
&ulCount)) {
|
|
MyFree(pClassGuidList);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (pClassGuid = pClassGuidList, i = 0;
|
|
i < ulCount;
|
|
pClassGuid++, i++) {
|
|
|
|
if (SetupDiGetClassDescription(pClassGuid,
|
|
szDescription,
|
|
MAX_PATH,
|
|
NULL)) {
|
|
|
|
SetupDiGetClassImageIndex(&ClassImageList,
|
|
pClassGuid,
|
|
&lviItem.iImage);
|
|
|
|
lviItem.pszText = szDescription;
|
|
lviItem.lParam = (LPARAM)pClassGuid;
|
|
ListView_InsertItem(hList, &lviItem);
|
|
|
|
if (memcmp(pClassGuid, &GUID_DEVCLASS_NET, sizeof(GUID)) == 0) {
|
|
bNetClassFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
ListView_SetItemState(hList, 0,(LVIS_SELECTED|LVIS_FOCUSED),
|
|
(LVIS_SELECTED|LVIS_FOCUSED));
|
|
|
|
ListView_EnsureVisible(hList, ListView_GetNextItem(hList, -1,
|
|
LVNI_SELECTED), FALSE);
|
|
|
|
//
|
|
// Resize the Column
|
|
//
|
|
ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
SendMessage(hList, WM_SETREDRAW, TRUE, 0L);
|
|
|
|
return 0; // Focus Allready Set
|
|
break;
|
|
}
|
|
|
|
|
|
case WM_DESTROY: {
|
|
//
|
|
// free resources
|
|
//
|
|
SetupDiDestroyClassImageList(&ClassImageList);
|
|
|
|
if (pClassGuidList != NULL) {
|
|
MyFree(pClassGuidList);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch(LOWORD(wParam)) {
|
|
|
|
case IDCANCEL: {
|
|
|
|
EndDialog(hDlg, SELECT_CANCEL);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
case IDOK: {
|
|
|
|
int iCur = 0;
|
|
LV_ITEM lviItem;
|
|
TCHAR szBuffer[512], szTemp[256], szCaption[64];
|
|
|
|
//
|
|
// retrieve private data from window long (stored there
|
|
// during WM_INITDIALOG)
|
|
//
|
|
pData = (PDEVINSTALL_DLG_DATA)(LONG)GetWindowLong(
|
|
hDlg, DWL_USER);
|
|
|
|
//
|
|
// Retrieve selected device class
|
|
//
|
|
iCur = (int)ListView_GetNextItem(GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST),
|
|
-1, LVNI_SELECTED);
|
|
if (iCur == LB_ERR) {
|
|
break;
|
|
}
|
|
|
|
lviItem.mask = LVIF_PARAM;
|
|
lviItem.iItem = iCur;
|
|
lviItem.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST),
|
|
&lviItem)) {
|
|
break;
|
|
}
|
|
|
|
pClassGuid = (LPGUID)lviItem.lParam;
|
|
|
|
//
|
|
// Is it the "madeup" network adapter entry that was
|
|
// selected?
|
|
//
|
|
IsRealClassInstaller = FALSE;
|
|
if ((memcmp(pClassGuid, &GUID_DEVCLASS_NET, sizeof(GUID)) == 0)) {
|
|
|
|
HKEY hk;
|
|
|
|
hk = SetupDiOpenClassRegKey((LPGUID)&GUID_DEVCLASS_NET, KEY_READ);
|
|
if(hk != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// Does this class have an "Installer32" entry?
|
|
//
|
|
if(RegQueryValueEx(hk,
|
|
REGSTR_VAL_INSTALLER_32,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL) == ERROR_SUCCESS) {
|
|
|
|
IsRealClassInstaller = TRUE;
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
if(!IsRealClassInstaller) {
|
|
LoadString(MyModuleHandle, IDS_NETADAPTER_PROMPT1,
|
|
szBuffer, 512);
|
|
LoadString(MyModuleHandle, IDS_NETADAPTER_PROMPT2,
|
|
szTemp, 256);
|
|
|
|
LoadString(MyModuleHandle, IDS_NETADAPTER_CAPTION,
|
|
szCaption, 64);
|
|
|
|
lstrcat(szBuffer, szTemp);
|
|
|
|
if (MessageBox(hDlg, szBuffer, szCaption,
|
|
MB_OKCANCEL) == IDOK) {
|
|
|
|
EndDialog(hDlg, SELECT_INSTALLNULLDRIVER);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID(pClassGuid, &GUID_DEVCLASS_UNKNOWN)) {
|
|
ZeroMemory(pClassGuid, sizeof(GUID));
|
|
}
|
|
|
|
//
|
|
// convert class guid to string and set class property
|
|
//
|
|
|
|
SetupDiDestroyDriverInfoList(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
SPDIT_CLASSDRIVER);
|
|
SetupDiDestroyDriverInfoList(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
SPDIT_COMPATDRIVER);
|
|
|
|
pSetupStringFromGuid(pClassGuid, szBuffer, 256);
|
|
SetupDiSetDeviceRegistryProperty(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
SPDRP_CLASSGUID,
|
|
(LPBYTE)szBuffer,
|
|
MAX_GUID_STRING_LEN * sizeof(TCHAR));
|
|
|
|
EndDialog(hDlg, SELECT_DOINSTALL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR FAR *)lParam)->code) {
|
|
|
|
case LVN_ITEMCHANGED: {
|
|
LPNM_LISTVIEW lpnmlv = (LPNM_LISTVIEW)lParam;
|
|
|
|
if ((lpnmlv->uChanged & LVIF_STATE) &&
|
|
(lpnmlv->uNewState & LVIS_SELECTED)) {
|
|
SelectedClass = lpnmlv->iItem;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
ImageList_SetBkColor((HIMAGELIST)SendMessage(GetDlgItem(hDlg, IDC_NDW_PICKCLASS_CLASSLIST),
|
|
LVM_GETIMAGELIST,
|
|
(WPARAM)(LVSIL_SMALL),
|
|
0L),
|
|
GetSysColor(COLOR_WINDOW));
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // PickClassDlgProc
|
|
|
|
|
|
|
|
BOOL
|
|
InstallNullDriver(
|
|
IN PDEVINSTALL_DLG_DATA pData
|
|
)
|
|
{
|
|
SP_DEVINSTALL_PARAMS DevInstallParams;
|
|
BOOL Status = TRUE;
|
|
|
|
DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
|
|
if (SetupDiGetDeviceInstallParams(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
&DevInstallParams)) {
|
|
|
|
DevInstallParams.FlagsEx |= DI_FLAGSEX_SETFAILEDINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
&DevInstallParams);
|
|
}
|
|
|
|
if(SetupDiSetSelectedDriver(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
NULL)) {
|
|
|
|
Status = InstallDev(pData->hDlg, pData->hDevInfo, pData->pDeviceInfoData);
|
|
}
|
|
|
|
if (SetupDiGetDeviceInstallParams(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
&DevInstallParams)) {
|
|
|
|
DevInstallParams.FlagsEx &= ~DI_FLAGSEX_SETFAILEDINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(pData->hDevInfo,
|
|
pData->pDeviceInfoData,
|
|
&DevInstallParams);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // InstallNullDriver
|
|
|
|
|
|
|
|
|