TechTalkz.com Logo

Go Back   TechTalkz.com Technology & Computer Troubleshooting Forums > Tech Support Archives > Microsoft > Microsoft Device Drivers

Notices

Reply
 
Thread Tools Display Modes
Old 29-11-2007, 06:01 PM   #1
J Prendergast
Guest
 
Posts: n/a
Help disabling a device using SetupDiXXX fns after WM_DEVICECHANGE

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:
1) In the above functionality, the call to SetupDiCallClassInstaller takes
approximately 30 seconds to execute, after which time the device gets
disabled successfully.
2) 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.
3) 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,ARR AYSIZE(strReboot)))
{
return false;
}
if (!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYS IZE(strFail)))
{
return false;
}

context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
context.strSuccess = strEnable;
}
else
{
if (!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZ E(strDisable)))
{
return false;
}
if (!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,AR RAYSIZE(strReboot)))
{
return false;
}
if (!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAY SIZE(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,&devPa rams) &&
(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.

JP
  Reply With Quote
Old 29-11-2007, 06:02 PM   #2
J Prendergast
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECHANGE

Hello again,

Following on from my earlier message, I've found something that helps with
the 30 second delay problem. Instead of calling the
SetupDiSetClassInstallParams / SetupDiCallClassInstaller functionality
directly from the WM_DEVICECHANGE / DBT_DEVICEARRIVAL case, I now set a
variable containing the device id to indicate that disabling is required.
I've set up a timer to fire every 5 seconds, and in the WM_TIMER case, I call
the SetupDiSetClassInstallParams / SetupDiCallClassInstaller functionality.
The disabling takes place immediately upon this code being called. I'm not
sure why this approach helps, but it does.

My changes have improved the responsiveness, but something else that I see
occasionally is that when I have sent a request to disable a device, the
device still looks enabled (no red cross through it) in Device Manager and is
accessible – yet the right-click context menu for the item in Device Manager
shows an “Enabled” option, giving the impression that the device _is_
disabled. This confusion seems to remain until the computer is restarted.
Does anyone have an idea how to get around this, so that the state changes
in Device Manager and no restart is required?

Interestingly, I saw a similar phenomenon when I set the timer to a shorter
period, e.g. 1 second, - i.e. the device looked enabled in Device Manager but
it was still accessible and the context menu for it showed an "Enable" option
implying that it's been disabled. It's almost as though the disabling had
been "half-done" somehow.

I'd be very grateful for any ideas about the above behaviour and possible
ways of dealing with, thanks again,

JP

"J Prendergast" wrote:

> 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:
> 1) In the above functionality, the call to SetupDiCallClassInstaller takes
> approximately 30 seconds to execute, after which time the device gets
> disabled successfully.
> 2) 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.
> 3) 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,ARR AYSIZE(strReboot)))
> {
> return false;
> }
> if (!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYS IZE(strFail)))
> {
> return false;
> }
>
> context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
> context.strSuccess = strEnable;
> }
> else
> {
> if (!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZ E(strDisable)))
> {
> return false;
> }
> if (!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,AR RAYSIZE(strReboot)))
> {
> return false;
> }
> if (!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAY SIZE(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,&devPa rams) &&
> (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.
>
> JP

  Reply With Quote
Old 29-11-2007, 06:02 PM   #3
Daniel Whitaker [MSFT]
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECHANGE

Greetings,

Interesting project Some of this is a bit curious, but I do know that
there is a difference when you are adding a new device/enabling for the
first time. Windows creates a special thread for the install and will not
allow other "PNP" events to occur until it completes. If it does not
succeed, you will normally see a "there was a problem with your hardware"
bubble. But otherwise there can be a delay for the next "PNP" event. They
sort of line up in a row and then are sent through this thread pipe. There
is a timeout value of up to 2 minutes, but generally it seems to be about
30 seconds to a minute.

Any other time, the function call is taken care of immediately. I would
think that calling the ClassInstaller when the thread is active would
probably cause a delay because of the install thread. Does that make any
sense?

Daniel Whitaker
DDK Support Team

This posting is provided "AS IS" with no warranties, and confers no rights



  Reply With Quote
Old 29-11-2007, 06:22 PM   #4
J Prendergast
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Many thanks for the response - yes, this does make sense.

In my previous message, I mentioned that I set a timer to wait for a few
seconds after a device is plugged in before sending a request to disable the
device. By doing this, the disabling took place immediately. If I didn't wait
for a few seconds, then it would take 30 seconds or more.

Ideally, I would prefer not to use a timer to wait for a specific amount of
time for the newly-added device to be fully installed - as I appreciate that
this may vary from computer to computer, and maybe even from device to device.

Does anyone know a way whereby I can detect / get a notification when a
device is fully initialised (i.e. when the special thread for the install has
completed) after being plugged in?
(at which time, I would be guaranteed a “safe” time to request disabling and
get an immediate response.)
The WM_DEVICECHANGE message with DBT_DEVICEARRIVAL wParam seems to tell me
the instant it is plugged in, but before it is all initialised and safe to
send to a disable request.

Thanks again for any ideas / comments.

JP

"Daniel Whitaker [MSFT]" wrote:

> Greetings,
>
> Interesting project Some of this is a bit curious, but I do know that
> there is a difference when you are adding a new device/enabling for the
> first time. Windows creates a special thread for the install and will not
> allow other "PNP" events to occur until it completes. If it does not
> succeed, you will normally see a "there was a problem with your hardware"
> bubble. But otherwise there can be a delay for the next "PNP" event. They
> sort of line up in a row and then are sent through this thread pipe. There
> is a timeout value of up to 2 minutes, but generally it seems to be about
> 30 seconds to a minute.
>
> Any other time, the function call is taken care of immediately. I would
> think that calling the ClassInstaller when the thread is active would
> probably cause a delay because of the install thread. Does that make any
> sense?
>
> Daniel Whitaker
> DDK Support Team
>
> This posting is provided "AS IS" with no warranties, and confers no rights
>
>
>

  Reply With Quote
Old 29-11-2007, 06:28 PM   #5
Daniel Whitaker [MSFT]
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Greetings,

Yes. I have a small program called USBMon which incorporates your ideas.
I think this can be used to definitively determine when a device is
completely installed. It's in VC6 and VC7 format, so where can I send the
code to?

Thanks

Daniel Whitaker
DDK Support Team

This posting is provided "AS IS" with no warranties, and confers no rights


  Reply With Quote
Old 29-11-2007, 06:29 PM   #6
Daniel Whitaker [MSFT]
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Greetings,

I sent you the code I promised. In short, and for the benefit of others,
the best way to determine when the PNP Manager is finished with any install
is to use the functions you have mentioned IN CONJUCTION with
CMP_WaitNoPendingInstallEvents.

This combo will give you the exact moment when the "You new hardware is now
ready for use" bubble tip arrives.

You may have noticed, depending upon how you called
RegisterDeviceNotification, that the notification structure is NULL. This
can keep you from determining the ID of the device that has arrived. The
way around this is to call:

hDevNotify = RegisterDeviceNotification(this->m_hWnd,
&NotifcationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE |DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);

Now the notification structures will be filled with all sorts of
information, including the ID of the arrived/departed device.

Using WM_DEVICECHANGE by itself does provide a number of "arrivals",
especially for USB devices. This is because your USB device, at a minimum,
is also connected to a USB controller and a USB HUB. They all "arrive"
when you plug something in, or even unplug it. So you need to sort of
encapsulate the WM_DEVICECHANGE messages inside the use of
CMP_WaitNoPendingInstallEvents.

In this way you get something like the following:

StartPNP
Arrival <- Hub
Arrival <- Controller
Removal Query <- device
Arrival <- device (final)
CompletePNP

Trying to access your device while inside this capsule can lead to, "Your
hardware has malfunctioned" or "Your machine needs to be rebooted".

So what I do is create an OnTimer function that gets fired every second and
checks the return value of CMP_WaitNoPendingInstallEvents. Like this:

OnTimer(UINT nIDEvent)
{
DWORD dwRet;
switch(nIDEvent)
{
case PNP_TIMER: // defined elsewhere
KillTimer(PNP_TIMER);
dwRet = CMP_WaitNoPendingInstallEvents(0); // returns
immediately
switch(dwRet)
{
case WAIT_OBJECT_0: // PNP complete
if(m_bPNPActive) // global flag defined elsewhere
{
m_bPNPActive = FALSE;
// do something, set an event, send a
message, etc
}
break;
case WAIT_TIMEOUT:
if(!m_bPNPActive) // do something like
m_ctlList.InsertString(0,"PNP Installing, please wait");
m_bPNPActive = TRUE;
break;
case WAIT_FAILED: // something bad happened
break;
}
SetTimer(PNP_TIMER,1000,NULL);
return;
}
CDialog::OnTimer(nIDEvent)
}

There are other ways to monitor PNP, but this is just one of them.
Basically, when WAIT_OBJECT_0 is returned, the "Your hardware is ready to
use" comes up within a second after that. By the way, this works for
ServerSide installs as well as ClientSide installs.

Hope this helps!

Daniel Whitaker
Microsoft DDK Team

This posting is provided "AS IS" with no warranties, and confers no rights.
  Reply With Quote
Old 29-11-2007, 06:30 PM   #7
J Prendergast
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Hi,

many thanks for sending this code. In the past day, I've found, with the
help of a colleague, that the reason for the 30 second delay seems to be that
I was calling the device control code in the same worker thread that is
receiving and processing Windows messages (e.g. WM_DEVICECHANGE) that are
sent by a synchronous SendMessage call. The code to process these messages
was not being executed at the point when the device control code is called,
and since the thread gets blocked until these messages are processed, the 30
second phenomena seems to have been due to a 30 second timeout on the sent
message expiring (i.e. it gives up on the message being handled and unblocks
the thread), and then the disable device code is executed finally.

Instead of directly requesting the device control functionality at the point
when a device is plugged in or enabled, I now post a Windows user message,
which ensures that the Windows message queue is still being processed, and on
receiving this Windows user message, I call the device control disabling
code, which gives an immediate result.

Thanks very much for sending the code and the explanation - I am going to
use the CMP_WaitNoPendingInstallEvents, at the time when I'm about to call
the device control (disabling) code, to ensure that it's a good time to do so
- thus eliminating possible "Your hardware has malfunctioned" or "Your
machine needs to be rebooted" messages.
Previously, if the SP_DEVINSTALL_PARAMS structure populated by my call to
SetupDiGetDeviceInstallParams to request disabling of the device indicated a
reboot is required (by setting the DI_NEEDRESTART and DI_NEEDREBOOT bits in
the Flags DWORD struct member) - my workaround was to have some retry code
whereby I would request enabling then disabling of the device, which cleared
the need for a reboot, usually the first time, but if not, I did another
retry (not ideal!).
Hopefully, using CMP_WaitNoPendingInstallEvents will eliminate the need for
this retry code.

Just as an aside - I don't use the DEVICE_NOTIFY_ALL_INTERFACE_CLASSES value
in my call to RegisterDeviceNotification, as this is only supported from
Windows XP onwards, and my application needs to also work on Windows 2000.

Many thanks for your help - it is very much appreciated.
I hope the solution to this problem helps others who experience similar
difficulties.

Best Regards,
JP


"Daniel Whitaker [MSFT]" wrote:

> Greetings,
>
> I sent you the code I promised. In short, and for the benefit of others,
> the best way to determine when the PNP Manager is finished with any install
> is to use the functions you have mentioned IN CONJUCTION with
> CMP_WaitNoPendingInstallEvents.
>
> This combo will give you the exact moment when the "You new hardware is now
> ready for use" bubble tip arrives.
>
> You may have noticed, depending upon how you called
> RegisterDeviceNotification, that the notification structure is NULL. This
> can keep you from determining the ID of the device that has arrived. The
> way around this is to call:
>
> hDevNotify = RegisterDeviceNotification(this->m_hWnd,
> &NotifcationFilter,
> DEVICE_NOTIFY_WINDOW_HANDLE |DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
>
> Now the notification structures will be filled with all sorts of
> information, including the ID of the arrived/departed device.
>
> Using WM_DEVICECHANGE by itself does provide a number of "arrivals",
> especially for USB devices. This is because your USB device, at a minimum,
> is also connected to a USB controller and a USB HUB. They all "arrive"
> when you plug something in, or even unplug it. So you need to sort of
> encapsulate the WM_DEVICECHANGE messages inside the use of
> CMP_WaitNoPendingInstallEvents.
>
> In this way you get something like the following:
>
> StartPNP
> Arrival <- Hub
> Arrival <- Controller
> Removal Query <- device
> Arrival <- device (final)
> CompletePNP
>
> Trying to access your device while inside this capsule can lead to, "Your
> hardware has malfunctioned" or "Your machine needs to be rebooted".
>
> So what I do is create an OnTimer function that gets fired every second and
> checks the return value of CMP_WaitNoPendingInstallEvents. Like this:
>
> OnTimer(UINT nIDEvent)
> {
> DWORD dwRet;
> switch(nIDEvent)
> {
> case PNP_TIMER: // defined elsewhere
> KillTimer(PNP_TIMER);
> dwRet = CMP_WaitNoPendingInstallEvents(0); // returns
> immediately
> switch(dwRet)
> {
> case WAIT_OBJECT_0: // PNP complete
> if(m_bPNPActive) // global flag defined elsewhere
> {
> m_bPNPActive = FALSE;
> // do something, set an event, send a
> message, etc
> }
> break;
> case WAIT_TIMEOUT:
> if(!m_bPNPActive) // do something like
> m_ctlList.InsertString(0,"PNP Installing, please wait");
> m_bPNPActive = TRUE;
> break;
> case WAIT_FAILED: // something bad happened
> break;
> }
> SetTimer(PNP_TIMER,1000,NULL);
> return;
> }
> CDialog::OnTimer(nIDEvent)
> }
>
> There are other ways to monitor PNP, but this is just one of them.
> Basically, when WAIT_OBJECT_0 is returned, the "Your hardware is ready to
> use" comes up within a second after that. By the way, this works for
> ServerSide installs as well as ClientSide installs.
>
> Hope this helps!
>
> Daniel Whitaker
> Microsoft DDK Team
>
> This posting is provided "AS IS" with no warranties, and confers no rights

  Reply With Quote
Old 29-11-2007, 06:31 PM   #8
Daniel Whitaker [MSFT]
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Greetings,

Yes. I have a small program called USBMon which incorporates your ideas.
I think this can be used to definitively determine when a device is
completely installed. It's in VC6 and VC7 format, so where can I send the
code to?

Thanks

Daniel Whitaker
DDK Support Team

This posting is provided "AS IS" with no warranties, and confers no rights


  Reply With Quote
Old 29-11-2007, 06:32 PM   #9
Daniel Whitaker [MSFT]
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Greetings,

I sent you the code I promised. In short, and for the benefit of others,
the best way to determine when the PNP Manager is finished with any install
is to use the functions you have mentioned IN CONJUCTION with
CMP_WaitNoPendingInstallEvents.

This combo will give you the exact moment when the "You new hardware is now
ready for use" bubble tip arrives.

You may have noticed, depending upon how you called
RegisterDeviceNotification, that the notification structure is NULL. This
can keep you from determining the ID of the device that has arrived. The
way around this is to call:

hDevNotify = RegisterDeviceNotification(this->m_hWnd,
&NotifcationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE |DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);

Now the notification structures will be filled with all sorts of
information, including the ID of the arrived/departed device.

Using WM_DEVICECHANGE by itself does provide a number of "arrivals",
especially for USB devices. This is because your USB device, at a minimum,
is also connected to a USB controller and a USB HUB. They all "arrive"
when you plug something in, or even unplug it. So you need to sort of
encapsulate the WM_DEVICECHANGE messages inside the use of
CMP_WaitNoPendingInstallEvents.

In this way you get something like the following:

StartPNP
Arrival <- Hub
Arrival <- Controller
Removal Query <- device
Arrival <- device (final)
CompletePNP

Trying to access your device while inside this capsule can lead to, "Your
hardware has malfunctioned" or "Your machine needs to be rebooted".

So what I do is create an OnTimer function that gets fired every second and
checks the return value of CMP_WaitNoPendingInstallEvents. Like this:

OnTimer(UINT nIDEvent)
{
DWORD dwRet;
switch(nIDEvent)
{
case PNP_TIMER: // defined elsewhere
KillTimer(PNP_TIMER);
dwRet = CMP_WaitNoPendingInstallEvents(0); // returns
immediately
switch(dwRet)
{
case WAIT_OBJECT_0: // PNP complete
if(m_bPNPActive) // global flag defined elsewhere
{
m_bPNPActive = FALSE;
// do something, set an event, send a
message, etc
}
break;
case WAIT_TIMEOUT:
if(!m_bPNPActive) // do something like
m_ctlList.InsertString(0,"PNP Installing, please wait");
m_bPNPActive = TRUE;
break;
case WAIT_FAILED: // something bad happened
break;
}
SetTimer(PNP_TIMER,1000,NULL);
return;
}
CDialog::OnTimer(nIDEvent)
}

There are other ways to monitor PNP, but this is just one of them.
Basically, when WAIT_OBJECT_0 is returned, the "Your hardware is ready to
use" comes up within a second after that. By the way, this works for
ServerSide installs as well as ClientSide installs.

Hope this helps!

Daniel Whitaker
Microsoft DDK Team

This posting is provided "AS IS" with no warranties, and confers no rights.
  Reply With Quote
Old 29-11-2007, 06:32 PM   #10
J Prendergast
Guest
 
Posts: n/a
RE: Help disabling a device using SetupDiXXX fns after WM_DEVICECH

Hi,

many thanks for sending this code. In the past day, I've found, with the
help of a colleague, that the reason for the 30 second delay seems to be that
I was calling the device control code in the same worker thread that is
receiving and processing Windows messages (e.g. WM_DEVICECHANGE) that are
sent by a synchronous SendMessage call. The code to process these messages
was not being executed at the point when the device control code is called,
and since the thread gets blocked until these messages are processed, the 30
second phenomena seems to have been due to a 30 second timeout on the sent
message expiring (i.e. it gives up on the message being handled and unblocks
the thread), and then the disable device code is executed finally.

Instead of directly requesting the device control functionality at the point
when a device is plugged in or enabled, I now post a Windows user message,
which ensures that the Windows message queue is still being processed, and on
receiving this Windows user message, I call the device control disabling
code, which gives an immediate result.

Thanks very much for sending the code and the explanation - I am going to
use the CMP_WaitNoPendingInstallEvents, at the time when I'm about to call
the device control (disabling) code, to ensure that it's a good time to do so
- thus eliminating possible "Your hardware has malfunctioned" or "Your
machine needs to be rebooted" messages.
Previously, if the SP_DEVINSTALL_PARAMS structure populated by my call to
SetupDiGetDeviceInstallParams to request disabling of the device indicated a
reboot is required (by setting the DI_NEEDRESTART and DI_NEEDREBOOT bits in
the Flags DWORD struct member) - my workaround was to have some retry code
whereby I would request enabling then disabling of the device, which cleared
the need for a reboot, usually the first time, but if not, I did another
retry (not ideal!).
Hopefully, using CMP_WaitNoPendingInstallEvents will eliminate the need for
this retry code.

Just as an aside - I don't use the DEVICE_NOTIFY_ALL_INTERFACE_CLASSES value
in my call to RegisterDeviceNotification, as this is only supported from
Windows XP onwards, and my application needs to also work on Windows 2000.

Many thanks for your help - it is very much appreciated.
I hope the solution to this problem helps others who experience similar
difficulties.

Best Regards,
JP


"Daniel Whitaker [MSFT]" wrote:

> Greetings,
>
> I sent you the code I promised. In short, and for the benefit of others,
> the best way to determine when the PNP Manager is finished with any install
> is to use the functions you have mentioned IN CONJUCTION with
> CMP_WaitNoPendingInstallEvents.
>
> This combo will give you the exact moment when the "You new hardware is now
> ready for use" bubble tip arrives.
>
> You may have noticed, depending upon how you called
> RegisterDeviceNotification, that the notification structure is NULL. This
> can keep you from determining the ID of the device that has arrived. The
> way around this is to call:
>
> hDevNotify = RegisterDeviceNotification(this->m_hWnd,
> &NotifcationFilter,
> DEVICE_NOTIFY_WINDOW_HANDLE |DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
>
> Now the notification structures will be filled with all sorts of
> information, including the ID of the arrived/departed device.
>
> Using WM_DEVICECHANGE by itself does provide a number of "arrivals",
> especially for USB devices. This is because your USB device, at a minimum,
> is also connected to a USB controller and a USB HUB. They all "arrive"
> when you plug something in, or even unplug it. So you need to sort of
> encapsulate the WM_DEVICECHANGE messages inside the use of
> CMP_WaitNoPendingInstallEvents.
>
> In this way you get something like the following:
>
> StartPNP
> Arrival <- Hub
> Arrival <- Controller
> Removal Query <- device
> Arrival <- device (final)
> CompletePNP
>
> Trying to access your device while inside this capsule can lead to, "Your
> hardware has malfunctioned" or "Your machine needs to be rebooted".
>
> So what I do is create an OnTimer function that gets fired every second and
> checks the return value of CMP_WaitNoPendingInstallEvents. Like this:
>
> OnTimer(UINT nIDEvent)
> {
> DWORD dwRet;
> switch(nIDEvent)
> {
> case PNP_TIMER: // defined elsewhere
> KillTimer(PNP_TIMER);
> dwRet = CMP_WaitNoPendingInstallEvents(0); // returns
> immediately
> switch(dwRet)
> {
> case WAIT_OBJECT_0: // PNP complete
> if(m_bPNPActive) // global flag defined elsewhere
> {
> m_bPNPActive = FALSE;
> // do something, set an event, send a
> message, etc
> }
> break;
> case WAIT_TIMEOUT:
> if(!m_bPNPActive) // do something like
> m_ctlList.InsertString(0,"PNP Installing, please wait");
> m_bPNPActive = TRUE;
> break;
> case WAIT_FAILED: // something bad happened
> break;
> }
> SetTimer(PNP_TIMER,1000,NULL);
> return;
> }
> CDialog::OnTimer(nIDEvent)
> }
>
> There are other ways to monitor PNP, but this is just one of them.
> Basically, when WAIT_OBJECT_0 is returned, the "Your hardware is ready to
> use" comes up within a second after that. By the way, this works for
> ServerSide installs as well as ClientSide installs.
>
> Hope this helps!
>
> Daniel Whitaker
> Microsoft DDK Team
>
> This posting is provided "AS IS" with no warranties, and confers no rights

  Reply With Quote
Reply

Thread Tools
Display Modes


Google
 


All times are GMT +5.5. The time now is 02:06 AM.


vBulletin, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO
Copyright © 2005-2008, TechTalkz.com. All Rights Reserved - Privacy Policy
Valid XHTML 1.0 Transitional