Search code examples
c#com

Is it possible to expose a reference type through COM?


I have a dll made by C# and need to call it from an existing native c++ exe. I have studied the basis of COM and done a simple example.

However, if a C# function has a reference type(especially user-defined class' reference) parameter, can I write an interface for it, and work fine?

I searched in Google but get few information.

Updated:
I tried to write one and saw its generated tlh file. The C# code is

void Test2(ref float num);

And the generated tlh is

HRESULT Test2 ( float * num );

The reference type is translated to pointer type. Seems COM is not supporting the reference type.


Solution

  • As Hans mentioned, this question isn't about reference types. It's about passing parameters by reference.

    When you pass parameters by reference in any language, you are explicitly or implicitly using a pointer. In C#, ref is stating "this parameter will take a pointer to data which has already been initialized." out is stating "this parameter will take a pointer to data which may or may not have already been initialized." By using ref or out, you are shielding yourself from the pointer semantics. That is, you don't have to specify float* in the method signature. But rest assured that you're still using pointers.

    COM fully supports passing parameters by reference. Here is an example to illustrate.

    C#:

    [Guid("962232c8-90b2-4b61-8ef3-83298901c63e")]
    [ComVisible(true)]
    public interface ICSCOMCLASS
    {
        [DispId(1)]
        void TestInParameter(double num);
    
        [DispId(2)]
        void TestRefParameter(ref double num);
    }
    

    After compiling this, you can run tlbexp.exe on the resulting assembly to export the COM type library. You can then view the IDL by opening the .tlb file in oleview.exe.

    [
        odl,
        uuid(962232C8-90B2-4B61-8EF3-83298901C63E),
        version(1.0),
        dual,
        oleautomation,
        custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "CSCOMLIB.ICSCOMCLASS")    
    ]
    interface ICSCOMCLASS : IDispatch
    {
        [id(0x00000001)]
        HRESULT TestInParameter([in] double num);
    
        [id(0x00000002)]
        HRESULT TestRefParameter([in, out] double* num);
    };
    

    Note the modifiers on the parameters. As regards your question about ref parameters, note that the parameter of the TestRefParameter() method is [in, out] double* num. This is the equivalent of C#'s ref double num.

    If you use this assembly or type library from C/C++ (for example, by using a #import statement), you'll see something like this:

    struct ICSCOMCLASS : IDispatch
    {
        virtual HRESULT __stdcall TestInParameter(double num) = 0;
        virtual HRESULT __stdcall TestRefParameter(double* num) = 0;
    };
    

    COM is designed to work at a C level and up. The majority of the tools that are used for C++ COM work are implemented to use C (not C++) as their baseline for support. Although C++ supports pass-by-reference with & (e.g. HRESULT TestRefParameter(double& num), C does not. Moreover, C++'s & is not as specific as C#'s ref and out. (Most modern C++ compilers assume that you want to initialize the data before passing by reference, and they will produce an error or warning if you do not.) Therefore, any parameters defined as ref or out in the C# definition will be simply defined in C++ as pointers.