2

I'm trying to use my C++ COM API (loaded inside a C++ application) from C# (as an addin) without any registration. I follow steps from this tutorials

So what I did is:

  1. to create an activation context from my C++ application at startup. I'm passing my COM API manifest file to CreateActCtx and I enable it by ActivateActCtx
  2. My C# addin is loaded by my C++ application (inside a custom AppDomain)
  3. Inside my C# addin I'm trying 3 differents scenario.
  • The first one is to create COM object inside the application main thread --> This is working

  • The second is to create COM object and casting COM object inside a separated STA thread --> This is working

  • The last case is to create COM object and casting COM object inside a separated MTA thread. And here I have a problem. I'm able to create an object but I'm not able to cast it. I received and exception like:

    Unable to cast COM object of type 'System.__ComObject' to interface type 'API.MyComObjectX'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{DE35BA2A-0566-40D3-AF1D-AD79D1133B09}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))
    

So bellow, the code is the one I used for tested

/*
Manifest file loaded by my C++ application

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

  <assemblyIdentity type="win32" name="ApiX.X" version="1.0.0.0"/>
  
  <file name="ApiX.dll">
    <comClass clsid="{76125B7A-E5FC-4362-93F8-3B912EB85D51}" threadingModel="Apartment"/> <!-- This define the MyComObjectX object -->
    <typelib tlbid="{4714D4C0-5AA9-47F7-82FF-458B341E7052}" version="1.0" helpdir=""/>
  </file>

  <comInterfaceExternalProxyStub name="IMyComObjectX" iid="{DE35BA2A-0566-40D3-AF1D-AD79D1133B09}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}" tlbid="{4714D4C0-5AA9-47F7-82FF-458B341E7052}" baseInterface="{00000000-0000-0000-C000-000000000046}" /> <!-- interface which is based on IDispatch -->

</assembly>

*/

public void testComFreeRegistration(object myComObjectX)
{
    try
    {
        MyComObjectX myComObjectX2 = new MyComObjectX();

        MyComObjectX myComObjectX3 = (MyComObjectX)myComObjectX; // Throw the exception

        MessageBox.Show("working");
    }
    catch (Exception e)
    {
        MessageBox.Show("fail");
        e.ToString();
    }
}


public void EntryPoint(object myComObjectX) // <-- Sent from my C++ application
{

    // The STA test which are working
    // var sta = new Thread(() => testComFreeRegistration(myComObjectX));
    // sta.SetApartmentState(ApartmentState.STA);
    // sta.Start();
    // sta.Join();

    var mta = new Thread(() => testComFreeRegistration(myComObjectX));
    mta.SetApartmentState(ApartmentState.MTA);
    mta.Start();
    mta.Join();
}

Another strange behavior is if I uncomment my STA test. Both test will work !! So I don't know what happen. It seems I have something not proerly initialized.

Do you have any idea of what happen or what I should check ? Why the STA <--> MTA mechanism seems to not load the comInterfaceExternalProxyStub configuration ?

Thanks in advance for your help ;)

David
  • 1,177
  • 3
  • 11
  • 26
  • For the third item where you create the COM object. Did you create it on the mainthread (STA) and then tried to cast it on the background thread (which will be MTA initialized)? I've found it is good practice to ALWAYS create com objects on an MTA initialized thread and communicate with them from an MTA thread. MTA created objects can be accessed from any other MTA object. STA objects can only be accessed (including typecasting/Queryinterface) from the exact same thread they're created on. I hope this answer helps a bit. – Pepijn Kramer Aug 24 '21 at 10:31
  • Do you have a reproducible sample ? – Simon Mourier Aug 24 '21 at 10:42
  • @Pepijn Kramer : No the object is created by the main thread of the C++ application which is an STA thread. – David Aug 24 '21 at 12:32
  • @Simon Mourier : Not really, there is too many code involve here. I just extracted relevant part of the problem. Someone know how I can get infos for debugging this ? – David Aug 24 '21 at 12:34
  • If you can't build a reproducing sample, it will be difficult to help because it can come from your "too many code" we don't see. – Simon Mourier Aug 24 '21 at 12:47
  • I think you should move the creation of the COM object into a lambda and let a Task (from System.Threading.Tasks;) execute the lambda. This will run the creation of the COM object on a background task, if I remember correctly then those threads have been initialized as MTA. Which you can check with System.Thread.CurrentThread.GetApparmentState() == AppartmentState.MTA (or something like it). – Pepijn Kramer Aug 24 '21 at 12:57
  • Yes I know, that why I 'm asking if you know how I can grab more information to be able to investigate myself. If I could be able to give you a sample, of course, I will give it @Pepijn Kramer : The object can be create in my MTA thread. The problem was on cast object which are initiliazed by my C++ application. I will try to change this c++ part to create it as MTA – David Aug 24 '21 at 12:58
  • Have you tried changing `threadingModel` attribute in your manifest? You've marked it as `Apartment`. If it really is multi-threaded, you could change to `Free` or `Both`. – Joseph Willcoxson Aug 24 '21 at 13:45
  • @Joseph Willcoxson Yes I already tried Both and Free but nothing change – David Aug 25 '21 at 12:48
  • Wrong cast, the code doesn't actually test the proxy. You've setup the proxy to marshal IMyComObjectX, but are not using it. Proper code should be `IMyComObjectX obj = new MyComObjectX();` – Hans Passant Aug 25 '21 at 20:57
  • @HansPassant I test what you said. And it's working. – David Aug 27 '21 at 07:43

0 Answers0