Customizing COM Callable Wrappers

Customizing a COM Callable Wrapper is a straightforward task. If the type you want to expose to a COM client has nonstandard marshaling requirements, apply the System.Runtime.InteropServices.MarshalAsAttribute attribute to a method parameter, class field, or return value to change the marshaling behavior.

As the following illustration shows, you can export a managed DLL without customizing the wrapper (shown on the left). Or you can add marshaling information to the source, compile it, and use the Type Library Exporter (Tlbexp.exe) to export the modified DLL and produce a custom wrapper.

Marshaling information in exported DLLs

Type Library Exporter

Note

All managed types, methods, properties, fields, and events that you want to expose to COM must be public. Types must have a public default constructor, which is the only constructor that can be invoked through COM. For additional information, seeQualifying .NET Types for Interoperation..

When marshaling data between managed and unmanaged code, the interop marshaler must recognize the representations of the data being passed:

  • For blittable types, managed and unmanaged representations are always the same. For example, a 4-byte integer is always marshaled to a 4-byte integer. The interop marshaler uses the managed signature to determine the data representation.

  • For non-blittable types, the interop marshaler recognizes the managed representation from its method signature, but is unable to do the same for the unmanaged representation. To marshal non-blittable types, you can use one of the following techniques:

    • Allow the marshaler to infer the representation from the managed representation.

    • Supply the unmanaged data representation explicitly.

For example, a string is converted to a BSTR type when marshaled from managed to unmanaged code, unless you explicitly apply the MarshalAsAttribute to marshal the string to another type, such as LPWSTR. You can apply this attribute to a parameter, field, or return value within the source of the type definition, as shown in the following examples.

Apply the MarshalAsAttribute to a parameter

Public Sub M1(<MarshalAs(UnmanagedType.LPWStr)> msg As String)
    ' ...
End Sub
void M1([MarshalAs(UnmanagedType.LPWStr)] string msg)
{
    // ...
}
void M1([MarshalAs(UnmanagedType::LPWStr)] String^ msg)
{
    // ...
}

Apply the MarshalAsAttribute to a field within a class

Class MsgText
    <MarshalAs(UnmanagedType.LPWStr)> _
    Public msg As String = ""
End Class
class MsgText
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string msg = "";
}
ref class MsgText
{
public:
    [MarshalAs(UnmanagedType::LPWStr)]
    String^ msg;

    MsgText()
    {
        msg = "";
    }
};

Apply the MarshalAsAttribute to a return value

Public Function M2() As <MarshalAs(UnmanagedType.LPWStr)> String
    Dim msg As New String(New char(128){})
    ' Load message here ...
    Return msg
End Function
[return: MarshalAs(UnmanagedType.LPWStr)]
public string GetMessage()
{
    string msg = new string(new char[128]);
    // Load message here ...
    return msg;
}
[returnvalue: MarshalAs(UnmanagedType::LPWStr)]
String^ GetMessage()
{
    String^ msg = gcnew String(gcnew array<Char>(128));
    // Load message here ...
    return msg;
}

You set the System.Runtime.InteropServices.UnmanagedType enumeration to indicate the desired format of the unmanaged type. In the previous signatures, msg data is marshaled as a null-terminated buffer of Unicode characters (LPWStr).

At times, the interop marshaler requires more information than is provided by the managed and unmanaged data format. To marshal an array, for example, you must supply the element type, rank, size, and bounds of the array. You can use the MarshalAsAttribute to specify required additional information.

See Also

Reference

Customizing COM Callable Wrappers

Concepts

COM Data Types

Customizing Runtime Callable Wrappers

Other Resources

Marshaling Data with COM Interop

Default Marshaling Behavior