Search code examples
c#.netcominteropcom-interop

InvalidCastException when using IClassFactory in C#


I am trying to instantiate an ActiveX control without registration in a C# project. I'm doing the following:

Guid guid = new Guid("(guid of my control here)");

var classFactory = ComHelper.GetClassFactoryFromDll("mycontrol.ocx", guid);
if (classFactory != null)
{
    object obj;
    classFactory.CreateInstance(null, ref guid, out obj); // getting the exception here
    if (obj != null) ocx = (obj as IMyControl);
}

The ComHelper code is mostly based on this article and appears to work fine with the IFilter example.

This is my interop code for IClassFactory:

    [ComVisible(false)]
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000001-0000-0000-C000-000000000046")]
    public interface IClassFactory
    {
        void CreateInstance([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, ref Guid refiid,
                            [MarshalAs(UnmanagedType.Interface)] out object ppunk);

        void LockServer(bool fLock);
    }

The IMyControl interface is the interface the aximp.exe /source generated. I want to avoid using satellite assemblies because I can't make them use reg-free COM and our deployment requires that. What throws the exception? I'm using the same guid I used for the traditional, satellite-assembly way. Is the guid not marshalled correctly? How can I make it work?


For completeness' sake: I managed to accomplish this. My method was to look at the AxHost source code (used ILSpy) and see what it does when creating an instance:

private object GetOcxCreate()
{
    if (this.instance == null)
    {
        this.CreateInstance();
        this.RealizeStyles();
        this.AttachInterfaces();
        this.oleSite.OnOcxCreate();
    }
    return this.instance;
}

Then I create an instance of the generated AxMyControl class and replaced the instance private field via reflection. That field originally gets a value by calling CoCreateInstance from CreateInstance. Then I called the other methods to fix up other connections (all of them are private, so again, reflection). If the class is not registered, an exception is thrown in the constructor (CoCreateInstance fails), so I do this machination in a finally block.

So far, it seems to work fine.


Solution

  • You are getting the Guid wrong here. The CreateInstance call requires you to pass an interface IID, not a coclass CLSID. Use typeof(IMyControl).GUID instead.

    Next big possible failure mode is the apartment state of the thread, it better be STA or COM is going to hunting for a proxy that probably doesn't exist. You are bypassing the safety guards that the CLR has in place, marshaling the interface pointer when required. Never use the object from the wrong thread. Not so sure this is worth the risk.