Thursday, September 16

Getting Bloomberg COM Interop to Work

To allow the use of existing COM objects, Microsoft developed the Interop mechanism for the .NET Framework. In short, it allows one to develop a type-safe .NET bridge, or wrapper classes around the target COM class, which is thus seen and used just as another .NET class - object instantiation marshalling of arguments and remote procedure calls are handled by the runtime. But that's not all. Due to the self describing nature of COM binaries, Microsoft also developed a tool which allows you to automatically generate the wrapping Interop DLL usually with a single command. Referencing a COM class via VS.NET creates this DLL transparently (you'll see a newly created class with an Interop. suffix in your list of references).

All good so far. Our friends at Bloomberg have an ActiveX control - used to subscribe to real time data feeds and the like. However although they say it is in development there is no .NET equivalent. Faced with the choice of either coding in C++/VB or using Interop, I chose the latter. Except that it didn't work. The COM method signature in question was:

HRESULT Subscribe(
[in] VARIANT Security,
[in] long cookie,
[in] VARIANT Fields,
[in, optional] VARIANT OverrideFields,
[in, optional] VARIANT Overrides,
[in, out, optional] VARIANT* Results,
[in, optional] VARIANT Monitor);

The trouble is with the out parameter. More specifically with the optional out parameter. In VB one is allowed to "miss out" arguments in a method call, so

obj.Subscribe(a, b, c, , , , )

is a valid method call. The good people at Microsoft knew that this would cause a problem for those coding in .NET (since we have no such luxury of skipping arguments), and so provided the static System.Reflection.Missing.Value to use as a placeholder for when you don't want to pass an argument. This is especially important in the Bloomberg control's case: the Result parameter MUST be left out if you would like to receive date after the call i.e. via an event.

Following so far? Good. The trouble, it seems, is that the fantastic tool that automatically generates the Interop stubs has a bug. For the above method call it provides the following:

Subscribe(object, int, object, object, object, out object, object)

Notice the error? The [in, out, optional] Results parameter has become a simple out one. What this means is that the .NET runtime assumes, even with the variable set as Missing.Value, that you're passing something that (understandbly) has no need to be forwarded since it's just an out - the Missing or null value doesn't get to the underlying COM object but a reference to an object does instead. It returns values in Results instead of firing events, and my program doesn't work.

So, how do we fix this? We need to somehow force the .NET runtime to forward the Missing.Value. Well let's take a look at the IDL which we get if we disassemble the Interop stub we were given:

instance void Subscribe(
[in] object marshal( struct) Security,
[in] int32 cookie,
[in] object marshal( struct) Fields,
[in][opt] object marshal( struct) OverrideFields,
[in][opt] object marshal( struct) Overrides,
[out][opt] object& marshal( struct) Results,
[in][opt] object marshal( struct) Monitor
) runtime managed internalcall

Note the Results parameter. Changing this to:

[in][out][opt] object& marshal(struct) Results,

and recompiling gives us the new signature:

Subscribe(object, int, object, object, object, ref object, object)

A ref parameter is one that may have data that needs to be passed on to the method as well as being used to pass data back. Calling this method with Missing.Value forces the runtime to acknowledge the value of the Result parameter. It sees that it has Missing.Value and replaces it with a null reference as required. The COM object sees this, knows it can't return anything so instead starts firing events which we can capture normally.

Hooray - We now have access to real time events from Bloomberg in a .NET application.

No comments:

Post a Comment