System.Runtime.InteropServices
Reference• 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.
System.Runtime.InteropServices
NamespaceThe 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.
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.
ArrayWithOffset
Value TypeYou 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#:
Visual Basic .NET:
C++:
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.
AssemblyRegistrationFlags
EnumerationThe 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.
AutomationProxyAttribute
Custom AttributeThis 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.”
BIND_OPTS
Value TypeThis 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.
BINDPTR
Value TypeThis 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.
CALLCONV
EnumerationThis 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.
CallingConvention
EnumerationUnlike 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.
CharSet
EnumerationThe 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.
ClassInterfaceAttribute
Custom AttributeThis 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.
ClassInterfaceType
EnumerationThis 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.
CoClassAttribute
Custom AttributeThis 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 CoclassName
Class
. 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.”
ComAliasNameAttribute
Custom AttributeThis 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.
ComConversionLossAttribute
Custom AttributeThis 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.
ComEventInterfaceAttribute
Custom AttributeThis 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.
COMException
ExceptionThis 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.”
ComImportAttribute
Pseudo-Custom AttributeThis 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.
ComInterfaceType
EnumerationThis 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.
ComMemberType
EnumerationThis 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.
ComRegisterFunctionAttribute
Custom AttributeThis 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.
ComSourceInterfacesAttribute
Custom AttributeThis 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.
ComUnregisterFunctionAttribute
Custom AttributeThis 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.
ComVisibleAttribute
Custom AttributeThis 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.
CONNECTDATA
Value TypeThis 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.
CurrencyWrapper
ClassThe 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.
DESCKIND
EnumerationThis 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.
DESCUNION
Value TypeThis 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.
DispatchWrapper
ClassThe 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 VARIANT
s 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.
DispIdAttribute
Custom AttributeThis 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.
DISPPARAMS
Value TypeThis 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.
DllImportAttribute
Pseudo-Custom AttributeThe 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.
ELEMDESC
Value TypeThis 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.
ErrorWrapper
ClassThe 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#:
Visual Basic .NET:
C++:
For more information, consult Chapter 3.
EXCEPINFO
Value TypeThis 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.
ExporterEventKind
EnumerationThis 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.
ExtensibleClassFactory
ClassThe 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
.
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.
ExternalException
ExceptionThis 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.
FieldOffsetAttribute
Pseudo-Custom AttributeThis 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;
}
<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.”
FILETIME
Value TypeThis 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.
FUNCDESC
Value TypeThis 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.
FUNCFLAGS
EnumerationThis 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.
FUNCKIND
EnumerationThis 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.
GCHandle
Value TypeThe 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.
Alloc
MethodThis 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.
Free
MethodThis 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.
AddrOfPinnedObject
MethodThis 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.
IsAllocated
PropertyThis 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
.
Target
PropertyThis 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.
GCHandle/IntPtr
Explicit Conversion OperatorEvery 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.
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 GCHandle
s with the same value as long as you only call Free
once.
GCHandleType
EnumerationThis 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.
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.
GuidAttribute
Custom AttributeThis 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.
HandleRef
Value TypeThe 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.)
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.
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.
ICustomAdapter
InterfaceThe 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.
ICustomFactory
InterfaceThe 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.
ICustomMarshaler
InterfaceThe 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.
IDispatchImplAttribute
Custom AttributeThis 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.
IDispatchImplType
EnumerationThe 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.
IDLDESC
Value TypeThis 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.
IDLFLAG
EnumerationThis 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.
IMPLTYPEFLAGS
EnumerationThis 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.
ImportedFromTypeLibAttribute
Custom AttributeThe 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.
ImporterEventKind
EnumerationThis 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.
InAttribute
Pseudo-Custom AttributeThe 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.
InterfaceTypeAttribute
Custom AttributeThis 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.
InvalidComObjectException
ExceptionInvalidComObjectException
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.
InvalidOleVariantTypeException
ExceptionThe 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.
INVOKEKIND
EnumerationThis 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.
IRegistrationServices
InterfaceThis 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.
ITypeLibConverter
InterfaceThis 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.
ITypeLibExporterNameProvider
InterfaceAn 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.
ITypeLibExporterNotifySink
InterfaceAn 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.
ITypeLibImporterNotifySink
InterfaceAn 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.
LayoutKind
EnumerationThis 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.
LCIDConversionAttribute
Custom AttributeThis 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:
[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.
LIBFLAGS
EnumerationThis 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.
Marshal
ClassThe 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!
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
• 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.
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.
AddRef
MethodAll 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.
AllocCoTaskMem
MethodThis 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.
AllocHGlobal
MethodThis 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.
BindToMoniker
MethodThis 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");
ChangeWrapperHandleStrength
MethodThis 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.
Copy
MethodThe 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);
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.
CreateWrapperOfType
MethodThe 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 CoClassName
Class
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.
DestroyStructure
MethodThe 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.
FreeBSTR
MethodThis 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
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
.
FreeCoTaskMem
MethodThis 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.
FreeHGlobal
MethodThis 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.
GenerateGuidForType
MethodThis 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.
GenerateProgIdForType
MethodThis 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.
GetActiveObject
MethodThe 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.
GetComInterfaceForObject
MethodThe 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.
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.
GetComObjectData
MethodEvery 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.
GetComSlotForMethodInfo
MethodThis 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
.
GetEndComSlot
MethodThis 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.
GetExceptionCode
MethodThis 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.
GetExceptionPointers
MethodThis 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;
GetHINSTANCE
MethodThis 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
.
Dynamic modules generated by Reflection Emit do not have a corresponding instance handle. Calling GetHINSTANCE
on such a module returns -1.
GetHRForException
MethodThe 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.
GetHRForLastWin32Error
MethodIt’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.
GetIDispatchForObject
MethodThe 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
.
GetITypeInfoForType
MethodThe 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
.
GetIUnknownForObject
MethodThe 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.
GetLastWin32Error
MethodThis 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.
GetManagedThunkForUnmanagedMethodPtr
MethodThis 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
.
GetMethodInfoForComSlot
MethodThis 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
.
GetNativeVariantForObject
MethodThe 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.
GetObjectForIUnknown
MethodThe 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.
GetObjectForNativeVariant
MethodThe 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.
GetObjectsForNativeVariants
MethodThe 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 VARIANT
s 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 VARIANT
s 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.
GetStartComSlot
MethodThis 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.
GetThreadFromFiberCookie
MethodThe 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.
GetTypedObjectForIUnknown
MethodThe 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 CoClassName
Class
.
GetTypeForITypeInfo
MethodThe 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
.
GetTypeInfoName
MethodThis 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.
GetTypeLibGuid
MethodThis 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
.
GetTypeLibGuidForAssembly
MethodThis 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.