Appendix A. System.Runtime.InteropServices Reference

In This Appendix

The System.Runtime.InteropServices Namespace

The System.Runtime.InteropServices.CustomMarshalers Namespace

The System.Runtime.InteropServices.Expando Namespace

The core APIs used for COM Interoperability and Platform Invocation Services reside in the System.Runtime.InteropServices namespace. This appendix is an alphabetical reference for types in this namespace, plus its two sub-namespaces:

System.Runtime.InteropServices.CustomMarshalers

System.Runtime.InteropServices.Expando

Entries are crossed-referenced with relevant chapters where appropriate. For .NET definitions of famous COM types, the description refers you to their original documentation at MSDN Online rather than describing them in detail. This reference site resides at msdn.microsoft.com.

The System.Runtime.InteropServices Namespace

The System.Runtime.InteropServices namespace contains types used for both COM Interoperability and PInvoke. This is a large namespace, but its types fall into eight broad categories:

• Custom attributes and associated enumerations

• Helper classes and associated enumerations

VARIANT type wrappers

• Custom marshaling

• Custom instantiation

• Exceptions

• Tool APIs

• .NET definitions of famous COM types

To help you navigate through these categories and their types, Figure A.1, which also appears on the inside front cover, displays the entire contents of the System.Runtime.InteropServices namespace according to the previous groups. Italicized custom attributes are pseudo-custom attributes.

Figure A.1. The contents of the System.Runtime.InteropServices namespace.

Image

Custom attributes dominate the namespace, because there are so many ways to customize .NET exposure to unmanaged code and vice versa. The Marshal helper class is the centerpiece of System.Runtime.InteropServices, providing static methods (Shared in VB .NET) that facilitate a wide range of scenarios. The other helper classes are much smaller and serve specific functions. The VARIANT type wrappers, discussed in Chapter 3, “The Essentials for Using COM in Managed Code,” control the type of a VARIANT instance corresponding to a .NET object when the object has more than one possible unmanaged representation.

The two custom marshaling interfaces are discussed in Chapter 20, “Custom Marshaling,” and the custom instantiation types are discussed in Chapter 6, “Advanced Topics for Using COM Components.” The System.Runtime.InteropServices namespace defines a handful of exceptions that can be thrown by the CLR when encountering problems related to interoperability services.

The Tool APIs provide the functionality for type library import, type library export, and assembly registration used by the TLBIMP.EXE, TLBEXP.EXE, and REGASM.EXE SDK tools and Visual Studio .NET. These APIs are covered in Chapter 22, “Using APIs Instead of SDK Tools.”

Finally, a large portion of the types defined in System.Runtime.InteropServices are not new .NET-specific types, but rather metadata definitions of some widely used COM interfaces, structures, unions, and enums. Some of these types are used by members of the Marshal class, some are used by custom marshalers and the type library importer, and others are simply common types that don’t have a definition in a type library. You should treat these as Primary Interop Definitions of the COM types and use them instead of defining the types yourself.

That’s the high-level overview of the contents of System.Runtime.InteropServices. For the remainder of this section, every type—listed in alphabetical order—is described in further detail.

The ArrayWithOffset Value Type

You can use this simple value type when you want to pass a subset of a .NET array to a PInvoke method expecting a C-style array. Similar to the HandleRef value type (also in the System.Runtime.InteropServices namespace), the PInvoke signature must be modified to use the ArrayWithOffset type rather than the usual array.

ArrayWithOffset has a constructor with two parameters—a System.Object that represents the array, and an integer that specifies the offset where the subset begins. Rather than having to create a new .NET array and copy elements, you can use this type, flagged specially by the Interop Marshaler, as a performance optimization. The marshaler adds the offset (a number of bytes) to the pointer value passed to unmanaged code, rather than passing a pointer to the first element.

The following code demonstrates the use of this type when calling a PInvoke method with the following unmanaged C++ signature:

void UnmanagedMethod(int* someArray);

An overload that doesn’t use ArrayWithOffset is also used to demonstrate the difference.

C#:

Image

Visual Basic .NET:

Image

C++:

Image

Caution

The array passed to ArrayWithOffset must have blittable elements, such as primitive types or user-defined value types with primitive fields. If an array with non-blittable elements (or a non-array) is passed to ArrayWithOffset’s constructor, an ArgumentException is thrown.

ArrayWithOffset can be used on COM methods, too, but the signatures would need to be customized to use ArrayWithOffset using either the techniques in Chapter 7, “Modifying Interop Assemblies,” or Chapter 21, “Manually Defining COM Types in Source Code.” Nothing prevents you from creating an ArrayWithOffset instance from a multi-dimensional array (or with an offset greater than the array’s length), but this is not a correct use of the type.

The AssemblyRegistrationFlags Enumeration

The AssemblyRegistrationFlags enumeration is used by RegistrationServices.RegisterAssembly and IRegistrationServices.RegisterAssembly to indicate how an assembly should be registered. It has two values:

None. The default registration should occur.

SetCodeBase. The assembly should be registered with a CodeBase value that provides the CLR with a hint as to where the assembly physically resides.

For more information, consult Chapter 22 or the RegistrationServices class listed later in this section.

The AutomationProxyAttribute Custom Attribute

This custom attribute can be placed on assemblies, classes, or interfaces to affect the behavior of type library export. The custom attribute has a single constructor with a boolean parameter. When set to true (the default), the marked type should be marshaled with the OLE Automation Marshaler (a process known as type library marshaling). When set to false, the marked type should be marshaled with a custom proxy/stub marshaler. It is up to the developer to supply and register such a marshaler.

From COM’s perspective, this attribute only affects interfaces. Therefore, marking this on an assembly applies to any interfaces contained within (unless individual interfaces override the assembly-level setting with their own AutomationProxyAttribute) and marking this on a class applies to its class interface if one is exported.

For more information, consult Chapter 12, “Customizing COM’s View of .NET Components.”

The BIND_OPTS Value Type

This is a .NET definition of the unmanaged BIND_OPTS structure used by the IBindCtx COM interface. In the .NET Framework, this definition is used by UCOMIBindCtx.GetBindOptions and UCOMIBindCtx.GetBindOptions.

For more information see the UCOMIBindCtx interface listed later in this section or consult MSDN Online for information about the original BIND_OPTS structure.

The BINDPTR Value Type

This is a .NET definition of the unmanaged BINDPTR union used by the ITypeComp COM interface. In the .NET Framework, this definition is used by UCOMITypeComp.Bind.

For more information see the UCOMITypeComp interface listed later in this section or consult MSDN Online for information about the original BINDPTR union.

The CALLCONV Enumeration

This is a .NET definition of the unmanaged CALLCONV enumeration used by the FUNCDESC value type.

For more information see the FUNCDESC value type listed later in this section or consult MSDN Online for information about the original CALLCONV enumeration.

The CallingConvention Enumeration

Unlike CALLCONV, which is defined only for the sake of existing COM type definitions, the CallingConvention enumeration is the official means of specifying a calling convention in managed code. The DllImportAttribute pseudo-custom attribute has a property that accepts one of its values. (A few methods in System.Reflection.Emit also use the CallingConvention enumeration for dynamically emitting PInvoke signatures.) The enumeration has the following values:

Cdecl. The caller is responsible for cleaning the stack. Therefore, this calling convention is appropriate for methods that accept a variable number of parameters (like printf).

FastCall. This is not supported by version 1.0 of the .NET Framework.

StdCall. This is the default convention for PInvoke methods running on Windows. The callee is responsible for cleaning the stack.

ThisCall. This is used for calling unmanaged methods defined on a class. All but the first parameter is pushed on the stack because the first parameter is the this pointer, stored in the ECX register.

Winapi. This isn’t a real calling convention, but instead indicates to use the default calling convention for the current platform. On Windows (but not Windows CE), the default calling convention is StdCall.

For more information, consult Chapter 18, “The Essentials of PInvoke,” or the DllImportAttribute pseudo-custom attribute listed later in this section.

The CharSet Enumeration

The CharSet enumeration is used to specify how .NET strings should be marshaled to unmanaged code. This is necessary because although there’s only one managed string type, there are several unmanaged string types. Both the DllImportAttribute and StructLayoutAttribute pseudo-custom attributes have a property that accepts one of its values. These values are:

Ansi. With this setting, every character is a one-byte ANSI character. When used with DllImportAttribute, the CLR attempts to invoke an entry point with an appended “A” if the entry point specified by the signature doesn’t exist.

Auto. The Ansi or Unicode setting is chosen based on the current operating system. Ansi is used on Windows 98 and ME, whereas Unicode is used on Windows NT, 2000, XP, and .NET Server.

None. This setting is obsolete and should not be used; it means the same thing as Ansi.

Unicode. With this setting, every character is a two-byte Unicode character. When used with DllImportAttribute, the CLR attempts to invoke an entry point with an appended “W” before attempting to invoke the entry point specified by the signature.

The C#, VB .NET, and C++ compilers default to Ansi behavior both in the context of DllImportAttribute and StructLayoutAttribute. (C++ actually defaults to None, but it means the same thing.)

For more information, consult Chapter 18 or the DllImportAttribute and StructLayoutAttribute pseudo-custom attributes listed later in this section.

The ClassInterfaceAttribute Custom Attribute

This custom attribute controls (or suppresses) the class interface exposed to COM for a .NET class. It can be marked on classes and assemblies to affect the behavior of type library export (and also run-time behavior to match what is exported). When marked on an assembly, it applies to all classes within the assembly unless individual classes override the setting with their own ClassInterfaceAttribute.

The attribute has two constructors—one that takes a 16-bit integer, and one that takes a ClassInterfaceType enumeration. The latter constructor should always be used. The type library importer always marks imported classes with this attribute and the ClassInterfaceType.None setting because existing COM classes never expose CLR-generated class interfaces.

For more information, consult Chapter 12 or the ClassInterfaceType enumeration listed next.

The ClassInterfaceType Enumeration

This enumeration is used by the ClassInterfaceAttribute custom attribute to control the behavior of class interfaces. It has the following values:

AutoDispatch. An empty-looking class interface is exposed, whose members can be invoked only through IDispatch. Although you can’t see them, the class interface with this setting contains all the members of the class interface under the AutoDual setting. This is the default setting for ClassInterfaceAttribute because COM clients typically can’t rely on method order or DISPIDs remaining constant from one version of a .NET class to the next.

AutoDual. A dual class interface is exposed, containing all of the class’s public non-static members (including base class members) except for methods directly marked as COM-invisible. This is handy because you get the benefits of early binding without having to bother defining interfaces in managed code, but it’s dangerous due to class versioning changing the interface’s definition. You should avoid shipping classes marked with this setting.

None. No class interface is exposed. The first public COM-visible interface listed as implemented is exposed as the coclass’s default interface. If no interface is listed as being implemented, the first public COM-visible interface implemented by a base class (starting from most-derived and working downward) becomes the default interface. If the class and none of its base classes implement any such interfaces, the exported default interface is _Object with this setting.

Using ClassInterfaceType.None is the only way to expose your own default interface to COM, although which interface becomes the default can be unreliable for classes that implement multiple interfaces. Usually the order that the interfaces are listed in metadata matches the order in source code, but it ultimately depends on your .NET compiler. For example, a C# class that implements two interfaces that are related via inheritance always lists the base interface first in metadata.

For more information, consult Chapter 12 and the ClassInterfaceAttribute custom attribute listed previously.

The CoClassAttribute Custom Attribute

This custom attribute is used by the type library importer when creating a coclass interface—an interface with the name of a coclass that derives from the coclass’s default interface and possibly an event interface if the coclass exposes any source interfaces. Its constructor takes a Type parameter that is the type of the .NET class with the name CoclassNameClass. This attribute is used by the C# and VB .NET compliers to enable you to instantiate the coclass interface in source code but actually be instantiating the .NET class type given in the CoClassAttribute.

For more information, consult Chapter 4, “An In-Depth Look at Imported Assemblies,” and Chapter 21, “Manually Defining COM Types in Source Code.”

The ComAliasNameAttribute Custom Attribute

This custom attribute is used by the type library importer when importing a parameter whose type is an alias (typedef) for a different type. The .NET parameter is the underlying type, but the custom attribute, whose only constructor takes a string parameter, gives the name of the original type in the type library. This custom attribute exists for informational purposes only, to show the author’s intent that the marked type has more meaning than what the underlying type conveys.

For more information, consult Chapter 4.

The ComConversionLossAttribute Custom Attribute

This custom attribute is used by the type library importer when the .NET data type chosen for the COM type loses information. For example, because a parameter that’s a pointer to a pointer to a structure cannot be accurately represented in managed code (unless using non-CLS-compliant pointers), the type library importer converts such a type to a System.IntPtr and marks its containing interface or class with ComConversionLossAttribute. This attribute exists for informational purposes only.

For more information, consult Chapter 4.

The ComEventInterfaceAttribute Custom Attribute

This custom attribute is used by the type library importer to mark the event interfaces it generates for a coclass that exposes at least one source interface. Such an event interface has the name SourceInterfaceName_Event, and contains an event member for each method of the source interface.

The custom attribute’s constructor takes two Type parameters. The first one must be set to the type of the source interface defined in metadata, and the second one must be set to the type of the event provider class generated by the type library importer (which has the name SourceInterfaceName_EventProvider). The CLR uses this custom attribute at run time to associate events on the interface to their implementation on the event provider class and to link them to the original COM source interface.

For more information, consult Chapters 4 and 21.

The COMException Exception

This is the generic exception type thrown whenever a COM object returns an unfamiliar HRESULT that gets mapped into a .NET exception. (An “unfamiliar HRESULT” is one that’s not listed in Table C.2 in Appendix C, “HRESULT to .NET Exception Transformations.”) The COMException type has the same members as any exception type plus a public ErrorCode property that contains the HRESULT value returned by the COM object.

If the COM object doesn’t set an error message using the IErrorInfo interface (or the Err object in Visual Basic 6), and if the CLR can’t obtain an error message from the operating system, the message of a COMException is:

Exception from HRESULT: 0xnnnnnnnn

where nnnnnnnn is the eight-digit hexadecimal HRESULT value.

A COMException could be thrown in managed code in order to return a specific HRESULT value to COM. COMException has several constructor overloads, and if an HRESULT value isn’t given, it defaults to the generic E_FAIL value (0x80004005). Throwing a COMException in managed code is never recommended, however, because it’s not a nice exception for .NET clients to encounter. Instead, you could define a new exception type and set its protected HResult property to the desired value if no existing exception already corresponds to that HRESULT.

COMException derives from ExternalException, which derives from System.SystemException. Be aware, however, that a COMException is usually thrown in response to an application error, not a system error.

For more information, consult Chapter 3, “The Essentials for Using COM in Managed Code,” Appendix C, “HRESULT to .NET Exception Transformations,” and Appendix D, “.NET Exception to HRESULT Transformations.”

The ComImportAttribute Pseudo-Custom Attribute

This parameter-less, pseudo-custom attribute is used by the type library importer to mark every class as a “COM class” and every interface as a “COM interface.” The metadata bit set by using this pseudo-custom attribute tells the CLR to call CoCreateInstance when instantiating a COM class and QueryInterface when casting it to an interface. It also indicates to the type library exporter that such types should not be exported to a type library.

Because this is a pseudo-custom attribute, reflection cannot detect that types are marked with ComImportAttribute through the normal means. Instead, the Type.IsImport property can be used to programmatically determine if a class or interface is marked with the attribute.

For more information, consult Chapters 4 and 21.

The ComInterfaceType Enumeration

This enumeration is used by the InterfaceTypeAttribute custom attribute to control how a .NET interface is exposed to COM. It has the following values:

InterfaceIsDual. The interface derives from IDispatch, supporting both v-table access and late binding. This is the default setting for InterfaceTypeAttribute.

InterfaceIsIDispatch. The interface is a dispinterface, meaning its methods can only be called by late binding via IDispatch. Defining a new .NET interface as a dispinterface is only desirable if it will serve as a source interface, as discussed in Chapter 13, “Exposing .NET Events to COM Clients.”

InterfaceIsIUnknown. The interface derives directly from IUnknown. Early bound access through the v-table is supported, but late binding is not because the interface doesn’t derive from IDispatch.

These values cannot be combined with bitwise operators; only one can be used at a time.

For more information, see Chapters 4, 12 and 21 and the InterfaceTypeAttribute custom attribute listed later in this section.

The ComMemberType Enumeration

This enumeration is used by the Marshal.GetMethodInfoForComSlot method. It has the following values:

Method. The MethodInfo corresponds to a regular method.

PropGet. The MethodInfo corresponds to a property’s get accessor method.

PropSet. The MethodInfo corresponds to a property’s set accessor method.

For more information, see the GetMethodInfoForComSlot method listed later in this section under “The Marshal Class” section.

The ComRegisterFunctionAttribute Custom Attribute

This parameter-less custom attribute can be marked on a method of a .NET class. If and when the class gets registered via RegistrationServices.RegisterAssembly (either directly or through inheritance), the method is invoked. RegistrationServices.RegisterAssembly is the standard assembly registration mechanism used by REGASM.EXE and Visual Studio .NET (when using the Register for COM Interop option).

The purpose of this custom attribute is to provide class authors the opportunity to run specialized code during the registration process. Whenever defining such a method, a corresponding unregistration method marked with ComUnregisterFunctionAttribute should be defined to reverse the custom registration work.

A method marked with ComRegisterFunctionAttribute can have any visibility (public, private, and so on) but must be static (Shared in VB .NET), and must contain one Type parameter. There can be at most one custom registration function per class. When invoked, its Type parameter contains an instance of the current type being registered (which may be a subclass of the class defining the registration method).

For more information, consult Chapter 12 and the ComUnregisterFunctionAttribute custom attribute listed later in this section.

The ComSourceInterfacesAttribute Custom Attribute

This custom attribute can be placed on a .NET class to make the type library exporter expose a coclass with source interfaces. This is necessary in order to expose .NET class with events to COM as a class with “COM events.” The custom attribute has several overloaded constructors to list one to four Type objects representing one to four source interfaces, and also has an overload that expects a string of null-delimited, assembly-qualified interface names.

The interfaces listed in this custom attribute must have .NET definitions, and each of its methods should have the same name as an event defined on the marked class with the same signature as the event’s delegate. The type library importer uses this custom attribute to map methods on imported source interfaces into .NET events.

For more information, consult Chapter 13.

The ComUnregisterFunctionAttribute Custom Attribute

This parameter-less custom attribute can be marked on a method of a .NET class. If and when the class gets unregistered via RegistrationServices.UnregisterAssembly (either directly or through inheritance), the method is invoked. RegistrationServices.UnregisterAssembly is the standard assembly registration mechanism used by REGASM.EXE’s /unregister option. It’s also called every time a Visual Studio .NET project using the Register for COM Interop option is rebuilt.

The purpose of this custom attribute is to provide class authors the opportunity to run specialized code during the unregistration process. Such a method doesn’t need to be defined unless a corresponding registration method marked with ComRegisterFunctionAttribute exists. The unregistration method should undo all the work from the custom registration function.

A method marked with ComUnregisterFunctionAttribute can have any visibility (public, private, and so on) but must be static (Shared in VB .NET), and must contain one Type parameter. There can be at most one custom unregistration function per class. When invoked, its Type parameter contains an instance of the current type being unregistered (which may be a subclass of the class defining the unregistration method).

For more information, consult Chapter 12 or the ComRegisterFunctionAttribute listed previously in this section.

The ComVisibleAttribute Custom Attribute

This custom attribute can be used to restrict a .NET type or member’s visibility from COM. It can be marked on an assembly, a type (a class, interface, struct, delegate, or enum) or on a member (method, property, or field). Although it can hide public .NET types and members from COM, it can never be used to expose non-public .NET types to COM.

Its constructor takes a single boolean parameter that can be set to true to make something COM-visible (the default behavior), or false to make it COM-invisible. When marked on an assembly, it applies to all the types contained within. When marked on a type, it’s never exported to a type library. When marked on a class, it’s never registered, and when marked on an interface, it’s never attainable from a QueryInterface call. When marked on a member, it is excluded from interfaces (including class interfaces) exposed to COM. Marking a type with ComVisibleAttribute overrides the assembly-level setting. Marking members as COM-invisible selectively hides them on a COM-visible type, but marking members as COM-visible on a COM-invisible type has no effect.

Many types and assemblies in the .NET Framework are marked with ComVisibleAttribute to prohibit their direct use from COM.

For more information, consult Chapter 8, “The Essentials for Using .NET Components from COM,” and Chapter 12.

The CONNECTDATA Value Type

This is a .NET definition of the unmanaged CONNECTDATA structure used by the IEnumConnections COM interface. In the .NET Framework, this definition is used by UCOMIEnumConnections.Next.

For more information see the UCOMIEnumConnections interface listed later in this section or consult MSDN Online for information about the original BINDPTR union.

The CurrencyWrapper Class

The CurrencyWrapper class can be used to pass a .NET Decimal type inside a VARIANT and have COM see it as VT_CY (a COM CURRENCY type) rather than VT_DECIMAL (a COM DECIMAL type). Although Decimal types are automatically transformed to COM CURRENCY types when passed early-bound to COM signatures expecting CURRENCY, the CLR can’t know to do this transformation when late binding or passing a type inside a VARIANT because the COM component’s expectations aren’t captured in the type information.

The CurrencyWrapper class has two constructors—one that takes a Decimal type and one that takes an Object type. (If the Object passed isn’t a Decimal, however, an ArgumentException is thrown.) When passing a System.Object type to a COM method (because Object corresponds to COM’s VARIANT), a CurrencyWrapper instance is flagged by the Interop Marshaler so the conversion can be done.

The following code demonstrates the use of this type when calling the following COM method:

HRESULT UnmanagedMethod(VARIANT v);

Its imported managed signature has a generic System.Object parameter.

C#:

Decimal d = ...;
// Pass a VARIANT containing a decimal (VT_DECIMAL)
obj.UnmanagedMethod(d);
// Pass a VARIANT containing a currency (VT_CY)
obj.UnmanagedMethod(new CurrencyWrapper(d));

Visual Basic .NET:

Dim d As Decimal = ...
' Pass a VARIANT containing a decimal (VT_DECIMAL)
obj.UnmanagedMethod(d)
' Pass a VARIANT containing a currency (VT_CY)
obj.UnmanagedMethod(New CurrencyWrapper(d))

C++:

Decimal d = ...;
// Pass a VARIANT containing a decimal (VT_DECIMAL)
obj->UnmanagedMethod(__box(d));
// Pass a VARIANT containing a currency (VT_CY)
obj->UnmanagedMethod(new CurrencyWrapper(d));

For more information, consult Chapter 3.

The DESCKIND Enumeration

This is a .NET definition of the unmanaged DESCKIND enumeration used by the ITypeComp COM interface. In the .NET Framework, this definition is used by UCOMITypeComp.Bind.

For more information see the UCOMITypeComp interface listed later in this section or consult MSDN Online for information about the original DESCKIND enumeration.

The DESCUNION Value Type

This is a .NET definition of the unmanaged DESCUNION union used as a field in the unmanaged ELEMDESC and VARDESC structures. In the .NET Framework, this definition is used by .NET definitions of ELEMDESC and VARDESC.

For more information consult MSDN Online for information about the original DESCUNION union.

The DispatchWrapper Class

The DispatchWrapper class can be used to pass a .NET Object type, a derived type, or an interface type inside a VARIANT and have COM see it as VT_DISPATCH (a COM object that implements IDispatch). Such objects are exposed as VT_DISPATCH VARIANTs by default anyway, so of what use is DispatchWrapper? It has one use—passing a VT_DISPATCH VARIANT containing a null pointer. Without the DispatchWrapper, a null instance (Nothing in VB .NET) as an Object parameter is marshaled as a VT_EMPTY VARIANT.

The DispatchWrapper class has one constructor that takes an Object type. The instance passed must not be a value type or an array, otherwise an ArgumentException is thrown. If the object’s CCW doesn’t implement IDispatch, an InvalidCastException is thrown. A CCW doesn’t implement IDispatch if the .NET class is COM-invisible (including being non-public), or if it is marked with ClassInterface(ClassInterfaceType.None) and the interface chosen as the default interface is marked with InterfaceType(ComInterfaceType.InterfaceIsIUnknown).

The following code demonstrates the use of this type when calling the following COM method:

HRESULT UnmanagedMethod(VARIANT v);

Its imported managed signature has a generic System.Object parameter.

C#:

// Pass a VT_EMPTY VARIANT
obj.UnmanagedMethod(null);
// Pass a VT_DISPATCH VARIANT with a null pdispVal pointer
obj.UnmanagedMethod(new DispatchWrapper(null));

Visual Basic .NET:

' Pass a VT_EMPTY VARIANT
obj.UnmanagedMethod(Nothing)
' Pass a VT_DISPATCH VARIANT with a null pdispVal pointer
obj.UnmanagedMethod(New DispatchWrapper(Nothing))

C++:

// Pass a VT_EMPTY VARIANT
obj->UnmanagedMethod(NULL);
// Pass a VT_DISPATCH VARIANT with a null pdispVal pointer
obj->UnmanagedMethod(new DispatchWrapper(NULL));

For more information, consult Chapter 3.

The DispIdAttribute Custom Attribute

This custom attribute marks class or interface members with DISPIDs. With its single constructor that takes an integer, it can be used to control the DISPIDs used by the type library exporter. The important DISPIDs—DISPID_VALUE (0) and DISPID_NEWENUM (-4)—are automatically handled by the CLR when you define a default member or a .NET enumerator. Specifying other DISPIDs might be desirable if you expose an auto-dual class interface and add members in a later version that may change the auto-generated DISPIDs of the existing members.

The type library importer marks members with DispIdAttribute in order to preserve the DISPIDs from their definitions in the type library.

For more information, consult Chapter 12.

The DISPPARAMS Value Type

This is a .NET definition of the unmanaged DISPPARAMS structure used by the ITypeInfo COM interface. In the .NET Framework, this definition is used by .NET definition of UCOMITypeInfo.Invoke.

For more information see the UCOMITypeInfo interface listed later in this section or consult MSDN Online for information about the original DISPPARAMS structure.

The DllImportAttribute Pseudo-Custom Attribute

The DllImportAttribute pseudo-custom attribute turns a .NET method definition into a PInvoke method—one that’s exposed by an unmanaged DLL as a static entry point. Whereas C# and C++ require that you use this attribute directly, the Visual Basic .NET compiler emits this pseudo-custom attribute when the Declare statement is used. This attribute has one required string parameter that is the name of the DLL containing the entry point with the .NET method’s signature. If no path is given, the DLL must be in the path at run time unless it’s already loaded by some other means. Otherwise, a full or relative path can be given. Fully qualified paths are not recommended due to their brittleness.

Besides the one required parameter, DllImportAttribute has 6 optional named parameters:

CallingConvention. This parameter is used with a member of the CallingConvention enumeration to specify the entry point’s calling convention. The default value is CallingConvention.Winapi.

CharSet. This parameter is used with a member of the CharSet enumeration to specify how string parameters should be marshaled and what entry point name should be invoked (the exact name given or one ending with an “A” or “W”). CharSet.Ansi is the default value chosen by C# and VB .NET (when using Declare). The CLR’s (and the C++ compiler’s) default is CharSet.None, which means the same thing.

EntryPoint. This parameter is used with a string to specify the entry point name (or ordinal number as “#ordinal”). If omitted, the name of the method marked with DllImportAttribute is used.

ExactSpelling. This boolean parameter controls whether or not the CharSet setting causes the CLR to look for entry point names other than the one specified (ending in “A” or “W”). If true, CharSet only affects the behavior of string parameters. The default value chosen by the CLR, C#, and C++ is false, but VB .NET chooses true by default (when using Declare).

PreserveSig. This boolean parameter, similar to PreserveSigAttribute, controls whether or not the signature is a direct translation of the unmanaged entry point. If true (the default in all languages), the .NET signature “preserves” the unmanaged signature. If false, then the return type in the .NET signature is assumed to be an [out, retval] parameter, and it’s assumed that the unmanaged signature returns an HRESULT value that will be transformed into an exception by the CLR when appropriate.

SetLastError. This boolean parameter should be set to true if the entry point provides additional error information retrievable via the Win32 GetLastError method, and false otherwise. False is the default value except when using Declare in VB .NET. If this parameter is set to true and if the function internally uses the SetLastError API, then this additional information can be retrieved from managed code by calling Marshal.GetLastWin32Error (or the VB .NET-specific Err.LastDllError).

For more information, consult Part VI, “Platform Invocation Services,” and the CallingConvention and CharSet enumerations listed earlier in this section.

The ELEMDESC Value Type

This is a .NET definition of the unmanaged ELEMDESC structure used by the unmanaged FUNCDESC and VARDESC structures. In the .NET Framework, this definition is used by .NET definitions of FUNCDESC and VARDESC.

For more information consult MSDN Online for information about the original ELEMDESC structure.

The ErrorWrapper Class

The ErrorWrapper class can be used to pass a .NET integer or Exception type inside a VARIANT and have COM see either type as VT_ERROR—a 32-bit integer that represents an error code. By default, an integer would be exposed as VT_I4 and an Exception object would be exposed as VT_DISPATCH or VT_UNKNOWN (if the object doesn’t implement IDispatch). When creating an ErrorWrapper from an integer, the error code has the exact same value as the integer. When creating an ErrorWrapper from an Exception object, the error code has the value of its protected HResult property.

The ErrorWrapper class has three constructors—one that takes a 32-bit integer, one that takes an Exception object, and one that takes a System.Object type. The object passed to the third overload must be an integer (not an Exception), otherwise an ArgumentException is thrown.

The following code demonstrates the use of this type when calling the following COM method:

HRESULT UnmanagedMethod(VARIANT v);

Its imported managed signature has a generic System.Object parameter.

C#:

Image

Visual Basic .NET:

Image

C++:

Image

For more information, consult Chapter 3.

The EXCEPINFO Value Type

This is a .NET definition of the unmanaged EXCEPINFO structure used by the ITypeInfo COM interface. In the .NET Framework, this definition is used by .NET definition of UCOMITypeInfo.Invoke.

For more information see the UCOMITypeInfo interface listed later in this section or consult MSDN Online for information about the original EXCEPINFO structure.

The ExporterEventKind Enumeration

This enumeration is used by the ITypeLibExporterNotifySink interface’s ReportEvent method. An implementer of ITypeLibExporterNotifySink has its ReportEvent method called by the CLR repeatedly during the process of type library export. The ExporterEventKind enumeration tells the ReportEvent implementer what type of event has just occurred. This enumeration has the following values:

ERROR_REFTOINVALIDASSEMBLY. This value is the one that represents a fatal error. This event is never reported in version 1.0 of the .NET Framework.

NOTIF_CONVERTWARNING. This value represents a warning during the export process. A common example of such a warning is the exposure of COM-invisible value types in a public signature. Warning notifications are fairly common, but ideally there would be none during export.

NOTIF_TYPECONVERTED. This value is simply a notification of normal events. Every time a type is exported, this event occurs.

For more information, consult Chapter 22 and the ITypeLibExporterNotifySink interface listed later in this section.

The ExtensibleClassFactory Class

The ExtensibleClassFactory class has a single static method (Shared in VB .NET) that enables .NET classes that derive from a COM class to customize their creation process. This static method is called RegisterObjectCreationCallback, and has a delegate parameter to enable such classes to plug in any implementation into the creation process.

Normally when a Runtime-Callable Wrapper (RCW) is created, the wrapper calls CoCreate-Instance if the COM object being wrapped needs to be instantiated. However, by registering an appropriate delegate, the RCW will invoke this custom method instead of calling CoCreateInstance.

Therefore, calling ExtensibleClassFactory.RegisterObjectCreationCallback is the way to write a customized class factory for .NET objects extending COM objects. (From COM’s perspective, the .NET subclass is aggregating the COM class.) The object can be instantiated however you like—as a singleton object, with a COM moniker, and so on. There are two important guidelines for calling this method:

RegisterObjectCreationCallback must be called inside a class’s class constructor, also known as a static initializer. This is a constructor marked static in C# and C++ or Shared in VB .NET that gets executed the first time an object of the class type is loaded.

RegisterObjectCreationCallback may only be called once per class type.

RegisterObjectCreationCallback’s parameter is a delegate type defined in System.Runtime.InteropServices named ObjectCreationDelegate. The delegate signature has a System.IntPtr parameter called aggregator and returns a System.IntPtr type. The aggregator parameter is never null and represents the pUnkOuter parameter that would be passed to CoCreateInstance. The returned IntPtr should be a pointer to an IUnknown interface, just like what CoCreateInstance would return.

The following code demonstrates the use of ExtensibleClassFactory.RegisterObjectCreationCallback to plug in a CustomCreateInstance method that replaces CoCreateInstance.

C#:

public class MyDerivedClass : MyCoClass
{
  // Class constructor
  public static MyDerivedClass()
  {
    MyDerivedClass c = new MyDerivedClass();
    ExtensibleClassFactory.RegisterObjectCreationCallback(
      new ObjectCreationDelegate(c.CustomCreateInstance));
  }

  public IntPtr CustomCreateInstance(IntPtr aggregator)
  {
    ...
  }
}

Visual Basic .NET:

Public Class MyDerivedClass
  Inherits MyCoClass

  ' Class constructor
  Public Shared Sub New()
    Dim c As MyDerivedClass = New MyDerivedClass()
    ExtensibleClassFactory.RegisterObjectCreationCallback( _
      New ObjectCreationDelegate(AddressOf c.CustomCreateInstance))
  End Sub

  Public IntPtr CustomCreateInstance(aggregator As IntPtr)
    ...
  End Sub
End Class

C++:

public __gc class MyDerivedClass : public MyCoClass
{
  // Class constructor
  public: static MyDerivedClass()
  {
    MyDerivedClass* c = new MyDerivedClass();
    ExtensibleClassFactory::RegisterObjectCreationCallback(
      new ObjectCreationDelegate(c, &MyDerivedClass::CustomCreateInstance));
  }

  public: IntPtr CustomCreateInstance(IntPtr aggregator)
  {
    ...
  }
};

For more information, consult Chapter 6.

The ExternalException Exception

This exception type represents an error that occurred external to the CLR. It defines a public ErrorCode property that contains an integer identifying the external error. ExternalException is the base class for both COMException and SEHException.

When inherited by COMException, the ErrorCode property represents an HRESULT value. On the other hand, when inherited by SEHException, the ErrorCode property represents a Structured Exception Handling (SEH) error code.

ExternalException derives from System.SystemException, and should never be thrown by an application. In fact, the CLR never throws it directly, but rather uses the derived COMException or SEHException types. Like COMException, its default ErrorCode value is the E_FAIL HRESULT (0x80004005).

For more information see the COMException exception and SEHException exception listed in this section.

The FieldOffsetAttribute Pseudo-Custom Attribute

This pseudo-custom attribute must be used in conjunction with the StructLayoutAttribute pseudo-custom attribute and its LayoutKind.Explicit setting to specify custom memory offsets for a value type’s fields. When using this attribute, all fields in the struct must be marked.

The attribute’s constructor takes an integer parameter that specifies the offset of a field in bytes. A union can be defined by setting every field’s offset to zero.

The following code demonstrates the use of FieldOffsetAttribute to define a union:

C#:

[StructLayout(LayoutKind.Explicit)]
struct BINDPTR
{
  [FieldOffset(0)] public IntPtr lpfuncdesc;
  [FieldOffset(0)] public IntPtr lpvardesc;
  [FieldOffset(0)] public IntPtr lptcomp;
}

Visual Basic .NET:

<StructLayout(LayoutKind.Explicit)> _
Structure BINDPTR
  <FieldOffset(0)> Public lpfuncdesc As IntPtr
  <FieldOffset(0)> Public lpvardesc As IntPtr
  <FieldOffset(0)> Public lptcomp As IntPtr
End Structure

C++:

[StructLayout(LayoutKind::Explicit)]
public __value struct BINDPTR
{
  [FieldOffset(0)] IntPtr lpfuncdesc;
  [FieldOffset(0)] IntPtr lpvardesc;
  [FieldOffset(0)] IntPtr lptcomp;
};

For more information, consult Chapter 19, “Deeper Into PInvoke and Useful Examples.”

The FILETIME Value Type

This is a .NET definition of the unmanaged FILETIME structure used by COM interfaces such as IMoniker and IRunningObjectTable. In the .NET Framework, this definition is used by the UCOMIMoniker and UCOMIRunningObjectTable interfaces and the STATSTG value type.

For more information, consult MSDN Online for information about the original FILETIME structure.

The FUNCDESC Value Type

This is a .NET definition of the unmanaged FUNCDESC structure used by the ITypeInfo COM interface. In the .NET Framework, this definition is not directly used by UCOMITypeInfo, because the methods that would use the type (GetFuncDesc and ReleaseFuncDesc) are defined to use the System.IntPtr type instead. However, when using these methods of UCOMITypeInfo, you’ll want to use the FUNCDESC type in conjunction with Marshal.PtrToStructure or Marshal.StructureToPtr to convert it to and from System.IntPtr.

For more information, consult MSDN Online for information about the original FUNCDESC structure.

The FUNCFLAGS Enumeration

This is a .NET definition of the unmanaged FUNCFLAGS enumeration used by the FUNCFLAGS COM structure. In the .NET Framework, this definition is not directly used by the .NET FUNCDESC structure because its underlying type is used in its place. This enumeration defines the same values as the TypeLibFuncFlags enumeration, but with slightly different names (the original names). The one difference is that FUNCFLAGS is a 16-bit enumeration, whereas TypeLibFuncFlags has a 32-bit underlying type.

For more information, consult MSDN Online for information about the original FUNCFLAGS enumeration.

The FUNCKIND Enumeration

This is a .NET definition of the unmanaged FUNCKIND enumeration used by the FUNCDESC COM structure. In the .NET Framework, this definition is used by the .NET FUNCDESC definition.

For more information, consult MSDN Online for information about the original FUNCKIND union.

The GCHandle Value Type

The GCHandle value type, which stands for Garbage Collector Handle, is sometimes needed when exposing .NET objects to unmanaged code. Its most important member is the static Alloc method, which enables you to create a handle corresponding to any .NET object. This handle can be one of four types, described by the GCHandleType enumeration.

The two types of handles useful when interacting with unmanaged code are normal and pinned. A normal handle prevents a .NET object from being garbage collected even when an unmanaged object is the only entity holding a reference to the object. A pinned handle enables you to obtain the memory address of the .NET object, preventing the garbage collector from moving the object in memory.

Unmanaged code permission (SecurityPermission with SecurityPermissionFlag.UnmanagedCode set) is required to be able to use members of the GCHandle type (except for the IsAllocated property).

The following code demonstrates the use of some of GCHandle’s members to pass a pointer to a .NET object directly to an unmanaged method. This is almost always how GCHandle is used when it’s needed for unmanaged code interaction:

C#:

GCHandle handle = new GCHandle();
try
{
  // Allocate a pinned handle for the myManagedObject instance
  handle = GCHandle.Alloc(myManagedObject, GCHandleType.Pinned);

  // Obtain the memory address value
  IntPtr rawPointer = handle.AddrOfPinnedObject();

  // Call the unmanaged method expecting a pointer to myManagedObject
  obj.UnmanagedMethod(rawPointer);
}
finally
{
  // Free the handle since it's causing myManagedObject to remain pinned
  if (handle.IsAllocated) handle.Free();
}

Visual Basic .NET:

Dim handle As GCHandle
Try
  ' Allocate a pinned handle for the myManagedObject instance
  handle = GCHandle.Alloc(myManagedObject, GCHandleType.Pinned)

  ' Obtain the memory address value
  Dim rawPointer As IntPtr = handle.AddrOfPinnedObject()

  ' Call the unmanaged method expecting a pointer to myManagedObject
  obj.UnmanagedMethod(rawPointer)
Finally
  ' Free the handle since it's causing myManagedObject to remain pinned
  If (handle.IsAllocated) Then handle.Free()
End Try

C++:

GCHandle handle;
try
{
  // Allocate a pinned handle for the myManagedObject instance
  handle = GCHandle::Alloc(myManagedObject, GCHandleType::Pinned);

  // Obtain the memory address value
  IntPtr rawPointer = handle.AddrOfPinnedObject();

  // Call the unmanaged method expecting a pointer to myManagedObject
  obj->UnmanagedMethod(rawPointer);
}
__finally
{
  // Free the handle since it's causing myManagedObject to remain pinned
  if (handle.IsAllocated) handle.Free();
}

More details about Alloc, Free, and AddrOfPinnedObject conversion appear in the following sections.

For more information about the GCHandle value type, consult Chapter 6 and the GCHandleType enumeration listed next.

The Alloc Method

This static method has two signatures—one that takes a .NET object for which we want a handle, and one that takes a .NET object plus a GCHandleType enumeration value that specifies what kind of handle we want to obtain. Both methods return a GCHandle instance. The first method always returns a normal handle (GCHandleType.Normal). A null object (Nothing in VB .NET) can even be passed to Alloc, because the handle’s corresponding object can be changed at any time using GCHandle’s Target property.

A pinned handle is required when passing an address of a .NET object out to unmanaged code using the AddrOfPinnedObject method. The GCHandle instance returned must be freed by calling its Free method as soon as it’s no longer needed.

The Free Method

This instance method must be called on an allocated GCHandle instance to release the handle as soon as it’s no longer needed. Forgetting to free a normal handle is bad because it prevents the corresponding object (also known as the handle’s referent) from being garbage collected. Forgetting to free a pinned handle is bad for the same reason, plus it keeps the corresponding object in a fixed memory location, which can cause the .NET garbage collector to perform poorly.

This method throws an InvalidOperationException if the handle has not been allocated or if it has already been freed. Therefore, callers must be sure to never call Free more than once for a given handle.

The AddrOfPinnedObject Method

This instance method takes no parameters and returns an IntPtr value that represents the memory address of a pinned object. AddrOfPinnedObject throws an InvalidOperation-Exception if the handle instance is not a pinned handle.

The IsAllocated Property

This read-only boolean property returns true if the GCHandle instance is currently allocated (its Alloc method has been called and its Free method has not been called), and false otherwise. Because calling Free on an unallocated handle is an error, this property is useful for checking the handle’s state before attempting to call Free.

The Target Property

This read-write Object property enables you to get or set the handle’s referent (the .NET object to which the handle applies). Use of the property’s set accessor enables you to reuse a single GCHandle instance for multiple .NET objects.

The GCHandle/IntPtr Explicit Conversion Operator

Every GCHandle instance is internally represented as a size-agnostic integer (System.IntPtr). Therefore, GCHandle defines two explicit conversion operators—one that converts a GCHandle instance to an IntPtr value, and vice versa.

Caution

Converting a GCHandle instance to an IntPtr value using the explicit conversion operator is not the same thing as calling GCHandle.AddrOfPinnedObject. The handle’s IntPtr representation is not the same value as the corresponding object’s memory location.

In C#, an explicit conversion operator is invoked when performing an explicit cast. Because Visual Basic .NET doesn’t support operator overloads, the “raw” static GCHandle.op_Explicit method must be called instead. The following code demonstrates the use of these explicit conversion operators:

C#:

GCHandle handle = GCHandle.Alloc(myManagedObject);

// Convert the GCHandle instance to an IntPtr
IntPtr internalValue = (IntPtr)handle;

// Convert the IntPtr to a GCHandle instance
GCHandle handle2 = (GCHandle)internalValue;

// Don't free handle2 because it's really the same handle
handle.Free();

Visual Basic .NET:

Dim handle As GCHandle = GCHandle.Alloc(myManagedObject)

' Convert the GCHandle instance to an IntPtr
Dim internalValue As IntPtr = GCHandle.op_Explicit(handle)

' Convert the IntPtr to a GCHandle instance
Dim handle2 As GCHandle = GCHandle.op_Explicit(internalValue)

' Don't free handle2 because it's really the same handle
handle.Free()

It is fine to have multiple GCHandles with the same value as long as you only call Free once.

The GCHandleType Enumeration

This enumeration is used by GCHandle.Alloc to describe the type of garbage collector handle to allocate for a .NET object. This enumeration has the following values:

Normal. A simple handle that uniquely identifies an object and prevents it from being garbage collected. Such a handle is often called an opaque handle. This is useful for keeping a .NET object alive when unmanaged code (which is undetectable from the .NET garbage collector) is the only entity holding a reference to the object.

Pinned.A Normal handle with the addition that you’re allowed to obtain the memory address of the object. Therefore, the garbage collector pins the corresponding object in place so its location doesn’t change. When calling unmanaged code, this type of handle is needed when directly exposing pointers to .NET objects that must keep a fixed memory location for the duration of the call.

Caution.

Using GCHandleType.Pinned interferes with the normal operation of the garbage collector, potentially reducing its efficiency. Therefore, it should be used as infrequently as possible, and any pinned handles should be freed (using GCHandle.Free) as quickly after allocation as possible.

Weak. This type of handle does not prevent the object’s garbage collection. When the object is ready for collection, the handle value is simply set to zero. Because this occurs before finalization, it’s possible that a finalizer could resurrect the object. If this happens, however, the handle’s value is still zero.

WeakTrackResurrection. A Weak handle with the addition that a resurrected object does not have a handle value of zero. The CLR waits until the object is actually finalized to set it to zero.

The GuidAttribute Custom Attribute

This custom attribute can be used to explicitly choose a GUID for a class, interface, and so on, instead of letting the CLR choose a GUID for you. This can be useful to keep GUIDs stable during the development process of ever-evolving types. The custom attribute has a constructor that takes a single string parameter that must contain a GUID in the following representation (with dashes but without curly braces—case doesn’t matter):

a08d8c8a-e1a0-40de-b2d9-6e78cd288a5b

The custom attribute can be placed on the following targets, taking on the following meanings:

• Assembly—The LIBID of the exported type library.

• Class—The coclass’s CLSID.

• Interface—The interface’s IID.

• Delegate—The CLSID of the delegate class.

• Struct—The GUID for the exported struct.

• Enum—The GUID for the exported enum.

The type library importer marks types with GuidAttribute to preserve the GUIDs from the original type definitions.

For more information, consult Chapter 12.

The HandleRef Value Type

The HandleRef value type is a special type recognized by the Interop Marshaler that provides a convenient way to prevent a .NET object that wraps an unmanaged resource from being garbage collected while the unmanaged member is being used from unmanaged code. This is needed when the .NET object destroys its unmanaged objects in its finalizer (which is necessary to avoid memory leaks), and only applies when the unmanaged item is not reference counted (such as a handle).

Because the .NET garbage collector cannot “manage” unmanaged code, you can easily run into subtle object lifetime issues when passing a managed object that wraps an unmanaged resource to unmanaged code. The following C# code demonstrates this scenario:

public class ManagedObjectThatWrapsUnmanagedObject
{
  Object someComObject;
  IntPtr someUnmanagedResource;

  public static void Main()
  {
    ...

    ManagedObjectThatWrapsUnmanagedObject managedObject =
      new ManagedObjectThatWrapsUnmanagedObject();

    managedObject.CallUnmanagedCode();

    ... more code that does not use managedObject
  }

  public void CallUnmanagedCode()
  {
    someComObject.UnmanagedMethod(someUnmanagedResource);
  }
}

The garbage collector might run at the exact moment that the code inside CallUnmanagedCode is being executed. Because there is no code on the stack that uses the managedObject instance, it could be collected. Assuming that the finalizer for ManagedObjectThatWrapsUnmanagedObject destroys someUnmanagedResource, the implementation of UnmanagedMethod would fail.

To prevent this from happening, the System.GC.KeepAlive method could be used after the call to unmanaged code to prevent the object from being collected prematurely. For example:

public void CallUnmanagedCode()
{
  someComObject.UnmanagedMethod(someUnmanagedResource);
  GC.KeepAlive(this);
}

In fact, any dummy method after the call that uses the object could keep it alive throughout the UnmanagedMethod call as long as it’s not optimized away by a compiler or the CLR just-in-time compiler. (For example, an empty virtual method would do the trick.)

Tip

Another solution for keeping an unmanaged resource alive would be to use the GCHandle type to allocate a normal (not pinned!) handle for its containing object. Using HandleRef, however, yields better performance.

The HandleRef value type can solve this problem as follows. It has a constructor that takes two parameters, a System.Object representing the wrapper object that must be kept alive, and a System.IntPtr that represents the unmanaged handle. When a HandleRef is passed to unmanaged code, the Interop Marshaler extracts the handle (passed as the second parameter to the constructor) and passes only that to unmanaged code. At the same time, however, the marshaler guarantees that the object passed as the first parameter to HandleRef’s constructor is not collected for the duration of the call. This can be used as follows:

public void CallUnmanagedCode()
{
  someComObject.UnmanagedMethod(new HandleRef(this, someUnmanagedResource));
}

When defining a PInvoke signature that expects such a pointer or platform-sized integer representing some sort of handle, you should make the argument type a HandleRef rather than an IntPtr. This way, callers will automatically avoid premature garbage collection without understanding this subtle issue. If you can’t control the signature, you’re out of luck. The parameter must be declared as HandleRef for the support to work; you can’t pass it as an Object parameter and have it work the desired way.

Rather than instantiating a new HandleRef on each call, the object that exposes a method such as the UnmanagedMethod example could expose a single HandleRef instance that can be used by clients. However, because HandleRef is a value type, instantiating one every time does not significantly hurt performance.

Tip

Had the .NET object implemented IDisposable for disposing its unmanaged resources, and had the client remembered to call Dispose when finished with ManagedObjectThatWrapsUnmanagedObject, this premature collection problem would not occur. That’s because the presence of a call to Dispose would keep the object alive up until Dispose is called!

HandleRef can be used on COM methods, too, but the signatures would need to be customized to use HandleRef using either the techniques in Chapter 7 or Chapter 21. You never need to use HandleRef for passing COM objects to unmanaged code, because they are reference counted.

The ICustomAdapter Interface

The ICustomAdapter interface should be implemented by any adapter object returned by a custom marshaler to provide its client with a means of obtaining the original object that’s being hidden by the custom marshaler.

The interface has a single method—GetUnderlyingObject—that takes no parameters but returns a System.Object that represents the object originally passed into the custom marshaler. Because a client of a custom marshaled object receives a new adapter object that maps specific functionality to the original object, the original object may provide additional functionality not reachable from the adapter object. If the adapter object implements ICustomAdapter, clients can call GetUnderlyingObject to get the original object and have access to whatever additional functionality it may provide.

For more information, consult Chapter 20.

The ICustomFactory Interface

The ICustomFactory interface, although defined in System.Runtime.InteropServices, is used with .NET Remoting rather than COM Interoperability. By implementing the ICustomFactory interface, a .NET proxy class that derives from System.MarshalByRefObject and marked with ProxyAttribute (defined in the System.Runtime.Remoting.Proxies namespace) can plug in custom activation code.

The interface has a single CreateInstance method that has a System.Type parameter and returns a System.MarshalByRefObject instance. The CLR calls CreateInstance when a new instance of the Type parameter is required. The CreateInstance implementer can then return the MarshalByRefObject instance via whatever means desired. Therefore, this is a means of writing a customized class factory, even when no unmanaged code is involved.

For more information, consult the .NET Framework SDK documentation for details about the .NET Remoting infrastructure.

The ICustomMarshaler Interface

The ICustomMarshaler interface is implemented by custom marshaler objects that customize interaction between managed and unmanaged code. This interface has five methods:

MarshalManagedToNative. This method does the transformation of a .NET object to an unmanaged object. It has a single System.Object parameter representing the original object and returns a System.IntPtr representing a pointer to the new unmanaged object.

MarshalNativeToManaged. This method does the transformation of an unmanaged object to a managed object. It has a single System.IntPtr parameter representing the original object and returns a System.Object that is the new managed object.

CleanUpNativeData. This method is responsible for freeing any unmanaged memory allocated in MarshalManagedToNative or MarshalNativeToManaged, and decrementing the reference count of any COM interfaces whose reference counts were incremented (via direct calls to QueryInterface or AddRef). It has a single System.IntPtr parameter representing a pointer to the type on which the cleanup action needs to be done.

CleanUpManagedData. This method is responsible for performing any cleanup of managed objects used in MarshalManagedToNative or MarshalNativeToManaged. It has a single System.Object parameter representing the object whose state should be cleaned up. This method usually has an empty implementation when custom marshaling by reference. When custom marshaling by value, calling IDisposable.Dispose is appropriate if the .NET object implements IDisposable.

GetNativeDataSize. This method is not used by the CLR in the first version of the .NET Framework, because custom marshaling of value types is not supported. Therefore, this method’s implementation should always return -1.

Besides implementing ICustomMarshaler, a custom marshaler must implement a static (Shared in VB .NET) method named GetInstance that has a string parameter and returns an ICustomMarshaler instance. This is called when the CLR requires an instance of the custom marshaler. The string parameter is the “cookie” value that the user of the custom marshaler specifies with MarshalAsAttribute.

The objects handed out by a custom marshaler should implement the ICustomAdapter interface to enable clients to obtain the original object.

For more information, see Chapter 20.

The IDispatchImplAttribute Custom Attribute

This custom attribute controls which of the two built-in IDispatch implementations the CLR provides on behalf of a .NET object. It can be marked on classes or assemblies. When marked on an assembly, it applies to all classes within the assembly unless individual classes override the setting with their own IDispatchImplAttribute.

The attribute has two constructors—one that takes a 16-bit integer and one that takes an IDispatchImplType enumeration. The latter constructor should always be used.

When a .NET object implements System.Reflection.IReflect, its custom implementation is always exposed when a COM client queries for IDispatch or IDispatchEx, but IDispatchImplAttribute still affects the implementation used when a client calls the IDispatch methods of any dual interfaces.

For more information, consult Chapter 14, “Implementing COM Interfaces for Binary Compatibility,” or the IDispatchImplType enumeration listed next.

The IDispatchImplType Enumeration

The IDispatchImplType enumeration is used by the IDispatchImplAttribute custom attribute to choose what kind of IDispatch implementation is exposed to COM for a .NET class. It has three values:

CompatibleImpl. Use the standard OLE Automation IDispatch implementation. This means that the CLR forwards calls to ITypeInfo.Invoke with type information from an exported type library. If no exported type library can be found, the CLR generates one in memory. For this setting to work, the interface exposed as the default interface must be dual.

InternalImpl. This is the default implementation, based on reflection technology.

SystemDefinedImpl. This setting is the same as InternalImpl, and should not be used.

For more information, consult Chapter 14 or the IDispatchImplAttribute custom attribute listed previously.

The IDLDESC Value Type

This is a .NET definition of the unmanaged IDLDESC structure used by the DESCUNION and TYPEATTR COM types. In the .NET Framework, this definition is used by .NET definitions of DESCUNION and TYPEATTR, which can be used with UCOMITypeInfo.

For more information, consult MSDN Online for information about the original IDLDESC structure.

The IDLFLAG Enumeration

This is a .NET definition of the unmanaged IDLFLAG enumeration used by the IDLDESC COM structure. In the .NET Framework, this definition is used by the .NET definition of IDLDESC just listed.

For more information, consult MSDN Online for information about the original IDLFLAG enumeration.

The IMPLTYPEFLAGS Enumeration

This is a .NET definition of the unmanaged IMPLTYPEFLAGS enumeration used by the ITypeInfo COM interface’s GetImplTypeFlags method. In the .NET Framework, this definition is not used directly by .NET definition of UCOMITypeInfo.GetImplTypeFlags, but can be handy to use with the method. UCOMITypeInfo.GetImplTypeFlags has an out parameter that’s an integer type, but this integer represents a value of IMPLTYPEFLAGS. Therefore, to interpret the value of this integer, simply convert it to an IMPLTYPEFLAGS enumeration first.

For more information, consult MSDN Online for information about the original IMPLTYPEFLAGS enumeration.

The ImportedFromTypeLibAttribute Custom Attribute

The type library importer places this custom attribute on an assembly to indicate that it was imported from a type library. This custom attribute is what identifies an assembly as an Interop Assembly. For example, the type library exporter checks for this custom attribute and doesn’t export a type library for the assembly if it exists.

Unless attempting to manually create an assembly just like what the type library importer produces, you should not mark an assembly with this custom attribute.

For more information, consult Chapter 4.

The ImporterEventKind Enumeration

This enumeration is used by the ITypeLibImporterNotifySink interface’s ReportEvent method. An implementer of ITypeLibImporterNotifySink has its ReportEvent method called by the CLR repeatedly during the process of type library import. The ImporterEventKind enumeration tells the ReportEvent implementer what type of event has just occurred. This enumeration has the following values:

ERROR_REFTOINVALIDTYPELIB. This value is the one that represents a fatal error. In version 1.0 of the .NET Framework, this event is never reported.

NOTIF_CONVERTWARNING. This value represents a warning during the import process. A common example of such a warning is when a pointer to a pointer to a structure is imported as a System.IntPtr type.

NOTIF_TYPECONVERTED. This value is simply a notification of normal events. Every time a type is imported, this event occurs.

For more information, consult Chapter 22 and the ITypeLibImporterNotifySink interface listed later in this section.

The InAttribute Pseudo-Custom Attribute

The InAttribute pseudo-custom attribute can be marked on a member’s parameters to affect their data flow when the member is implemented in managed code and called from unmanaged code, or vice versa. The In marking means that data flows from the caller to the callee.

In pure managed code, value types have “in” behavior and reference types have behavior like “in/out” because references are passed such that callees can change the object’s state (except for the immutable System.String, which has “in” behavior). All by-reference types have “in/out” behavior, and C#-style out parameters have “out” behavior. The Interop Marshaler, however, treats all reference type parameters except StringBuilder with “in” behavior, which is often a source of confusion. When types are blittable, they appear to have “in/out” behavior, because the Interop Marshaler pins them, but this behavior is no longer seen when COM marshaling is involved across contexts.

Marking a C#-style out parameter with InAttribute is disallowed, but it can by marked on a by-reference behavior to suppress the “out” part of marshaling. Marking a formatted reference type parameter with both InAttribute and OutAttribute is often recommended to preserve the “in/out” semantics expected for reference types. This is important not only when crossing the managed/unmanaged boundary, but for any COM clients that may use an exported type library’s definitions across context boundaries. If you expect “in/out” behavior, you better mark the parameter as such!

The type library importer marks any parameters with InAttribute when the corresponding parameter has an IDL [in] marking in the input type library. In Visual Basic .NET, square brackets must be used when using the short form of InAttribute (i.e. <[In]>) because In is a keyword.

For more information, see Chapter 12.

The InterfaceTypeAttribute Custom Attribute

This custom attribute can be marked on an interface definition to describe what kind of interface it looks like to COM—dual, IUnknown-only, or disp-only. If no InterfaceTypeAttribute exists, the interface is treated as a dual interface.

The attribute has two constructors—one that takes a 16-bit integer and one that takes a ComInterfaceType enumeration. The latter constructor should always be used. The type library importer always marks non-dual imported interfaces with the custom attribute and the appropriate value.

For more information, see Chapters 4, 12, and 21 and the ComInterfaceType enumeration listed earlier.

The InvalidComObjectException Exception

InvalidComObjectException is thrown by the CLR when one of the following two situations occur:

• A COM object becomes separated from its RCW.

• Someone attempts to create an instance of a COM object using a System.__ComObject type that hasn’t been obtained from Type.GetTypeFromProgID or Type.GetTypeFromCLSID. Such an object doesn’t have a class factory associated with it, so instantiation cannot succeed.

The first situation can occur in the graphical application developed in Chapter 22, if you change the background thread to be an STA thread then click on nodes of exported type libraries. In this case, the STA thread on which a COM object was created terminates and leaves the RCW detached.

The following illegal code demonstrates the second situation in C#:

// The type returned must be System.__ComObject, meaning that the
// CLR could not locate a metadata definition for the class type.
Object o = comObject.ReturnSomeInterface();

// Attempting to create an instance using this __ComObject type
// causes the InvalidComObjectException to be thrown.
Object o2 = Activator.CreateInstance(o.GetType());

InvalidComObjectException derives from System.SystemException, and should never be thrown by an application. Its protected HResult property (which can be exposed to COM when the exception is thrown) has the value COR_E_INVALIDCOMOBJECT, defined by the .NET Framework SDK as 0x80131527.

The InvalidOleVariantTypeException Exception

The Interop Marshaler throws an InvalidOleVariantTypeException when encountering a VARIANT instance whose type (specified in its vt field) is not valid. The valid types for a VARIANT are a subset of the values of the VARENUM enumeration, such as VT_I2, VT_BOOL, and so on. If a COM client attempts to pass a VARIANT with an illegal vt value such as VT_VOID or VT_BLOB (or even a number not defined by the VARENUM enumeration), the exception is thrown.

This exception only applies when marshaling a COM VARIANT to a .NET Object. In the reverse direction, it’s possible that marshaling a .NET Object to a COM VARIANT causes a similar error if the object implements System.IConvertible. Because the marshaler figures out what type to make the exposed VARIANT by calling IConvertible.GetTypeCode, an object that returns a TypeCode value that doesn’t correspond to a valid value from the VARENUM subset causes an exception. The exception for this direction, however, is System.NotSupportedException rather than InvalidOleVariantTypeException.

InvalidOleVariantTypeException derives from System.SystemException, and should never be thrown by an application. Its protected HResult property (which can be exposed to COM when the exception is thrown) has the value COR_E_INVALIDOLEVARIANTTYPE, defined by the .NET Framework SDK as 0x80131531.

The INVOKEKIND Enumeration

This is a .NET definition of the unmanaged INVOKEKIND enumeration used by the ITypeInfo COM interface. In the .NET Framework, this definition is used by UCOMITypeInfo.AddressOfMember, UCOMITypeInfo.GetDllEntry, and also as a field of the FUNCDESC value type.

For more information, consult MSDN Online for information about the original INVOKEKIND enumeration.

The IRegistrationServices Interface

This interface, implemented by the RegistrationServices class, defines methods used for registering and unregistering assemblies for use by COM. It also contains several methods that can be helpful in discovering the entries that would be placed in the registry, so you could create a .reg registry file, for example. The interface defines the following methods:

GetManagedCategoryGuid

GetProgIdForType

GetRegistrableTypesInAssembly

RegisterAssembly

RegisterTypeForComClients

TypeRepresentsComType

TypeRequiresRegistration

UnregisterAssembly

For more information, see the RegistrationServices class listed later in this section, as well as Chapter 22.

The ITypeLibConverter Interface

This interface, implemented by the TypeLibConverter class, defines methods used for importing and exporting type libraries. The interface defines the following methods:

ConvertAssemblyToTypeLib

ConvertTypeLibToAssembly (two overloads)

GetPrimaryInteropAssembly

For more information, see the TypeLibConverter class listed later in this section, as well as Chapter 22.

The ITypeLibExporterNameProvider Interface

An object can implement ITypeLibExporterNameProvider to choose the casing of identifiers in an exported type library. Type libraries are case-insensitive, and the first case of any identifier emitted into an exported type library “wins.” The result of this is that a parameter name such as name can cause a member name such as Name to appear as name in the exported type library, and vice versa. Therefore, adding any exportable types or members to an assembly can change (from COM’s perspective only) untouched APIs emitted later in metadata if they use the same identifiers with a different case.

Furthermore, the order in which types are emitted to metadata by a .NET compiler can change based on subtle changes to source code or even the order in which input source files are processed, potentially causing the case of exported names to oscillate on a daily basis! In such a situation, recompiling a case-sensitive COM client (such as one written in unmanaged C++) can cause compilation errors, requiring you to update the case of identifiers on a regular basis.

Implementing the ITypeLibExporterNameProvider can solve this problem. The interface has a single method—GetNames—that returns an array of strings. Each string is treated as an identifier with the “correct” case. If an exported type library contains one of the identifiers in the array, it will always appear in the casing returned by GetNames. (If the returned array contains the same identifier in different cases, the first one is used.)

Of course, an object implementing ITypeLibExporterNameProvider needs to be recognized and used by the type library exporter to have any effect. An object implementing the interface can be passed to ITypeLibConverter.ConvertAssemblyToTypeLib (as the notifySink parameter), which is implemented by the TypeLibConverter class. Any object that implements ITypeLibExporterNameProvider must also implement ITypeLibExporterNotifySink because that’s the type of the notifySink parameter that the ConvertAssemblyToTypeLib implementation requires.

It is the responsibility of the ITypeLibExporterNameProvider to determine an array of identifiers that can solve any potential case problems. For example, you could use reflection on the input assembly to find identifiers that appear in multiple cases and would be exported to a type library. For each such identifier, a “correct” version must be chosen.

The TLBEXP.EXE SDK tool uses an ITypeLibExporterNameProvider implementation if the user specifies the /names option with a filename of a names file. This file can contain a list of names in the correct case (one per line) that populates the string array returned by GetNames.

For more information, see Chapter 22, Appendix B, and the ITypeLibExporterNotifySink interface listed next.

The ITypeLibExporterNotifySink Interface

An object implementing ITypeLibExporterNotifySink must be passed to TypeLibConverter.ConvertAssemblyToTypeLib as its notifySink parameter to handle callbacks during the type library export process. This interface has two methods:

ReportEvent. This method has three parameters—an ExporterEventKind enumeration value, an integer, and a string. When called by TypeLibConverter, the enumeration value describes the type of event, the integer represents an HRESULT value that describes the event more specifically, and the string contains a method describing the event. The “event” is not a .NET event, but a simple callback.

ResolveRef. This method has an Assembly parameter and returns an Object type. TypeLibConverter passes an in-memory dependent assembly, and it’s the job of the ResolveRef implementation to return an object implementing UCOMITypeLib that represents a type library that should be used for this dependent assembly. A typical ResolveRef implementation attempts to use an existing type library, and exports a new one from the input assembly if one can’t be found.

For more information, see Chapter 22 the ExporterEventKind enumeration listed previously, and the TypeLibConverter class listed later in this section.

The ITypeLibImporterNotifySink Interface

An object implementing ITypeLibImporterNotifySink must be passed to TypeLibConverter.ConvertTypeLibToAssembly as its notifySink parameter to handle callbacks during the type library import process. This interface has two methods:

ReportEvent. This method has three parameters—an ImporterEventKind enumeration value, an integer, and a string. When called by TypeLibConverter, the enumeration value describes the type of event, the integer represents an HRESULT value that describes the event more specifically, and the string contains a method describing the event. The “event” is not a .NET event, but a simple callback.

ResolveRef. This method has an Object parameter and returns an Assembly type. TypeLibConverter passes an in-memory dependent type library (an object implementing UCOMITypeLib), and it’s the job of the ResolveRef implementation to return an Assembly instance that should be used for this dependent type library. A typical ResolveRef implementation attempts to use an existing assembly, and imports a new one from the input type library if one can’t be found.

For more information, see Chapter 22 the ImporterEventKind enumeration listed previously, and the TypeLibConverter class listed later in this section.

The LayoutKind Enumeration

This enumeration is used by the StructLayoutAttribute pseudo-custom attribute to control the layout of a type when exposed to COM as a structure. It has three values:

Auto. The CLR decides how to arrange a type’s fields. This is the best choice for purely .NET applications, because the CLR may be able to make optimizations based on how the type is used. However, this setting cannot be used on types exposed to unmanaged code as structures because memory layout must be known and stable.

Explicit. Every field is marked with an explicit location in memory, as a byte offset from the beginning of the type. This setting must be used in conjunction with the FieldOffsetAttribute marked on every field.

Sequential. The type’s fields are arranged in memory sequentially, in the order they appear in the value type’s definition. The fields are not necessarily contiguous, because value types may have packing behavior (specified with StructLayoutAttribute’s Pack property) that aligns fields on certain byte boundaries.

Although Auto is the default layout chosen by the CLR, the C#, VB .NET, and C++ compilers all specify Sequential on value types by default, due to the non-intuitive problems that may arise by passing Auto value types to unmanaged code.

.NET clients attempting to manipulate a value type in “unsafe ways” (such as C# unsafe code or mixed mode C++) cannot assume anything about the type’s layout if it contains non-blittable fields. In such cases, the layout is only applied to the marshaled structure exposed to unmanaged code via the Interop Marshaler.

For more information, consult Chapter 12, Part VI, and the StructLayoutAttribute pseudo-custom attribute listed later in this section.

The LCIDConversionAttribute Custom Attribute

This custom attribute can be marked on a .NET method (or property accessor) to indicate that the corresponding unmanaged signature has an LCID parameter that’s hidden from the .NET view.

An LCID parameter is a special parameter (marked in a type library with the lcid IDL attribute) that contains a locale identifier (LCID). Such a parameter enables the method implementer to take special action based on the user’s locale. The parameter is marked with lcid so rich clients like Visual Basic 6 can hide it from the user and fill in a value corresponding to the user’s locale obtained from the operating system.

The custom attribute’s constructor has an integer parameter that represents a zero-based offset of which parameter should be the lcid parameter. Note that this offset doesn’t refer to an existing parameter, but one that is inserted into the COM method definition. Here is an example of method definitions using LCIDConversionAttribute and their exported representation:

C#:

[LCIDConversion(0)]
void MultiCulturalMethod1(int a, int b);
[LCIDConversion(2)]
void MultiCulturalMethod2(int a, int b);

Visual Basic .NET:

<LCIDConversion(0)> _
Sub MultiCulturalMethod1(a As Integer, b As Integer)
<LCIDConversion(2)> _
Sub MultiCulturalMethod2(a As Integer, b As Integer)

C++:

[LCIDConversion(0)]
void MultiCulturalMethod1(int a, int b);
[LCIDConversion(2)]
void MultiCulturalMethod2(int a, int b);

Exported IDL signatures:

HRESULT MultiCulturalMethod1([in, lcid] long p1, [in] long a, [in] long b);
HRESULT MultiCulturalMethod2([in] long a, [in] long b, [in, lcid] long p3);

The Interop Marshaler takes care of filling in the appropriate value when a .NET client calls such a method (and vice versa), based on the current thread’s locale information. .NET clients can determine the LCID parameter value passed by a COM client by checking System.Threading.Thread.CurrentThread.CurrentCulture. The raw LCID value can even be obtained by checking CurrentCulture’s LCID property!

The type library importer marks all methods with an LCID parameter with LCIDConversionAttribute in order to hide it from the imported signature.

For more information, consult Chapter 12.

The LIBFLAGS Enumeration

This is a .NET definition of the unmanaged LIBFLAGS enumeration used by the TYPELIBATTR COM structure. In the .NET Framework, this definition is used by the .NET definition of TYPELIBATTR.

For more information, consult MSDN Online for information about the original LIBFLAGS structure.

The Marshal Class

The Marshal class is by far the largest member of the System.Runtime.InteropServices namespace. It’s your one-stop shopping place for language-neutral interoperability functionality. It provides .NET clients with raw and powerful APIs available to unmanaged clients, exposes pieces of the Interop Marshaler’s functionality (useful for high-performance custom marshaling), and much more.

A handful of Marshal’s methods are widely used, such as the familiar BindToMoniker, GetLastWin32Error, ReleaseComObject, SizeOf, and StructureToPtr methods. Most of its members, however, are considered for advanced use only, to work around the areas in which COM Interoperability and PInvoke don’t naturally bridge the managed and unmanaged worlds. Most members are useful for custom marshaling (shown in Chapter 20), or when mixing unmanaged and managed code in C++. Members of the Marshal class even enable the same kind of manipulations you’d do with pointers in CLS-compliant APIs that are usable from any .NET language!

Tip

Just because Visual Basic .NET doesn’t have pointer syntax, don’t be fooled into believing that you can’t do the same kind of high performance pointer manipulations as you could in C++ or C# unsafe code. As described in Chapter 6, using methods of the Marshal class, any .NET language can do the same kind of memory manipulations as in C# unsafe code! In some cases, functionality enabled by the Marshal class is more powerful than what can be done in C# unsafe code (such as converting an integer to an interface pointer)!

The class is enormous, but its members fall into 12 broad categories:

• Data Transformations

• Memory Management

• Direct Reading & Writing

• Structure Inspection

• COM Library Functions

• IUnknown Methods

• Error Handling

• Type Information

COM Interop Utilities

• PInvoke Utilities

• Hosting Utilities

• Advanced Marshaling

To help you navigate through these categories and their members, Figure A.2 displays the entire contents of the Marshal class according to the previous groups. This illustration also appears on this book’s inside back cover.

Figure A.2. The contents of the System.Runtime.InteropServices.Marshal class.

Image

Every member of the Marshal class is static (Shared in VB .NET). Therefore, the class doesn’t have or need a public constructor. Unmanaged code permission (SecurityPermission with SecurityPermissionFlag.UnmanagedCode set) is required to be able to use methods of the Marshal class. However, this is required via a link demand, so it’s important that users of its methods are careful not to expose functionality to untrusted code that could be exploited maliciously.

For the remainder of this section, every member—listed in alphabetical order—is described in detail.

The AddRef Method

All three IUnknown methods—QueryInterface, AddRef, and Release—are exposed through the Marshal class and can be called on any COM object. Marshal.AddRef exposes a COM object’s IUnknown.AddRef method, which increments the object’s reference count. It takes a System.IntPtr parameter that represents the IUnknown pointer for a COM object, and returns the 32-bit integer returned by the COM object’s AddRef implementation. This return value roughly represents the object’s reference count, and should be used only for testing purposes.

To get an IntPtr value that represents an object, you can call GetComInterfaceForObject, GetIDispatchForObject, or GetIUnknownForObject. These methods, plus the Marshal.AddRef method, can even be used on purely .NET objects! In this case, the COM interfaces represent the ones implemented by the .NET object’s COM-Callable Wrapper.

Because the CLR manages a COM object’s reference count for you, it’s almost never necessary to call Marshal.AddRef. In some advanced cases, however, like custom marshaling or working around COM object oddities, it can become necessary. In these cases, you are given an interface pointer as an IntPtr value and want to manipulate its lifetime. As in COM, don’t forget to decrement the object’s reference count (using a method like Marshal.Release) at some point after calling Marshal.AddRef.

The following code demonstrates the use of Marshal.AddRef, using Marshal.GetIUnknownForObject to obtain an IntPtr value representing the COM object’s IUnknown interface pointer:

C#:

IntPtr pUnk = Marshal.GetIUnknownForObject(myComObject);
int refCount = Marshal.AddRef(pUnk);

Visual Basic .NET:

Dim pUnk As IntPtr = Marshal.GetIUnknownForObject(myComObject)
Dim refCount As Integer = Marshal.AddRef(pUnk)

C++:

IntPtr pUnk = Marshal::GetIUnknownForObject(myComObject);
int refCount = Marshal::AddRef(pUnk);

For more information, see Marshal.QueryInterface and Marshal.Release listed later in this section.

The AllocCoTaskMem Method

This method is one of the two memory allocation APIs in the Marshal class (the other being AllocHGlobal). AllocCoTaskMem exposes the similarly-named CoTaskMemAlloc COM API, the COM task memory allocator. It has a single integer parameter that represents the number of bytes to allocate, and returns a System.IntPtr type representing a pointer to the allocated block of memory.

As with using CoTaskMemAlloc in COM, any memory allocated with this method must be freed with CoTaskMemFree. In the .NET Framework, this method is exposed as Marshal.FreeCoTaskMem.

The initial memory content returned is undefined, and the memory allocated might be larger than the requested number of bytes. Whereas CoTaskMemAlloc returns null on failure, Marshal.AllocCoTaskMem throws an OutOfMemoryException on failure.

For more information, see MSDN Online for a description of CoTaskMemAlloc, and the FreeCoTaskMem and ReAllocCoTaskMem methods listed later in this section.

The AllocHGlobal Method

This method is one of the two memory allocation APIs in the Marshal class (the other being AllocCoTaskMem). AllocHGlobal allocates fixed memory from the “global heap.” Win32 does not provide a separate global heap and local heap, so this method exposes the Win32 GlobalAlloc API from KERNEL32.DLL, which does the same thing as the LocalAlloc API from the same DLL.

This method has two overloads. One has a 32-bit integer parameter specifying the number of bytes to allocate, and one has a System.IntPtr parameter specifying the number of bytes to allocate. Both do the same thing, and both return a System.IntPtr value representing a pointer to the allocated block of memory.

As with using LocalAlloc in Win32 programming, any memory allocated with this method must be freed with LocalFree. In the .NET Framework, this method is exposed as Marshal.FreeHGlobal. Marshal.AllocHGlobal calls LocalAlloc with the LMEM_FIXED flag to specify that fixed memory is desired.

The initial memory content returned is undefined, and the memory allocated might be larger than the requested number of bytes. (Any extra amounts may be used, and the Win32 LocalSize method can be called to determine how much was returned.) Whereas LocalAlloc returns null on failure, Marshal.AllocHGlobal throws an OutOfMemoryException on failure.

For more information, see MSDN Online for a description of LocalAlloc, and the FreeHGlobal and ReAllocHGlobal methods listed later in this section.

The BindToMoniker Method

This method enables the location and creation of an object from a COM moniker. It has a single string parameter that contains the moniker name, and returns a System.Object instance created by the moniker.

BindToMoniker first calls the CreateBindCtx COM API to obtain a bind context object, then calls the MkParseDisplayName COM API with the bind context object and the user-supplied moniker name to get an IMoniker instance. Finally, the BindMoniker COM API is called, requesting an IUnknown interface pointer that gets marshaled to the returned System.Object type. BindMoniker locates the COM object and activates it if it isn’t already active. The returned object can be cast to whatever COM interface is desired.

This method provides the same functionality as the GetObject method in Visual Basic 6 and Visual Basic .NET. The VB .NET GetObject method (defined in the global Microsoft.VisualBasic.Interaction module) calls either Marshal.BindToMoniker, Marshal.GetActiveObject, or Activator.CreateInstance(Type.GetTypeFromProgID("...")) depending on the strings passed to it.

If BindToMoniker fails, a COMException is thrown with the HRESULT value returned by the COM API that failed, for example MK_E_SYNTAX (0x800401E4) from MkParseDisplayName or MK_E_NOOBJECT (0x800401E5) from BindMoniker.

The following code demonstrates using this method to take advantage of the SOAP (Simple Object Access Protocol) moniker provided by Windows XP, to seamlessly use an XML Web service from managed code via COM Interoperability:

C#:

object translator = Marshal.BindToMoniker(
  "SOAP:wsdl=http://www.xmethods.net/sd/2001/BabelFishService.wsdl");

Visual Basic .NET:

Dim translator As Object = Marshal.BindToMoniker( _
  "SOAP:wsdl=http://www.xmethods.net/sd/2001/BabelFishService.wsdl")

C++:

Object* translator = Marshal::BindToMoniker(
  S"SOAP:wsdl=http://www.xmethods.net/sd/2001/BabelFishService.wsdl");

The ChangeWrapperHandleStrength Method

This method can change a COM-Callable Wrapper’s (CCW’s) strength from strong to weak and vice versa. This is used for object pooling functionality, but should never need to be called directly by user code.

ChangeWrapperHandleStrength has two parameters—a System.Object instance whose COM-Callable Wrapper is being changed, and a boolean value. This value can be set to true to make the wrapper always weak, or false to make its strength depend on its reference count.

The Copy Method

The Copy method is useful for copying a subset of a one-dimensional .NET array to an unmanaged C-style array, and vice versa. There are 14 overloads of Copy, covering seven different data types with both directions of copying.

In the managed-to-unmanaged direction, the seven Copy overloads have the following form, where XXX represents one of the seven data types covered—System.Byte, System.Char, System.Int16, System.Int32, System.Int64, System.Single, and System.Double:

C#:

public static void Copy(XXX [] source, int startIndex,
  IntPtr destination, int length);

Visual Basic .NET:

Public Overloads Shared Sub Copy(source As XXX(), startIndex As Integer, _
  destination As IntPtr, length As Integer)

C++:

public: static void Copy(XXX source __gc[], int startIndex,
  IntPtr destination, int length);

These methods are useful for copying data into pre-allocated unmanaged memory. For these methods, a one-dimensional .NET array is passed in as the source parameter, a zero-based index where the copying should begin is passed as the startIndex integer, an IntPtr type representing the pointer to the unmanaged C-style array is passed as the destination parameter, and the number of elements to copy is passed as the length integer.

If the startIndex and length values are not valid for the .NET array (for example, extending past the end of it), an ArgumentOutOfRangeException is thrown.

In the unmanaged-to-managed direction, the seven Copy overloads have the following form, where XXX represents one of the same seven data types covered—System.Byte, System.Char, System.Int16, System.Int32, System.Int64, System.Single, and System.Double:

C#:

public static void Copy(IntPtr source, XXX [] destination,
  int startIndex, int length);

Visual Basic .NET:

Public Overloads Shared Sub Copy(source As IntPtr, destination As XXX(), _
  startIndex As Integer, length As Integer)

C++:

public: static void Copy(IntPtr source, XXX destination __gc[],
  int startIndex, int length);

For these methods, an IntPtr type representing the pointer to the unmanaged C-style array is passed as the source parameter, a zero-based index where the copying should begin is passed as the startIndex integer, a one-dimensional .NET array is passed in as the destination parameter, and the number of elements to copy is passed as the length integer.

Unlike with the reverse direction, no validation of the startIndex and length values can be done because C-style arrays do not carry bounds information with them. Be careful, because whatever data the IntPtr value points to will simply get sucked into a .NET array whether it makes sense or not. The .NET destination array must be initialized with the appropriate size before calling Copy.

For more information, see Chapter 6.

The CreateWrapperOfType Method

The CreateWrapperOfType method provides a way to convert one COM class type to another COM class type. The “Wrapper” refers to the fact that these COM objects are Runtime-Callable Wrappers. This method is usually used to convert a COM object whose class type is unknown (and therefore a generic System.__ComObject type) to a specific class type. It takes two parameters—a System.Object that is the source COM object and a System.Type instance of the type of object you want to convert the source object to. The method returns a System.Object destination COM object that’s an instance of the desired type.

The Type instance must represent a ComImport-marked class, and the Object instance’s type must be a ComImport-marked class, otherwise an ArgumentException is thrown.

This method won’t let you convert one wrapper to another arbitrarily. To determine whether one class type can be converted to another class type, this method takes every COM interface that the destination type claims to implement and calls QueryInterface for each interface on the source object. If the source object implements every COM interface listed by the destination type, the new wrapper is successfully created. Otherwise, an InvalidCastException is thrown with the message:

Source object cannot be converted to the destination type since it does not
support all the required interfaces.

This check does not include any interfaces implemented by the class that aren’t marked with ComImportAttribute, such as IEnumerable if the RCW supports enumeration.

Remember that the type used as the second parameter must be a class and not an interface. When using types generated by the type library importer, this means using the CoClassNameClass type because the type with the name of the coclass is an interface.

An important aspect of using CreateWrapperOfType is that you lose the identity of the input COM object because a new RCW instance ends up wrapping the same IUnknown interface pointer. For more information, see Chapter 6.

The DestroyStructure Method

The DestroyStructure method is used to free reference type fields (such as strings) of an unmanaged structure, using the same deallocation API that would be used by the Interop Marshaler (CoTaskMemFree). This method does not free the memory of the structure itself.

The method has two parameters—a System.IntPtr type representing the pointer to the unmanaged structure, and a System.Type instance representing the type of the structure whose reference type fields are being freed.

The .NET type can be a value type or reference type, but if it is marked with automatic layout (rather than sequential or explicit), an ArgumentException is thrown. If the entire structure is blittable, DestroyStructure does no work because there are no references whose memory needs to be freed. This method is used by Marshal.StructureToPtr to avoid memory leaks when reusing memory occupied by a structure.

For more information, see the Marshal.StructureToPtr method and Chapter 6.

The FreeBSTR Method

This method is one of the three memory-deallocation APIs in the Marshal class (the others being FreeCoTaskMem and FreeHGlobal). This method calls the SysFreeString COM API, which frees a string allocated by any of the following methods:

SysAllocString

SysAllocStringByteLen

SysAllocStringLen

SysReAllocString

SysReAllocStringLen

Tip

None of these string allocation methods are defined in the Marshal class because Marshal.StringToBSTR is defined instead. You could, however, define these signatures using PInvoke. See Appendix E for such definitions in C#.

For more information, see MSDN Online for a description of SysFreeString.

The FreeCoTaskMem Method

This method is one of the three memory-deallocation APIs in the Marshal class (the others being FreeBSTR and FreeHGlobal). This should be called to free any memory allocated by Marshal.AllocCoTaskMem and Marshal.ReAllocCoTaskMem (or an equivalent unmanaged API). It exposes the similarly named CoTaskMemFree COM API, which frees memory allocated by CoTaskMemAlloc or CoTaskMemRealloc. FreeCoTaskMem has a single System.IntPtr parameter that represents a pointer to the block of memory to free. If IntPtr.Zero (a null value) is passed, the method does nothing.

As with using CoTaskMemFree in COM, all the bytes are freed, and the memory pointed to by the input parameter can no longer be used after the call.

For more information, see MSDN Online for a description of CoTaskMemFree, and the AllocCoTaskMem and ReAllocCoTaskMem methods listed in this section.

The FreeHGlobal Method

This method is one of the three memory-deallocation APIs in the Marshal class (the others being FreeBSTR and FreeCoTaskMem). This should be called to free any memory allocated on the “global heap” by Marshal.AllocHGlobal and Marshal.ReAllocHGlobal (or an equivalent unmanaged API). Win32 does not provide a separate global heap and local heap, so this method exposes the Win32 GlobalFree API from KERNEL32.DLL, which does the same thing as the LocalFree API from the same DLL.

This method has a single System.IntPtr parameter representing a pointer to the memory. Therefore, the IntPtr value returned by AllocHGlobal or ReAllocHGlobal should be passed as this parameter. If IntPtr.Zero (a null value) is passed, the method does nothing.

As with using LocalFree in Win32, all the bytes are freed, and the memory pointed to by the input parameter can no longer be used after the call.

For more information, see MSDN Online for a description of LocalFree, and the AllocHGlobal and ReAllocHGlobal methods listed in this section.

The GenerateGuidForType Method

This method returns a GUID for any .NET type. It has a single System.Type parameter and returns a System.Guid instance. If the type is marked with the GuidAttribute custom attribute, its GUID value is returned. If not, the GUID that is automatically generated for a .NET type by the type library exporter is returned.

Therefore, this method can be used to figure out the GUID that would be used for COM for any .NET types, even COM-invisible ones! The only exported types whose GUIDs cannot be determined programmatically are class interfaces, because they don’t directly correspond to any .NET type. This method is redundant with System.Type’s GUID property; calling this method returns the exact same GUID that the property returns.

For more information about the algorithms used to generate GUIDs for .NET types, see Chapter 11.

The GenerateProgIdForType Method

This method returns a ProgID for a .NET class. It has a single System.Type parameter and returns a string that’s the ProgID. If the type is marked with the ProgIdAttribute custom attribute, its string value is returned. If not, the default ProgID that is automatically generated for a .NET class is returned—the namespace-qualified class name.

Unlike GenerateGuidForType, this method only works on classes that would be registered. Attempting to use a type that’s COM-invisible, not a class, a class that doesn’t have a public default constructor, and so on, causes an ArgumentException to be thrown.

For more information about ProgIDs of .NET classes, see Chapter 12.

The GetActiveObject Method

The Marshal.GetActiveObject exposes COM’s GetActiveObject API, which returns an instance of a currently running COM object. The running object is retrieved from the OLE object table, also known as the running object table.

Unlike the unmanaged API, which expects a CLSID of the running object, Marshal.GetActiveObject expects a ProgID of the running object. It has a single string parameter (the ProgID) and returns a System.Object. This returned object can be cast to the desired COM interface in order to use its members in an early-bound fashion.

If you need to get an instance of a running COM object that is not registered with a ProgID, you’ll have to resort to using PInvoke to define the original GetActiveObject method from OLEAUT32.DLL. There are no methods in the Marshal class for putting an object in the running object table, so you’ll need to use PInvoke for that as well.

For more information, see Chapter 6.

The GetComInterfaceForObject Method

The GetComInterfaceForObject method retrieves a pointer to a COM interface implemented by any object and returns it as a System.IntPtr value. In this case, the term “COM interface” means any interface that is visible to COM, not just interfaces marked with ComImportAttribute.

The returned interface pointer’s reference count is incremented before being returned, so you must decrement it with Marshal.Release once you’re finished with it. When dealing with “raw” COM interfaces pointers, all the old COM rules apply because you’re no longer communicating with an RCW that handles these details for you.

Tip

As the rules of COM dictate, the reference count of a returned interface pointer gets incremented and the caller is responsible to eventually decrement it. Therefore, you must remember to call Marshal.Release when you’re finished with the interface! This applies to all Marshal methods that return interface pointers as IntPtr types.

Like Marshal.GetIUnknownForObject and Marshal.GetIDispatchForObject, this method has a System.Object parameter and returns a System.IntPtr. This method has a second parameter, however—a System.Type instance that specifies the desired interface type. This means that the interface being retrieved must have a .NET definition. If the type passed is not an interface, or if it is not visible from COM, an ArgumentException is thrown.

This method can even be used on a purely .NET object! In this case, the interface pointer corresponds to the .NET object’s COM-Callable Wrapper (so you could get an interface pointer for UCOMIConnectionPointContainer on any .NET object). If the object passed in does not support the desired interface, an InvalidCastException is thrown.

This method can be useful when calling a method that exposes a COM object parameter as an IntPtr type, or with custom marshaling.

It’s not possible to directly obtain a .NET object’s class interface using this method because there’s no corresponding .NET interface type to pass as the second parameter. However, using Marshal.GetIDispatchForObject on a .NET object gives you the ability to invoke members on the COM-Callable Wrapper’s default interface, which is usually an auto-dispatch class interface.

To get an object’s IUnknown or IDispatch interface pointer, you can call Marshal.GetIUnknownForObject or Marshal.GetIDispatchForObject.

For more information, see Chapters 6 and 20.

The GetComObjectData Method

Every COM object wrapped in a Runtime-Callable Wrapper has a hashtable associated with it. This method is a means of retrieving this data, and SetComObjectData is a means of setting the data. User code should have no reason to call this method.

The GetComSlotForMethodInfo Method

This method enables you to retrieve an integer for a System.Reflection.MemberInfo instance that represents its zero-based slot value in a v-table that would be exposed to COM. It has a single MemberInfo parameter and returns a 32-bit integer. Note that although the name uses “MethodInfo,” it accepts a MemberInfo.

The returned slot number accounts for the presence of three IUnknown methods and possibly four IDispatch methods. Therefore, the first method on a .NET type will have a slot value of either 3 or 7.

You can even get slot numbers for members of a COM-invisible or private interface. In this case, they simply correspond to the v-table that would be exposed to COM had the interface been exposed to COM. You can also get slot numbers for COM-invisible members because they still occupy a slot in an exposed v-table (although COM clients can’t use the slot).

The type must be an interface type or an ArgumentException is thrown. This method cannot be used on class interfaces by passing a MemberInfo from a class.

This method does the reverse action of Marshal.GetMethodInfoForComSlot.

The GetEndComSlot Method

This method retrieves the last zero-based slot in a .NET type’s v-table. GetEndComSlot has a single System.Type parameter whose v-table is being inspected, and returns a 32-bit integer containing the last slot number. GetEndComSlot throws an ArgumentException if the type passed in is COM-invisible. The type can be an interface or a class. The slot numbers given for a class type refer to its class interface. If the class has an auto-dispatch class interface, -1 is always returned because this is a true disp-only interface that doesn’t have a v-table exposed to .NET clients, unlike a dispinterface that has a metadata definition.

This method, along with GetStartComSlot, can be used with GetMethodInfoForComSlot to only pass slots in the valid range.

The GetExceptionCode Method

This method exists for compiler support of Structured Exception Handling (SEH). It has no parameters, but returns a 32-bit integer representing the SEH error code of the last exception thrown.

If GetExceptionCode is called before any exception is thrown, this method simply returns 0xCCCCCCCC.

The GetExceptionPointers Method

This method exists for compiler support of Structured Exception Handling (SEH). It has no parameters, but returns a System.IntPtr representing a pointer to an EXCEPTION_POINTERS struct. This struct is defined in winnt.h from the Windows Platform SDK as follows:

typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;
  PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

The GetHINSTANCE Method

This method returns an instance handle (HINSTANCE) for any .NET module. It has a single System.Reflection.Module parameter and returns a System.IntPtr value containing the HINSTANCE value.

This method is useful when calling unmanaged functions that expect an HISNTANCE, such as the Win32 RegisterClass API. To get the module that any type is contained within, you can use the System.Type.Module property. For example, to get the module from which an object o was instantiated, use o.GetType().Module.

Caution

Dynamic modules generated by Reflection Emit do not have a corresponding instance handle. Calling GetHINSTANCE on such a module returns -1.

The GetHRForException Method

The GetHRForException method retrieves the HRESULT value for any kind of .NET exception and also calls the Windows SetErrorInfo API with the CCW for exception (which implements IErrorInfo appropriately). This is exactly what the Interop Marshaler does when a COM client calls a .NET member that throws an exception. This method is useful for custom marshaling scenarios because you can determine the HRESULT value COM clients see for any kind of exception thrown. The method has a single Exception parameter and returns an integer representing the exception’s corresponding HRESULT.

Most exceptions’ HRESULT values are captured in the protected HResult property with no way for clients to check the value directly. This is done because checking error codes is supposed to be a thing of the past; the .NET thing to do is check exception types. Because interoperability scenarios still require error codes at times, GetHRForException enables you to check these values.

An example of a time to use GetHRForException is in a .NET class’s implementation of a COM interface with a method marked with the PreserveSigAttribute pseudo-custom attribute. If the method needs to return an HRESULT value, letting an exception propagate outside the method would not give correct behavior. (If a COM client calls such a method through a v-table, a thrown exception would be swallowed by the CLR.) Instead, the method can catch any exception that may be thrown and return the appropriate HRESULT value.

For more information, see Chapter 20 and Appendix D.

The GetHRForLastWin32Error Method

It’s a common mistake to treat a Win32 error code as an HRESULT value. Because it’s only a portion of an HRESULT however, treating it as an HRESULT would make it look like a success value rather than failure! To help users avoid this mistake, Marshal.GetHRForLastWin32Error works just like Marshal.GetLastWin32Error (taking no parameters and returning an integer) but transforms the returned error code into an appropriate HRESULT value. This transformation performs a bitwise-OR operation with the severity bit, Win32 facility (a category of HRESULT values), and the error code:

SEVERITY_ERROR | FACILITY_WIN32 | ErrorCode

In other words, if the Win32 error code were 0xnnnn, this method transforms the value to 0x8007nnnn.

As with GetLastWin32Error, this method can only be used to obtain error codes set by PInvoke methods if the signature’s DllImportAttribute pseudo-custom attribute has its SetLastError named parameter set to true. This is false by default in C# and C++, but true by default when using the Declare statement in Visual Basic .NET.

For more information, see the GetLastWin32Error method listed later in this section.

The GetIDispatchForObject Method

The GetIDispatchForObject method retrieves a pointer to any object’s IDispatch interface and returns it as a System.IntPtr value. The method has a single System.Object parameter and returns a System.IntPtr. The returned interface pointer’s reference count is incremented before being returned, so you must call Marshal.Release when you’re finished using it.

In managed code, you rarely need to deal with the IDispatch interface directly. This method, however, can be useful when calling a method that exposes a COM object parameter as an IntPtr type, or with custom marshaling.

This method can even be used on a purely .NET object! In this case, the IDispatch pointer corresponds to the .NET object’s COM-Callable Wrapper. If the object passed in does not support the IDispatch interface, an InvalidCastException is thrown.

To get a COM interface pointer other than IDispatch, you can call Marshal.GetIUnknownForObject or the generic Marshal.GetComInterfaceForObject.

The GetITypeInfoForType Method

The GetITypeInfoForType method takes a System.Type parameter and returns an IntPtr value representing a pointer to an ITypeInfo implementation based on the original type. This method, together with the Marshal.GetTypeForITypeInfo method, exposes the same functionality as the TypeToTypeInfoMarshaler custom marshaler from the System.Runtime.InteropServices.CustomMarshalers namespace. The returned interface pointer’s reference count is incremented before being returned, so you must call Marshal.Release when you’re finished using it.

To expose a .NET type as an ITypeInfo interface to COM, you could therefore use MarshalAsAttribute to plug in the TypeToTypeInfoMarshaler custom marshaler, or manually call GetITypeInfoForType and return the interface pointer directly. To use the returned IntPtr value in managed code, you could convert it to a UCOMITypeInfo interface using Marshal.GetObjectForIUnknown.

The GetIUnknownForObject Method

The GetIUnknownForObject method retrieves a pointer to any object’s IUnknown interface and returns it as a System.IntPtr value. The method has a single System.Object parameter and returns a System.IntPtr. The returned interface pointer’s reference count is incremented before being returned, so you must call Marshal.Release when you’re finished using it.

This method does the reverse action of Marshal.GetObjectForIUnknown. In managed code, you rarely need to deal with the IUnknown interface directly. This method, however, can be useful when calling a method that exposes a COM object parameter as an IntPtr type, or with custom marshaling.

This method can even be used on a purely .NET object! In this case, the IUnknown pointer corresponds to the .NET object’s COM-Callable Wrapper.

To get a COM interface pointer other than IUnknown, you can call Marshal.GetIDispatchForObject or the generic Marshal.GetComInterfaceForObject.

For more information, see Chapters 6 and 20.

The GetLastWin32Error Method

This method exposes the Win32 GetLastError API from KERNEL32.DLL. The method, which takes no parameters and returns an integer, gives the last error code set by a call to the Win32 SetLastError API. If you want such an error code while using PInvoke from managed code, you must call Marshal.GetLastWin32Error rather than defining your own PInvoke definition of GetLastError and calling it. The reason for this is that the CLR may have made calls to operating system APIs between the failure and the call to GetLastError, overwriting the error code value.

This method can only be used to obtain error codes set by PInvoke methods if the signature’s DllImportAttribute pseudo-custom attribute has its SetLastError named parameter set to true. This is false by default in C# and C++, but true by default when using the Declare statement in Visual Basic .NET.

For more information, see Chapter 18 and GetHRForLastWin32Error listed earlier in this section.

The GetManagedThunkForUnmanagedMethodPtr Method

This advanced method enables you to directly get a callable signature in managed code from a pointer to an unmanaged method. It is intended for use by compilers rather than regular applications. GetManagedThunkForUnmanagedMethodPtr has the following signature:

C#:

public static IntPtr GetManagedThunkForUnmanagedMethodPtr(
  IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature);

Visual Basic .NET:

Public Shared Function GetManagedThunkForUnmanagedMethodPtr( _
   pfnMethodToWrap As IntPtr, pbSignature As IntPtr, cbSignature As Integer _
) As IntPtr

C++:

public: static IntPtr GetManagedThunkForUnmanagedMethodPtr(
   IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature);

This method does the opposite action of GetUnmanagedThunkForManagedMethodPtr.

The GetMethodInfoForComSlot Method

This method enables you to retrieve a System.Reflection.MemberInfo instance corresponding to any type, using a number that represents a slot in the v-table that would be exposed to COM for the .NET type. Note that although the name uses “MethodInfo,” it’s a MemberInfo that gets returned. The method has the following signature:

C#:

public static MemberInfo GetMethodInfoForComSlot(
  Type t, int slot, ref ComMemberType memberType);

Visual Basic .NET:

Public Shared Function GetMethodInfoForComSlot( _
   t As Type, slot As Integer, ByRef memberType As ComMemberType _
) As MemberInfo

C++:

public: static MemberInfo* GetMethodInfoForComSlot(
   Type* t, int slot, ComMemberType& memberType);

The first parameter is the type whose MemberInfo will be retrieved. An ArgumentException is thrown if the type is COM-invisible. The type can be an interface or a class. Using a class type means that the slot corresponds to its class interface.

The second parameter is the zero-based slot number of the method. This number must correctly account for the presence of IUnknown and possibly IDispatch methods. The Marshal.GetStartComSlot and Marshal.GetEndComSlot can be used to get the valid range of values that can be used with this method.

The third by-reference parameter is a ComMemberType enumeration defined in System.Runtime.InteropServices. Its value passed in doesn’t matter, but on return it contains the type of the COM member that corresponds to the returned MemberInfo—a regular method, a property accessor (get, set, or other), or an event accessor (add or remove).

This method does the reverse action of Marshal.GetComSlotForMethodInfo.

The GetNativeVariantForObject Method

The GetNativeVariantForObject method enables you to obtain the unmanaged VARIANT representation of any object. This involves the same transformation done by the Interop Marshaler when exposing a System.Object type to unmanaged code. The method has two parameters, a System.Object whose VARIANT representation we wish to obtain, and an IntPtr value that should contain the address of pre-allocated memory that will hold the VARIANT.

This method does the reverse action of Marshal.GetObjectForNativeVariant. Although the Interop Marshaler doesn’t support the VT_RECORD VARIANT type (a user-defined structure), GetNativeVariantForObject returns a VT_DISPATCH or VT_UNKNOWN VARIANT if you pass in a boxed value type.

In order to easily manipulate the VARIANT pointed to by the IntPtr value after the call, a .NET definition of a VARIANT structure is desirable. Chapter 6 defines such a structure and shows some uses of it.

The GetObjectForIUnknown Method

The GetObjectForIUnknown method creates a .NET object (an RCW) for any COM interface pointer represented as a System.IntPtr type. The method has a single System.IntPtr parameter and returns a System.Object. The IntPtr parameter represents an IUnknown interface pointer, but because all COM interfaces ultimately derive from IUnknown, a pointer value for any COM interface can be passed to this method.

The returned object that wraps the IUnknown pointer is often a System.__ComObject type. To invoke members on this object, you could use late binding (if the COM object also implements IDispatch) or cast the object to an appropriate COM interface, which makes the CLR call QueryInterface to determine whether the object supports the desired interface.

If the COM object implements IProvideClassInfo and an Interop Assembly containing a corresponding .NET class definition is registered with REGASM.EXE, it can be wrapped in the specific .NET class type. If you want the returned object to have a specific class type other than System.__ComObject without these requirements, call Marshal.GetTypedObjectForIUnknown instead.

For more information, see Chapter 20.

The GetObjectForNativeVariant Method

The GetObjectForNativeVariant method enables you to obtain a .NET object corresponding to a raw pointer to an unmanaged VARIANT type. This involves the same transformation done by the Interop Marshaler when exposing a VARIANT type to managed code. The method has a single IntPtr parameter that should contain the address of a VARIANT instance. It returns a System.Object which is the .NET view of the VARIANT type.

This method does the reverse action of Marshal.GetNativeVariantForObject. If the input VARIANT does not have a valid VARIANT type, an InvalidOleVariantTypeException is thrown. If the input VARIANT has an unsupported type (such as VT_RECORD), a NotSupportedException is thrown, just as if the Interop Marshaler attempted the conversion.

For more information, see Chapter 6.

The GetObjectsForNativeVariants Method

The GetObjectsForNativeVariants method enables you to obtain an array of .NET objects corresponding to a raw pointer to a C-style array of unmanaged VARIANT types. This involves the same transformation done by the Interop Marshaler when exposing VARIANT types to managed code. This method has the following signature:

C#:

public static object [] GetObjectsForNativeVariants(
  IntPtr aSrcNativeVariant, int cVars);

Visual Basic .NET:

Public Overloads Shared Function GetObjectsForNativeVariants( _
  aSrcNativeVariant As IntPtr, cVars As Integer) As Object()

C++:

public: static Object* GetObjectsForNativeVariants(
  IntPtr aSrcNativeVariant, int cVars) __gc[];

The IntPtr parameter should contain the address of the first element of the VARIANT array, and the integer parameter should contain the number of VARIANTs in the array. It returns an array of System.Object types, with each element corresponding to each of the input VARIANT types. If 0 is passed for the cVars parameter, an empty array is returned. If a negative number is passed, an ArgumentOutOfRangeException is thrown.

If any of the input VARIANTs in the array does not have a valid VARIANT type or an unsupported type (VT_RECORD), the same exception is thrown as would be thrown by the Interop Marshaler in such a situation.

For more information, see Chapter 6.

The GetStartComSlot Method

This method retrieves the first zero-based slot in a .NET type’s v-table that corresponds to a .NET method definition. Because all v-tables begin with the three methods of IUnknown, this starting slot is always 3 or greater. Furthermore, because the only other v-table methods that would not correspond to .NET methods are the four IDispatch methods, the starting slot can only be 3 or 7.

GetStartComSlot has a single System.Type parameter whose v-table is being inspected, and returns a 32-bit integer containing the beginning slot number. GetStartComSlot throws an ArgumentException if the type passed in is COM-invisible. The type can be an interface or a class. The slot numbers given for a class type refer to its class interface. If the class has an auto-dispatch class interface, -1 is always returned because this is a true disp-only interface that doesn’t have a v-table exposed to .NET clients, unlike a dispinterface that has a metadata definition.

GetStartComSlot returns 3 for IUnknown-only interfaces and 7 for dual interfaces and dispinterfaces. Although dispinterfaces don’t have a v-table from COM’s perspective, they do from the .NET perspective so they can be treated just like dual interfaces.

This method, along with GetEndComSlot, can be used with GetMethodInfoForComSlot to only pass slots in the valid range.

The GetThreadFromFiberCookie Method

The CLR’s hosting APIs enable advanced hosts to schedule fibers, lightweight objects consisting of a stack and register context, onto operating system threads. The GetThreadFromFiberCookie returns a System.Threading.Thread instance for a passed-in integer representing a “cookie” value that uniquely identifies the thread on which the fiber is scheduled.

The GetTypedObjectForIUnknown Method

The GetTypedObjectForIUnknown method creates a .NET object (an RCW) for any COM interface pointer represented as a System.IntPtr type. The difference between this method and Marshal.GetObjectForIUnknown is that you can get a “strongly-typed” object back rather than a generic System.__ComObject. This method has the following signature:

C#:

public static object GetTypedObjectForIUnknown(IntPtr pUnk, Type t);

Visual Basic .NET:

Public Shared Function GetTypedObjectForIUnknown(pUnk As IntPtr, t As Type) _
  As Object

C++:

public: static Object* GetTypedObjectForIUnknown(IntPtr pUnk, Type* t);

The first parameter represents an IUnknown interface pointer, but because all COM interfaces ultimately derive from IUnknown, a pointer value for any COM interface can be passed to this method. The second parameter is the type of the object you want to be returned. This must be a ComImport-marked class type and not an interface type (meaning a coclass interface type won’t work), or an ArgumentException is thrown. For types generated by the type library importer, this would be a type named CoClassNameClass.

The GetTypeForITypeInfo Method

The GetTypeForITypeInfo method takes a System.IntPtr parameter that represents a pointer to an ITypeInfo interface and returns a System.Type instance based on the original interface. This method, together with the Marshal.GetITypeInfoForType method, exposes the same functionality as the TypeToTypeInfoMarshaler custom marshaler from the System.Runtime.InteropServices.CustomMarshalers namespace.

The type library importer automatically uses the custom marshaler to convert ITypeInfo parameters to System.Type parameters. However, if you obtain an ITypeInfo interface through some other means (such as an imported signature with an IUnknown parameter representing an object that also implements ITypeInfo), you can use GetTypeForITypeInfo to manually do the same transformation. You can convert a UCOMITypeInfo interface to the IntPtr value needed to call this method using Marshal.GetComInterfaceForObject or Marshal.GetIUnknownForObject.

The GetTypeInfoName Method

This method tells you the name of the type description passed in as a UCOMITypeInfo instance. It has a single UCOMITypeInfo parameter and returns a string with the desired name.

You can get this same name by calling UCOMITypeInfo.GetDocumentation and passing -1 for its first parameter. This is less convenient, however, because this method has four out parameters that need to be handled.

The GetTypeLibGuid Method

This method tells you the LIBID of a type library if you pass it a UCOMITypeLib instance. This differs from GetTypeLibGuidForAssembly because it extracts the LIBID directly from an existing type library rather than calculating what a LIBID would be that corresponds to an assembly.

GetTypeLibGuid has a single UCOMITypeLib parameter and returns a System.Guid instance with the LIBID value. You can get this same information by calling UCOMITypeLib.GetLibAttr and checking the guid field of the returned structure. This requires significantly more code, however, to deal with converting an IntPtr type to a TYPELIBATTR structure and releasing the structure with UCOMITypeLib.ReleaseTLibAttr.

Other information about a type library can be obtained from Marshal.GetTypeLibLcid and Marshal.GetTypeLibName.

The GetTypeLibGuidForAssembly Method

This method returns a LIBID for a .NET assembly. This LIBID is the same as the LIBID that an exported type library for the assembly would have, which is based on the assembly’s identity. The method has a single System.Reflection.Assembly parameter and returns a System.Guid representing the LIBID.

If the assembly is marked with the GuidAttribute custom attribute, its value is returned. If not, the default LIBID that is automatically generated for a .NET assembly is returned. This method works for any assemblies, even ones marked as COM-invisible, because it’s still possible to generate a type library for such assemblies (even if they end up being empty).

For more information about the automatic LIBID generation for assemblies, see Chapter 11.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.154.171