Marshaling Introduction (1 of N)
Marshaling (similar to serialization) is the process of transforming the memory representation of an object to a data format suitable for storage or transmission. It is typically used when data must be moved between different parts of a computer program or from one program to another.
in the .NET scenario, Marshaling is the process of creating a bridge between managed code and unmanaged code; it is the homer that carries messages from the managed to the unmanaged environment and reverse. It is one of the core services offered by the CLR (Common Language Runtime.) Because much of the types in unmanaged environment do not have counterparts in managed environment, you need to create conversion routines that convert the managed types into unmanaged and vice versa; and that is the marshaling process. As a refresher, we call .NET code “managed” because it is controlled (managed) by the CLR. Other code that is not controlled by the CLR is called
You already know that there is no such compatibility between managed and unmanaged environments. In other words, .NET does not contain such the types HRESULT, DWORD, and HANDLE that exist in the realm of unmanaged code. Therefore, you need to find a .NET substitute or create your own if needed. That is what called marshaling.
An example is the unmanaged DWORD; it is an unsigned 32-bit integer, so we can marshal it in .NET as System.UInt32. Therefore, System.UInt32 is a substitute for the unmanaged DWORD.
Marshaling comes handy when you are working with unmanaged code, whether you are working with Windows API or COM components. It helps you interoperating (i.e. working) correctly with these environments by providing a way to share data between the two environments.
Interop marshaling governs how data is passed in method arguments and return values between managed and unmanaged memory during calls. Interop marshaling is a run-time activity performed by the common language runtime’s marshaling service.
Most data types have common representations in both managed and unmanaged memory. The interop marshaler handles these types for you. Other types can be ambiguous or not represented at all in managed memory.
An ambiguous type can have either multiple unmanaged representations that map to a single managed type, or missing type information, such as the size of an array. For ambiguous types, the marshaler provides a default representation and alternative representations where multiple representations exist. You can supply explicit instructions to the marshaler on how it is to marshal an ambiguous type.
- Platform invoke, which enables managed code to call functions exported from an unmanaged library.
- COM interop, which enables managed code to interact with COM objects through interfaces.
Marshaling data with platform Invoke (PInvoke)
To call functions exported from an unmanaged library, a .NET Framework application requires a function prototype in managed code that represents the unmanaged function. To create a prototype that enables platform invoke to marshal data correctly, you must do the following:
Apply the DLLImportAttribute attribute to the static function or method in managed code.
- Substitute managed data types for unmanaged data types.
You can use the documentation supplied with an unmanaged function to construct an equivalent managed prototype by applying the attribute with its optional fields and substituting managed data types for unmanaged types.
The interop marshaler always attempts to free memory allocated by unmanaged code. This behavior complies with COM memory management rules, but differs from the rules that govern native C++.
Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers.
see the following example
BSTR MyMethod (BSTR b)
However, if you define the method as a platform invoke prototype, replace each BSTR type with a String type, and call
Method, the common language runtime attempts to free
variable b twice in the above example. You can change the marshaling behavior by using IntPtr types rather than String types.
The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method. Similarly, you can avoid automatic memory freeing in situations where memory should never be freed.
Directional attributes are optional. You apply them to method parameters when you want to alter the default behavior of the marshaler. If you omit directional attributes from a method parameter, the marshaler determines the directional flow based on the type of the parameter (value or reference) and its modifiers, if any.
Some languages provide keywords that enable you to modify the directional flow of method parameters. The following table lists the direction-related keywords provided by Visual Basic .NET and C# and shows the equivalent IDL interface attribute.
Default marshaling of method arguments to unmanaged code
ByRef, ref, and out parameter modifiers cause method arguments to be marshaled by reference rather than by value. Method arguments passed by value are marshaled to unmanaged code as values on the stack; arguments passed by reference are marshaled as pointers on the stack. The figure below shows the default marshaling behavior of value types and reference types with parameter modifiers.
By default, reference types (classes, arrays, strings, and interfaces) passed by value are marshaled as In parameters for performance reasons. You do not see changes to these types unless you apply InAttribute and OutAttribute (or just OutAttribute) to the method parameter. The StringBuilder class, which is an exception to this rule, is marshaled as an In/Out parameter.
The interop marshaler guarantees the following behavior with regard to directional attributes:
- The interop marshaler never generates a write operation to an In parameter passed from unmanaged code. Thus, unmanaged code can safely pass a pointer to a read-only page, or a pointer to concurrently accessed data.
- When the copied object contains an allocated object, such as a BSTR, the marshaler always executes the correct sequence of allocations and destructions demanded by the In/Out settings.
To call functions exported from an unmanaged library, a .NET Framework application requires
Most data types have a common representation in both managed and unmanaged memory and do not require special handling by the interop marshaler. These types are called blittable types because they do not require conversion when passed between managed and unmanaged code.
- One-dimensional arrays of blittable types, such as an array of integers
- Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types).
The following table lists non-blittable types from the System namespace. Delegates, which are data structures that refer to a static method or to a class instance, are also non-blittable.
No comments yet.