GetTypeLibLcid
MethodThis method tells you the locale identifier (LCID) of a type library if you pass it a UCOMITypeLib
instance. GetTypeLibLcid
has a single UCOMITypeLib
parameter and returns a 32-bit integer that contains the LCID value. Using this LCID, you could create a System.Globalization.CultureInfo
instance by using its constructor that accepts an integer.
You can get this same LCID by calling UCOMITypeLib.GetLibAttr
and checking the lcid
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.GetTypeLibGuid
and Marshal.GetTypeLibName
.
GetTypeLibName
MethodThis method tells you the name of a type library if you pass it a UCOMITypeLib
instance. It has a single UCOMITypeLib
parameter and returns a string with the type library’s name. This name is not a filename, but the identifier used with the library statement, such as ADODB
for the Microsoft ADO type library.
You can get this same name by calling UCOMITypeLib.GetDocumentation
and passing -1 for its first parameter to get information about the type library itself. This is less convenient, however, because this method has four out parameters that need to be handled.
To get other information about a type library, see Marshal.GetTypeLibGuid
and Marshal.GetTypeLibLcid
.
GetUnmanagedThunkForManagedMethodPtr
MethodThis advanced method enables you to directly get a callable signature to unmanaged code from a pointer to a managed method. It is intended for use by compilers rather than regular applications. GetUnmanagedThunkForManagedMethodPtr
has the following signature:
C#:
public static IntPtr GetUnmanagedThunkForManagedMethodPtr(
IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature);
Visual Basic .NET:
Public Shared Function GetUnmanagedThunkForManagedMethodPtr( _
pfnMethodToWrap As IntPtr, pbSignature As IntPtr, cbSignature As Integer _
) As IntPtr
public: static IntPtr GetUnmanagedThunkForManagedMethodPtr(
IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature);
This method does the opposite action of GetManagedThunkForUnmanagedMethodPtr
.
IsComObject
MethodThis method can be used to determine whether or not any object is a COM object. Specifically, it returns true if the class type of the instance is marked with ComImport
or derives from a class marked ComImport
, and false otherwise.
It has a single System.Object
parameter and a boolean return type. Calling IsComObject(o)
is the same as calling o.GetType().IsCOMObject
because this property of System.Type
has the same semantics.
This method differs from System.Type.IsImport
, because this property returns true only if the exact type (class or interface) is marked with ComImport
. It also differs from the System.Runtime.InteropServices.RegistrationServices.TypeRepresentsComType
method, which returns true if the type is marked with ComImport
or derives from the ComImport
type with the same GUID, and false otherwise.
IsTypeVisibleFromCom
MethodThis method tells you whether or not a .NET type is invisible to COM. It takes a single System.Type
parameter and returns true if it’s COM-visible, false if it’s COM-invisible.
This method comes in handy because without it, checking for COM-invisibility is not a one-step process. A type is COM-invisible if it’s non-public, or if it’s marked with ComVisibleAttribute
with the value of false, or if its containing assembly is marked with ComVisibleAttribute
with the value of false, unless the type is marked with the ComImportAttribute
pseudo-custom attribute, which makes it COM-visible regardless of everything else!
For more information about COM-visibility, see Chapter 12.
NumParamBytes
MethodNumParamBytes
returns the number of bytes that is required to hold all of a method’s parameters. It has a single System.Reflection.MethodInfo
parameter and returns a 32-bit integer. This method is intended for use by compilers rather than regular applications.
OffsetOf
MethodThis method tells you the memory location of a .NET structure’s field, as an offset in bytes from the beginning of the structure. This offset applies to the unmanaged view of the structure, which may not correspond to the offset in the managed structure because marshaling can transform a .NET structure into an unmanaged structure with different offsets.
OffsetOf
takes two parameters, a System.Type
instance for the structure to inspect, and a string parameter called fieldName
that contains the case-sensitive name of the field whose offset is desired. It returns a System.IntPtr
that contains the offset value.
The type passed to OffsetOf
can be a value type or a formatted reference type (which has sequential or explicit layout). If not, or if the type has a field that can’t be marshaled to unmanaged code (such as a class type not marked with UnmanagedType.Interface
), an ArgumentException
is thrown with the message, “Type typeName cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.” If the fieldName
parameter doesn’t contain a name of a field in the passed-in type, an ArgumentException
is thrown with the message, “Field passed in is not a marshaled member of the type typeName.”
To obtain the size of an entire structure, see the Marshal.SizeOf
method.
Prelink
MethodThis method, which has a single System.Reflection.MethodInfo
parameter and returns nothing, can be used to perform early initialization of a PInvoke method. Without using this method, the first time a PInvoke method is invoked, the CLR performs initialization that does the following:
• Checks the validity of the signature’s metadata
• Checks that all the method’s parameters are legal to use in a PInvoke signature
• Finds and loads the DLL specified by the DllImportAttribute
(or the Declare
statement in Visual Basic .NET), if it hasn’t been loaded already
• Finds the specified entry point in the DLL
If any of these tasks fail, then an exception is thrown. If you want to catch any such errors without waiting for the first method invocation or if you want to eagerly load the DLL, calling Prelink
using a MethodInfo
instance representing the PInvoke method causes all these steps to be performed immediately.
The following code demonstrates using Prelink
on an instance with a private static PInvoke signature for MessageBox
from USER32.DLL
.
C#:
// Get a MethodInfo for the private static method
MethodInfo mi = obj.GetType().GetMethod("MessageBox",
BindingFlags.Static | BindingFlags.NonPublic);
// Call Prelink using the MethodInfo instance
Marshal.Prelink(mi);
Visual Basic .NET:
' Get a MethodInfo for the Private Shared Sub
Dim mi As MethodInfo = obj.GetType().GetMethod("MessageBox", _
BindingFlags.Static Or BindingFlags.NonPublic)
' Call Prelink using the MethodInfo instance
Marshal.Prelink(mi)
C++:
// Get a MethodInfo for the private static method
MethodInfo* mi = obj->GetType()->GetMethod(S"MessageBox",
(BindingFlags)(BindingFlags::Static | BindingFlags::NonPublic));
// Call Prelink using the MethodInfo instance
Marshal::Prelink(mi);
Calling Prelink
on a non-PInvoke method has no effect. To perform this action on all PInvoke methods in a type, use Marshal.PrelinkAll
.
PrelinkAll
MethodThe PrelinkAll
method invokes Marshal.Prelink
on every method for a given type, which only has an effect on PInvoke methods. This method has a single System.Type
parameter and returns nothing. It can be used as follows:
C#:
Marshal.PrelinkAll(obj.GetType());
Visual Basic .NET:
Marshal.PrelinkAll(obj.GetType())
C++:
Marshal::PrelinkAll(obj->GetType());
For information about the actions of Marshal.Prelink
, see the previous section.
PtrToStringAnsi
MethodThis method copies the contents of an unmanaged ANSI string to a new .NET string. Although the name may lead you to believe the method produces an ANSI string, you should think of it as “pointer-to-String: the ANSI version,” or “pointer-to-ANSI to String.” The method has the following two overloads:
public static string PtrToStringAnsi(IntPtr ptr, int len);
public static string PtrToStringAnsi(IntPtr ptr);
Visual Basic .NET:
Public Overloads Shared Function PtrToStringAnsi(ptr As IntPtr, _
len As Integer) As String
Public Overloads Shared Function PtrToStringAnsi(ptr As IntPtr) As String
C++:
public: static String* PtrToStringAnsi(IntPtr ptr, int len);
public: static String* PtrToStringAnsi(IntPtr ptr);
The System.IntPtr
parameter represents a pointer to the original unmanaged string, and the integer len
parameter represents the length of the string, in characters. Therefore, len
characters get copied to the returned string. If you use the overload that doesn’t specify a length, all characters up to the first null character get copied.
This method does the opposite of Marshal.StringToCoTaskMemAnsi
and Marshal.StringToHGlobalAnsi
, which create an ANSI string from the contents of a System.String
. Because this method makes a copy of the original unmanaged string’s contents, you are still responsible for freeing the original string when appropriate.
You normally don’t need to interact with pointers to unmanaged strings in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++. To create a .NET string from an unmanaged Unicode string or a BSTR
, see PtrToStringAuto
, PtrToStringBSTR
, and PtrToStringUni
.
PtrToStringAuto
MethodThis method copies the contents of a platform-dependent unmanaged string to a new .NET string. Although the name may lead you to believe the method produces an “auto string,” you should think of it as “pointer-to-String: the Auto version,” or “pointer-to-Auto to String.” The method has the following two overloads:
C#:
public static string PtrToStringAuto(IntPtr ptr, int len);
public static string PtrToStringAuto(IntPtr ptr);
Public Overloads Shared Function PtrToStringAuto(ptr As IntPtr, _
len As Integer) As String
Public Overloads Shared Function PtrToStringAuto(ptr As IntPtr) As String
C++:
public: static String* PtrToStringAuto(IntPtr ptr, int len);
public: static String* PtrToStringAuto(IntPtr ptr);
The System.IntPtr
parameter represents a pointer to the original unmanaged string; the integer len
parameter represents the length of the string, in characters. Therefore, len
characters get copied to the returned string. If you use the overload that doesn’t specify a length, all characters up to the first null character get copied.
This method does the opposite of Marshal.StringToCoTaskMemAuto
and Marshal.StringToHGlobalAuto
, which create a platform-dependent string from the contents of a System.String
. Because this method makes a copy of the original unmanaged string’s contents, you are still responsible for freeing the original string when appropriate.
You normally don’t need to interact with pointers to unmanaged strings in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++. To create a .NET string from a BSTR
or a string that’s always either ANSI or Unicode, see PtrToStringAnsi
, PtrToStringBSTR
, and PtrToStringUni
.
PtrToStringBSTR
MethodThis method copies the contents of a BSTR
to a new .NET string. Although the name may lead you to believe the method produces a BSTR
, you should think of it as “pointer-to-String: the BSTR
version,” or “pointer-to-BSTR
to String.” The method has a single System.IntPtr
parameter that represents a pointer to a BSTR
, and returns a System.String
instance with the desired contents.
This method does the opposite of Marshal.StringToBSTR
, which creates a BSTR
from the contents of a System.String
. Because this method makes a copy of the original BSTR
’s contents, you are still responsible for freeing the original BSTR
when appropriate.
You normally don’t need to interact with BSTR
types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++. To create a .NET string from an unmanaged ANSI or Unicode string, see PtrToStringAnsi
, PtrToStringAuto
, and PtrToStringUni
.
PtrToStringUni
MethodThis method copies the contents of an unmanaged Unicode string to a new .NET string. Although the name may lead you to believe the method produces an unmanaged Unicode string, you should think of it as “pointer-to-String: the Unicode version,” or “pointer-to-Unicode to String.” The method has the following two overloads:
C#:
public static string PtrToStringUni(IntPtr ptr, int len);
public static string PtrToStringUni(IntPtr ptr);
Visual Basic .NET:
Public Overloads Shared Function PtrToStringUni(ptr As IntPtr, _
len As Integer) As String
Public Overloads Shared Function PtrToStringUni(ptr As IntPtr) As String
C++:
public: static String* PtrToStringUni(IntPtr ptr, int len);
public: static String* PtrToStringUni(IntPtr ptr);
The System.IntPtr
parameter represents a pointer to the original unmanaged string, and the integer len
parameter represents the length of the string, in characters. Therefore, len
characters get copied to the returned string. If you use the overload that doesn’t specify a length, all characters up to the first null character get copied.
This method does the opposite of Marshal.StringToCoTaskMemUni
and Marshal.StringToHGlobalUni
, which create a Unicode string from the contents of a System.String
. Because this method makes a copy of the original unmanaged string’s contents, you are still responsible for freeing the original string when appropriate.
You normally don’t need to interact with pointers to unmanaged strings in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++. To create a .NET string from an unmanaged ANSI string or a BSTR
, see PtrToStringAnsi
, PtrToStringAuto
, and PtrToStringBSTR
.
PtrToStructure
MethodThe PtrToStructure
method can be used to obtain a .NET type with sequential or explicit layout from a raw pointer value. (The Marshal.StructureToPtr
method does the reverse of this.) This is often necessary in COM Interoperability and PInvoke when structure parameters are represented as an IntPtr
value. This method has two overloads:
public static object PtrToStructure(IntPtr ptr, Type structureType);
public static void PtrToStructure(IntPtr ptr, object structure);
Visual Basic .NET:
Public Overloads Shared Function PtrToStructure(ptr As IntPtr, _
structureType As Type) As Object
Public Overloads Shared Sub PtrToStructure(ptr As IntPtr, _
structure As Object)
C++:
public: static Object* PtrToStructure(IntPtr ptr, Type* structureType);
public: static void PtrToStructure(IntPtr ptr, Object* structure);
The first signature is more commonly used, and expects an IntPtr
value representing the raw pointer, and a Type
instance representing the type of structure desired. An instance of this type is returned if successful (and is boxed if it’s a value type).
The second signature can be used to convert the pointer to a pre-allocated object (the second parameter). In this case, the object parameter must be a formatted reference type (one with sequential or explicit layout). Passing a boxed value type causes an ArgumentException
to be thrown.
For either overload, attempting to convert a pointer to an object without sequential or explicit layout results in an ArgumentException
.
The following code demonstrates using PtrToStructure
after calling UCOMITypeInfo.GetTypeAttr
to obtain the desired TYPEATTR
structure. GetTypeAttr
is defined with a by-reference IntPtr
parameter because the unmanaged signature’s TYPEATTR**
parameter is not supported by the Interop Marshaler.
C#:
UCOMITypeInfo typeInfo = ...;
IntPtr ptr = IntPtr.Zero;
typeInfo.GetTypeAttr(ref ptr);
TYPEATTR attr = (TYPEATTR)Marshal.PtrToStructure(ptr, typeof(TYPEATTR));
Visual Basic .NET:
Dim typeInfo As UCOMITypeInfo = ...
Dim ptr As IntPtr = IntPtr.Zero
typeInfo.GetTypeAttr(ptr)
Dim attr As TYPEATTR = _
CType(Marshal.PtrToStructure(ptr, GetType(TYPEATTR)), TYPEATTR)
For more information, see the Marshal.StructureToPtr
method listed later in this section, and Chapter 6.
QueryInterface
MethodAll three IUnknown
methods—QueryInterface
, AddRef
, and Release
—are exposed through the Marshal
class and can be called on any COM object. Marshal.QueryInterface
exposes a COM object’s IUnknown.QueryInterface
method, which attempts to obtain a specific interface pointer from a COM object.
This method takes three parameters—a System.IntPtr
parameter representing the IUnknown
pointer for the COM object whose QueryInterface
implementation will be called, a by-reference System.Guid
parameter containing the requested IID, and an out-only System.IntPtr
parameter representing the returned interface pointer that corresponds to the requested IID if the call succeeds. It returns an integer, which is the HRESULT
value returned by IUnknown.QueryInterface
. The Guid
parameter is passed by-reference simply because the definition of IUnknown.QueryInterface
expects a pointer to a GUID
structure, not because the value may be changed by the callee.
To get an IntPtr
value that represents an object passed as the first parameter, you can call GetComInterfaceForObject
, GetIDispatchForObject
, or GetIUnknownForObject
. These methods, plus the Marshal.QueryInterface
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. To make use of the third parameter’s IntPtr
value after the call, you can call GetObjectForIUnknown
to convert it to an interface type. If the call fails, an InvalidCastException
is thrown because the E_NOINTERFACE HRESULT
returned by IUnknown.QueryInterface
always gets mapped to that exception.
Marshal.QueryInterface
increments the reference count of the returned interface pointer, so you must call Marshal.Release
when you’re finished using it.
For COM objects, the action of using Marshal.QueryInterface
is the same as doing a casting operation in managed code. Casting is preferred for its brevity and clarity. One interesting behavior that you get from Marshal.QueryInterface
is with .NET objects; because this acts on the object’s COM-Callable Wrapper (CCW), you can successfully obtain interface pointers for interfaces implemented by the CCW, such as IMarshal
or IConnectionPointContainer
! A regular cast operation could not do this because casting a .NET object looks solely at its metadata, which knows nothing about these COM interfaces unless implemented directly in managed code. Furthermore, using Marshal.QueryInterface
can be convenient if you have an IntPtr
representing a COM interface and you don’t want to wrap it in an RCW.
The following code demonstrates the use of Marshal.QueryInterface
. It uses Marshal.GetIUnknownForObject
to obtain an IntPtr
value representing the COM object’s IUnknown
interface pointer. It assumes that the interface we want to “cast” to, IComInterface
, has a .NET definition. After the call, it uses Marshal.GetObjectForIUnknown
to convert the returned IntPtr
value to the IComInterface
interface type. At some time later, Marshal.Release
is called on the returned interface pointer. In this contrived scenario, the code is equivalent to the casting operations shown in the beginning comment.
C#:
// The following code does the same thing as:
// IComInterface i = (IComInterface)myComObject;
// Prepare the parameters
IntPtr pUnk = Marshal.GetIUnknownForObject(myComObject);
Guid g = typeof(IComInterface).GUID;
IntPtr ppv;
// Make the QueryInterface call
int hresult = Marshal.QueryInterface(pUnk, ref g, out ppv);
// Check for failure HRESULT value
if (hresult < 0)
Marshal.ThrowExceptionForHR(hresult);
// Convert the raw pointer to a useable instance
IComInterface i = (IComInterface)Marshal.GetObjectForIUnknown(ppv);
...
Marshal.Release(ppv);
Marshal.Release(pUnk);
Visual Basic .NET:
' The following code does the same thing as:
' Dim i As IComInterface = CType(myComObject, IComInterface)
' Prepare the parameters
Dim pUnk As IntPtr = Marshal.GetIUnknownForObject(myComObject)
Dim g As Guid = GetType(IComInterface).GUID
Dim ppv As IntPtr
' Make the QueryInterface call
Dim hresult As Integer = _
Marshal.QueryInterface(pUnk, g, ppv)
' Check for failure HRESULT value
If (hresult < 0) Then _
Marshal.ThrowExceptionForHR(hresult)
' Convert the raw pointer to a useable instance
Dim i As IComInterface = _
CType(Marshal.GetObjectForIUnknown(ppv), IComInterface)
...
Marshal.Release(ppv)
Marshal.Release(pUnk)
C++:
// The following code does the same thing as:
// IComInterface* i = dynamic_cast<IComInterface*>(myComObject);
// Prepare the parameters
IntPtr pUnk = Marshal::GetIUnknownForObject(myComObject);
Guid g = __typeof(IComInterface)->GUID;
IntPtr ppv;
// Make the QueryInterface call
int hresult = Marshal::QueryInterface(pUnk, &g, &ppv);
// Check for failure HRESULT value
if (hresult < 0)
Marshal::ThrowExceptionForHR(hresult);
// Convert the raw pointer to a useable instance
IComInterface* i =
dynamic_cast<IComInterface*>(Marshal::GetObjectForIUnknown(ppv));
...
Marshal::Release(ppv);
Marshal::Release(pUnk);
For more information, see Marshal.Release
and Marshal.AddRef
listed in this section.
ReadByte
MethodThe ReadByte
method is useful for extracting a single byte value at a given offset from a pointer to an unmanaged object. It has three overloads:
public static byte ReadByte([MarshalAs(UnmanagedType.AsAny)] object ptr,
int ofs);
public static byte ReadByte(IntPtr ptr, int ofs);
public static byte ReadByte(IntPtr ptr);
Visual Basic .NET:
Public Overloads Shared Function ReadByte(<MarshalAs(UnmanagedType.AsAny)> _
ptr As Object, ofs As Integer) As Byte
Public Overloads Shared Function ReadByte(ptr As IntPtr, ofs As Integer) _
As Byte
Public Overloads Shared Function ReadByte(ptr As IntPtr) As Byte
C++:
public: static unsigned char ReadByte(
[MarshalAs(UnmanagedType::AsAny)] Object* ptr, int ofs);
public: static unsigned char ReadByte(IntPtr ptr, int ofs);
public: static unsigned char ReadByte(IntPtr ptr);
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), and an integer offset. The object passed as the first parameter could even be a .NET object with the appropriate format (such as an array), but there would be no reason to pass such an object to this method. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being read is within the range of the input object.
This method enables direct interaction with an unmanaged C-style byte array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array before reading its element values. This is demonstrated in the following code:
C#:
IntPtr unmanagedArray = ...;
// One way to print the 10 elements of the C-style unmanagedArray
byte [] newArray = new byte[10];
Marshal.Copy(unmanagedArray, newArray, 0, 10);
for (int i = 0; i < newArray.Length; i++)
Console.WriteLine(newArray[i]);
// Another way to print the 10 elements of the C-style unmanagedArray
for (int i = 0; i < 10; i++)
Console.WriteLine(Marshal.ReadByte(unmanagedArray, i));
Visual Basic .NET:
Dim unmanagedArray As IntPtr = ...
Dim i As Integer
' One way to print the 10 elements of the C-style unmanagedArray
Dim newArray As Byte(9)
Marshal.Copy(unmanagedArray, newArray, 0, 10)
For i = 0 To newArray.Length
Console.WriteLine(newArray(i))
Next i
' Another way to print the 10 elements of the C-style unmanagedArray
For i = 0 To 10
Console.WriteLine(Marshal.ReadByte(unmanagedArray, i))
Next i
C++:
IntPtr unmanagedArray = ...;
// One way to print the 10 elements of the C-style unmanagedArray
unsigned char newArray __gc[] = new unsigned char __gc[10];
Marshal::Copy(unmanagedArray, newArray, 0, 10);
for (int i = 0; i < newArray.Length; i++)
Console::WriteLine(newArray[i]);
// Another way to print the 10 elements of the C-style unmanagedArray
for (int i = 0; i < 10; i++)
Console::WriteLine(Marshal::ReadByte(unmanagedArray, i));
To read other data types directly from an unmanaged pointer, see Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
. To write data directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
.
ReadInt16
MethodThe ReadInt16
method is useful for extracting a single 16-bit integer value at a given offset from a pointer to an unmanaged object. It has three overloads just like Marshal.ReadByte
, but with an Int16
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), and an integer offset. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being read is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array before reading its element values. It is used just like the examples shown in the Marshal.ReadByte
section, but with a different array element type.
To read other data types directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
. To write data directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
.
ReadInt32
MethodThe ReadInt32
method is useful for extracting a single 32-bit integer value at a given offset from a pointer to an unmanaged object. It has three overloads just like Marshal.ReadByte
, but with an Int32
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), and an integer offset. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being read is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array before reading its element values. It is used just like the examples shown in the Marshal.ReadByte
section, but with a different array element type.
To read other data types directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
. To write data directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
.
ReadInt64
MethodThe ReadInt64
method is useful for extracting a single 64-bit integer value at a given offset from a pointer to an unmanaged object. It has three overloads just like Marshal.ReadByte
, but with an Int64
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), and an integer offset. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being read is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array before reading its element values. It is used just like the examples shown in the Marshal.ReadByte
section, but with a different array element type.
To read other data types directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, and Marshal.ReadIntPtr
. To write data directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
.
ReadIntPtr
MethodThe ReadIntPtr
method is useful for extracting a single platform-sized integer value at a given offset from a pointer to an unmanaged object. It has three overloads just like Marshal.ReadByte
, but with an IntPtr
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), and an integer offset. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being read is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array before reading its element values. It is used just like the examples shown in the Marshal.ReadByte
section, but with a different array element type.
To read other data types directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, and Marshal.Read64
. To write data directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
.
ReAllocCoTaskMem
MethodThis method is one of the two memory re-allocation APIs in the Marshal
class (the other being ReAllocHGlobal
). ReAllocCoTaskMem
exposes the similarly-named CoTaskMemRealloc
COM API, which re-allocates COM task memory. It has two parameters—a System.IntPtr
type representing the pointer to the memory to be reallocated (which must have been previously allocated by Marshal.AllocCoTaskMem
or the equivalent unmanaged API), and an integer containing the new size, in bytes, of the memory block. The method returns a System.IntPtr
value representing a pointer to the re-allocated block of memory. If IntPtr.Zero
(representing a null value) is passed as the first parameter, new memory is allocated just as AllocCoTaskMem
would do.
As with using CoTaskMemRealloc
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 beginning of the reallocated memory content is the same as the original memory, although the entire memory block may be in a different location. Whereas CoTaskMemRealloc
returns null on failure, Marshal.ReAllocCoTaskMem
throws an OutOfMemoryException
on failure.
For more information, see MSDN Online for a description of CoTaskMemRealloc
, and the FreeCoTaskMem
and ReAllocCoTaskMem
methods listed later in this section.
ReAllocHGlobal
MethodThis method is one of the two memory re-allocation APIs in the Marshal
class (the other being ReAllocCoTaskMem
). ReAllocHGlobal
re-allocates fixed memory from the “global heap.” Win32 does not provide a separate global heap and local heap, so this method exposes the Win32 GlobalRealloc
API from KERNEL32.DLL
, which does the same thing as the LocalRealloc
API from the same DLL.
This method has two parameters—a System.IntPtr
type representing the pointer to the memory to be reallocated (which must have been previously allocated by Marshal.AllocHGlobal
or the equivalent unmanaged APIs), and a System.IntPtr
type containing the new size, in bytes, of the memory block. The method returns a System.IntPtr
value representing a pointer to the re-allocated block of memory.
As with using LocalRealloc
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.ReAllocHGlobal
calls LocalRealloc
with the LMEM_MOVEABLE
flag.
Whereas LocalRealloc
returns null on failure, Marshal.ReAllocHGlobal
throws an OutOfMemoryException
on failure. If this occurs, the memory that was previously allocated with Marshal.AllocHGlobal
has not been freed, so calling Marshal.FreeHGlobal
(or an equivalent unmanaged API) is still necessary.
For more information, see MSDN Online for a description of LocalRealloc
, and the FreeHGlobal
and AllocHGlobal
methods listed in this section.
Release
MethodAll three IUnknown
methods—QueryInterface
, AddRef
, and Release
—are exposed through the Marshal
class and can be called on any COM object. Marshal.Release
exposes a COM object’s IUnknown.Release
method, which decrements 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 Release
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.Release
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. Be sure not to call Release
unless you were responsible for a corresponding AddRef
call. Calling Release
after the reference count has reached zero causes undefined behavior.
Because the CLR manages a COM object’s reference count for you, it’s almost never necessary to call Marshal.Release
. In some advanced cases, however, like custom marshaling or working around COM object oddities, it can become necessary. For example, this method should be called after calling any of the other Marshal
methods that return an IntPtr
representing a COM interface pointer.
The following code demonstrates the use of Marshal.Release
, 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.Release(pUnk);
Dim pUnk As IntPtr = Marshal.GetIUnknownForObject(myComObject)
Dim refCount As Integer = Marshal.Release(pUnk)
C++:
IntPtr pUnk = Marshal::GetIUnknownForObject(myComObject);
int refCount = Marshal::Release(pUnk);
For more information, see Marshal.QueryInterface
and Marshal.AddRef
listed earlier in this section.
ReleaseComObject
MethodReleaseComObject
provides a means to instantly release a COM object rather than waiting for its Runtime-Callable Wrapper to be garbage collected. It’s often necessary to use this method with COM objects that hold onto limited resources (such as a database connection or Windows graphics objects) and don’t release them until the object is destroyed.
The method takes a System.Object
parameter and returns a 32-bit integer representing the RCW’s reference count on the COM object. Note that ReleaseComObject
only decrements the wrapper’s reference count—the original COM object won’t be destroyed until its own reference count reaches zero.
For more information, see Chapter 6.
ReleaseThreadCache
MethodThis method is obsolete, and should never be called.
SetComObjectData
MethodEvery COM object wrapped in a Runtime-Callable Wrapper has a hashtable associated with it. This method is a means of setting this data, and GetComObjectData
is a means of retrieving the data. User code should have no reason to call this method.
SizeOf
MethodThis method can be used to discover the size, in bytes, of the unmanaged view of a structure that has a managed definition. This size might not correspond to the managed size because marshaling can transform a .NET structure into something with a different size.
SizeOf
has two overloads—both return a 32-bit integer containing the structure’s size and both have a single parameter. The first overload takes a System.Object
parameter whereas the second overload takes a System.Type
parameter. The overload with a System.Object
parameter can be used on an instance of the structure. This can be a reference type or a boxed value type, but must have sequential or explicit layout. The overload with a System.Type
parameter can be used when you don’t have an instance of the structure. The same layout restrictions apply to this overload as well. Attempting to call SizeOf
with an illegal type or instance results in an ArgumentException
with the message, “Type typeName cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.”
C# has a built-in sizeof
operator that works on a type instance, but this is not the same thing as Marshal.SizeOf
! It reports the managed size of a structure rather than the unmanaged size of the marshaled structure. See Chapter 6 for more information.
To obtain the offset of a single field in a structure, see the Marshal.OffsetOf
method.
StringToBSTR
MethodThis method creates a COM BSTR
type with contents copied from a .NET string. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated BSTR
. This method does the opposite of Marshal.PtrToStringBSTR
, which creates a System.String
from the contents of a BSTR
.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates a BSTR
with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this BSTR
when finished with it using Marshal.FreeBSTR
or one of its corresponding unmanaged APIs.
You normally don’t need to interact with BSTR
types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
StringToCoTaskMemAnsi
MethodThis method creates an unmanaged ANSI string, allocated with CoTaskMemAlloc
, and copies the contents of a .NET string into it. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringAnsi
, which creates a System.String
from the contents of an ANSI string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates an ANSI string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeCoTaskMem
or its corresponding unmanaged API.
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated on the global heap instead, call Marshal.StringToHGlobalAnsi
.
StringToCoTaskMemAuto
MethodThis method creates an unmanaged string, allocated with CoTaskMemAlloc
, and copies the contents of a .NET string into it. This string is either ANSI or Unicode based on the current operating system. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringAuto
, which creates a System.String
from the contents of a platform-dependent string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates a platform-dependent string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeCoTaskMem
or its corresponding unmanaged API.
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated on the global heap instead, call Marshal.StringToHGlobalAuto
.
StringToCoTaskMemUni
MethodThis method creates an unmanaged Unicode string, allocated with CoTaskMemAlloc
, and copies the contents of a .NET string into it. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringUni
, which creates a System.String
from the contents of a Unicode string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates a Unicode string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeCoTaskMem
or its corresponding unmanaged API. (The original string is always copied despite the fact that .NET strings are Unicode internally.)
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated on the global heap instead, call Marshal.StringToHGlobalUni
.
StringToHGlobalAnsi
MethodThis method creates an unmanaged ANSI string, allocated with the same memory allocator used by AllocHGlobal
, and copies the contents of a .NET string into it. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringAnsi
, which creates a System.String
from the contents of an ANSI string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates an ANSI string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeHGlobal
or an equivalent unmanaged API.
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated using the COM task memory allocator instead, call Marshal.StringToCoTaskMemAnsi
.
StringToHGlobalAuto
MethodThis method creates an unmanaged string, allocated with the same memory allocator used by AllocHGlobal
, and copies the contents of a .NET string into it. This string is either ANSI or Unicode based on the current operating system. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringAuto
, which creates a System.String
from the contents of a platform-dependent string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates a platform-dependent string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeHGlobal
or an equivalent unmanaged API.
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated using the COM task memory allocator instead, call Marshal.StringToCoTaskMemAuto
.
StringToHGlobalUni
MethodThis method creates an unmanaged Unicode string, allocated with the same memory allocator used by AllocHGlobal
, and copies the contents of a .NET string into it. It has a single System.String
parameter and returns a System.IntPtr
value that represents the pointer to the allocated string. This method does the opposite of Marshal.PtrToStringUni
, which creates a System.String
from the contents of a Unicode string.
If you pass a null string, this method returns IntPtr.Zero
. Otherwise, it allocates a Unicode string with the contents of the input string. This method throws an OutOfMemoryException
if the string to return could not be allocated. You must always free this string when finished with it using Marshal.FreeHGlobal
or an equivalent unmanaged API. (The original string is always copied despite the fact that .NET strings are Unicode internally.)
You normally don’t need to interact with unmanaged string types in managed code. This method, however, can be useful in custom marshaling scenarios or when mixing managed and unmanaged code in C++.
If you want the returned string to be allocated using the COM task memory allocator instead, call Marshal.StringToCoTaskMemUni
.
StructureToPtr
MethodThe StructureToPtr
method can be used to obtain a raw pointer to the memory address of a .NET type with sequential or explicit layout. (The Marshal.PtrToStructure
method does the reverse of this.) This is often necessary in COM Interoperability and PInvoke when structure parameters are represented as an IntPtr
value. This method has the following signature:
public static void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);
Visual Basic .NET:
Public Shared Sub StructureToPtr(structure As Object, ptr As IntPtr, _
fDeleteOld As Boolean)
C++:
public: static void StructureToPtr(Object* structure, IntPtr ptr,
bool fDeleteOld);
The first parameter is the object for which the pointer is desired. This can be a reference type or a boxed value type, but if the object does not have sequential or explicit layout, an ArgumentException
is thrown.
The second parameter is a pointer value that must point to memory that has already been allocated. Therefore, the IntPtr
is passed by-value. The .NET object will be copied to the specified location in memory. It’s up to the user to free the memory appropriately after the StructureToPtr
call, depending on how the memory was allocated.
The last parameter, if true, assumes that the IntPtr
parameter points to an existing structure of the same type as the first parameter and destroys any fields that are reference types (but does not free the memory required for the new structure). Passing false suppresses this behavior. If StructureToPtr
simply overwrote memory containing a structure with, for example, an unmanaged string field, the string would never get freed and would result in a memory leak. To free the appropriate fields when passing true for fDeleteOld
, StructureToPtr
calls the Marshal.DestroyStructure
method.
If you don’t want to copy a structure but simply want to pin an existing structure and get its address as an IntPtr
value, use the GCHandle
type in System.Runtime.InteropServices
to create a pinned handle for the structure. This is more efficient because it avoids copying.
Although using the GCHandle
type is the best language-neutral way to get a pointer from a structure, StructureToPtr
is useful for swapping one structure with another in the same memory location. The following code demonstrates this.
IntPtr addressOfStructure1 = ...;
TYPEATTR structure2 = ...;
Marshal.StructureToPtr(structure2, addressOfStructure1, true);
Visual Basic .NET:
Dim IntPtr addressOfStructure1 As IntPtr = ...
Dim structure2 As TYPEATTR = ...
Marshal.StructureToPtr(structure2, addressOfStructure1, True)
C++:
IntPtr addressOfStructure1 = ...;
TYPEATTR structure2 = ...;
Marshal::StructureToPtr(__box(structure2), addressOfStructure1, true);
For more information, see the Marshal.PtrToStructure
and Marshal.DestroyStructure
methods listed earlier in this section, and Chapter 6.
SystemDefaultCharSize
PropertyThis integer read-only property returns the default character size, in bytes, for the current operating system. This value is used by the PtrToStringAuto
, StringToHGlobalAuto
, and StringToCoTaskMemAuto
methods to make the appropriate string transformations from managed to unmanaged and vice versa.
On Unicode platforms, this value is 2. On ANSI platforms, this value is 1.
SystemMaxDBCSCharSize
PropertyThis integer read-only property returns the maximum double-byte character set (DBCS) size, in bytes, for the current operating system. This value is used by the StringToHGlobalAnsi
and StringToCoTaskMemAnsi
methods to make the appropriate string transformations.
On English Windows platforms, this value is 1. On some international platforms, such as Japanese, this value is 2.
ThrowExceptionForHR
MethodThe ThrowExceptionForHR
method, the opposite of GetHRForException
, throws a .NET exception type that corresponds to an HRESULT
value. The nice thing about this method is that it doesn’t simply throw a COMException
with the ErrorCode
property set to the passed-in HRESULT
. (Otherwise, there wouldn’t be much point in having this method.) Instead, it makes use of the HRESULT
-to-exception conversions listed in Appendix C. So whereas a custom HRESULT
is simply mapped to a COMException
, an HRESULT
like E_OUTOFMEMORY
is mapped to an OutOfMemoryException
.
The method has two overloads—one that takes a 32-bit integer representing the HRESULT
, and one that takes the same 32-bit integer plus a System.IntPtr
type that represents a pointer to an IErrorInfo
interface. This can be used to give the .NET exception a customized error message, source, and so on. If -1 is passed for this IntPtr
value, no extra error information is used. If IntPtr.Zero
is passed (the same as calling the single-parameter overload), the Windows GetErrorInfo
API is called to attempt to retrieve an error object available on the current thread. Otherwise, a valid pointer to an IErrorInfo
interface could be passed to directly set this extra information.
Notice that the exception is thrown directly from the method so you can’t check the type before throwing it without catching it first then rethrowing the exception. This method is useful for custom marshaling because throwing a reasonable exception for a returned HRESULT
can be done without extra work. However, you might wish to further customize the exceptions thrown to reduce the number of situations in which COMException
is thrown.
If you want to throw an exception with a success HRESULT
value (rather than changing your signature to return an HRESULT
directly using PreserveSigAttribute
), ThrowExceptionForHR
does not enable this. Calling it with a success HRESULT
value does nothing. Instead, you could throw a COMException
with an ErrorCode
set to the success HRESULT
value.
For more information, see Chapter 20 and Appendix C.
UnsafeAddrOfPinnedArrayElement
MethodThe UnsafeAddrOfPinnedArrayElement
method can give you the memory address of any element of a pinned .NET array. It takes two parameters—a System.Array
type representing the .NET array, and an integer index of the element whose address is desired. It returns a System.IntPtr
value representing the memory address.
The reason the UnsafeAddrOfPinnedArrayElement
method begins with the word “Unsafe” is to alert you to the fact that, for maximum performance, it does no validation on the array passed in. This means that you can pass an unpinned array and not cause an exception to be thrown. Use caution, because passing an unpinned array can cause unexpected results.
WriteByte
MethodThe WriteByte
method is useful for writing a single byte value at a given offset directly to a pointer to an unmanaged object. It has three overloads:
public static void WriteByte([MarshalAs(UnmanagedType.AsAny)] object ptr,
int ofs, byte val);
public static void WriteByte(IntPtr ptr, int ofs, byte val);
public static void WriteByte(IntPtr ptr, byte val);
Visual Basic .NET:
Public Overloads Shared Sub WriteByte(<MarshalAs(UnmanagedType.AsAny)> _
ptr As Object, ofs As Integer, val As Byte)
Public Overloads Shared Sub WriteByte(ptr As IntPtr, ofs As Integer, _
val As Byte)
Public Overloads Shared Sub WriteByte(ptr As IntPtr, val As Byte)
C++:
public: static void WriteByte([MarshalAs(UnmanagedType::AsAny)] Object* ptr,
int ofs, unsigned char val);
public: static void WriteByte(IntPtr ptr, int ofs, unsigned char val);
public: static void WriteByte(IntPtr ptr, unsigned char val);
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), an integer offset, and the value to write. The object passed as the first parameter could even be a .NET object with the appropriate format (such as an array), but there would be no reason to pass such an object to this method. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being written to is within the range of the input object.
This method enables direct interaction with an unmanaged C-style byte array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array, setting its element values, then copying it back. This is demonstrated in the following code, which sets an unmanaged array’s ten elements to the values 1 through 10:
C#:
IntPtr unmanagedArray = ...;
// One way to set the 10 elements of the C-style unmanagedArray
byte [] newArray = new byte[10];
Marshal.Copy(unmanagedArray, newArray, 0, 10);
for (int i = 0; i < newArray.Length; i++)
newArray[i] = i+1;
Marshal.Copy(newArray, 0, unmanagedArray, 10);
// Another way to set the 10 elements of the C-style unmanagedArray
for (int i = 0; i < 10; i++)
Marshal.WriteByte(unmanagedArray, i, i+1);
Visual Basic .NET:
Dim unmanagedArray As IntPtr = ...
Dim i As Integer
' One way to set the 10 elements of the C-style unmanagedArray
Dim newArray As Byte(9)
Marshal.Copy(unmanagedArray, newArray, 0, 10)
For i = 0 To newArray.Length
newArray(i) = i+1
Next i
Marshal.Copy(newArray, 0, unmanagedArray, 10)
' Another way to set the 10 elements of the C-style unmanagedArray
For i = 0 To 10
Marshal.WriteByte(unmanagedArray, i, i+1)
Next i
C++:
IntPtr unmanagedArray = ...;
// One way to set the 10 elements of the C-style unmanagedArray
unsigned char newArray __gc[] = new unsigned char __gc[10];
Marshal::Copy(unmanagedArray, newArray, 0, 10);
for (int i = 0; i < newArray.Length; i++)
newArray[i] = i+1;
Marshal::Copy(newArray, 0, unmanagedArray, 10);
// Another way to set the 10 elements of the C-style unmanagedArray
for (int i = 0; i < 10; i++)
Marshal::WriteByte(unmanagedArray, i, i+1);
To write other data types directly to an unmanaged pointer, see Marshal.WriteInt16
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
. To read data directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
.
WriteInt16
MethodThe WriteInt16
method is useful for writing a single 16-bit integer value or a character value at a given offset directly to a pointer to an unmanaged object. Unlike the other Write...
methods, it has six overloads—three for writing a plain 16-bit integer, and three for writing a character value.
In version 1.0 of the .NET Framework, the overloads of Marshal.WriteInt16
that write a character rather than a plain 16-bit value should not be used. This is because they incorrectly write only one byte of the character rather than two; treating it as an ANSI character rather than Unicode! To work around this, you can first convert a System.Char
type to a 16-bit integer by calling System.Convert.ToInt16
, and then call Marshal.WriteInt16
with this value.
Two overloads take an object representing the unmanaged entity (marshaled as a void*
), an integer offset, and the value to write. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
Two more overloads work the same way as the previous two, but expect the pointer to the unmanaged object to be passed via an IntPtr
type. The two remaining overloads use an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being written to is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array, setting its element values, and then copying it back. It is used just like the examples shown in the Marshal.WriteByte
section, but with a different array element type.
To write other data types directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt32
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
. To read data directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
.
WriteInt32
MethodThe WriteInt32
method is useful for writing a single 32-bit integer value at a given offset directly to a pointer to an unmanaged object. It has three overloads just like Marshal.WriteByte
, but with an Int32
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), an integer offset, and the value to write. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being written to is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array, setting its element values, and then copying it back. It is used just like the examples shown in the Marshal.WriteByte
section, but with a different array element type.
To write other data types directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt64
, and Marshal.WriteIntPtr
. To read data directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
.
WriteInt64
MethodThe WriteInt64
method is useful for writing a single 64-bit integer value at a given offset directly to a pointer to an unmanaged object. It has three overloads just like Marshal.WriteByte
, but with an Int64
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), an integer offset, and the value to write. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being written to is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array, setting its element values, and then copying it back. It is used just like the examples shown in the Marshal.WriteByte
section, but with a different array element type.
To write other data types directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, and Marshal.WriteIntPtr
. To read data directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
.
WriteIntPtr
MethodThe WriteIntPtr
method is useful for writing a single platform-sized integer value at a given offset directly to a pointer to an unmanaged object. It has three overloads just like Marshal.WriteByte
, but with an IntPtr
return type.
The first overload takes an object representing the unmanaged entity (marshaled as a void*
), an integer offset, and the value to write. Any object passed that can’t be treated appropriately causes an ArgumentException
to be thrown with the message, “No PInvoke conversion exists for value passed to Object-typed parameter.”
The second overload works the same way as the first, but expects the pointer to the unmanaged object to be passed via an IntPtr
type. The third overload works just like the second one, but with an implied offset of zero. For all overloads, it is the responsibility of the caller to ensure that the memory being written to is within the range of the input object.
This method enables direct interaction with an unmanaged C-style array so you don’t need to incur the expense of copying an entire unmanaged array (using Marshal.Copy
) to a separate .NET array, setting its element values, and then copying it back. It is used just like the examples shown in the Marshal.WriteByte
section, but with a different array element type.
To write other data types directly to an unmanaged pointer, see Marshal.WriteByte
, Marshal.WriteInt16
, Marshal.WriteInt32
, and Marshal.WriteInt64
. To read data directly from an unmanaged pointer, see Marshal.ReadByte
, Marshal.ReadInt16
, Marshal.ReadInt32
, Marshal.ReadInt64
, and Marshal.ReadIntPtr
.
MarshalAsAttribute
Pseudo-Custom AttributeMarshalAsAttribute
is a widely–used, pseudo-custom attribute that controls which .NET data types (when used as parameters, fields, or return values) correspond to which unmanaged data types. Every .NET data type has a corresponding unmanaged type to which they are marshaled by default. Several data types (such as blittable ones) only have a single unmanaged representation that can’t be changed. Others have a few corresponding unmanaged types, such as System.String
, which can be marshaled to a BSTR
, LPSTR
, LPWSTR
, and so on.
The default marshaling behavior of some data types depends on how they are used. For parameters and return types, some types have different default marshaling behavior if used with a COM member or with a PInvoke method. The default marshaling behavior for fields of structures mostly matches the PInvoke behavior regardless of whether the structure is used with COM Interoperability or with PInvoke. For example:
• String
parameters in COM methods marshal as BSTR
by default, but String
parameters in PInvoke methods and String
fields in value types marshal as LPSTR
by default.
• Boolean
parameters in COM methods marshal as VARIANT_BOOL
by default, but Boolean
parameters in PInvoke methods and Boolean
fields in value types marshal as BOOL
(a 32-bit integer) by default.
• Object
parameters in COM methods marshal as VARIANT
by default, but Object
parameters in PInvoke methods and Object
fields in value types marshal as an IUnknown
interface pointer by default.
MarshalAsAttribute
has two constructors—one that takes a 16-bit integer and one that takes an UnmanagedType
enumeration. The latter constructor should always be used. It also has several named parameters that can be used with certain UnmanagedType
values. (In the case of UnmanagedType.CustomMarshaler
and UnmanagedType.ByValArray
, an additional named parameter must be used.) These parameters are:
• ArraySubType
. Valid with UnmanagedType.LPArray
or UnmanagedType.ByValArray
. This parameter can be set to an UnmanagedType
value specifying the unmanaged type of the array’s elements. The rules are the same for this element type as for applying MarshalAsAttribute
to a simple parameter. If no value is specified, the default unmanaged type corresponding to the managed array’s element type is used.
• MarshalCookie
. Valid with UnmanagedType.CustomMarshaler
. This parameter can be set to a string that supplies a custom marshaler with additional information. The ExpandoToDispatchExMarshaler
that ships with the .NET Framework uses this string set to either “IReflect” or “IExpando.”
• MarshalType
. Valid with UnmanagedType.CustomMarshaler
. Either MarshalType
or MarshalTypeRef
must be used with UnmanagedType.CustomMarshaler
to specify the custom marshaler type for the marked parameter, return type, or field. MarshalType
can be set to a string representing the assembly-qualified type name of the custom marshaler. Although this can be useful for late-bound references, using MarshalTypeRef
is preferred.
• MarshalTypeRef
. Valid with UnmanagedType.CustomMarshaler
. Either MarshalType
or MarshalTypeRef
must be used with UnmanagedType.CustomMarshaler
to specify the custom marshaler type for the marked parameter, return type, or field. MarshalTypeRef
can be set to a Type
object using typeof
in C#, GetType
in VB .NET, or __typeof
in C++. This is the preferred means of specifying a custom marshaler.
• SafeArraySubType
. Valid with UnmanagedType.SafeArray
. This can be set to a value from the VarEnum
enumeration to specify the type of the SAFEARRAY
’s elements, such as VarEnum.VT_R8
or VarEnum.VT_UNKNOWN
. If none is specified, the managed element type’s default type that would be used when passed in a VARIANT
is chosen.
• SafeArrayUserDefinedSubType
.Valid with UnmanagedType.SafeArray
only when SafeArraySubType
is set to VarEnum.VT_RECORD
, VarEnum.VT_DISPATCH
, or VarEnum.VT_UNKNOWN
. This can be set to a Type
object for the user-defined value type representing the array’s element type. This is only useful when the marked data type is the generic System.Array
, to be able to expose a SAFEARRAY
of type VT_RECORD
to COM with an arbitrary rank or non-zero lower bounds.
• SizeConst
. Valid with UnmanagedType.ByValArray
or UnmanagedType.ByValTStr
. This parameter can be set to an integer to specify the number of elements in the array, or to specify the number of characters (not bytes) in the string. UnmanagedType.ByValArray
and UnmanagedType.ByValTStr
are not valid without the use of SizeConst
.
• SizeParamIndex
. Valid with UnmanagedType.LPArray
or UnmanagedType.ByValArray
. This parameter can be set to a 16-bit integer to specify the zero-based index of a parameter containing the size of the array. The parameter containing the size must be a by-value integer type. (If both SizeConst
and SizeParamIndex
are used with UnmanagedType.LPArray
, their values are simply added together to get the total size.)
The type library importer marks imported parameters, return types, and fields with this attribute when the data type in the input type library is not the default unmanaged data type for the corresponding managed data type. For clarity, the importer always marks String
and Object
types with the attribute even when it exhibits default behavior.
For a complete list of which UnmanagedType
values can be used on which .NET data types, consult Chapter 12. For MarshalAsAttribute
information that’s helpful for PInvoke, consult Chapter 19. For the special IL Assembler syntax of MarshalAsAttribute
and its corresponding values, see Chapter 7. For information about the named parameters specific to custom marshaling, see Chapter 20.
MarshalDirectiveException
ExceptionThe Interop Marshaler sometimes throws a MarshalDirectiveException
when it encounters a signature with an improperly used MarshalAsAttribute
pseudo-custom attribute. In many cases, unfortunately, the marshaler silently ignores incorrect markings when the correct course of action is obvious.
Because many invalid uses of MarshalAsAttribute
are caught at compile-time (thanks to being a pseudo-custom attribute), many invalid uses are never emitted into metadata in the first place. However, it’s still fairly easy to come up with an example that causes the exception to be thrown. For example, the following C# signature:
// Error: Cannot marshal a string as an interface
void InvalidMarshaling([MarshalAs(UnmanagedType.Interface)] string s);
causes MarshalDirectiveException
to be thrown with the following message:
Cannot marshal parameter #1: Invalid managed/unmanaged type combination (Strings must be paired with LPStr, LPWStr, LPTStr, BStr, TBStr, or AnsiBStr).
However, the exception only occurs when the Interop Marshaler is involved, so either when unmanaged code calls InvalidMarshaling
implemented by a .NET class, or vice versa.
An Interop Assembly produced from the type library importer should almost never cause a MarshalDirectiveException
to be thrown, assuming it hasn’t been modified as in Chapter 7.
MarshalDirectiveException
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_MARSHALDIRECTIVE
, defined by the .NET Framework SDK as 0x80131535.
ObjectCreationDelegate
DelegateObjectCreationDelegate
is the delegate used by ExtensibleClassFactory.RegisterObjectCreationCallback
. The delegate’s signature has one aggregator parameter of type System.IntPtr
, and returns a System.IntPtr
type. When this delegate is passed to RegisterObjectCreationCallback
, the method implementation is invoked instead of CoCreateInstance
when a .NET object needs to instantiate the COM object it derives from. The input IntPtr
parameter represents the IUnknown
pointer of the .NET object. The delegate is responsible for returning an IntPtr
representing the IUnknown
pointer of the COM object. For version 1.0 of the .NET Framework, this delegate must always be constructed with an instance method rather than a static method.
For more information, consult Chapter 6 and the ExtensibleClassFactory
class listed earlier in this section.
OptionalAttribute
Pseudo-Custom AttributeThe parameter-less OptionalAttribute
pseudo-custom attribute can be marked on a parameter in order to turn it into an optional parameter. This pseudo-custom attribute sets the same metadata bit set by VB .NET’s Optional
keyword. This means that despite popular opinion, members with optional parameters can be defined in C#! This doesn’t change the important fact, however, that optional parameters can’t be used like optional parameters in C#.
This pseudo-custom attribute is useful when manually defining type information for COM members that use optional parameters in languages other than Visual Basic .NET. You can define a C# interface with optional parameters, and a VB .NET client can omit the optional arguments when calling its members.
An important restriction of using OptionalAttribute
, however, is that there’s no direct way to set a default value unless the language supports such a concept (as in VB .NET). Therefore, OptionalAttribute
should only be used on System.Object
parameters, because such parameters have an implicit default value of System.Reflection.Missing
(the type of the Type.Missing
field).
The type library importer marks any optional parameters from the input type library with OptionalAttribute
.
For more information, consult Chapter 21.
OutAttribute
Pseudo-Custom AttributeThe OutAttribute
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 Out
marking means that data flows from the callee to the caller.
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.
The following are three valid ways to use OutAttribute
:
• Marking a by-reference parameter with OutAttribute
and not InAttribute
makes it equivalent to a C#-style out parameter. C#, however, forbids this combination because you can use the out
keyword instead.
• Marking a by-value formatted reference type parameter or array parameter with OutAttribute
and not InAttribute
gives it “out”-only behavior rather than “in”-only. Still, the change in behavior isn’t noticed when crossing the managed/unmanaged boundary in the same apartment when the type has all blittable fields or elements because the Interop Marshaler pins such instances.
• Marking a by-value formatted reference type parameter or array parameter with both OutAttribute
and InAttribute
is often recommended to preserve the “in/out” semantics expected for reference types. For example, unless you mark an array of non-blittable Object
types with InAttribute
and OutAttribute
, callers don’t see any changes to the array’s elements made by the callee.
Marking a value type parameter that is passed by-value with OutAttribute
has no effect, because there’s no way for the object’s data to be passed out to the caller.
Because the first two actions cancel the default “in” part of the data flow behavior by omitting InAttribute
, using OutAttribute
can provide better performance for non-blittable types when you don’t care about passing data into the callee.
For the third action, OutAttribute
is used in conjunction with InAttribute
to provide the same .NET semantics to reference type parameters such as arrays when calling into unmanaged code as one would expect when calling into managed code. 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 had better mark the parameter as such!
The type library importer marks any parameters with OutAttribute
if the corresponding parameter has an IDL [out]
marking in the input type library (although it omits it on return values because OutAttribute
can only be applied on parameters).
For more information, see Chapter 12.
PARAMDESC
Value TypeThis is a .NET definition of the unmanaged PARAMDESC
structure used by the DESCUNION
COM union. In the .NET Framework, this definition is used by the DESCUNION
value type that redefines the unmanaged union for managed clients.
For more information, consult MSDN Online for information about the original PARAMDESC
structure.
PARAMFLAG
EnumerationThis is a .NET definition of the unmanaged PARAMFLAG
enumeration used by the PARAMDESC
value type.
For more information, consult MSDN Online for information about the original PARAMFLAG
enumeration.
PreserveSigAttribute
Pseudo-Custom AttributeThe PreserveSigAttribute
pseudo-custom attribute can be marked on methods (or property accessors) to indicate that a .NET signature “preserves” its corresponding COM signature. The CLR does not do any HRESULT
-to-exception transformation for such signatures. Because most COM members return an HRESULT
, most managed signatures marked with PreserveSigAttribute
return an integer representing the HRESULT
, whether it’s a success value or a failure value. Any out
, retval
parameters are left as out parameters in the managed signature.
Marking a COM signature with PreserveSigAttribute
is necessary if the member returns more than one success HRESULT
value (such as S_OK
and S_FALSE
) and you want to detect the different values. Members that aren’t marked with PreserveSigAttribute
have failure HRESULT
return values translated into exceptions, but success HRESULT
s for such members are discarded.
The type library importer only marks members of dispinterfaces with PreserveSigAttribute
.
For more information, see Chapters 7, and 12.
PrimaryInteropAssemblyAttribute
Custom AttributeThe PrimaryInteropAssemblyAttribute
can be marked on an assembly to identify it as a Primary Interop Assembly. The custom attribute’s constructor has two integer parameters—major
and minor
—that identify the version of the type library represented by the assembly. Nothing else about the original type library is contained in the custom attribute because other custom attributes that are marked on any Interop Assembly already contain this information: the assembly’s GuidAttribute
contains the type library’s LIBID, and the assembly’s ImportedFromTypeLibAttribute
contains the type library’s filename.
A Primary Interop Assembly can be created using the TypeLibConverter
class or with the TLBIMP.EXE
SDK tool and its /primary
option. Visual Studio .NET looks for Primary Interop Assemblies registered under HKEY_CLASSES_ROOTTypeLib{
LIBID
}
in the Windows Registry. The RegistrationServices
class and the REGASM.EXE
SDK tool place the necessary Primary Interop Assembly registry entries when encountering an assembly marked with PrimaryInteropAssemblyAttribute
.
The registration process can register a single Primary Interop Assembly for multiple versions of the same input type library, simply by having the assembly marked with multiple PrimaryInteropAssemblyAttribute
custom attributes. However, the type library importer does not provide a means to create a Primary Interop Assembly with more than one PrimaryInteropAssemblyAttribute
custom attribute, so doing this requires the techniques of Chapter 7.
For more information about the process of creating a Primary Interop Assembly, see Chapter 15, “Creating and Deploying Useful Primary Interop Assemblies.”
ProgIdAttribute
Custom AttributeThe ProgIdAttribute
custom attribute can be marked on .NET classes to choose a ProgID that gets registered for the class. The attribute has a single constructor that takes a string parameter containing the ProgID. If no ProgIdAttribute
is used, the class’s name is used, qualified with its namespace.
For simplicity, you should refrain from using ProgIdAttribute
. However, the rules of COM dictate that a ProgID must:
• have 39 characters or fewer
• contain no punctuation besides periods—not even underscores
• not start with a digit
• be different from the class name of any OLE 1 application
It’s a good idea to choose a different ProgID if the namespace-qualified class name breaks any of these rules. Many existing COM components don’t obey these rules, however.
Another purpose for using ProgIdAttribute
is to match an existing COM object’s ProgID with a new .NET class.
RegistrationServices
ClassThis class, which implements IRegistrationServices
, contains methods used to register and unregister assemblies for use by COM. The REGASM.EXE
SDK tool and Visual Studio .NET use RegistrationServices
to add or remove COM-enabling registry entries for .NET components. The methods that add and remove registry entries also invoke custom registration or unregistration functions for any types that define them. The action of generating a registry file (exposed by REGASM.EXE
’s /regfile
option) is not provided by RegistrationServices
, but the class contains several methods that can be helpful in discovering the entries that would be placed in such a file.
The RegistrationServices
methods are described in the following sections. For more information about any them, see Chapter 22. For more information about custom registration functions, see Chapter 12 and the ComRegisterFunction
and ComUnregisterFunction
custom attributes listed previously in this section.
GetManagedCategoryGuid
MethodThe GetManagedCategoryGuid
method takes no parameters and returns a Guid
instance representing the CATID for the “.NET Category” that’s listed as an implemented category by every registered .NET type. This CATID is: 62C8FE65-4EBB-45e7-B440-6E39B2CDBF29
.
GetProgIdForType
MethodThe GetProgIdForType
method takes a System.Type
parameter and returns a string containing what the ProgID would be for the input type if it were registered. This string is either a string specified in a ProgIdAttribute
marked on the type or the fully qualified type name if no such attribute exists.
GetRegistrableTypesInAssembly
MethodThe GetRegistrableTypesInAssembly
method takes an Assembly
parameter and returns an array of Type
instances representing every type inside the assembly that would be registered by the RegisterAssembly
method. This means that the returned array has a Type
object for every public and COM-visible class with a public default constructor.
RegisterAssembly
MethodRegisterAssembly
, as you might have guessed, registers an assembly. It takes two parameters—an Assembly
instance for the assembly to register, and an AssemblyRegistrationFlags
enumeration value to customize the registration process. It returns true if types were registered successfully, or false if the assembly has no types that are appropriate to be registered by COM. For example, this would occur if every type was non-public, COM-invisible, or a class without a public default constructor.
Besides entering the standard registry entries, RegisterAssembly
invokes any custom registration functions defined by types using the ComRegisterFunctionAttribute
custom attribute.
RegisterTypeForComClients
MethodRegisterTypeForComClients
is equivalent to calling the CoRegisterClassObject
COM API. It takes two parameters, a System.Type
instance and a System.Guid
instance. Calling this method registers the class factory for the specified type using the GUID passed as the second parameter.
You should avoid this method due to subtle threading behavior. If the current thread’s state has not already been initialized, calling this method causes it to be set to MTA!
TypeRepresentsComType
MethodThe TypeRepresentsComType
method returns true if the input type is marked as ComImport
(or if it derives from the ComImport
type and has the same GUID), and false otherwise. It takes a single System.Type
instance as input. See Chapter 22 for a comparison of all the .NET Framework methods for checking whether an object is a COM object.
This method is useful for creating registry files because the registry entries differ based on whether the type is marked with ComImport
. For ComImport
types, the only keys added are Class
, Assembly
, RuntimeVersion
, and possibly CodeBase
, under the InprocServer32
key.
TypeRequiresRegistration
MethodTypeRequiresRegistration
simply returns true if the input type would be registered by a call to RegisterAssembly
, and false otherwise. It takes a single System.Type
instance as input.
UnregisterAssembly
MethodUnregisterAssembly
is the method that unregisters an assembly. It takes only one parameter—an Assembly
instance for the assembly to unregister. It returns true if types were unregistered successfully, or false if the assembly has no types that were registered.
Besides removing the standard registry entries, UnregisterAssembly
invokes any custom unregistration functions defined by types using the ComUnregisterFunctionAttribute
custom attribute.
RuntimeEnvironment
ClassThis class has four static (Shared
in VB .NET) members that provide information about the Common Language Runtime that’s running in the current process. (Although multiple CLRs can run simultaneously on the same computer, only one can be running in a given process.) These members aren’t directly related to COM Interoperability and PInvoke and have nothing to do with unmanaged code, but they can be helpful nonetheless in scenarios requiring knowledge of the CLR’s whereabouts. The members are:
• The SystemConfigurationFile
property. This is a read-only string property that returns the fully qualified name of the currently-running CLR’s configuration file. For example, it might return a string that looks like the following:
C:WindowsMicrosoft.NETFrameworkv1.2.3456configmachine.config
Using this property requires FileIOPermission
with the PathDiscovery
flag.
• The FromGlobalAccessCache
method. This method takes a single System.Reflection.Assembly
parameter and returns true if the assembly was loaded from the Global Assembly Cache, or false otherwise. (The “Global Access Cache” name is just a historical artifact.)
• The GetRuntimeDirectory
method. This parameter-less method returns a string containing the directory in which the currently-running CLR resides. Because multiple CLRs can run on the same computer side-by-side, the directory contains the CLR’s version number, for example:
C:WindowsMicrosoft.NETFrameworkv1.2.3456
Using this method requires FileIOPermission
with the PathDiscovery
flag.
• The GetSystemVersion
method. This parameter-less method returns a string containing the version of the currently running CLR. This string has the format “vVersionNumber” to match how the version is used in the path returned by GetRuntimeDirectory
. For example, GetSystemVersion
might return a string like the following:
v1.2.3456
SafeArrayRankMismatchException
ExceptionThis exception is thrown by the CLR when an unmanaged component attempts to pass a SAFEARRAY
parameter or field with a different number of dimensions than the .NET array parameter or field is defined with. For example, a simple, one-dimensional array as a .NET parameter or field is exposed to COM as a SAFEARRAY
by default, which has no static rank encoded in its type information. If a COM client attempted to pass a multi-dimensional array, this exception would be thrown.
There’s a second scenario in which this exception is thrown. If a COM client instead passed a SAFEARRAY
with the right number of dimensions but the wrong bounds (for instance, a 1-based SAFEARRAY
passed to managed code expecting a standard 0-based array), SafeArrayRankMismatchException
is thrown. Therefore, this applies to bounds in addition to rank.
The message that always accompanies SafeArrayRankMismatchException
is, “SafeArray cannot be marshaled to this array type because it has either nonzero lower bounds or multiple dimensions.”
SafeArrayRankMismatchException
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_SAFEARRAYRANKMISMATCH
, defined by the .NET Framework SDK as 0x80131538.
SafeArrayTypeMismatchException
ExceptionThis exception is thrown by the CLR when an unmanaged component attempts to pass a SAFEARRAY
parameter or field with a different element type than what the .NET array parameter or field is defined with. For example, a COM client attempting to pass a SAFEARRAY
with type VT_BSTR
(an array of strings) to managed code expecting an array of integers causes this exception to be thrown.
In version 1.0 of the .NET Framework, this exception is always thrown when attempting to marshal a SAFEARRAY
of CURRENCY
types between unmanaged code and managed code. To work around this, you can use do-it-yourself marshaling, as shown in Chapter 6.
SAFEARRAY
s created using the SafeArrayCreateEx
or SafeArrayCreateVectorEx
COM methods describe their element type. SAFEARRAY
s created using the earlier SafeArrayCreate
or SafeArrayCreateVector
COM methods do not, so the CLR checks the element size of such arrays to determine whether the element type matches. For this reason, certain SAFEARRAY
instances can be passed as .NET arrays with a little more flexibility than others, depending on how they were originally created.
The message that always accompanies SafeArrayTypeMismatchException
is, “Specified array was not of the expected type.”
SafeArrayTypeMismatchException
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_SAFEARRAYTYPEMISMATCH
, defined by the .NET Framework SDK as 0x80131533.
SEHException
ExceptionWhereas a COMException
is thrown when a COM object returns a failure HRESULT
, an SEHException
is thrown when an unmanaged component throws a C++-style Structured Exception Handling (SEH) exception. It has an ErrorCode
property that represents the exception’s error code. This is not necessarily an HRESULT
value.
This exception type has a CanResume
method that returns true if the exception is recoverable (if execution can continue without problems), and false otherwise. It only returns true when a filtered exception handler has corrected the problem that caused the exception in the first place.
In version 1.0 of the .NET Framework, the functionality of recovering from an SEHException
is not yet implemented. Therefore, CanResume
always returns false.
SEHException
derives from ExternalException
(which 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 generic value E_FAIL
(0x80004005).
STATSTG
Value TypeThis is a .NET definition of the unmanaged STATSTG
structure used by the IStream
COM interface. In the .NET Framework, this definition is used by the UCOMIStream.Stat
method.
For more information, consult MSDN Online for information about the original STATSTG
structure.
StructLayoutAttribute
Pseudo-Custom AttributeThis pseudo-custom attribute can be marked on a value type or reference type to control how its members are laid out from the perspective of unmanaged clients.
The attribute has two constructors—one that takes a 16-bit integer and one that takes a LayoutKind
enumeration. The latter constructor should always be used. The type library importer always marks imported value types with this attribute and the LayoutKind.Sequential
setting because this accurately describes structures from a type library. The C#, VB .NET, and C++ compilers all mark value types with sequential layout by default unless the user overrides the setting with StructLayoutAttribute
.
Besides the LayoutKind
enumeration value, StructLayoutAttribute
has three named parameters:
• CharSet
. This can be set to a value from the System.Runtime.InteropServices.CharSet
enumeration. This affects whether string fields are marshaled as LPSTR
or LPWSTR
by default. Any string fields explicitly marked with MarshalAsAttribute
are not affected by this setting. The C#, VB .NET, and C++ compilers set this to CharSet.Ansi
by default.
• Pack
. This must be set to 0, 1, 2, 4, 8, 16, 32, 64, or 128. It affects how a structure’s fields are aligned along byte boundaries. The default packing size for value types defined in managed code is 8, although the packing size of unmanaged structures is often 4. A packing size of zero tells the CLR to use a packing size specific to the current operating system.
• Size
. This can be set to an integer value that specifies the total size of the structure, in bytes. This must be set to a number greater than or equal to the default structure size (the sum of its fields sizes taking into account how fields are packed). This can be useful for extending the memory occupied by a structure for direct unmanaged access, such as dealing with unions that can’t be directly represented in metadata.
For more information, see the LayoutKind
enumeration listed earlier in this section, and Chapter 19.
SYSKIND
EnumerationThis is a .NET definition of the unmanaged SYSKIND
enumeration used by such COM APIs as LoadTypeLibEx
. In the .NET Framework, this definition is used by the TYPELIBATTR
structure.
For more information, consult MSDN Online for information about the original SYSKIND
enumeration.
TYPEATTR
Value TypeThis is a .NET definition of the unmanaged TYPEATTR
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 (GetTypeAttr
and ReleaseTypeAttr
) are defined to use the System.IntPtr
type instead. However, if using these methods of UCOMITypeInfo
, you’ll want to use the TYPEATTR
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 TYPEATTR
structure.
TYPEDESC
Value TypeThis is a .NET definition of the unmanaged TYPEDESC
structure used by the ELEMDESC
and TYPEATTR
COM structures. In the .NET Framework, this definition is used by .NET definitions of ELEMDESC
and TYPEATTR
.
For more information, consult MSDN Online for information about the original TYPEDESC
structure.
TYPEFLAGS
EnumerationThis is a .NET definition of the unmanaged TYPEFLAGS
enumeration used by the TYPEATTR
COM structure. In the .NET Framework, this definition is used by the .NET definition of TYPEATTR
. This enumeration defines the same values as the TypeLibTypeFlags
enumeration, but with slightly different names (the original names). The one difference is that TYPEFLAGS
is a 16-bit enumeration, whereas TypeLibTypeFlags
has a 32-bit underlying type.
For more information, consult MSDN Online for information about the original TYPEFLAGS
enumeration.
TYPEKIND
EnumerationThis is a .NET definition of the unmanaged TYPEKIND
enumeration used by the ITypeInfo
COM interface. In the .NET Framework, this definition is used by the TYPEATTR
value type and the UCOMITypeInfo.GetTypeInfoType
method.
For more information, consult MSDN Online for information about the original TYPEKIND
structure.
TYPELIBATTR
Value TypeThis is a .NET definition of the unmanaged TLIBATTR
structure (notice the name change!) used by the ITypeLib
COM interface. In the .NET Framework, this definition is not directly used by UCOMITypeLib
, because the methods that would use the type (GetLibAttr
and ReleaseTLibAttr
) are defined to use the System.IntPtr
type instead. However, if using these methods of UCOMITypeLib
, you’ll want to use the TYPELIBATTR
type in conjunction with Marshal.PtrToStructure
or Marshal.StructureToPtr
to convert it to and from System.IntPtr
.
This value type is also used by the AxImporter
class in the System.Windows.Forms.Design
namespace, as the type of its GeneratedTypeLibAttributes
property and a parameter of its GetFileOfTypeLib
method.
For more information, consult MSDN Online for information about the original TLIBATTR
(not TYPELIBATTR
!) structure.
TypeLibConverter
ClassThis class, which implements ITypeLibConverter
, contains methods used to convert a type library to an assembly and vice versa. These actions are commonly referred to as importing and exporting a type library. The TLBIMP.EXE
, TLBEXP.EXE
, and REGASM.EXE
SDK tools and Visual Studio .NET use TypeLibConverter
to import and export type libraries.
The TypeLibConverter
methods are described in the following sections. For more information about any them, see Chapter 22.
ConvertAssemblyToTypeLib
MethodThis method takes a System.Reflection.Assembly
instance as input and returns a System.Object
that represents an in-memory type library (an object that implements the ITypeLib
COM interface). Besides the input Assembly
parameter, this method also expects a string with the output type library filename—which can be null (Nothing
in VB .NET) to accept the default, a TypeLibExporterFlags
enumeration value to customize the process, and an object that implements the ITypeLibExporterNotifySink
interface.
Managed clients can save the returned object as a type library file by casting it to UCOMITypeLib
and calling its SaveAllChanges
method.
ConvertTypeLibToAssembly
MethodThis method takes an in-memory type library (an object that implements the ITypeLib
COM interface), and returns a System.Reflection.Emit.AssemblyBuilder
instance. An AssemblyBuilder
is a dynamic assembly, and can be saved by calling its Save
method.
Besides the input System.Object
parameter for the in-memory type library, ConvertTypeLibToAssembly
expects several additional parameters. Two overloads of this method exist, but the one that should always be used has the following additional parameters:
• asmFileName
. This is a string that can be set to the desired filename of the output assembly, or null (Nothing
in VB .NET) to accept the default filename.
• flags
. This is a TypeLibImporterFlags
enumeration value that customizes the importing process.
• notifySink
. This is an object that implements the ITypeLibImporterNotifySink
interface that is used for callbacks during import.
• publicKey
. This is an array of bytes that can contain a public key used for producing a strong named assembly with a public key only, or null for producing a simply named assembly (or if keyPair
is non-null).
• keyPair
. This is a StrongNameKeyPair
instance that can be used to produce a strong named assembly, or null for producing a simply named assembly (or if publicKey
is non-null).
• asmNamespace
. This is a string that can be set to the desired namespace of the output assembly, or null to accept the default.
• asmVersion
. This is a System.Version
instance that can be used to control the output assembly’s version, or null to accept the default.
GetPrimaryInteropAssembly
MethodThe GetPrimaryInteropAssembly
method is the official means of determining whether a Primary Interop Assembly is registered on the current computer. This method returns true if one exists, and false otherwise. It requires you to pass a System.Guid
representing the corresponding type library’s LIBID, plus the major and minor version numbers and locale identifier (LCID) of the corresponding type library. Via two string by-reference parameters, it returns the assembly name and, if it exists, a codebase value if the method returned true.
TypeLibExporterFlags
EnumerationThis enumeration is passed to ITypeLibConverter.ConvertAssemblyToTypeLib
to control the process of type library export. It has a single value that may be set as a bit flag—OnlyReferenceRegistered
. This flag should be set when you want the exporter to only look for dependent type libraries in the registry rather than also looking in the same directory as the input assembly.
For more information, see Chapter 22.
TypeLibFuncAttribute
Custom AttributeThis custom attribute is used by the type library importer to mark imported members with their original flags from the type library definition. Any flags that have meaning in the .NET world already present themselves in a different way in metadata. However, this attribute is useful for discovering information about imported members that would have been lost in metadata if it weren’t for this custom attribute.
The attribute has two constructors—one that takes a 16-bit integer and one that takes a TypeLibFuncFlags
enumeration. The latter constructor should always be used. Be aware that the type library exporter ignores this custom attribute when exporting member definitions.
For more information, see the TypeLibFuncFlags
enumeration listed next, and Chapter 21.
TypeLibFuncFlags
EnumerationThis enumeration is used by TypeLibFuncAttribute
to describe various attributes of an imported member definition. It has all the same values as the FUNCFLAGS
enumeration, but has a 32-bit underlying type rather than 16-bit and fields with slightly different names.
For more information about this enumeration’s values, consult MSDN Online for information about the original FUNCFLAGS
enumeration.
TypeLibImporterFlags
EnumerationThis enumeration is passed to ITypeLibConverter.ConvertTypeLibToAssembly
to control the process of type library import. It has the following values that are used as bit flags:
• PrimaryInteropAssembly
. This flag should be set when you want to create a Primary Interop Assembly. TLBIMP.EXE
exposes this as its /primary
option.
• SafeArrayAsSystemArray
. This flag should be set when you want to import all SAFEARRAY
types as System.Array
types instead of one-dimensional arrays. This is handy when a type library contains SAFEARRAY
parameters that are multi-dimensional or have non-zero lower bounds, because it makes their managed signatures usable. TLBIMP.EXE
exposes this as its /sysarray
option.
• UnsafeInterfaces
. This flag should be set when you want every imported class and interface to be marked with System.Security.SuppressUnmanagedCodeSecurityAttribute
. “UnsafeInterfaces” is a bit of a misnomer because it applies to classes as well. This attribute tells the .NET security system to not do a run-time security check for UnmanagedCode
permission (which involves a stack walk) when calling into unmanaged code. Instead, the check for UnmanagedCode
is only done at “link time”—during just-in-time compilation, regardless of whether the class implementing an unsafe interface is managed or unmanaged. TLBIMP.EXE
exposes this as its /unsafe
option.
For more information, see Chapter 22.
TypeLibTypeAttribute
Custom AttributeThis custom attribute is used by the type library importer to mark imported types with their original flags from the type library definition. Any flags that have meaning in the .NET world already present themselves in a different way in metadata. However, this attribute is useful for discovering information about imported types that would have been lost in metadata if it weren’t for this custom attribute.
The attribute has two constructors—one that takes a 16-bit integer and one that takes a TypeLibTypeFlags
enumeration. The latter constructor should always be used. Be aware that the type library exporter ignores this custom attribute when exporting type definitions.
For more information, see the TypeLibTypeFlags
enumeration listed next, and Chapter 21.
TypeLibTypeFlags
EnumerationThis enumeration is used by TypeLibTypeAttribute
to describe various attributes of an imported member definition. It has all the same values as the TYPEFLAGS
enumeration, but has a 32-bit underlying type rather than 16-bit and fields with slightly different names.
For more information about this enumeration’s values, consult MSDN Online for information about the original TYPEFLAGS
enumeration.
TypeLibVarAttribute
Custom AttributeThis custom attribute is used by the type library importer to mark imported structure fields with their original flags from the type library definition. Any flags that have meaning in the .NET world already present themselves in a different way in metadata. However, this attribute is useful for discovering information about imported fields that would have been lost in metadata if it weren’t for this custom attribute.
The attribute has two constructors—one that takes a 16-bit integer and one that takes a TypeLibVarFlags
enumeration. The latter constructor should always be used. Be aware that the type library exporter ignores this custom attribute when exporting field definitions.
For more information, see the TypeLibVarFlags
enumeration listed next, and Chapter 21.
TypeLibVarFlags
EnumerationThis enumeration is used by TypeLibVarAttribute
to describe various attributes of an imported member definition. It has all the same values as the VARFLAGS
enumeration, but has a 32-bit underlying type rather than 16-bit and fields with slightly different names.
For more information about this enumeration’s values, consult MSDN Online for information about the original VARFLAGS
enumeration.
UCOMIBindCtx
InterfaceThis is a .NET definition of the IBindCtx
COM interface. In the .NET Framework, this interface is used by several methods of the UCOMIMoniker
interface. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IEnumString
, BIND_OPTS
, and IRunningObjectTable
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IBindCtx
interface.
UCOMIConnectionPoint
InterfaceThis is a .NET definition of the IConnectionPoint
COM interface. In the .NET Framework, this interface is used by the UCOMIConnectionPointContainer
and UCOMIEnumConnectionPoints
interfaces. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IEnumConnections
and IConnectionPointContainer
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IConnectionPoint
interface.
UCOMIConnectionPointContainer
InterfaceThis is a .NET definition of the IConnectionPointContainer
COM interface. In the .NET Framework, this interface is used by the UCOMIConnectionPoint.GetConnectionPointContainer
method. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IEnumConnectionPoints
and IConnectionPoint
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IConnectionPointContainer
interface.
UCOMIEnumConnectionPoints
InterfaceThis is a .NET definition of the IEnumConnectionPoints
COM interface. In the .NET Framework, this interface is used by the UCOMIConnectionPointContainer.EnumConnectionPoints
method. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IEnumConnectionPoints
and IConnectionPoint
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IEnumConnectionPoints
interface.
UCOMIEnumConnections
InterfaceThis is a .NET definition of the IEnumConnections
COM interface. In the .NET Framework, this interface is used by the UCOMIConnectionPoint.EnumConnections
method. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as CONNECTDATA
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IEnumConnections
interface.
UCOMIEnumMoniker
InterfaceThis is a .NET definition of the IEnumMoniker
COM interface. In the .NET Framework, this interface is used by the UCOMIMoniker
and UCOMIRunningObjectTable
interfaces. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IMoniker
, have corresponding .NET definitions.
For more information, consult MSDN Online for information about the original IEnumMoniker
interface.
UCOMIEnumString
InterfaceThis is a .NET definition of the IEnumString
COM interface. In the .NET Framework, this interface is used by the UCOMIBindCtx.EnumObjectParam
method. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types).
For more information, consult MSDN Online for information about the original IEnumString
interface.
UCOMIEnumVARIANT
InterfaceThis is a .NET definition of the IEnumVARIANT
COM interface. In the .NET Framework, this interface is implemented by EnumVariantViewOfEnumerator
in the System.Runtime.InteropServices.CustomMarshalers
namespace, an adapter object used by the custom marshaler that bridges IEnumVARIANT
with the .NET IEnumerator
interface.
Two methods of UCOMIEnumVARIANT
have incorrect definitions in version 1.0 of the .NET Framework. Its Clone
method has a 32-bit integer parameter instead of a UCOMIEnumVARIANT
parameter. Also, its Next
method has three 32-bit integer parameters rather than an integer, a System.Object
(representing a VARIANT
), and an integer. On 32-bit platforms, you could construct a System.IntPtr
from the 32-bit value and use the standard workarounds (such as Marshal.GetObjectForNativeVariant
), but this definition won’t work on 64-bit platforms. If you require the functionality from Next
or Clone
, or plan on writing a .NET class that implements the IEnumVARIANT
COM interface, you should consider writing your own private .NET definition of IEnumVARIANT
.
For more information, consult MSDN Online for information about the original IEnumVARIANT
interface.
UCOMIMoniker
InterfaceThis is a .NET definition of the IMoniker
COM interface. In the .NET Framework, this interface is used by the UCOMIEnumMoniker
and UCOMIRunningObjectTable
interfaces. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IBindCtx
, IEnumMoniker
, FILETIME
, and IStream
, have corresponding .NET definitions. Some parameters that have an unmanaged type of void**
are represented as a by-reference System.Object
(marked with MarshalAs(UnmanagedType.Interface)
) rather than a System.IntPtr
type, which works fine because the value should always point to a COM interface pointer.
The COM IMoniker
definition derives from the IPersistStream
COM interface, which derives from the IPersist
COM interface. Because IPersistStream
and IPersist
do not have a .NET definition in System.Runtime.InteropServices
, UCOMIMoniker
does not claim to derive from any interfaces (although it contains the methods of IPersistStream
and IPersist
). Therefore, a .NET object that implements UCOMIMoniker
does not successfully respond to COM QueryInterface
calls for the IPersistStream
or IPersist
interfaces unless the class author has done some additional work. See Chapter 14 for more details.
For more information, consult MSDN Online for information about the original IMoniker
interface.
UCOMIPersistFile
InterfaceThis is a .NET definition of the IPersistFile
COM interface. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types).
The COM IPersistFile
definition derives from the IPersist
COM interface. Because IPersist
does not have a .NET definition in System.Runtime.InteropServices
, UCOMIPersistFile
does not claim to derive from any interfaces (although it contains the methods of IPersist
). Therefore, a .NET object that implements UCOMIPersistFile
does not successfully respond to COM QueryInterface
calls for the IPersist
interface unless the class author has done some additional work. See Chapter 14 for more details.
For more information, consult MSDN Online for information about the original IPersistFile
interface.
UCOMIRunningObjectTable
InterfaceThis is a .NET definition of the IRunningObjectTable
COM interface. In the .NET Framework, this interface is used by the UCOMIBindCtx.GetRunningObjectTable
method. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IEnumMoniker
, IMoniker
, and FILETIME
, have corresponding .NET definitions.
One aspect of the interface definition that could be more user-friendly is the fact that the first parameter of UCOMIRunningObjectTable.Register
is a plain 32-bit integer rather than an enumeration defining its two possible values: ROTFLAGS_REGISTRATIONKEEPSALIVE
(1) and ROTFLAGS_ALLOWANYCLIENT
(2). However, the original COM definition of IRunningObjectTable.Register
defines its first parameter as a plain DWORD
, so the .NET definition is faithful to the original.
For more information, consult MSDN Online for information about the original IRunningObjectTable
interface.
UCOMIStream
InterfaceThis is a .NET definition of the IStream
COM interface. In the .NET Framework, this interface is used by the UCOMIMoniker
interface. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as IMoniker
, have corresponding .NET definitions.
One aspect of the interface definition that could be more user-friendly is the fact that UCOMIStream.Commit
’s parameter, the last parameter of UCOMIStream.LockRegion
and UCOMIStream.UnlockRegion
, the second parameter of UCOMIStream.Seek
, and the last parameter of UCOMIStream.Stat
are all plain 32-bit integers rather than enumerations defining their possible values. These enumerations are defined in the unmanaged world as STGC
, LOCKTYPE
, STREAM_SEEK
, and STATFLAG
, respectively. However, the original COM definition of these methods on the IStream
interface defines all of these parameters as plain DWORD
types, so the .NET definition is faithful to the original.
The COM IStream
definition derives from the ISequentialStream
COM interface. Because ISequentialStream
does not have a .NET definition in System.Runtime.InteropServices
, UCOMIStream
does not claim to derive from any interfaces (although it contains the methods of ISequentialStream
). Therefore, a .NET object that implements UCOMIStream
does not successfully respond to COM QueryInterface
calls for the ISequentialStream
interface unless the class author has done some additional work. See Chapter 14 for more details.
For more information, consult MSDN Online for information about the original IStream
interface.
UCOMITypeComp
InterfaceThis is a .NET definition of the ITypeComp
COM interface. In the .NET Framework, this interface is used by the UCOMITypeInfo.GetTypeComp
and UCOMITypeLib.GetTypeComp
methods. This is a “complete” interface definition that requires no workarounds when using it (such as handling System.IntPtr
types). All of its COM parameter types, such as ITypeInfo
, DESCKIND
, and BINDPTR
, have corresponding .NET definitions.
One aspect of the interface definition that could be more user-friendly is the fact that the third parameter of UCOMITypeComp.Bind
is a plain 16-bit integer rather than an INVOKEKIND
enumeration defining its possible values. However, the original COM definition of ITypeComp.Bind
defines its first parameter as a plain unsigned short
, so the .NET definition is faithful to the original signature (except for changing the unsigned type to a CLS-compliant signed type).
For more information, consult MSDN Online for information about the original ITypeComp
interface.
UCOMITypeInfo
InterfaceThis is a .NET definition of the ITypeInfo
COM interface. In the .NET Framework, this interface is used by the UCOMITypeComp
and UCOMITypeLib
interfaces, plus the Marshal.GetTypeInfoName
method. This interface can also be used with the Marshal.GetITypeInfoForType
and Marshal.GetTypeForITypeInfo
methods, although those signatures use a System.IntPtr
type to represent the interface. Therefore, using Marshal.GetIUnknownForObject
and Marshal.GetObjectForIUnknown
need to be used to use these methods with a UCOMITypeInfo
instance.
The UCOMITypeInfo
has a handful of members using System.IntPtr
types, requiring do-it-yourself marshaling using the Marshal
class. For example, the AddressOfMember
uses a by-reference IntPtr
parameter to represent the unmanaged signature’s void**
parameter. This is necessary because the parameter doesn’t point to a COM interface. The CreateInstance
method, on the other hand, has an unmanaged void**
parameter represented as a by-reference System.Object
(marked with MarshalAs(UnmanagedType.IUnknown)
) because the value should always point to an IUnknown
interface pointer.
The second parameter of GetFuncDesc
, GetTypeAttr
, and GetVarDesc
is an IntPtr
type instead of FUNCDESC
, TYPEATTR
, and VARDESC
, respectively. ReleaseFuncDesc
, ReleaseTypeAttr
, and ReleaseVarDesc
also use an IntPtr
parameter rather than FUNCDESC
, TYPEATTR
, and VARDESC
, respectively. This is necessary because these unmanaged types use a custom memory allocation scheme, so the standard Interop Marshaling treatment would not work.
An aspect of the interface definition that could be more user-friendly is the fact that the second parameter of GetImplTypeFlags
is a 32-bit integer rather than an IMPLTYPEFLAGS
enumeration, but the unmanaged definition also defines the parameter as a plain integer, so the .NET definition is faithful to the original signature.
For more information, consult MSDN Online for information about the original ITypeInfo
interface.
UCOMITypeLib
InterfaceThis is a .NET definition of the ITypeLib
COM interface. In the .NET Framework, this interface is used by the UCOMITypeInfo
interface, a few methods of the Marshal
class, and a few methods of the AxImporter
class from the System.Windows.Forms.Design
namespace. It’s also useful with methods of the TypeLibConverter
class, although the relevant parameters are typed as System.Object
rather than UCOMITypeLib
.
GetLibAttr
and ReleaseTLibAttr
have an IntPtr
parameter instead of the .NET TYPELIBATTR
structure (which represents the unmanaged TLIBATTR
structure) because the unmanaged type uses a custom memory allocation scheme, so the standard Interop Marshaling treatment would not work.
The definition of UCOMITypeLib.FindName
is incorrect in version 1.0 of the .NET Framework. Its second-to-last parameter (rgMemId
) is supposed to be an array of MEMBERIDs (32-bit integers), but instead is defined to be a single 32-bit integer. You can work around this on 32-bit platforms by converting an IntPtr
that represents a pointer to an array into a 32-bit integer to pass to the FindName
method, but this will not work on 64-bit platforms. If you want to call FindName
, or if you plan on writing a .NET class that implements the ITypeLib
COM interface, you should consider writing your own private .NET definition of ITypeLib
.
For more information, consult MSDN Online for information about the original ITypeLib
interface.
UnknownWrapper
ClassThe UnknownWrapper
class can be used to pass a .NET Object
type inside a VARIANT
and have COM see it as VT_UNKNOWN
(any COM object) rather than VT_DISPATCH
(a COM object implementing IDispatch
). Although Object
types are automatically transformed to COM IUnknown
types when passed early-bound to COM signatures expecting IUnknown
, 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.
By default, a .NET object passed in a VARIANT
appears with the VT_DISPATCH
type unless the object doesn’t implement IDispatch
. This occurs when the class is COM-invisible (including being non-public), or if it’s marked with ClassInterface(ClassInterfaceType.None)
and the interface chosen as the default interface is marked with InterfaceType (ComInterfaceType.InterfaceIsIUnknown)
. Most COM clients shouldn’t care if they expect a VT_UNKNOWN VARIANT
and receive a VT_DISPATCH VARIANT
, but using this wrapper can satisfy those that do.
The UnknownWrapper
class has one constructor that takes an Object
type. The Object
passed must not be a value type or an array, otherwise an ArgumentException
is thrown.
The following code demonstrates the use of this type when calling a COM method whose managed signature has a generic System.Object
parameter:
C#:
MyClass c = ...;
// Pass a VARIANT containing an IDispatch pointer (VT_DISPATCH)
obj.Method(c);
// Pass a VARIANT containing an IUnknown pointer (VT_UNKNOWN)
obj.Method(new UnknownWrapper(c));
Visual Basic .NET:
Dim c As MyClass = ...
' Pass a VARIANT containing an IDispatch pointer (VT_DISPATCH)
obj.Method(c)
' Pass a VARIANT containing an IUnknown pointer (VT_UNKNOWN)
obj.Method(New UnknownWrapper(c))
C++:
MyClass* c = ...;
// Pass a VARIANT containing an IDispatch pointer (VT_DISPATCH)
obj->Method(c);
// Pass a VARIANT containing an IUnknown pointer (VT_UNKNOWN)
obj->Method(new UnknownWrapper(c));
For more information, consult Chapter 3.
UnmanagedType
EnumerationThe UnmanagedType
enumeration is used by the MarshalAsAttribute
pseudo-custom attribute to specify how a .NET data type should be marshaled to unmanaged code. These enumeration values can be categorized as follows:
I list LPStruct
under “unused” because its name is misleading, but it can be used in one scenario. It can be placed on a by-value Guid
parameter in a PInvoke signature to marshal it as an in-only pointer to the Guid
. An example of this is shown in Chapter 19.
For a complete description of the meaning of these UnmanagedType
values, including which can be used on which .NET data types, consult Chapter 12. For the special IL Assembler syntax of the UnmanagedType
enumeration values, see Chapter 7.
VARDESC
Value TypeThis is a .NET definition of the unmanaged VARDESC
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 (GetVarDesc
and ReleaseVarDesc
) are defined to use the System.IntPtr
type instead. However, if using these methods of UCOMITypeInfo
, you’ll want to use the VARDESC
type in conjunction with Marshal.PtrToStructure
or Marshal.StructureToPtr
to convert it to and from System.IntPtr
.
In version 1.0 of the .NET Framework, the definition of System.Runtime.InteropServices.VARDESC
doesn’t correctly match the unmanaged definition, so it should not be used in managed code. Instead, you could use your own definition like the following (in C#):
where DESCUNION
and VARKIND
are defined as follows:
For more information, consult MSDN Online for information about the original VARDESC
structure.
VarEnum
EnumerationThis is a .NET definition of the unmanaged VARENUM
structure (notice the change in case). This enumeration is used in OLE Automation to describe the type of a VARIANT
, SAFEARRAY
, TYPEDESC
, or OLE property set. In the .NET Framework, this definition is used by the MarshalAsAttribute
pseudo-custom attribute’s SafeArraySubType
named parameter to describe the unmanaged element type of an array’s elements.
Although VarEnum
has 44 values, about half of them are valid for a SAFEARRAY
’s element type. The following ten values are the ones commonly used to change the default marshaling behavior of SAFEARRAY
s:
This is the only use of VarEnum by the Interop Marshaler. If you’re performing do-it-yourself marshaling with hand-written VARIANT or SAFEARRAY structures, as shown in Chapter 6, this enumeration may come in handy.
VARFLAGS
EnumerationThis is a .NET definition of the unmanaged VARFLAGS
enumeration. This enumeration defines the same values as the TypeLibVarFlags
enumeration, but with slightly different names (the original names). The one difference is that VARFLAGS
is a 16-bit enumeration, whereas TypeLibVarFlags
has a 32-bit underlying type.
For more information, consult MSDN Online for information about the original VARFLAGS
enumeration.
System.Runtime.InteropServices.CustomMarshalers
NamespaceThis namespace contains a handful of custom marshalers that are included with the .NET Framework. These classes reside in the CustomMarshalers
assembly (CustomMarshalers.dll
).
EnumerableToDispatchMarshaler
ClassThis custom marshaler marshals the COM IDispatch
interface to the .NET System.Collections.IEnumerable
interface and vice versa (when a member with a DISPID of -4 exists). It’s used automatically by the CLR (without the use of MarshalAsAttribute
) to enable the bridging of COM enumerators to .NET enumerators. When the type library importer creates a class that implements IEnumerable
, it’s the EnumerableToDispatchMarshaler
custom marshaler that makes it work. When this custom marshaler is used, calling IEnumerable.GetEnumerator
results in IDispatch.Invoke
being called with DISPID -4, and vice versa.
EnumeratorToEnumVariantMarshaler
ClassThis custom marshaler marshals the COM IEnumVARIANT
interface to the .NET System.Collections.IEnumerator
interface and vice versa. It’s used automatically by the CLR (without the use of MarshalAsAttribute
) to enable the bridging of COM enumerators to .NET enumerators. The IEnumerator
type returned by an imported COM class’s GetEnumerator
method uses EnumeratorToEnumVariantMarshaler
to map the calls to the IEnumVARIANT
interface pointer returned by the COM object’s member with DISPID -4.
The IEnumerator.MoveNext
implementation exposed by the custom marshaler naturally maps to IEnumVARIANT.Next
. The IEnumerator.Reset
implementation calls IEnumVARIANT.Reset
. The IEnumerator.Current
implementation is handled entirely by the custom marshaler’s adapter object because IEnumVARIANT
doesn’t have an analogous property.
In the reverse direction, the IEnumVARIANT.Next
implementation exposed by the custom marshaler naturally maps to IEnumerator.MoveNext
, and the IEnumVARIANT.Skip
implementation also uses IEnumerator.MoveNext
to provide its functionality. In this direction, IEnumVARIANT.Reset
is not supported, so it always returns S_FALSE
as the IEnumVARIANT
contract requires. IEnumVARIANT.Clone
is supported if the .NET object that implements IEnumerator
also implements ICloneable
. Otherwise, E_FAIL
is returned.
The type library importer marks IEnumerable.GetEnumerator
signatures with the appropriate MarshalAsAttribute
with UnmanagedType.CustomMarshaler
, but this isn’t strictly necessary because the CLR always uses the custom marshaler with GetEnumerator
methods anyway. This enables type library export to do the desired transformation without the .NET class author having to worry about using the custom marshaler explicitly.
ExpandoToDispatchExMarshaler
ClassThis custom marshaler marshals the COM IDispatchEx
interface to either the .NET System.Runtime.InteropServices.Expando.IExpando
interface or System.Reflection.IReflect
interface, and vice versa. This bridges the COM means of providing dynamic member addition and deletion with the .NET means. This custom marshaler is not used automatically by the CLR, so it must be used as follows:
C#:
void UseCustomMarshaler([MarshalAs(UnmanagedType.CustomMarshaler,
MarshalTypeRef=typeof(ExpandoToDispatchExMarshaler))] IExpando expando);
Visual Basic .NET:
Sub UseCustomMarshaler(<MarshalAs(UnmanagedType.CustomMarshaler, _
MarshalTypeRef:=GetType(ExpandoToDispatchExMarshaler))> expando As IExpando)
void UseCustomMarshaler([MarshalAs(UnmanagedType::CustomMarshaler,
MarshalTypeRef=__typeof(ExpandoToDispatchExMarshaler))] IExpando* expando);
For the IExpando
implementation exposed from IDispatchEx
, calling AddField
always throws a NotSupportedException
because IDispatchEx
only deals with properties and methods. Calling AddMethod
also throws a NotSupportedException
. Only properties can be added via the AddProperty
method.
The IExpando.RemoveMember
implementation that’s exposed supports removing properties and methods, but not fields.
TypeToTypeInfoMarshaler
ClassThis custom marshaler marshals the COM ITypeInfo
interface to the .NET System.Type
class and vice versa. The ITypeInfo
interface exposed for a .NET type is based on the information that would appear in an exported type library. Likewise, the Type
instance exposed for an ITypeInfo
interface is based on the information that would appear in an imported assembly.
The type library importer marks ITypeInfo
parameters with the appropriate MarshalAsAttribute
with UnmanagedType.CustomMarshaler
when converting signatures that use ITypeInfo
parameters to signatures that use Type
parameters.
This same conversion functionality can be accomplished without the custom marshaler, by calling Marshal.GetITypeInfoForType
and Marshal.GetTypeForITypeInfo
.
System.Runtime.InteropServices.Expando
NamespaceThis namespace only contains a single type—the IExpando
interface. This interface derives from System.Reflection.IReflect
, adding the capabilities of dynamic member addition and deletion. There are no available classes in the .NET Framework that implement this interface, but the ExpandoToDispatchExMarshaler
custom marshaler from the previous section provides an implementation of this interface for COM objects that implement IDispatchEx
, a COM interface that also provides dynamic member addition and deletion.
This interface has four methods:
• AddField
. This takes a string parameter with a field’s name and returns a FieldInfo
instance that represents the newly added field.
• AddMethod
. This takes a string parameter with a method’s name and a Delegate
instance with the desired method’s signature, and returns a MethodInfo
instance that represents the newly added method.
• AddProperty
. This takes a string parameter with a property’s name and returns a PropertyInfo
instance that represents the newly added property.
• RemoveMember
. This takes a MemberInfo
instance representing the field, method, or property that should be removed. Usually, only a member that was dynamically added can be removed, but this is ultimately implementation-specific.
This interface is used by JScript .NET to provide the same kind of dynamic behavior available in JScript.