Monday, February 24, 2014

Constant integer values and multi-language COM interop

I recently moved some code from a legacy C++ application into a COM library for more general use. The original code was duplicated a couple times in different C++ applications. Then a need arose to use this code in C#. To clean up both the duplication and make it available to .Net applications, we decided to untangle it from the original applications and move it into its own COM library.

One of the issues I ran into that took a bit to figure out involved constant values.

The old C code had several things defined as integers with associated constant definitions to handle bit-mapped values. Some might argue these should be converted to enumerations but we wanted to minimize changes to existing code structure and so decided to keep them as integer constants. The issue with this was how to move them to a common location for all COM clients to access.

It took a bit of research to find the answer as I didn't find a complete answer in one place. Hopefully this article will help fill that gap.

IDL files can have #define statements. This works for C code. The IDL compiler maps these as corresponding #defines in the corresponding intermediate .h files. The problem is when .Net creates an Interop assembly, they are ignored. This means they don't exist for use in .Net languages.

Next I used const definitions in the library. Again, this worked for C code but the Interop assembly in the .Net world did not have them.

I moved the const definitions into an interface. Still, C was perfectly happy but the Interop ignored them.

I search around some more. I ran into some forum discussions that indicated it was impossible.

Finally I found a reference to something that pointed me in the correct direction. IDL files can have the concept of modules, something I hadn't run into before. I put the const declaration in a module section in the IDL file. In the intermediate .h file for C++, these are simply constants and callers are still happy. But in this case, when the .Net Interop file is created they are not ignored. The Interop generator maps them to an abstract class named with the name of the module and the constants become static consts inside the class. This makes them available for any .Net user to access.

So, the IDL file ended up looking like this:
library SomeComLibrary
{
    module Constants
    {
        const DWORD UniqueValue = 0xFFFFFFFF;
    }
}
C's intermediate .h file looks like this:
#ifndef __Constants_MODULE_DEFINED__
#define __Constants_MODULE_DEFINED__
/* module Constants */
const DWORD UniqueValue = 0xffffffff;
#endif /* __Constants_MODULE_DEFINED__ */
And the .Net Interop file looks like this:
namespace SomeComLibrary
{
    public abstract class Constants
    {
        public const uint UniqueValue = -1;
    }
}
The solution ended up being quite easy, but finding it was a bit of a challenge.

Things I searched for trying to find a solution to this problem included:
  • idl const not in .net
  • idl const not in interop
  • idl const not in C# interop
  • midl keywords
  • idl const .net