Proxies for COM Type Libraries

ProxyGen.exe generates managed proxy types that are based on COM types in the object model of the host application. For the most part, the proxy types have the same public members as the host types. However, there are some characteristics of COM host types that ProxyGen.exe does not support, and the proxy types have some differences from the host types.

For information about the structure of the proxy code file, see Architecture of Generated Proxy Code.

This topic provides the following information:

  • Unsupported features of COM object models

  • Conversion of OLE types to managed types

  • Conversion of IDL attributes to managed attributes

  • Conversion of host types to proxy types

  • Conversion of host members to proxy members

Unsupported Features of COM Object Models

ProxyGen.exe does not support structs, unions, records, or modules. If the type library that you pass to ProxyGen.exe defines one of these data types, ProxyGen.exe omits the type from the proxy descriptor file.

If the host object model has methods or properties that use one of these data types, you must redesign the object model so that it does not use the data type.

Conversion of OLE Types to Managed Types

The following table shows how ProxyGen.exe maps OLE types to managed types.

OLE types

Managed types

Boolean

SByte

char, small

SByte

short

Int16

long, int

Int32

hyper

Int64

unsigned char, byte

Byte

wchar_t, unsigned short

UInt16

unsigned long, unsigned int

UInt32

unsigned hyper

UInt64

float

Single

double

Double

VARIANT_BOOL

Boolean

void

Void

void*

IntPtr

DWORD

Int32

HRESULT

Int16

SCODE

Int32

BSTR

String

LPSTR or [string, ...] char

String

LPWSTR or [string, ...] wchar_t

String

IDispatch*

Object

VARIANT

Object

DECIMAL

Decimal

DATE

DateTime

GUID

Guid

CURRENCY

Decimal

SAFEARRAY(type)

managed type[]

Conversion of IDL Attributes to Managed Attributes

The following table shows COM attributes that have special behavior.

Attribute

Description

[restricted]

[hidden]

ProxyGen.exe ignores these attributes on types and members. The type or member is generated in the proxy code without any attributes that mark it as restricted or hidden.

[optional]

ProxyGen.exe applies the OptionalAttribute attribute to the parameter.

[defaultvalue]

ProxyGen.exe applies the DefaultParameterValueAttribute attribute to the parameter, unless the parameter has an [out] attribute. In these cases, OptionalAttribute is used instead.

Conversion of COM Types to Proxy Types

During conversion, ProxyGen.exe applies the HostTypeAttribute attribute to all proxy types, with the exception of entry points. For more information, see Architecture of Generated Proxy Code.

Because managed types are fundamentally different from COM types, ProxyGen.exe generates proxy types that use managed constructs to approximate the behavior of the COM types. However, there are some specific differences in the behavior of the constructs that are explained in the following sections.

Interfaces

ProxyGen.exe generates a proxy interface for each interface in the object model. The proxy interface has the same definition as the host interface. However, be aware of the following issues:

  • ProxyGen.exe does not generate members that the COM interface inherits from the IUnknown and IDispatch interfaces.

  • If the COM interface is implemented by more than one coclass in the host object model, it is recommended that each coclass also implement the IProvideClassInfo interface.

The following code examples show how ProxyGen.exe generates a proxy interface from a COM interface. The first example shows the COM interface definition in IDL. The second example shows the generated proxy interface. This is a simplified version of the code that is generated by ProxyGen.exe.

[
    object,
    uuid(AA4B9334-63A0-4C8B-AEE1-A759C0E66209),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IShape : IDispatch{
    [propget, id(1)] HRESULT Color([out, retval] IColorInfo** pVal);
    [propput, id(1)] HRESULT Color([in] IColorInfo* newVal);
    [id(2)] HRESULT ContainsPoint([in] IPointInfo* p, [out,retval] VARIANT_BOOL* retVal);
};
[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy.IShape")]
[GuidAttribute("AA4B9334-63A0-4C8B-AEE1-A759C0E66209")]
public partial interface IShape
{
    ShapeApp.Proxy.Color Color {
        [MarshalAsAttribute(UnmanagedType.Interface)]
        [HostMemberAttribute("Color", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)]
        get;

        [MarshalAsAttribute(UnmanagedType.Interface)]
        [HostMemberAttribute("Color", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty)]
        set;
    }

    [HostMemberAttribute("ContainsPoint", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    bool ContainsPoint(
        [MarshalAsAttribute(UnmanagedType.Interface)]
        ShapeApp.Proxy.Point p
    );
}

DispInterfaces

The code generation for dispinterfaces follows the same rules as interfaces. The following code examples show how ProxyGen.exe generates a proxy interface from a COM dispinterface.

The following example shows the IDL definition of an event dispinterface, followed by the generated proxy interface. This is a simplified version of the code that is generated by ProxyGen.exe.

[
    uuid(CD353795-08AA-4C04-B7B6-02A3C19372E1)
]
dispinterface _IShapeEvents
{
    methods:
    [id(1)] void ColorChanged(IDispatch * sender, IDispatch * e);
};
[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy._IShapeEvents")]
[GuidAttribute("CD353795-08AA-4C04-B7B6-02A3C19372E1")]
public partial interface _IShapeEvents
{
    [HostEventAttribute(1, HostEventMethodType= HostEventMethodType.Unspecified)]
    event ShapeApp.Proxy.ColorChangedEventHandler ColorChanged;
}

The following example shows the IDL definition of a non-event dispinterface, followed by the generated proxy interface. This is a simplified version of the code that is generated by ProxyGen.exe.

[
    uuid(99DD807C-BE4F-4142-B8A0-B97530D3C31E)
]
dispinterface IAddedEventArgs
{
    properties:
    [id(1)] IDispatch* AddedObject;
};
[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy.IAddedEventArgs")]
[GuidAttribute("99DD807C-BE4F-4142-B8A0-B97530D3C31E")]
public partial interface IAddedEventArgs
{
    object AddedObject {
        [HostMemberAttribute("AddedObject", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)]
        get;
        [HostMemberAttribute("AddedObject", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty)]
        set;
    }
}

Coclasses

ProxyGen.exe generates a managed proxy class for every coclass in the host object model. The proxy class has the following characteristics:

  • ProxyGen.exe derives the proxy class from MarshalByRefObject. This enables instances of the proxy class to be marshaled between the add-in and the host application.

  • The proxy class has a default constructor that is used internally by Visual Studio Tools for Applications, and is not intended to be used in the add-in code.

In the proxy class, ProxyGen.exe implements all interfaces (and all ancestor interfaces) that are specified in the original coclass definition, including interfaces that are marked with the [default] attribute. For interfaces that are marked with [default, source], ProxyGen.exe generates an event in the proxy class and a corresponding delegate.

ProxyGen.exe recognizes only [default, source] event interfaces in a coclass.

The following code examples show how ProxyGen.exe generates a proxy class for a coclass that implements a [default] interface and a [default, source] interface. The first example shows the definitions of the interfaces and the coclass in IDL. The second example shows the generated proxy code for this coclass. This is a simplified version of the code that is generated by ProxyGen.exe.

[
    object,
    uuid(AA4B9334-63A0-4C8B-AEE1-A759C0E66209),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IShape : IDispatch{
    [id(1)] HRESULT Draw([in] PVOID hdc);
};

[
    uuid(CD353795-08AA-4C04-B7B6-02A3C19372E1),
]
dispinterface _IShapeEvents
{
    methods:
    [id(1)] void ColorChanged(IDispatch * sender, IDispatch * e);
};

[ uuid(20AC235F-6525-4245-AF2C-50A669F29E48) ]
coclass Shape
{
    [default] interface IShape;
    [default, source] dispinterface _IShapeEvents;
};
public delegate void ColorChangedEventHandler(
    [MarshalAsAttribute(UnmanagedType.IDispatch)] object sender, 
    [MarshalAsAttribute(UnmanagedType.IDispatch)] object e);

[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy.Shape",
    SourceInterfaceGuid="CD353795-08AA-4C04-B7B6-02A3C19372E1")]
[GuidAttribute("20AC235F-6525-4245-AF2C-50A669F29E48")]
public partial class Shape : System.MarshalByRefObject, ShapeApp.Proxy.IShape, ShapeApp.Proxy._IShapeEvents
{
    [HostMemberAttribute("Draw", BindingFlags= BindingFlags.Instance | 
        BindingFlags.Public | BindingFlags.InvokeMethod)]
    public virtual void Draw(ref object hdc)
    {
        throw new System.NotImplementedException();
    }

    virtual public event ShapeApp.Proxy.ColorChangedEventHandler ColorChanged {
        [HostEventAttribute(1, HostEventMethodType = HostEventMethodType.Add)]
        add
        {
           throw new System.NotImplementedException();
        }
        [HostEventAttribute(1, HostEventMethodType = HostEventMethodType.Remove)]
        remove
        {
            throw new System.NotImplementedException();
        }
     }
}

Resolving Naming Conflicts

If the names of members of a [default] interface and a [default, source] interface conflict, you must edit the proxy descriptor file to rename the conflicting members. For example, a [default, source] interface could have an Activate method, and the [default] interface could also have an Activate method. This is permitted in COM. However, in managed code, you cannot have an event and a method with the same name. In these cases, ProxyGen.exe generates code that contains conflicting names. You must resolve these conflicts by renaming the members in the proxy descriptor file.

Enumerations

ProxyGen.exe generates an enumeration in the proxy descriptor file and the code file for each enumeration in the host object model. However, the generated enumeration is not a proxy; that is, it does not derive from MarshalByRefObject, and objects that are declared by the add-in that contain one of the enumeration values are not marshaled between the add-in and the host application.

By default, the generated proxy enumerations have an underlying type of Int32. COM enumerations are 32-bit values by default.

The following code examples show how ProxyGen.exe generates an enumeration in the proxy code file for a COM enumeration. The first example shows the definition of the COM enumeration in IDL. The second example shows the generated enumeration in the proxy code.

enum 
{
    Black = 0,
    Red = 1,
    Yellow = 2,
    Green = 3,
    Cyan = 4,
    Blue = 5,
    Magenta = 6,
    White = 7
} Color;
public enum Color : Int32
{
    Black = 0,
    Red = 1,
    Yellow = 2,
    Green = 3,
    Cyan = 4,
    Blue = 5,
    Magenta = 6,
    White = 7
}

Arrays of OLE Types

The only COM arrays that ProxyGen.exe recognizes are SAFEARRAY objects. ProxyGen.exe treats all COM SAFEARRAY objects as Array objects in the generated proxy code.

By default, SAFEARRAY objects are converted to one-dimensional arrays in the proxy code. To correctly marshal multidimensional SAFEARRAY objects in parameters and return values, add the SafeArrayDefaultRankAttribute to the parameter or return value in the proxy descriptor file. This attribute specifies the actual rank of the array. Apply the SafeArrayDefaultRankAttribute also to parameters and return values of type Array or System.Object[] that do not convey rank information.

COM Enumerators

ProxyGen.exe maps COM enumerators to managed enumerators. In COM, an enumerator is a property that has the dispatch identifier value DISPID_NEWENUM and has the name _NewEnum. In managed code, COM enumerators are represented by the GetEnumerator method. The corresponding interface has IEnumerable added to its base interfaces.

Constants

ProxyGen.exe converts constants that are defined in a module to a managed class with the same name as the module. Constants defined outside of a module are not converted.

Conversion of COM Members to Proxy Members

During conversion, ProxyGen.exe applies attributes to members of COM types. For more information, see Architecture of Generated Proxy Code.

The members of proxy types that are generated by ProxyGen.exe have the following differences from the members of COM host types.

Methods

COM methods are defined with the following syntax:

[attributes] <return value> identifier (
    [attributes] <param type 1> <param name 1>, 
    [attributes] <param type 2> <param name 2>);

ProxyGen.exe preserves the semantics of the method declaration according to the OLE type conversion that is described in the section Conversion of OLE types to managed types. ProxyGen.exe also maps certain attributes to their functionally equivalent attribute in the .NET Framework. For more information, see Conversion of IDL attributes to managed attributes.

The following code examples demonstrate how ProxyGen.exe generates proxy code for several different types of COM methods. The first example shows the definitions of the COM methods in IDL. The second example shows the generated proxy code for these methods. This is a simplified version of the code that is generated by ProxyGen.exe.

interface IShape : IDispatch{
    [id(1)] HRESULT ContainsPoint([in] IPointInfo* p, [out,retval] VARIANT_BOOL* retVal);
    [id(2)] HRESULT Draw([in] PVOID hdc);
    [id(3)] HRESULT Load(SAFEARRAY(BYTE) savedState);
    [id(4)] HRESULT Save([out, retval] SAFEARRAY(BYTE) *savedState);
    [id(5)] HRESULT Clone([out, retval] IShape **ppNewShape);
};
[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy.IShape")]
[GuidAttribute("AA4B9334-63A0-4C8B-AEE1-A759C0E66209")]
public partial interface IShape
{
    [HostMemberAttribute("ContainsPoint", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    bool ContainsPoint(
        [MarshalAsAttribute(UnmanagedType.Interface)]
        ShapeApp.Proxy.Point p
    );

    [HostMemberAttribute("Draw", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    void Draw(ref object hdc);

            
    [HostMemberAttribute("Load", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    void Load([MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] 
              byte[] savedState);

    [HostMemberAttribute("Save", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    [return: MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType=InteropServices.VarEnum.VT_UI1)]
    byte[] Save();

    [HostMemberAttribute("Clone", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod)]
    [return: MarshalAsAttribute(UnmanagedType.Interface)]
    ShapeApp.Proxy.IShape Clone();
}

Pointer Parameters and Return Values

Any parameter or return value that contains a pointer to a supported OLE type is mapped by ProxyGen.exe to the equivalent .NET Framework type:

  • IDispatch* values are converted to Object values.

  • IDispatch** values are converted to Object values that are passed by reference (that is, ref object).

  • Pointers to custom interfaces are converted to an instance of the corresponding proxy interface. For example, a IMyInterface* value is converted to IMyInterface.

  • Double pointers to custom interfaces are converted to an instance of the corresponding proxy interface that is passed by reference. For example, a IMyInterface** value is converted to refIMyInterface.

Properties

ProxyGen.exe attempts to map COM properties to equivalent .NET Framework properties. In many cases the mapping is straightforward:

  • [propget] properties are mapped to a corresponding get property.

  • [propput] and [propputref] properties are mapped to a corresponding set property.

  • If a property has both [propput] and [propputref] declarations, it is converted to multiple methods. For more information, see the section Parameterized Properties.

The following code examples demonstrate how ProxyGen.exe generates proxy code for a COM property. The first example shows the definitions of the COM property in IDL. The second example shows the generated proxy code for this property. This is a simplified version of the code that is generated by ProxyGen.exe.

interface IShape : IDispatch{
    [propget, id(1)] HRESULT Color([out, retval] IColorInfo** pVal);
    [propput, id(1)] HRESULT Color([in] IColorInfo* newVal);
};
[HostTypeAttribute("ShapeApp.Proxy, ShapeApp.Proxy.IShape")]
[GuidAttribute("AA4B9334-63A0-4C8B-AEE1-A759C0E66209")]
public partial interface IShape
{
    ShapeApp.Proxy.Color Color {
        [MarshalAsAttribute(UnmanagedType.Interface)]
        [HostMemberAttribute("Color", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)]
        get;

        [MarshalAsAttribute(UnmanagedType.Interface)]
        [HostMemberAttribute("Color", BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty)]
        set;
    }
}

Indexer Properties

ProxyGen.exe generates an indexer property in the proxy code only if the COM indexer property in the host object model is named Item, and if the dispatch ID of the property is 0. Otherwise, ProxyGen.exe ignores indexer properties in the host object model.

Parameterized Properties

Any parameterized properties that are not indexer properties are mapped to methods according to the following rules:

  • [propget] is mapped to a method named get_ followed by the name of the property.

  • [propput] is mapped to a method named set_ followed by the name of the property.

  • [propputref] is mapped to a method named put_ followed by the name of the property.

If a property that does not take parameters is declared with multiple setters ([propput] and [propputref]), ProxyGen.exe follows the same rules to covert the property to a method.

See Also

Concepts

Architecture of Generated Proxy Code

Creating Proxies

Defining Entry Points and Other Proxy Changes

ProxyGen Descriptor Schema Reference

Reference

Proxies for Managed Assemblies

Proxy Generation Tool (ProxyGen.exe)