Automation types are essentially an evolution of the types used by early versions of Visual Basic. There are four main complex types to consider: bstr, variant, safearray, and CURRENCY.
The bstr type is a length-prefixed string—or more accurately, a size-prefixed buffer because the prefix gives the size of the buffer in bytes, not characters. The runtime will happily convert a bstr value to a managed string type during marshaling, but because the bstr contains no information about the type of string it holds (it can even hold a binary buffer), you have to provide information through [MarshalAs]. unmanagedType.BStr is used for bstr values generated on Win32 systems (and hence Unicode), and UnmanagedType.AnsiBStr is used for Win16 bstr values and binary buffers.
The variant type has a member that contains the type of the data held in the variant, so when a variant parameter is passed to managed code, the marshaler will convert it to the appropriate managed type. The tlbimp tool will convert a variant parameter to an Object reference, which you can cast to the appropriate type (checking the type by calling object.GetType () if necessary).
Managed arrays contain information about their rank and bounds. In this respect they are much like the safearray type. The difference is that safearray objects are generic and contain the type of the data they hold as a data member, whereas a managed array is typed to the type of data that it holds. Because of the close similarity between safearray objects and managed arrays, COM Interop is quite straightforward. For example, this IDL:
[id(1)] HRESULT GetArray(
[out, retval] SAFEARRAY(BSTR)* sa); // Visual C++ COM attributes [id(1)] HRESULT GetArray(
is imported into managed C# as follows:
The automation currency data type presents something of a problem because it is defined as a 64-bit integer data type and is scaled by 10,000 to give four decimal places and 15 digits to the left. The problem occurs when you are marshaling a currency value from the managed world to the unmanaged world, particularly if the conversion is done through a variant or Object parameter, because you need to indicate that the data you pass to such a method is the automation type vt_cy, and not vt_r8 (if you use a managed double type) or vt_i8 (if you use a managed long type). The solution lies in the framework class CurrencyWrapper. When this class is passed as a parameter, the marshaler knows that the data type is currency and hence ensures that the variant value is initialized accordingly:
decimal cheque = 100.23;
Similarly, if you want to pass a managed object via a variant so that the COM code can call the object through an IDispatch pointer (vt_dispatch), you can use the DispatchWrapper class. If you want to pass an object as an iunknown pointer (vt_unknown), you can use the unknownWrapper class. Finally, if you want to pass an error value (vt_error), you can use the ErrorWrapper class.
Was this article helpful?