The GetTypeLibLcid Method

This 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.

The GetTypeLibName Method

This 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.

The GetUnmanagedThunkForManagedMethodPtr Method

This 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

C++:

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

This method does the opposite action of GetManagedThunkForUnmanagedMethodPtr.

The IsComObject Method

This 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.

The IsTypeVisibleFromCom Method

This 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.

The NumParamBytes Method

NumParamBytes 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.

The OffsetOf Method

This 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.

The Prelink Method

This 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.

The PrelinkAll Method

The 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.

The PtrToStringAnsi Method

This 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:

C#:

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.

The PtrToStringAuto Method

This 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);

Visual Basic .NET:

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.

The PtrToStringBSTR Method

This 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.

The PtrToStringUni Method

This 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.

The PtrToStructure Method

The 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:

C#:

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.

The QueryInterface Method

All 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.

The ReadByte Method

The 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:

C#:

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.

The ReadInt16 Method

The 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.

The ReadInt32 Method

The 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.

The ReadInt64 Method

The 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.

The ReadIntPtr Method

The 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.

The ReAllocCoTaskMem Method

This 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.

The ReAllocHGlobal Method

This 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.

The Release Method

All 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);

Visual Basic .NET:

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.

The ReleaseComObject Method

ReleaseComObject 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.

The ReleaseThreadCache Method

This method is obsolete, and should never be called.

The SetComObjectData Method

Every 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.

The SizeOf Method

This 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.”

Tip

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.

The StringToBSTR Method

This 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++.

The StringToCoTaskMemAnsi Method

This 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.

The StringToCoTaskMemAuto Method

This 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.

The StringToCoTaskMemUni Method

This 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.

The StringToHGlobalAnsi Method

This 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.

The StringToHGlobalAuto Method

This 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.

The StringToHGlobalUni Method

This 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.

The StructureToPtr Method

The 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:

C#:

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.

Tip

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.

C#:

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.

The SystemDefaultCharSize Property

This 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.

The SystemMaxDBCSCharSize Property

This 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.

The ThrowExceptionForHR Method

The 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.

The UnsafeAddrOfPinnedArrayElement Method

The 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.

Caution

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.

The WriteByte Method

The 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:

C#:

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.

The WriteInt16 Method

The 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.

Caution

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.

The WriteInt32 Method

The 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.

The WriteInt64 Method

The 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.

The WriteIntPtr Method

The 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.

The MarshalAsAttribute Pseudo-Custom Attribute

MarshalAsAttribute 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.

The MarshalDirectiveException Exception

The 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);

Image

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.

The ObjectCreationDelegate Delegate

ObjectCreationDelegate 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.

The OptionalAttribute Pseudo-Custom Attribute

The 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.

The OutAttribute Pseudo-Custom Attribute

The 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.

The PARAMDESC Value Type

This 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.

The PARAMFLAG Enumeration

This 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.

The PreserveSigAttribute Pseudo-Custom Attribute

The 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 HRESULTs for such members are discarded.

The type library importer only marks members of dispinterfaces with PreserveSigAttribute.

For more information, see Chapters 7, and 12.

The PrimaryInteropAssemblyAttribute Custom Attribute

The 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.”

The ProgIdAttribute Custom Attribute

The 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.

The RegistrationServices Class

This 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.

The GetManagedCategoryGuid Method

The 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.

The GetProgIdForType Method

The 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.

The GetRegistrableTypesInAssembly Method

The 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.

The RegisterAssembly Method

RegisterAssembly, 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.

The RegisterTypeForComClients Method

RegisterTypeForComClients 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!

The TypeRepresentsComType Method

The 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.

The TypeRequiresRegistration Method

TypeRequiresRegistration 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.

The UnregisterAssembly Method

UnregisterAssembly 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.

The RuntimeEnvironment Class

This 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

The SafeArrayRankMismatchException Exception

This 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.

The SafeArrayTypeMismatchException Exception

This 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.

Caution

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.

SAFEARRAYs created using the SafeArrayCreateEx or SafeArrayCreateVectorEx COM methods describe their element type. SAFEARRAYs 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.

The SEHException Exception

Whereas 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.

Caution

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).

The STATSTG Value Type

This 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.

The StructLayoutAttribute Pseudo-Custom Attribute

This 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.

The SYSKIND Enumeration

This 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.

The TYPEATTR Value Type

This 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.

The TYPEDESC Value Type

This 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.

The TYPEFLAGS Enumeration

This 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.

The TYPEKIND Enumeration

This 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.

The TYPELIBATTR Value Type

This 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.

The TypeLibConverter Class

This 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.

The ConvertAssemblyToTypeLib Method

This 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.

The ConvertTypeLibToAssembly Method

This 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.

The GetPrimaryInteropAssembly Method

The 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.

The TypeLibExporterFlags Enumeration

This 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.

The TypeLibFuncAttribute Custom Attribute

This 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.

The TypeLibFuncFlags Enumeration

This 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.

The TypeLibImporterFlags Enumeration

This 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.

The TypeLibTypeAttribute Custom Attribute

This 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.

The TypeLibTypeFlags Enumeration

This 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.

The TypeLibVarAttribute Custom Attribute

This 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.

The TypeLibVarFlags Enumeration

This 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.

The UCOMIBindCtx Interface

This 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.

The UCOMIConnectionPoint Interface

This 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.

The UCOMIConnectionPointContainer Interface

This 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.

The UCOMIEnumConnectionPoints Interface

This 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.

The UCOMIEnumConnections Interface

This 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.

The UCOMIEnumMoniker Interface

This 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.

The UCOMIEnumString Interface

This 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.

The UCOMIEnumVARIANT Interface

This 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.

Caution

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.

The UCOMIMoniker Interface

This 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.

Caution

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.

The UCOMIPersistFile Interface

This 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).

Caution

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.

The UCOMIRunningObjectTable Interface

This 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.

The UCOMIStream Interface

This 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.

Caution

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.

The UCOMITypeComp Interface

This 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.

The UCOMITypeInfo Interface

This 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.

The UCOMITypeLib Interface

This 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.

Caution

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.

The UnknownWrapper Class

The 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.

The UnmanagedType Enumeration

The 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:

Image

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.

The VARDESC Value Type

This 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.

Caution

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#):

Image

where DESCUNION and VARKIND are defined as follows:

Image

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

The VarEnum Enumeration

This 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 SAFEARRAYs:

Image

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.

The VARFLAGS Enumeration

This 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.

The System.Runtime.InteropServices.CustomMarshalers Namespace

This namespace contains a handful of custom marshalers that are included with the .NET Framework. These classes reside in the CustomMarshalers assembly (CustomMarshalers.dll).

The EnumerableToDispatchMarshaler Class

This 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.

The EnumeratorToEnumVariantMarshaler Class

This 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.

The ExpandoToDispatchExMarshaler Class

This 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)

C++:

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.

The TypeToTypeInfoMarshaler Class

This 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.

The System.Runtime.InteropServices.Expando Namespace

This 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.

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

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