I'm struggling to work out how to access a COM interface provided by a C++ application and use it from a C# .NET application.
I try to access my COM object (which is provided by a running process) from my C# app like this:
object obj = Marshal.GetActiveObject("MyLibrary.Application");
MyLibrary.IMainApp app = (MyLibrary.IMainApp)obj;
The value of obj is non-null, but when I try to cast it I get System.InvalidCastException.
I tried implementing a custom wrapper and also linked against a DLL produced by TlbImp.exe.  Both produced the same exception.
There seems to be a lot of documentation regarding interop, but it's not making sense to me -- mostly because I'm not COM-savvy (I am stuck maintaining code that someone else developed), and am entering the scary world of C#, .NET and WPF by diving headlong into it.
Here's what I have on the C++ end....
IDL File
Actually, the extension is .odl.  I don't know if that's a different thing.  Note that I've replaced the GUIDs with placeholders like GUID-1, GUID-2 etc...
import "oaidl.idl";
[ uuid(GUID-1), helpstring("My Type Library"), version(1.1) ]
library MyLibrary
{
    importlib("stdole32.tlb");
    //  Primary dispatch interface for CMainApp
    [ uuid(GUID-2) ]
    dispinterface IMainApp
    {
    properties:
        [id(1)] BOOL Mode;
    methods:
        [id(2)] void Quit();
        [id(3)] void LogEventMessage(BSTR szMessageType, BSTR szMessage);
        [id(4)] BOOL GetNoteDetails(BSTR szQualifiedName, BSTR* lpbstrOperator, DATE* lpdtDate);
    };
    //  Class information for CMainApp
    [ uuid(GUID-3) ]
    coclass Application
    {
        [default] dispinterface IMainApp;
    };
};
Example Usage
When another C++ application wants to call a function (eg Quit), they do this:
CLSID       clsid;
HRESULT     hr;
LPUNKNOWN   lpUnk;
LPDISPATCH  lpDisp;
BOOL        bReturn = FALSE;
// Get Class ID
if (CLSIDFromProgID( OLESTR("MyLibrary.Application"), &clsid ) == S_OK )
{
    // Get Active Object from ROT
    if ( ( hr = GetActiveObject( clsid, NULL, &lpUnk ) ) == S_OK )
    {
        hr = lpUnk->QueryInterface( IID_IDispatch, (LPVOID*)&lpDisp );
        lpUnk->Release();
        if ( hr == S_OK )
        {
            CMainApp oMainApp;
            oMainApp.AttachDispatch( lpDisp );
            TRY
            {
                oMainApp.Quit();
                bReturn = TRUE;
            }
            CATCH( CException, e )
            {
                bReturn = FALSE;
            }
            END_CATCH
        }
    }
}
The class CMainApp seems to have been auto-generated by the IDL compiler.  That file is compiled into the project that wants to use it, and basically does a whole lot of InvokeHelper calls.  I'm happy enough to accept this is just the way it works, and I hope that C# is less convoluted.
Attempt to wrap in C#
I followed an MSDN guide on COM interop in C#.  It suggested I could use TlbImp.exe but that threw up enough warnings to put me off.  Besides, I don't want to distribute another DLL.  So I took the other approach of writing a wrapper.
This is what I wrote:
using System.Runtime.InteropServices;
namespace MyLibrary
{
    // Declare MainApp COM coclass
    [ComImport, Guid("GUID-3")]
    class MainApp
    {
    }
    [Guid("GUID-2"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
    interface IMainApp
    {
        public void Quit();
        public void LogEventMessage(
            [In, MarshalAs(UnmanagedType.BStr)] string szMessageType,
            [In, MarshalAs(UnmanagedType.BStr)] string szMessage );
        public bool GetNoteDetails(
            [In, MarshalAs(UnmanagedType.BStr)] string szQualifiedName,
            [Out, MarshalAs(UnmanagedType.BStr)] out string lpbstrOperator );
            out DateTime lpdtDate );
    }
}
Can someone gimme a hand to understand this? Perhaps point out a totally obvious stupid mistake that I've made? =)