Interesting USB problems…

We manufacture a USB device: ACS USB Servo II . It provides program control over 8 radio control style servos, 8 current sink outputs and 8 current sink inputs.
 
I recently received a technical support request from a customer experiencing a problem using the device from within a Visual Basic application – specifically our supplied demo application. So I dusted off my R & D prototype, and proceded to try and duplicate then fix the problem.
 
First, when I connected my prototype to my Windows 2000 development machine, I was perplexed to find that it no longer worked. I tried a brand new unit out of stock, and it didn’t work either.  I then tried my prototype on the Windows XP workstation that we use for testing these devices in production, and it worked with no problems.
 
So something was broken with the driver on my Windows 2000 development machine. I uninstalled and reinstalled the driver several times without success. I then broke out RegEdit and started looking in the registry to see what was wrong.
 
Our device is a two phase USB device in that it identifies itself as one VID/PID combination using some very simple built-in firmware which downloads the actual target firmware into the product. It then ‘re-numerates’ itself as a new VID/PID combo which is the actual device. This is great because it allows us to update the device firmware easily in the field. By examining the HKLMSYSTEMCurrentControlSetEnumUSB key for our VID/PID combinations, I could see that the device wasn’t getting past the first enumeration, and moreover it wasn’t using the installed matching INF file.
 
I deleted the incorrect enumeration then added addtional logging to the SETUPAPI by modifying the registry key: HKLMSOFTWAREMicrosoftWindowsCurrentVersionSetupLogLevel and unplugged and replugged the device. Examining the produced SETUPAPI.LOG file, I could see that while the matching INF files were being scanned, they were not seen as a match for the correct VID/PID. Very mysterious, for these very same files work as-is on Windows XP and used to work on Windows 2000.
 
After about a day of Googling and searching MSDN, I found an article about INF files:
Creating an INF File. I proceded to examined my hand-crafted INF files that were originally supplied by the IC manufacturer and that had worked up to some point in the past in a line-by-line fashion looking for potential problems.  I know that there is a DDK utility CHKINF that purports to do this, but it requires installing PERL, and was yet another unknown that I wasn’t quite ready to take on at this time.
 
In perusing the DDK information, I stumbled upon this page: System-Supplied Device Setup Classes. This mentions the correct CLASS and CLASSGUID entries for INF files using the system drivers. Upon examing the entry for USB drivers, I found that my ‘loader’ INF was missing the CLASSGUID entry. I add this missing line, and lo and behold now the device correctly installed under Windows 2000. This is remarkable because this same INF file had worked on Windows 2000 since November 2000 and still works on Windows XP with and without SP2 to this day. Go figure.  Something changed since March 2005 which was the last time that I had worked with this device on my development machine.
 
First hurdle solved – only took the better part of a day. Now onto the customer’s problem. Or so I thought anyways…
 
I attempted to access the now installed servo device via our ActiveX control and Visual Basic. Now I was getting an error that the class library wasn’t registered. Break out RegEdit again and have a look. Weird – all of the CLSID and TYPELIB entries were there and correct. Fire up the Visual C++ debugger on the control and attach to the Visual Basic program, then try and call the Initialize method on our control. Fails with and HResult indicating that the class library is not registered. Unfortunately, with VB I can’t see what exactly is failing since it doesn’t ever access the method in the control – it fails before that point.
 
So I fire up a MFC dialog project that I wrote that also calls the control. Added a couple of breakpoints in InitDialog where I call CreateDispatch() and then on the Initialize() method call. I can step through the CreateDispatch call OK, but I get an OleException on the Initialize method call. Stepping through this code, I find that the exception is generated by the COleDispatchDriver::InvokeHelperV() at the end of the function when it actually makes the call to the control via m_lpDispatch->Invoke().
 
By now I’m going nuts. I try and Google a lot of searches for ATL and ActiveX and "Library not Registered" and run down a lot of dead ends. I downloaded and installed DependencyWalker and use this to verify that Regsvr32.exe was working as advertised, and then verify what was happening when my MFC application was trying to use the ActiveX control. After the call to DllGetClassObject then DllCanUnloadNow was called, and the TLIB_NOT_REGISTERED error was returned. Strange. OLEView shows everything looking OK.
 
Finally, in desperation I add a DebugBreak() call to the control’s DllGetClassObject() entrypoint and try and trace the code from there.
 
I end up in the bowels of ATL (which the ActiveX control is written in) at IDispatchImpl. This class is instantiated in the control and has a couple of version variables: wMajor and wMinor that have default values in the template of 1 and 0. I can now see these values in the debugger that were hidden in the ATL source code and not visible in my control. Back in March I had changed the version from 1.0 to 2.0 in what I thought were all the right places, but had missed the fact that I now had to override the hidden major version value on the class instantiation. So I added a ", 2" to my inheritance of:
 
public IDispatchImpl<IUsbServo, &IID_IUsbServo, &LIBID_USBSERVOCONTROLLib, 2>
 
and now its working.  The strange thing was that this used to work previously without this change. Oh well, another 1/2 day.
 
So now its back to the customer’s problem. Visual Basic now can call the control and everything works.  However, if I compile the application and run the resulting .EXE outside of the IDE, then triggering an Input event from our servo controller crashes the application.  Input events are handled by a secondary thread in our ActiveX control which waits on an overlapped I/O event then fires the input changed event so we don’t have to poll the servo controller. Since VB is not thread-safe, the application crashes. 
 
I had addressed this issue back when I first wrote the ActiveX control, and had added some code to address this problem. A good description of the problem and its resolution appears here: COM/ATL Issues. Back then I had used the Global Interface Table solution to the problem, and it had worked. Now for unknown reason it wasn’t. So I tried adding the first solution, which is to add a hidden window and post a message to it which then fires the actual event. VB supplies the STA thread’s message loop.
 
Tested, and now the compiled VB application no longer crashes when receiving input change events. Updated the relevant files into the setup project, rebuild the setup project, e-mail it to the customer.  Update the copy on the website, and check the project into the server.
 
Today I received a response from the customer that the updated software now works.  I wish that I could’ve known how, why and when it actually broke.
 
 
Advertisements
Post a comment or leave a trackback: Trackback URL.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: