Search code examples
c#initializationdefault-constructorregistration-free-com

Registration Free COM Interop Initialization - parameterless constructor


I need to write a Registration Free COM Interop library see MSDN link

One of the requirements is, and I quote

"For a .NET-based class to be compatible with registry-free activation from COM, the class must have a default constructor and must be public."

As I read it, I need to create the following... (this technically works, and I have no problem instantiating this via COM)

[ComVisible(true)]
[Guid("...")]
public interface ITest
{
    X();
    Y();
}

[ComVisible(true)]
[Guid("...")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class Test : ITest
{
    private string x;

    public Test() // default constructor
    {
    }

    X()
    {
        // do something with "x" BUT "x" MUST be initialized before this method is called
    }

    Y()
    {
        // do something else with "x" BUT "x" MUST be initialized before this method is called
    }
}

I'm looking for the best way to ensure that this class is initialized before any methods are called (via the interface), so, other than a constructor, whats my next best option to initialize "x"? As far as I can tell overloading the constructor with a parameter isn't an option - Usually I would initialize this class with a constructor that took parameters, however with Registration Free COM, I don't have that option (or do I??).

I think my alternative is an "Initialize" function, such as...

public interface ITest
{
    Initialize(string x);
    X();
    Y();
}

public class Test : ITest
{
    private string x;
    private bool Initialized;

    public Test() // default constructor
    {
        Initialized = false;
    }

    Initialize(string x)
    {
        this.x = x;
        Initialized = true;
    }

    X()
    {
        if (Initialized)
        {
            // do something with x
        }
        else
        {
            throw...
        }
    }

    Y()
    {
        if (Initialized)
        {
            // do something else with x
        }
        else
        {
            throw...
        }
    }
}

I feel that this is messy, but doable... but what better option am I missing?


Solution

  • You are not missing that much. COM uses a universal object factory and, because it is universal, it can't take any arguments. Which is why you must create a C# class with a default constructor, there isn't any way to pass constructor arguments.

    The solution is a pretty simple one, all that you need is your own object factory and expose it to the client code. The factory function can take any arguments you need to create your C# object. And you make your Test class inaccessible to the client code, since you want to insist it uses the factory, simply done by omitting the [ComVisible] attribute. Some sample declarations that fleshes this out:

    [ComVisible(true)]
    public interface ITestFactory {
        ITest Create(string arg);
    }
    
    [ComVisible(true)]
    public class TestFactory {
        public ITest Create(string arg) {
            return new Test(arg);
        }
    }
    
    [ComVisible(true)]
    public interface ITest {
         // etc...
    }
    
    internal class Test {
        private string needed;
        public Test(string arg) {
            needed = arg;
        }
        // ITest methods ...
    }
    

    Good examples of these kind of object factories can be found in Office interop. Excel does not allow you to create a spreadsheet directly for example, you have to use Application.Workbooks.Add().