Exported Member Conversion 

This topic describes how the export process converts the following members:

  • Methods

  • Properties

  • Events

Methods

COM clients expect to call methods, passing familiar COM data types such as parameters, and receiving HRESULTs in return. In the .NET realm, however, your classes have no such restriction on return types (and, in fact, don't use HRESULTs).

In order to satisfy both models, every method of a managed type has a .NET signature and an implied COM signature. The two signatures are typically very different. .NET clients interact with the server using the .NET signature, while (possibly at the same time) COM clients interact with the server using the COM signature. The server implements the method with the .NET signature and the runtime marshaling service is responsible for providing a stub with the COM signature that delegates the call to the managed method.

HRESULT Translation

A managed signature is converted to an unmanaged signature by changing the managed return value to an [out, retval] parameter and changing the type of the unmanaged return value to HRESULT. For example, the DoSomething method might have the following signatures:

Managed signature

short DoSomething(short i);

Unmanaged signature

HRESULT DoSomething([in] short i, [out, retval] short *rv);

Notice that the COM signature returns an HRESULT and has an additional out parameter for the return value. The return value from the managed implementation always returns as an [out, retval] parameter added to the end of the unmanaged signature, whereas the unmanaged signature always returns an HRESULT. If the managed method has a void return, the runtime omits the [out, retval] parameter. For example:

Managed signature

void DoSomething(short i);

Unmanaged signature

HRESULT DoSomething([in] short i);

Under some circumstances, it is preferable to leave the managed signature unchanged. You can use the PreserveSigAttribute to do this. For example:

Managed signature

[PreserveSig] short DoSomething(short i);

Unmanaged signature

short DoSomething ([in] short i);

Having two distinct method signatures makes it easy to use the class from both COM and .NET clients seamlessly. Moreover, both COM and .NET clients can use a .NET class simultaneously. As the author of the class, you implement the managed signature only. Using Tlbexp.exe (or an equivalent API) automatically exports the signature to a type library generated for the class.

Overloaded Methods

Although .NET supports overloaded methods, the IDispatch interface relies solely on method name for binding, rather than the complete method signature. It is therefore not capable of supporting overloaded methods. However, to provide access to overloaded methods of a type, Tlbexp.exe decorates the names of overloaded methods with an ordinal number so that each method name is unique.

The following managed and unmanaged signatures show the inclusion of numbers:

Managed signature

interface INew {
public:
    void DoSomething();
    void DoSomething(short s);
    void DoSomething(short l);
    void DoSomething(float f);
    void DoSomething(double d);
}

Unmanaged signature

interface INew {
    void DoSomething();
    void DoSomething_2(short s);
    void DoSomething_3(short l);
    void DoSomething_4(float f);
    void DoSomething_5(double d);
}

The COM signature for the methods appears as a single DoSomething method followed by a series of decorated DoSomething_x methods, where x starts at 2 and increments for each overloaded form of the method. Note that some of the overloaded methods can be inherited from a base type. However, there is no guarantee that overloaded methods will retain the same number as the type version advances.

Although .NET clients can use the overloaded form of the method, COM clients have to access the decorated methods. Object browsers display all forms of the decorated method with the method signature to enable you to select the correct method. The late-bound client can also call IDispatch::GetIdsOfNames, passing in the decorated name to get the DispID of any overloaded method.

Properties

Managed classes and interfaces can have properties. A managed property is of a specific data type that may have an associated get method and set method. These methods are defined separately just like any other method. The following code example shows an interface containing a Height property. Classes that implement the interface are required to provide a get and set method for the property.

    interface IMammal {
 
    IMammal Mother{get;set;}
    IMammal Father{get;set;}
    int     Height{get;set;}
    int     Weight{get;set;}

    }

    class Human : IMammal
    {
        int weight;
        int height;
        IMammal father;
        IMammal mother;

        public IMammal Mother { get { return mother; } set { mother = value; } }
        public IMammal Father { get { return father; } set { father = value; } }
        public int Height { get { return height; } set { height = value; } }
        public int Weight { get { return weight; } set { weight = value; } }
    }

During the export, Tlbexp.exe converts the property set method to [propput] and the get method to [propget]. The property name in COM remains the same as the managed property name. This rule has the following exceptions:

  • If the property type, excluding value types, is a class or interface, the property set method becomes a [propputref], giving the parameters an added level of indirection.

  • If the property has no get or set method, Tlbexp.exe omits the property from the type library.

Like properties, managed fields are exported to the type library. The runtime marshaling service automatically generates the get and set methods for all public fields. During the conversion process, Tlbexp.exe generates a [propput] (or [propputref]) function and a [propget] function for each field, as the following type library representation shows.

Type library representation

interface IMammal : IDispatch {
         [propget]    HRESULT Mother([out, retval] IMammal** pRetVal);
         [propputref] HRESULT Mother([in] IMammal* pRetVal);
         [propget]    HRESULT Father([out, retval] IMammal** pRetVal);
         [propputref] HRESULT Father([in] IMammal* pRetVal);
         [propget]    HRESULT Height([out, retval] long* pRetVal);
         [propput]    HRESULT Height([in] long pRetVal);
         [propget]    HRESULT Weight([out, retval] long* pRetVal);
         [propput]    HRESULT Weight([in] long pRetVal);
         [propget]    HRESULT Age([out, retval] long* pRetVal);
         [propput]    HRESULT Age([in] long pRetVal);    
};

Events

If you are not familiar with the event model in COM interop, see Managed and Unmanaged Events. Managed types implement events using a delegate-base event model. For example, the Class1Events interface in the following code example raises the Click event.

    Public Delegate Sub ClickDelegate()
    <GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967"), _
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)>

    Public Interface Class1Event
        Sub Click ()
    End Interface
<ComSourceInterfaces("Class1Event, EventSrc")> _
    Public Class Class1
        Public Event Click As ClickDelegate
   End Class
    public delegate void Click();

    public interface Class1Event
    {
        void Click();
    }
   [ComSourceInterfaces("Class1Event, EventSrc")]
    public class Class1
    {
        public event ClickDelegate Click;
    }

During export, Tlbexp.exe marks the event interface as the source in its coclass. As the following type library representation shows, the exported ComClass1Events interface is marked as the source interface.

Type library representation

    disinterface Class1Event {
        properties:
        methods:
        [id(0x60020000)]
        HRESULT Click();
    };

    coclass Class1
    {
    …
    [default, source] Class1Event;
    };

See Also

Concepts

Exported Assembly Conversion
Exported Module Conversion
Exported Type Conversion
Exported Parameter Conversion

Other Resources

Assembly to Type Library Conversion Summary