COM Shim Wizard 2.3.1.0


We’ve released a slight update to the COM Shim Wizard. This is available as a free download here, and the covering article is on MSDN here. The differences Between v2.3.0.0 and v2.3.1.0 are summarized below.


Setup


The major difference between v2.3.0.0 and v2.3.1.0 is that v2.3.0.0 was built to install with Visual Studio 2005, whereas v2.3.1.0 installs with either Visual Studio 2005 or Visual Studio 2008, or both. The setup for the COM Shim Wizard now gives you the option to choose which version or versions of Visual Studio to install for. There are 3 additional enhancements to the setup for the COM Shim Wizard v2.3.1.0:


1.       The setup disables installation on a per-user basis. This is because installing per-user (and therefore registering to HKCU) causes problems on Vista with UAC turned on, or when running Visual Studio in an elevated process.


2.       The custom actions are set to NoImpersonate, so that they work correctly on Vista with UAC on.


3.       The setup is formatted so that any error messages are displayed (otherwise they are not visible in Vista, the user just sees Error 2869).


CLR Start Bug


I documented this fix as a community comment added to the MSDN documentation for the COM Shim Wizard v2.3.0.0. Part of what the shim does is to load the CLR: it does this by calling CorBindToRuntimeEx. We test the return from CorBindToRuntimeEx, and if it’s S_FALSE, this indicates that the CLR has already been loaded. In the released version of the shim, if we get S_FALSE, we simply return.


However, there is a scenario where the CLR could already have been loaded but not started. It’s an unusual scenario: it could happen if some other managed code was loaded into the process but never executed. The problem is that the shim code assumes that if the CLR is loaded it is not necessary for us to start it. Of course, if the only other things in the process that load the CLR are all shims created with the COM Shim Wizard, then this assumption would be true – because the first one to load would have loaded the CLR and started it. However, this assumption doesn’t hold if there are other native components that load the CLR but don’t start it.


We fixed this in the code generated by v2.3.1.0 of the wizard by simply removing the test for S_FALSE – this works because we do call Start later on.


Post-build step to copy ManagedAggregator.dll


In v2.3.0.0, a post-build step in the generated shim project copies the ManagedAggregator.dll from the \bin\debug folder unconditionally. We changed this in v2.3.1.0 to copy the dll from the target folder for each project configuration. For example, for a standard debug configuration, it will copy from \bin\debug; but for a release configuration, it will copy from \bin\release.

Comments (33)

  1. XL-Dennis says:

    Thanks Andrew – Highly appreciated.

    Kind regards,

    Dennis

  2. Garry Trinder says:

    Dennis – thanks, you’re very welcome 🙂

  3. XL-Dennis says:

    Andrew,

    According to the documentation the shim dll should have the Registry setting of vsdrpCOM. But shouldn’t it read vsdrpCOMSelfReg instead?

    Kind regards,

    Dennis

  4. Garry Trinder says:

    Dennis – vsdrpCOMSelfReg causes the DLL to be invoked at install time to register, requiring dependencies to be present and laid down in the right order; plus, the code runs at that point. The regular vsdrpCOM setting runs the registration at the time the MSI is built, and puts the registration data into the MSI itself. Then, at install time, the file gets copied and Windows writes the registry entries, making this almost always the better way because you’re not depending on registration code to work at install/uninstall time, just at MSI build time.

    Also, from the perspective of a system admin, vsdrpCOM is better because Windows can always reliably roll back an installation done this way. Conversely, vsdrpCOMSelfReg allows the developer to put any arbitrary code in their reg/unreg functions (not limited to registration/unregistration) – and the admin is at the mercy of the developer to make sure the ‘unreg’ is a complete roll-back of the ‘reg’.

  5. XL-Dennis says:

    Andrew,

    Point taken – Thanks for the detailed information.

    What about installation on Vista? Do we need to install it under HKLM User Settings?

    Kind regards,

    Dennis

  6. XL-Dennis says:

    Please disregard my previously comment. A type error caused some issues.

    Kind regards,

    Dennis

  7. Anonymous says:

    is this working with office 2003 too?

    i have a strange behaviour when using excel 2003 udfs: the com add in gets registered and loaded – i can see it in excel, select it as add-in and i also can debug it. the add-in gets initalized at excel startup (i can debug ctor and onconnection), but shortly afterwards it seems the add-in crashes. when i’m opening the function wizard of excel the add-in tries for a second time, but again, after ctor and onconnection the object dies and there are no udf entries in the wizard.

    ps: the com object works fine via vba (createobject())!

  8. Garry Trinder says:

    interzone – yes, I tested this with Excel 2003 also. It should work the same way as with Excel 2007. Are you sure you have the typelib registered? Without the typelib, Excel will not be able to determine what functions the add-in exposes.

  9. Anonymous says:

    hi andrew!

    you are right! i’ve missed the tlbs. is it neccessary to keep also the tlb of the managed dll file or is only the C++ dll tlb the important one?

    cheers!

  10. Garry Trinder says:

    interzone – that depends on what you’ve put into each typelib. Typically, the tlb for the managed add-in contains all the information about the functions that you’re exposing, while the tlb for the shim typically is very minimal and does not contain this information. So, in the default case, you need to register the tlb for the add-in, not the shim.

    However, you could put the full function information into the IDL for the shim if you want to, and then register the tlb for the shim. Either way works. It tends to be better to use the add-in’s tlb because that always gets built (if you have Register for COM interop checked) every time you make changes to the add-in itself. The shim doesn’t actually implement any of the UDFs in the add-in, so it would be a little strange to built this UDF information into the shim tlb.

  11. Anonymous says:

    currently i’m deploying both tlb files. i’m using the windows installer for deployment and the only thing i’m doing besides setting the shim output to vsdrpCOM is copying these tlb files into the setup directory. it’s working on each tested machne now, but is that enough? because you are talking about registering the tlb – do i need regtlib to do that?

  12. Garry Trinder says:

    interzone – you’re right, the typelib doesn’t need to be registered, so long as it is in the same folder as the shim/add-in. When you build the add-in, and set "Register for COM interop", this builds the typelib and also registers it as an atomic operation – but this is only on the dev machine, of course. On the target user machine, you don’t need to register it.

  13. Anonymous says:

    I have a Add in project , added the shim projects and aggrateion object, added their outputs to the setup project. run the install and load up MS WORD

    looking at the Options, Addin sectios it shows my addin as "Rose Word Addin Shim  Mscoree.dll  Com-ADDin

    I thought that the shim was suppose to get me away from the MSCOREE.dll problem? how do i know if this is loaded and have the benieft of the shim that i just created?

    should there be a COM xxxx.Connect interface as well and should it be wired to my DLL or the MSCOREE.dll.??

  14. Garry Trinder says:

    Karl – yes, the shim does explicitly avoid the mscoree problem. I suspect your setup project is not registering the various DLLs correctly. The only way you would end up with mscoree.dll registered for the add-in is if the add-in DLL itself is registered – which it should not be, if you’re using a shim. (I assume you’re installing the add-in on a different machine from the one where you’re developing and building it.)

    In the covering article on MSDN, there’s a note that says: "If you build a setup project for your add-in, you should add the primary output of both the shim and the ManagedAggregator projects to your setup. You should also change the value of the Register property for all three project outputs. Set this to vsdrpCOM for the shim DLL, and vsdrpDoNotRegister for both the original add-in DLL and the ManagedAggregator DLL."

  15. iceeyes1021 says:

    I want to deploy the add-in to x64 platform. How can this tool support on x64. I have tried some method. But I found that the add-in was always in inactive add-ins and have a runtime error when loaded. But I cannot know what was the error.

    So could you help please?

  16. Garry Trinder says:

    iceeyes – this is a Visual Studio 2005/2008 tool. Visual Studio is 32-bit, and it runs on 32-bit and 64-bit OS’s – so you should have no problem installing and running the COM Shim wizards on 64-bit.

    The COM Shim wizards generate only 32-bit add-in code – because right now Office only supports 32-bit. 32-bit Office will run of course on 64-bit OS’s – so again, you should have no problem installing and running an add-in on 64-bit.

    If you are still experiencing problems, please supply more details of exactly what you’re trying to do.

  17. iceeyes1021 says:

    Thanks andreww, I have already figure out the x64 problem.

    But now I have a problem with OneNote. Because this wizard don’t support OntNote, but we need a shim for OneNote.

    I have tried select another application like PPT, then I modify the registry. But it doesn’t work for OneNote.

    How to do that?

    Thank you.

  18. Garry Trinder says:

    iceeyes1021 – The wizard doesn’t support OneNote because OneNote doesn’t support COM add-ins.

  19. Anonymous says:

    I have created a Word 2007 addin using C# and also created a Shim for this addin. The problem is when I exit Word, it stays in memory(seen in TaskManager). While debugging, the call to m_pCorRuntimeHost->UnloadDomain is not returning. When I comment out this line it quits properly.

    Why the call to UnloadDomain is not returning?

    Thanks,

  20. Garry Trinder says:

    VSP – it’s difficult to figure out what might be happening – this really needs debugging. Please can you send me the simplest possible solution that reproduces this problem? If so, I can take a look at it.

  21. Anonymous says:

    This happens only when a 3rd party add-in is enabled.  When that add-in is disabled, UnloadDomain returns from my add-in during exit. I do not have source for that add-in. But that add-in is not using Shim and in the COMAdd-ins dialog it shows the .manifest file.

    Thanks,

  22. Garry Trinder says:

    VSP – hmm… this is interesting. The primary purpose of the shim is to provide isolation between loaded add-ins at the appdomain level: each add-in gets loaded into its own independent appdomain that its shim creates for it. If the 3rd party add-in you mention is registered with a .manifest, then it is almost certainly a VSTO v2 add-in. VSTO add-ins also get loaded into their own independent appdomain because the VSTO runtime contains a variation of the shim functionality. All of which means that there should be NO interaction between these add-ins, and the 3rd party add-in should not be causing a problem with the appdomain unload code in your add-in.

    So this is a bit of a mystery.

    What is the 3rd party add-in?

    Do these add-ins communicate with each other via the COMAddIns collection?

  23. Anonymous says:

    I created a Word 2007 Add-in based on VSTO 2 and simply built the add-in. Now my Word contains only two add-ins one based on VSTO and another based on this Shim. When I close the Word, it stays in the memory.

    Thanks,

  24. Garry Trinder says:

    VSP – thanks, I am investigating this, and I will get back to you as soon as I can.

  25. Garry Trinder says:

    VSP – thanks again for bringing this problem to our attention.

    The problem is that the ManagedAggregator.CreateAggregatedInstance method is taking in an IComAggregator object (the CConnectProxy object, which is the outer object in the aggregation), but is not releasing it. This is a problem because the object was passed from the native shim code to the managed aggregator code and wrapped as an RCW. If we don’t release the RCW, the CLR will potentially hold on to it (and the underlying COM object) until the corresponding AppDomain is unloaded. However, in the current implementation the AppDomain is unloaded when COM object’s reference counter reaches zero. This essentially constitutes a circular reference problem thus preventing the host from terminating cleanly. The fix is simple: in the finally block, add a call to Marshal.ReleaseComObject on the outer object:

    public void CreateAggregatedInstance(

       string assemblyName, string typeName, IComAggregator outerObject)

    {

       IntPtr pOuter = IntPtr.Zero;

       IntPtr pInner = IntPtr.Zero;

       try

       {

           pOuter = Marshal.GetIUnknownForObject(outerObject);

           object innerObject =

               AppDomain.CurrentDomain.CreateInstanceAndUnwrap(

               assemblyName, typeName);

           pInner = Marshal.CreateAggregatedObject(pOuter, innerObject);

           outerObject.SetInnerPointer(pInner);

       }

       finally

       {

           if (pOuter != IntPtr.Zero)

           {

               Marshal.Release(pOuter);

           }

           if (pInner != IntPtr.Zero)

           {

               Marshal.Release(pInner);

           }

           // Call ReleaseComObject on the outer object (ConnectProxy)

           // to make sure we delete the RCW, and prevent the CLR from

           // holding onto it indefinitely (and keeping the host alive).

           Marshal.ReleaseComObject(outerObject);

       }

    }

    Not sure when we’ll be able to release an update to the shim wizard, but I’ll post the workaround to MSDN also. Thanks again for your feedback.

  26. Anonymous says:

    A customer (VSP) was using the COM Shim and identified a scenario where a bug in the shim code could

  27. Anonymous says:

    With this change it is working fine.

    Thank you very much.

  28. Anonymous says:

    If I alter the interface to my C# automation addin then rebuild the Addin and the Shim, when I open Excel it throws a Debug Assertation Failure, stating that release called on a pointer that has already been released. (in atlcom.h line: 2409)

    Do I have to remove the Shim Project and add a completely new one everytime I extend the addin???

  29. Garry Trinder says:

    RichL – no, you don’t have to replace the shim – the shim contains no code that is specific to your interface, and it doesn’t care what changes you make to the interface. On the other hand, it does specify the name of the class that implements IDTExtensibility2 as well as the public key token for that add-in assembly – so you’d have to update that code if you change any of those items.

    Does the add-in work correctly without the shim? Is the typelib registered correctly?

  30. Anonymous says:

    It was my mistake I had accidently screwed up my appSettings section in my config file at the same time as I changed the addin interface. I tied the symptoms to the wrong root cause, easily done.

    Please feel free to remove the thread. – Thanks for the advice anyway re the no-reference to interface, good to know.

  31. Anonymous says:

    Hi Andrew,

    When I tried generating the (2.3.1.0) Shim for the real time data sample (in latest release) using Visual Studio 2008, I get the following MDA after I closing the test sheet (running in Debug mode):

    LoaderLock was detected

    Message: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.

    The interesting part is that I don’t get this MDA if I create the shim using VS 2005.  

    Need you help in understanding why this happens in VS2008,

    Sumant

  32. Garry Trinder says:

    Sumant – the MDA is innocuous – you can ignore it. What’s happening is the CCLRLoader destructor is running very late – it’s running when the Excel process terminates and the CLR itself is getting unloaded. Unfortunately, the CCLRLoader destructor attempts to call Release on the appdomain object – and it’s too late to do this.

    If you really want to avoid the MDA, you can change the RTD shim code to unload the appdomain and release the appdomain pointer during the FinalRelease of the RTDProxy object. You can see how to do this if you look at the code generated for an add-in. We changed the add-in shim code a couple of times since the last time the RTD shim code was last updated.

  33. Anonymous says:

    Thanks a ton Andrew! I changed the shim code as you suggested, and it now works like a charm! =)

    Sumant