Assumptions or dependences about the internal characteristics of a data type can lead to the need for custom marshaling. Characteristics that are commonly taken for granted are internal representation, element size, and element position. When upgrading a part of a Visual Basic 6.0 application that depends on a piece of code that has special assumptions about a data type, it may be necessary to make the two pieces of code interact through .NET interoperability. In this type of scenario, the part of the application that was upgraded to Visual Basic .NET needs to stay compatible with the component that is not upgraded. This will require the use of user-defined marshaling for some of the data types involved in the interaction between both components.
The recommended approach to do the necessary data conversions in this kind of situation is to write a wrapper class that includes all the code to perform these conversions. This class, called a marshaling wrapper class, will provide a bridge between an old and a new interface. It allows clients that expect a particular interface to work with components that implement a different interface.
In most cases, a marshaling wrapper class will be enough to handle all interaction and compatibility issues. In the cases where a declarative, low level technique is necessary to handle marshaling you can use custom marshalers.
The MarshalAsAttribute attribute can be used to apply a custom marshaler to a parameter or function return value. The custom marshaler is identified by this attribute and implements the ICustomMarshaler interface to provide the appropriate wrappers to the .NET CLR. It will call the MarshalNativeToManaged and MarshalManagedToNative methods on the custom marshaler to activate the correct wrapper for handling calls between interoperating components. For more information, see "Custom Marshaling" in the .NET Framework Developer's Guide on MSDN.
The following example demonstrates when the custom interop marshaling must be used to stay compatible with a component that is not upgraded to Visual Basic 6.0. The original application has a COM class named Converter that defines the function ConvertCurrency that receives and returns values with the Currency Visual Basic 6.0 data type. This function is used by the rest of the application to perform financial calculations. The original declaration of ConvertCurrency is shown here.
Public Function ConvertCurrency(amount As Currency, curFrom As String, _ curTo As String) As Currency ConvertCurrency = amount * GetConversionFactor(curFrom, curTo) End Function
The Converter class is used in the client code as shown here.
Private Sub Form_Load()
Dim converter As New Utils.Converter
Dim res As Currency Dim amount As Currency amount = GetInitia1Amount()
res = converter.ConvertCurrency(amount, "DOL", "EUR") End Sub
The following class is based on the result obtained from an automatic upgrade of the Converter class to Visual Basic .NET.
<System.Runtime.InteropServices.ProgId("Converter_NET.Converter"), _ C1assInterface(C1assInterfaceType.AutoDua1)> Public Class Converter Public Function ConvertCurrency(ByRef amount As Decimal, _
ByRef curFrom As String, ByRef curTo As String) As Decimal ConvertCurrency = amount * _
Notice that the class attribute ClassInterface(ClassInterfaceType.AutoDual) has been manually included to make the class member declarations available to Visual Basic 6.0. The resulting project is also automatically registered for COM interop. Note that in order to expose this class to COM clients, the ComClassAttribute attribute may also have been used; the preceding example uses the ClassInterfaceType.AutoDual attribute only to simplify the example.
Assume that the application's upgrade plan determined that the new Converter class will be accessed by unmanaged COM clients, as shown in the previous Form_Load example. According to this plan, the references to the original Converter class must be replaced by the new managed one. However, when the resulting application is compiled the following error message is shown for the invocation of ConvertCurrency: "Compile error: Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic."
The problem is that the COM client identifies the Decimal data type as an unsupported Variant type. The solution to this problem requires the use of custom marshaling in the definition of the Visual Basic .NET component, as shown here.
<System.Runtime.InteropServices.ProgId("Converter_NET.Converter"), _ C1assInterface(C1assInterfaceType.AutoDua1)> Public Class Converter Public Function ConvertCurrency( _
<Marsha1As(UnmanagedType.Currency)> ByRef amount As Decimal _ ByRef curFrom As String, ByRef curTo As String) _ As <MarshalAs(UnmanagedType.Currency)> Decimal ConvertCurrency = amount * _
The MarshalAsAttribute attribute indicates that one of the function parameters and the function return value must be marshaled as a COM currency data type instead of Decimal. With this adjustment, the component can still be used from Visual Basic 6.0 clients.
Was this article helpful?