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.
public__gc __interface IMammal {
public:
__property IMammal* get_Mother();
__property void set_Mother(IMammal*p);
__property IMammal* get_Father();
__property void set_Father(IMammal*p);
__property int get_Height();
__property void set_Height(int i);
__property int get_Weight();
__property void set_Weight(int i);
};
public__gc class Human : public IMammal {
public:
int Age;
__property IMammal* get_Mother() {return 0};
__property void set_Mother(IMammal*p) {};
__property IMammal* get_Father() {return 0};
__property void set_Father(IMammal*p) {};
__property int get_Height() {return 0};
__property void set_Height(int i) {};
__property int get_Weight() {return 0};
__property void set_Weight(int i) {};
};
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
[C#]
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
Assembly to Type Library Conversion Summary | Exported Assembly Conversion | Exported Module Conversion | Exported Type Conversion | Exported Parameter Conversion