Passing a COM object into .NET via Interop and then back out to COM is problematic
I'm stuck. I've been trying to do something with .NET interop that should "just work", but it doesn't, and all the obvious things I've tried haven't worked. If you have any insights to offer, I'd be very grateful.
I came across this issue while working with a Tridion events system, but it's not about Tridion (although definitely relevant to Tridion technicians). It's about interop between .NET and COM, and in order to allow everyone to follow along even if they haven't got a Tridion system handy, I've knocked up some source code so that all you need is a copy of VB6 and Visual Studio 2005 (preferably one which will allow you to attach a debugger to a process - so not the express edition). You can download the source here. (I haven't included the built versions, so you will need to go round twice to build with binary compatibility.)
The Tridion API is simulated by the code that you'll find in the APIwithHooks folder. Tridion exposes its functionality via various objects, modelling various web content management entities such as Page, Component, Folder etc. Extensibility is provided by allowing you to implement a COM class with a well-known Progid. As long as your class implements a specific interface (defined as part of the API) then as the objects undergo various events, your class will be instantiated, and the appropriate method will be called, with the object itself being passed as an argument.
APIwithHooks contains sources for a dll which provides an implementation of the Thing object, and the definition of the Hooks interface. After building the dll, you can build the GUI project which allows you to exercise this API. Once everything is built, you will also need to create a primary interop assembly using the script provided. Using the GUI, you can choose one of the two buttons, which will instantiate a Thing, and Wallop it.
Once you've walloped a Thing, it fires the event by instantiating the handler and calling OnWallop. The OnWallop method gets passed a reference to the Thing, so that it can call its methods etc. If that's all you want to do, then everything's fine. The problem comes when you implement your handler in .NET, but you then want to dispatch the calls to your legacy implementation written in VB6. (Of course, to do this, you need to recompile your legacy code with a different ProgId.)
The other implementation is in C#, and it's supposed to do exactly the same thing, but it doesn't. Why this should be has got me completely stumped. I'm hesitant to say that .NET is broken, but I'm definitely considering it as a possibility. Anyway - to get this working, assuming you already have a compiled version of your legacy client, you'll also need to create an interop assembly that explicitly references the pia where the interface is defined. There's a batch file provided. (If you've got anything other than a vanilla install for VS, you'll have to fix a couple of things up.)
The symptoms are rather strange. If you click either button once, it works. Then the second time you click either button (it doesn't matter which one you clicked first) it fails. How it fails depends on your configuration. In my "real life" example, my middle layer is a ServicedComponent, and runs in a COM+ application under Windows 2003 server. In this scenario, I get error 424 "Object required". If you just compile things up from the sample, (at least if you're running on XP like I am now) you're likely to see an error saying "Run time error '-2146233088 (80131500)' : Creating an instance of the COM component with CLSID {.... the Legacy Client} from the IClassFactory failed due to the following error 800a0046"
The first of these error numbers is COR_E_EXCEPTION, so probably doesn't tell us much. The error at the end (800a0046) is a bit mysterious. Googling for it, the commonest thing you'll see is a Permission denied error from VBScript, which I'm not sure is relevant.
The thing that remains constant in each scenario is that the first attempt succeeds, and the second fails. This makes me think a permissions failure is quite unlikely. I thought it might be a reference counting problem, but I grabbed hold of an extra reference before making the failing call, and that didn't help.
So is .NET broken, or do I need to make some special invocations of interop-foo, or do I need to re-design the whole thing? Any suggestions would be very welcome, even if they are just hints for a direction to look in.

