This week I've been working on a case from a customer who was migrating his web application to Windows 2003, and while on the old servers (Windows 2000) and on his development machine everything was working fine (as usual ), after deployment on the production server he got a nasty "ActiveX component can't create object" error message, and was not able to fix it.
The troubleshooting started from the usual and maybe most obvious possible causes (component registration, permissions etc...), but we soon realized the problem was not that obvious as we expected. The application was made by a custom ActiveX VB6 object which internally referenced other custom dlls and msrdo20.dll; this was then embedded into a .aspx page to show some use interface in IE to the end user. The customer sent me his application to try to reproduce the problem on my machine, and at the first run I got exactly the same error message he reported! Ah Ah! I got you! On my machine I got rid of it registering all the involved components, something we already tried on the phone, but worth another check. I came back to the customer and set up an EasyAssist session to carefully check his server's configuration to spot (and fix) the problem... but things went a bit differently. Everything was looking fine, so we decided to build a smaller sample to check if at least we were able to instantiate other ActiveX: we tried to work with msrdo20.dll directly, and that also failed with the same error.
We then decided to capture a crash dump with adplus to have a full dump on the exception (we used the command "adplus -crash -iis -FullOnFirst" to obtains a full dump on first chance exceptions), and to make a long story short, after analyzing the dump (and digging into the VB6 runtime with the help of my colleague Carlo Colombo, Escalation Engineer in the COM+ team) we came to the conclusion that the runtime was trying to read the CLSID for msrdo20.dll in the wrong registry hive, HKEY_USER instead that HKEY_LOCAL_MACHINE as expected. By the way, that also could explain the reason why running the same code and loading the same msrdo20.dll in a WinForm application worked fine: the desktop app was able to find the CLSID for the component and to load it, while the web app does not have access to some registry hives... the customer already sent us a copy of its system registry to check the various CLSID involved, and a quick search confirmed our assumption: we found the CLSID 9A8831F0-A263-11D1-8DCF-00A0C90FFFC2 (msrdo20.dll) under HKEY_USER and not under HKEY_LOCAL_MACHINE. I forgot to mention that we were in a absolute hurry, because the application was supposed to to go live exactly that evening...
Anyway, at that point we needed to confirm our theory: I called the customer to explain our findings and ask him to manually register (with regsvr32) msrdo20.dll, but a quick search in the registry showed that nothing was changed, we were in the same situation. Then I asked him to login as the Domain Administrator (which he was logger as the Local Administrator on the server) and check again: this did the trick! The CLSID was finally created under the right registry hive, and another quick test from a client confirmed the application was then able to instantiate the ActiveX... Eureka!
In trying to understand the reason for the HKEY_USERS registration, one theory might be the following: msrdo20.dll is installed through an MSI package. The MSI technology has the possibility to do a per-user or per-machine installation, or a "per machine, but fallback to per-user if per-machine fails" (ALLUSERS property set to 2): see http://msdn2.microsoft.com/en-us/library/Aa371865.aspx. If the user running the MSI package is not an admin, he won't have write permissions to HKCR\CLSID so per-machine registration will fail and we'll fail back to per-user installation. In per-user installation the registration is made in the HKEY_USERS registry hive: see http://msdn2.microsoft.com/en-us/library/aa370813.aspx.
Thanks a lot to Carlo Colombo for his assistance on this one!