Mostly as notes to myself, I’m posting my general practices for COM interop.

General notes:

1) Create VS Solution with (typically) two or more projects: one for the actual .NET stuff, another to act specifically as the COM interface. Core logic, etc stays in the non-COM project(s) in order to promote decoupling and separation of concerns.

1-a) COM wrapper project must be targeted to a specific architecture (x86 or x64), dual/any is not supported.

2) COM project will expose Events, Enums and Methods. Structs are not yet handled. Enums, on the COM consumer side, will be prefixed with the .NET enum name (e.g., public enum This { That = 0, Other = 1 } will come through COM with members This_That = 0, This_Other = 1. To override this default behavior requires IDL exporting and recompiling of the TLB, which are not yet worked out. Exceptions throw back from the .NET COM project to the COM consumer will be the generic .NET Exception type, formatted with an error number in brackets before the message. This allows the COM consumer to have custom error numbers, but will have to be parsed out of the message. In the case of Access/VBA, the error number that can be caught is -2147024809 (Automation Error).

3) On the .NET COM project, disable Register for COM Interop either via project settings or via AssemblyInfo.cs. We will manually add [ComVisible(true)] only as required. Likewise, all GUIDs are to be manually generated.

4) COM project/visible class requires interfaces that will expose the members through COM. Exposed events must have their own interface. [DispId(number)] must be used for each exposed member in the class.

5) The .NET COM Project’s AssemblyName must be a single word, with no dots. Example: ProjectComponentsComponentName as opposed to the usual .NET practice of Project.Components.ComponentName. COM only supports two part naming, where the first part is the Assembly Name and the second part is the exposed Class name.

6) All COM interface/classes will expose a string Version() property that returns the version of the COM wrapper. The parameter is the Version enum, either Assembly or File (where Assembly is the “interface” version – changes to the public COM interface, and File is the “underlying” version, which signifies no changes to the public interfaces but instead underlying changes).

7) Example .NET COM Class/Interfaces:

Notes on various .NET entity requirements

IExampleInterface

IExampleInterfaceEvents

Exposed Class

Enums

Enums must be declare outside the class at namespace level.

All enum values should be assigned explicitly. Enum members will take Name_Member format for COM consumer (e.g., Version_Assembly)

Registering and Referencing

– Only the COM wrapper library need be registered, but dependent .NET libraries must be in the same folder.
– Register the outputted library using RegAsm with the /codebase and /tlb switches.
– Before registering, attempt to unregister previous versions via regasm with /u switch
– Registration is per-machine. Thus, libraries should be in centralized location if they intend to be consumed by multiple clients (e.g, not a COM client’s project folder)
– regasm.exe is part of the .NET Framework install and should be present on any machine that has the .NET framework required to run the interop library (if you need to ensure, bootstrap it with your installer)

Example bat file (must run as admin!!!):

.NET Assembly/File version numbers need not be maintained for this to work, but are highly recommended to maintain.

COM project GUIDs need not be updated to properly re-register new versions of the interop library.

From your COM client, set a reference to the COMBase.tlb file that was outputted as the regasm call.