Hello,
I'm working on an application that acts on WM_DEVICECHANGE messages
with wParam value == DBT_DEVICEARRIVAL (indicating the device has
either just been plugged into the system or has just been enabled in
Device Manager), examines the device ID, and if it matches a
particular wildcard value, it disables the device using calls to
SetupDiSetClassInstallParams and SetupDiCallClassInstaller with a
control.context value == DICS_DISABLE.
Here's the phenomena I'm seeing:
- In the above functionality, the call to SetupDiCallClassInstaller
takes approximately 30 seconds to execute, after which time the device
gets disabled successfully. - If I write similar code that responds to WM_DEVICECHANGE /
DBT_DEVICEREMOVECOMPLETE messages (when a device is disabled) by
calling the code to enable the device, it works fine and immediately. - If I have a button in my application that when pressed just calls
these functions to enable or disable a device, the code works
absolutely fine and the device state is changed immediately (for all
of my tests, I'm running Device Manager alongside my application so I
can monitor whether the device has a red cross on it, indicating a
disabled state, or not, indicating an enabled state).
My question then is - why does it take so long to disable a device in
response to it being added / enabled?
Is there an alternative approach that would give better results?
I wondered if it might be a timing problem, e.g. I'm trying to disable
the device too soon after it's been added / enabled, and it isn't
fully initialised yet. So I tried adding a Sleep into the code, even
up to 20 seconds, but it made no difference.
The code I use for the actual enabling / disabling has been lifted
from the DDK Devcon sample application (which exposes all of Device
Manager's functionality via a command line interface), a combination
of the cmdEnable, cmdDisable and ControlCallback functions:
bool CDeviceControlHelper::EnableDevice(const HDEVINFO Devs,
const PSP_DEVINFO_DATA DevInfo,
const bool& bEnable,
tstring& sRetMessage)
{
GenericContext context;
TCHAR strEnable[80];
TCHAR strDisable[80];
TCHAR strReboot[80];
TCHAR strFail[80];
if (bEnable)
{
if (!LoadString(NULL,IDS_ENABLED,strEnable,ARRAYSIZE(strEnable)))
{
return false;
}
if (!LoadString(NULL,IDS_ENABLED_REBOOT,strReboot,ARRAYSIZE(strReboot)))
{
return false;
}
if (!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYSIZE(strFail)))
{
return false;
}
context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
context.strSuccess = strEnable;
}
else
{
if (!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZE(strDisable)))
{
return false;
}
if (!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,ARRAYSIZE(strReboot)))
{
return false;
}
if (!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAYSIZE(strFail)))
{
return false;
}
context.control = DICS_DISABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
context.strSuccess = strDisable;
}
context.reboot = FALSE;
context.count = 0;
context.strReboot = strReboot;
context.strFail = strFail;
// ControlCallback function body
SP_PROPCHANGE_PARAMS pcp;
SP_DEVINSTALL_PARAMS devParams;
switch(context.control)
{
case DICS_ENABLE:
//
// enable both on global and config-specific profile
// do global first and see if that succeeded in enabling the device
// (global enable doesn't mark reboot required if device is still
// disabled on current config whereas vice-versa isn't true)
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = context.control;
pcp.Scope = DICS_FLAG_GLOBAL;
pcp.HwProfile = 0;
//
// don't worry if this fails, we'll get an error when we try config-
// specific.
if (SetupDiSetClassInstallParams(Devs, DevInfo,
&pcp.ClassInstallHeader, sizeof(pcp)))
{
SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo);
}
//
// now enable on config-specific
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = context.control;
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
pcp.HwProfile = 0;
break;
default:
//
// operate on config-specific profile
//
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.StateChange = context.control;
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
pcp.HwProfile = 0;
break;
}
if (!SetupDiSetClassInstallParams(Devs, DevInfo, &pcp.ClassInstallHeader,
sizeof(pcp)) ||
!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo))
{
//
// failed to invoke DIF_PROPERTYCHANGE
//
DumpDeviceWithInfo(Devs,DevInfo,context.strFail);
}
else
{
//
// see if device needs reboot
//
devParams.cbSize = sizeof(devParams);
if (SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) &&
(devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT)))
{
DumpDeviceWithInfo(Devs,DevInfo,context.strReboot);
context.reboot = TRUE;
}
else
{
//
// appears to have succeeded
//
DumpDeviceWithInfo(Devs,DevInfo,context.strSuccess);
}
context.count++;
}
bool bRet = false;
if (bEnable)
{
if (!context.count)
{
sRetMessage.LoadString(IDS_MSG_ENABLE_TAIL_NONE);
}
else if (!context.reboot)
{
sRetMessage.Format(IDS_MSG_ENABLE_TAIL, context.count);
bRet = true;
}
else
{
sRetMessage.Format(IDS_MSG_ENABLE_TAIL_REBOOT, context.count);
}
}
else
{
if (!context.count)
{
sRetMessage.LoadString(IDS_MSG_DISABLE_TAIL_NONE);
}
else if (!context.reboot)
{
sRetMessage.Format(IDS_MSG_DISABLE_TAIL, context.count);
bRet = true;
}
else
{
sRetMessage.Format(IDS_MSG_DISABLE_TAIL_REBOOT, context.count);
}
}
return bRet;
}
Many thanks in advance for any help / advice that anyone can offer in
response to this problem.
HP