Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
5 |
|
|
|
Interop Marshaling Overview
Topic Objective
To provide an overview of interop marshalling.
Lead-in
Interop marshaling governs how data is passed in method arguments and return values between managed and unmanaged memory during calls.
!Interop marshaling governs how data is passed between managed and unmanaged memory during calls
#Is a run time activity performed by the common language runtime marshaling service
!Blittable data types are common to managed and unmanaged memory and require no conversion – data types (C# keyword) :
# System.Byte (byte) |
System.SByte (sbyte) |
# System.Int16 (short) |
System.UInt16 (ushort) |
# System.Int32 (int) |
System.UInt32 (uint) |
# System.Int64 (long) |
System.IntPtr System.UIntPtr |
*****************************ILLEGAL FOR NON-TRAINER USE******************************
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 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.
The following types from the System namespace are blittable types:
!System.Byte
!System.SByte
!System.Int16
!System.UInt16
!System.Int32
!System.UInt32
!System.Int64
!System.IntPtr
!System.UintPtr
6Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Interop Marshaling Overview (continued)
Topic Objective
To complete the overiew of interop marshalling.
Lead-in
Let’s discuss how nonblittable types differ from blittable types with regard to Interop marshaling and how custom attributes may be used to in place of the standard marshaling operations.
!Non-blittable types have different or ambiguous representations in managed and unmanaged languages and may require conversion
#For example, managed strings are non-blittable types
!The standard RCW or CCW usually provides adequate marshaling for calls that cross the boundary between COM and the .NET Framework
#Custom attributes can optionally adjust the way the runtime represents managed and unmanaged code
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Non-blittable types have different or ambiguous representations in managed and unmanaged languages. These types may require conversion when they are marshaled between managed and unmanaged code. For example, managed strings are non-blittable types because they can have several different unmanaged representations, some of which can require conversion.
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.
Non-blittable type |
Description |
|
|
System.Array |
Converts to a C-style or a SAFEARRAY. |
System.Boolean |
Converts to a 1, 2, or 4-byte value with true as 1 or -1. |
System.Char |
Converts to a Unicode or ANSI character. |
System.Class |
Converts to a class interface. |
System.Object |
Converts to a variant or an interface. |
System.Mdarray |
Converts to a C-style array or a SAFEARRAY. |
System.String |
Converts to a null-terminated string or to a BSTR. |
System.Valuetype |
Converts to a structure with a fixed memory layout. |
System.Szarray |
Converts to a C-style array or a SAFEARRAY. |
In most cases, the standard RCW or CCW that is generated by the runtime provides adequate marshaling for calls that cross the boundary between COM and the .NET Framework. Using custom attributes, you can optionally adjust the way the runtime represents managed and unmanaged code.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
7 |
|
|
|
" Platform Invoke
Topic Objective
To introduce platform invoke.
Lead-in
In this section you will learn how platform invoke can be used to call unmanaged code.
!How Platform Invoke Works
!Calling a Win32 API From Managed Code
!Calling Unmanaged Functions
!Pinning
!Marshaling
!Performance Considerations and Limitations of Platform Invoke
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Platform invoke allows managed code to call unmanaged functions that are implemented in a DLL. Platform invoke is primarily used to call C functions that are part of the Win32 API. To use these functions, you must import the DLL that hosts the functions. The .NET Framework provides default marshaling to marshal data between managed and unmanaged code. You can, however, override the marshaling provided by the .NET Framework whenever you require custom marshaling to be performed.
8Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
How Platform Invoke Works
Topic Objective
To describe how platform invoke executes methods that are implemented in a DLL.
Lead-in
This is how platform invoke loads and executes a method that is implemented in a DLL.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
When platform invoke calls an unmanaged function, it performs the following sequence of actions:
1.Platform invoke locates the DLL containing the function.
2.It loads the DLL into memory.
3.It locates the address of the function in memory and pushes its arguments onto the stack, marshaling data as required.
4.Platform invoke then transfers control to the unmanaged function.
You provide the DLL and function information to platform invoke by annotating your code. This information is used by platform invoke to locate the DLL and load it into the memory of the process, after which the address of the function is located and control is transferred.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
9 |
|
|
|
Calling a Win32 API From Managed Code
Topic Objective
To describe how to call a Win32 API from managed code.
Lead-in
To use an unmanaged function that is implemented in a DLL, you must first import the DLL into your code.
!Declare the Method with the static and extern C# Keywords
!Import the DLL That Implements the Unmanaged Function That You Wish to Call
[DllImport(<DLL name>, EntryPoint=<function name>, [DllImport(<DLL name>, EntryPoint=<function name>,
CharSet=<type of characterset>] CharSet=<type of characterset>]
!Optionally, Specify Custom Marshaling Information to Override the .NET Framework Default Marshaling
*****************************ILLEGAL FOR NON-TRAINER USE******************************
To call an unmanaged function, the first step is to annotate your code by importing the DLL file in which the function is implemented. You use the DllImport attribute to import the DLL. The DllImport attribute requires the DLL_name argument, and also takes any of the following additional arguments.
Argument |
Description |
EntryPoint |
Specifies the DLL entry point to be called. |
CharSet |
Controls name mangling and the way that string arguments |
|
should be marshaled to the function. The default is |
|
CharSet.Ansi. |
ExactSpelling |
Prevents an entry point from being modified to correspond to |
|
the character set. If CharSet.Auto is enabled, the default is |
|
False. Otherwise, the default is True. |
CallingConvention |
Specifies the calling-convention values used in passing method |
|
arguments. The default is Winapi. |
PreserveSig |
Indicates that the managed method signature should not be |
|
transformed into an unmanaged signature that returns an |
|
HRESULT, and might have an additional [out, retval] |
|
argument for the return value. |
|
The default is True (the signature should not be transformed). |
SetLastError |
Enables the caller to use the GetLastError Win32 API |
|
function to determine whether an error occurred while |
|
executing the method. In Microsoft Visual Basic®, the default is |
|
True; in C# and C++, the default is False. |
If the DLL has both an ANSI and a Unicode implementation of the same function, then the CharSet parameter specifies which version of the function is called.
10 |
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
Calling Unmanaged Functions
Topic Objective
To describe how to annotate unmanaged method calls so that they can be called from
.NET Framework applications.
Lead-in
Unmanaged methods that are implemented as a DLL and called from managed code must be annotated before they can be executed by the runtime.
!To Call a Function That Is Implemented in an Unmanaged DLL
#Add a using statement for System.Runtime.InteropServices
#Add a DllImport attribute to a method, specifying the name of the unmanaged DLL that exports the function to be called
#Declare with the static and extern C# keywords the method that is used to call unmanaged code without providing any implementation for the method
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
……
[DllImport("msvcrt.dll", CharSet=CharSet.Ansi)] [DllImport("msvcrt.dll", CharSet=CharSet.Ansi)] public static extern int puts(String str); public static extern int puts(String str);
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Delivery Tip
Refer to the .NET SDK for more information about
DllImport, calling conventions, marshaling, and character sets.
When you call unmanaged functions from .NET Framework applications, you must add a DllImport attribute to a method that is to be used by platform invoke. The DllImport attribute is defined in System.Runtime.InteropServices. Platform invoke uses the information in the attribute to call the appropriate underlying DLL function, marshal parameters to the function, and return values from the function when the method is called. The example on the slide shows the use of the DllImport attribute.
To annotate a method in managed code that is used to call a function in an unmanaged DLL, you must specify the name of the DLL that exports the unmanaged function. You must also specify with the static and extern C# keywords the name of the exported function or its equivalent entry point ordinal, if the exported function name is different than the name of the method that you declared in the managed module. Optionally, you can specify a flag to indicate the underlying calling convention and to automatically marshal string data types correctly. The calling convention flag can be set to Cdecl, FastCall, StdCall, ThisCall, or Winapi. If no calling convention is specified, Winapi is the default. You can also optionally specify the character set to use as part of the function call. The values that you can specify for the character set are ansi, unicode, or auto.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
11 |
|
|
|
There are two general approaches when declaring methods that match unmanaged functions:
!You can declare the method to call the unmanaged DLL without providing any implementation for the method, as shown in the following example:
using System;
using System.Runtime.InteropServices;
…
[DllImport("msvcrt.dll", CharSet=CharSet.Ansi)] public static extern int puts(String str);
…
static void Main()
{
String Str = "Hello World!"; puts(Str);
}
The compiler uses the method declaration to generate the metadata that the runtime needs to locate and correctly marshal the call to the DLL. When declaring the method in your managed code, you can substitute the equivalent runtime types for the function’s types. In the previous example, a string object is substituted for a character string.
!You can also declare a .NET Framework class that is a collection of declarations for an API exported by an unmanaged DLL. The advantage of creating such a class is that you do not have to declare and annotate the methods for the unmanaged DLL that you want to call in all the applications that you create. After it is created, you can include the class in your application, and you can call the unmanaged functions that are declared in the class as if they were .NET Framework methods.
Annotated methods behave like managed .NET Framework methods. When an annotated method is called, platform invoke calls the unmanaged function, marshaling any data that needs to be marshaled to and from the unmanaged function.
12 |
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
Pinning
Topic Objective
To define pinning.
Lead-in
When data is marshaled between managed and unmanaged code, it can either be copied from one memory location to another, or it can be pinned.
!Data Is Temporarily Locked in Its Current Memory Location to Keep It from Being Relocated by the Common Language Runtime’s Garbage Collector
!Pinning Is Performed When Data Has to Be Passed Between Managed and Unmanaged Code and
#Has a fixed layout and common data representation in both managed and unmanaged memory
-or -
#Has a fixed layout but the data representation is different in managed and unmanaged memory and the class is marshaled by reference
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Pinning is a technique in which data is temporarily locked in its current memory location to keep it from being relocated during garbage collection. Whether data is copied or pinned during the marshaling process depends on the type of the data and its InAttribute and OutAttribute.
When classes that have a fixed layout and common data representation in both managed and unmanaged memory require marshaling, a pointer to the actual object is passed to the called function. The called function is then free to change the contents of the memory location being referenced by the pointer.
When classes have a fixed layout but the data representation is different in managed and unmanaged memory and the class is marshaled by reference, the called function receives a pointer to a copy of the data structure. If the InAttribute is set, the copy is always initialized with the instance’s state. If the OutAttribute is set, the state is always copied back in to the instance upon return. If both the InAttribute and OutAttribute are set, the copy is always initialized with the instance’s state, and the last state is always copied back into the instance on return. If either attribute is omitted, the runtime may choose to eliminate either copy as an optimization.
When classes that do not have a fixed layout—such as System.String and System.Text.StringBuilder—have to be marshaled by value or by reference to unmanaged code, the runtime typically copies the data of either type to a secondary buffer and passes a reference to the buffer to the called function. The reference is always allocated with CoTaskMemAlloc.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
13 |
|
|
|
Marshaling
Topic Objective
To describe how marshalling is performed by platform invoke.
Lead-in
Platform invoke automatically marshals data between unmanaged types and managed types.
!Platform Invoke Marshals Simple Data Types Between Managed and Unmanaged Code
!Custom Marshalling Can Be Specified by Using the MarshalAs Attribute
public static extern int MessageBoxW( public static extern int MessageBoxW(
int h, int h,
[MarshalAs(UnmanagedType.LPWStr)] string m, [MarshalAs(UnmanagedType.LPWStr)] string m, …); …);
*****************************ILLEGAL FOR NON-TRAINER USE******************************
For arguments that are simple data types—such as bytes or integers—platform invoke marshals the managed argument to the corresponding unmanaged data type. The following table shows how the data types used in the Win32 API (wtypes.h) should be expressed in C#.
Data Type in wtypes.h |
C# Data Type |
HANDLE |
int |
BYTE |
byte |
SHORT |
short |
WORD |
ushort |
INT |
int |
UINT |
uint |
LONG |
int |
ULONG |
uint |
BOOLEAN |
int |
CHAR |
char |
LPSTR (and most other string types) |
String for in, StringBuilder for inout |
FLOAT |
float |
DOUBLE |
double |
If any parameter is passed by reference, the ref keyword in C# should be added to the method declaration used to call unmanaged code.
14 |
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code |
For example, the GetUserNameEx Win32 API function has the following declaration:
BOOLEAN GetUserNameEx( |
|
|
EXTENDED_NAME_FORMAT NameFormat, |
// name format |
LPTSTR lpNameBuffer, |
// name |
buffer |
PULONG nSize |
// size |
of name buffer |
); |
|
|
To call this from managed code, you would use the following declaration:
[DllImport("secur32.dll", CharSet=CharSet.Auto)]
public static extern int GetUserNameEx (int nameFormat, StringBuilder userName, ref uint userNameSize);
Notice that an integer is substituted for the enumeration parameter, a StringBuilder object is substituted for the string parameter, and the ref keyword is added to the last parameter, because it is passed by reference.
Your choice of data types may affect applications when the data types are marshaled to and from managed code. For example, in Win32 APIs, a LONG data type is 32 bits, whereas the C# long and Visual Basic .NET Long data types are 64 bits.
When calling API functions, you can specify custom marshaling attributes by adding a MarshalAsAttribute to the parameters of the function. You can also use the MarshalAsAttribute with structures. When using the MarshalAsAttribute with structures, you must also use the StructLayout attribute to set the native layout of the structure.