![]() |
|
|||||||
| Notices |
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
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 |
|
|
|
#2 |
|
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 |
|
|
|
#3 |
|
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 thatthere 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 |
|
|
|
#4 |
|
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 > > > |
|
|
|
#5 |
|
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 |
|
|
|
#6 |
|
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. |
|
|
|
#7 |
|
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 |
|
|
|
#8 |
|
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 |
|
|
|
#9 |
|
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. |
|
|
|
#10 |
|
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 |
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|