How to make make a .NET COM object apartment-threaded? How to make make a .NET COM object apartment-threaded? multithreading multithreading

How to make make a .NET COM object apartment-threaded?


You can inherit from StandardOleMarshalObject or ServicedComponent for that effect:

Managed objects that are exposed to COM behave as if they had aggregated the free-threaded marshaler. In other words, they can be called from any COM apartment in a free-threaded manner. The only managed objects that do not exhibit this free-threaded behavior are those objects that derive fromĀ ServicedComponent or StandardOleMarshalObject.


Paulo Madeira's excellent answer provides a great solution for when the managed class being exposed to COM can be derived from StandardOleMarshalObject.

It got me thinking though, how to deal with the cases when there is already a base class, like say System.Windows.Forms.Control, which doesn't have StandardOleMarshalObject in its inheritance chain?

It turns out, it's possible to aggregate the Standard COM Marshaler. Similar to the Free Threaded Marshaler's CoCreateFreeThreadedMarshaler, there is an API for that: CoGetStdMarshalEx. Here's how it can be done:

[ComVisible(true)][ClassInterface(ClassInterfaceType.None)][ComDefaultInterface(typeof(IComObject))]public class ComObject : IComObject, ICustomQueryInterface{    IntPtr _unkMarshal;    public ComObject()    {        NativeMethods.CoGetStdMarshalEx(this, NativeMethods.SMEXF_SERVER, out _unkMarshal);    }    ~ComObject()    {        if (_unkMarshal != IntPtr.Zero)        {            Marshal.Release(_unkMarshal);            _unkMarshal = IntPtr.Zero;        }    }    // IComObject methods    public void Test()    {        Console.WriteLine(new { Environment.CurrentManagedThreadId });    }    // ICustomQueryInterface    public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)    {        ppv = IntPtr.Zero;        if (iid == NativeMethods.IID_IMarshal)        {            if (Marshal.QueryInterface(_unkMarshal, ref NativeMethods.IID_IMarshal, out ppv) != 0)                return CustomQueryInterfaceResult.Failed;            return CustomQueryInterfaceResult.Handled;        }        return CustomQueryInterfaceResult.NotHandled;    }    static class NativeMethods    {        public static Guid IID_IMarshal = new Guid("00000003-0000-0000-C000-000000000046");        public const UInt32 SMEXF_SERVER = 1;        [DllImport("ole32.dll", PreserveSig = false)]        public static extern void CoGetStdMarshalEx(            [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,            UInt32 smexflags,            out IntPtr ppUnkInner);    }}