Go C # Interoperability Primer Series (iii): data marshaling in platform invoke

Source: Internet
Author: User
Tags string back

Transmission Door

C # Interop Series articles:

    1. C # Interoperability Primer Series (i): Introduction to Interoperability in C #
    2. C # Interoperability Primer series (ii): Invoke the Win32 function using platform invoke
    3. C # Interoperability Primer Series (iii): data marshaling in platform invoke
    4. C # Interoperability Primer Series (iv): Calling COM components in C #

Summary of the topic

    • Introduction to data marshaling
    • Marshaling Win32 data types
    • Handling of marshaling strings
    • Handling of marshaling structures
    • Handling of marshaling classes
    • Summary

First, data marshaling introduction

When you see this topic, your first question must be-what is data marshaling? ( this series of topics using hypothetical friends to explain the concept of the question, is to hope that everyone with questions to learn the topic content, as well as everyone in the normal learning process can also be used in this way, the personal feel that this way can make their learning efficiency has improved, even so in the learning process may appear slow, But this approach will have a deeper impression on the knowledge you have seen. Far faster than watching, and finally found that there is not much to remember, here to share the learning method, that the acceptable friends can be in peacetime learning can be tried, if you feel bad, I believe you will certainly have their own better way of learning. The explanation for this is that data marshaling is the process by which data is passed between managed and unmanaged memory through the method's parameters and return values when interoperating with unmanaged functions in managed code, which is the CLR (Common language Runtime) Marshaling service (that is, the marshaler) is completed .

The marshaler mainly carries out 3 tasks:

          1. Convert data from a managed type to an unmanaged type, or from an unmanaged type to a managed type
          2. Copy type-converted data from managed-code memory to unmanaged memory or from unmanaged memory to managed memory
          3. When the call is complete, releases the memory allocated during the marshaling process

Ii. Marshaling Win32 data types

When interoperating with unmanaged code, there is bound to be marshaling of data. There are, however, two types of data that need to be processed during marshaling-a type that can be copied directly into a native structure (blittable) and a type that is not directly copied into the native structure (non-bittable). Here is an introduction to each of these two data types.

2.1 Types that can be copied directly into a native structure

Because in managed and unmanaged code, data types are not represented in the same way as managed memory and unmanaged memory, because of this, we need to marshal the data so that when we call an unmanaged function in managed code, Pass the correct incoming parameters to the unmanaged function and return the correct return value to the managed code. However, not all data types are represented differently in memory, when we refer to data types that have the same representation in managed memory and unmanaged memory-types that can be copied directly into a native structure. These data types do not require any special processing by the marshaler to be passed between managed and unmanaged code , and the following lists some simple data types that are copied directly into the native structure:

Windows Data types

unmanaged data types

Managed data types

Managed Data Type Interpretation

Byte/uchar/uint8

unsigned char

System.Byte

Unsigned 8-bit integer

Sbyte/char/int8

Char

System.SByte

Signed 8-bit integer

Short/int16

Short

System.Int16

Signed 16-bit integer

Ushort/word/uint16/wchar

unsigned short

System.UInt16

Unsigned 16-bit integer

Bool/hresult/int/long

Long/int

System.Int32

Signed 32-bit integer

Dword/ulong/uint

unsigned long/unsigned int

System.UInt32

Unsigned 32-bit integer

Int64/longlong

_int64

System.Int64

Signed 64-bit integer

Uint64/dwordlong/ulonglong

_uint64

System.UInt64

Unsigned 64-bit integer

Int_ptr/handle/wparam

Void*/int or _int64

System.IntPtr

Signed pointer type

HANDLE

void*

System.UIntPtr

Unsigned pointer type

FLOAT

Float

System.Single

Single-precision floating-point number

DOUBLE

Double

System.Double

Double-precision floating-point number

In addition to the simple types listed in the table above, there are also replication types that are data types that can be copied directly into the native structure:

(1) The data element is a unary array that can be copied directly into the native structure, such as an integer array, a floating-point group, etc.

(2) contains only formatted value types that can be copied directly into the native structure

(3) member variables are all types that can be copied into a native structure and marshaled as a formatted type

The formatting mentioned above refers to--when a type is defined, the memory layout of a member is explicitly specified at the time of declaration. Decorate the specified type with the StructLayout property in the code, and set the StructLayout LayoutKind property to sequential or explicit, for example:

The struct below the using system.runtime.interopservices;//also belongs to a type that can be copied directly into the native structure [StructLayout (layoutkind.sequential)]public struct Point {public   int x;   public int y;}   

2.2 Types that are not directly copied to the native structure

If a type is not a type that can be copied directly into the native structure, it is a type that is not directly copied to the native structure. Because some types behave differently in managed memory and unmanaged memory, for this type, the marshaler needs to convert them to the called function after the appropriate type conversion , listing some data types that are not directly copied to the native structure:

Windows Data types

unmanaged data types

Managed data types

Managed Data Type Interpretation

Bool

bool

System.Boolean

Boolean type

Wchar/tchar

char/wchar_t

System.Char

ANSI character/unicode character

Lpcstr/lpcwstr/lpctstr/lpstr/lpwstr/lptstr

Const CHAR*/CONST wchar_t*/char*/wchar_t*

System.String

ANSI string/unicode string that, if unmanaged code does not need to update this string, declares the string type in managed code with string type

Lpstr/lpwstr/lptstr

char*/wchar_t*

System.stringbuilder

ANSI string/unicode string, if unmanaged code needs to update this string and then pass the updated string back to managed code, then declare the string in managed code with the StringBuilder type

In addition to the types listed in the previous table, there are many other types that belong to types that are not directly copied into the native structure, such as other pointer types and handle types. After understanding the differences between the blittable and non-blittable types, you can better handle the marshaling of the data in the interop process, with a brief introduction to some of the specific data type marshaling issues

Third, the processing of the marshaling string

In the previous topic, we have already dealt with the marshaling of strings (the previous topic used the string as an in parameter to the Win32 MessageBox function to see the previous topic). So in this section will introduce--marshal as the return value of the string, here is a demo code, the code is mainly called the Win32 GetTempPath function to get the return to return the temporary path, at this time the device will need to send back the returned string to the managed code.

The example of the return value in a managed function that is marshaled back to a managed function is defined by the class program {//Win32 GetTempPath function as follows://dword WINAPI GetTempPath (//_in_ D WORD nbufferlength,//_out_ LPTSTR lpbuffer//);//focus on how to define the function prototype in managed code      
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, setlasterror=true)] public static extern uint GetTempPath (int Bufferlength, StringBuilder buffer); static void Main (string[] args) {StringBuilder buffer = new StringBuilder (300); UINT Temppath=gettemppath (+, buffer); String path = buffer. ToString (); if (TempPath = = 0) {int errorcode =marshal.getlastwin32error (); Win32Exception win32expection = new Win32Exception (errorcode); Console.WriteLine ("An exception occurred calling an unmanaged function, the exception information is:" +win32expection.) Message); } Console.WriteLine ("Call the unmanaged function successfully. "); Console.WriteLine ("Temp path is:" + buffer); Console.read (); } }

The result of the operation is:

Iv. handling of the marshaling structure

When we actually call the Win32 API function, we often need to marshal the struct and class, such as the type of replication, the following is the Win32 function GetVersionEx as an example to demonstrate how to marshal the structure as a parameter. In order to invoke unmanaged code in managed code, first we need to know the definition of the unmanaged function, the following is the GetVersionEx unmanaged definition (more information about the function can be found on the MSDN link: http://msdn.microsoft.com/en-us/ library/ms885648.aspx):

BOOL GetVersionEx (   

The parameter lpversioninformation is a pointer type that points to the osversioninfo struct, so we must know before we getversionex the function in managed code for the function osVersionInfo the unmanaged definition of the struct, and then defines an equivalent struct type as a parameter in managed code. The following is an unmanaged definition of the OSVERSIONINFO struct:

typedef struct  _osversioninfo{    DWORD dwosversioninfosize;       This is initialized to the size of the structure DWORD dwmajorversion before using GetVersionEx    ;               System major Version number    DWORD dwminorversion;               System Minor version number    DWORD dwbuildnumber;               System build number    DWORD dwPlatformId;                  System-supported Platforms    TCHAR szcsdversion[128];          The name of the system patch pack    WORD wservicepackmajor;            The major version of the System fix pack    WORD Wservicepackminor;            The minor version of the system patch pack    WORD wsuitemask;                      Identify the program groups on the system    BYTE Wproducttype;                    Identity system type    BYTE wreserved;                         reserved, not used} osVersionInfo;

Now that we know the definition of the osversioninfo struct in unmanaged code, we need to define an equivalent structure in managed code and make sure that two structs are laid out in memory. The struct body in managed code is defined as follows:

  Because the Win32 GetVersionEx function parameter lpversioninformation is a data structure that points to the OSVERSIONINFO        //So the struct is defined in managed code and the struct object as an unmanaged function parameter        [StructLayout (Layoutkind.sequential,charset=charset.unicode)]         public struct OSVERSIONINFO        {public            UInt32 osversioninfosize;//structure size, before calling the method to initialize the field public            UInt32 MajorVersion; System major Version number public            UInt32 minorversion;//System This version number public            UInt32 BuildNumber;  System build number public            UInt32 PlatformID;  Platform supported by System            //This property is used to indicate that it is marshaled to an inline array            [MarshalAs (unmanagedtype.byvaltstr,sizeconst=128)] public            string CSDVersion; The name of the system patch package is public            UInt16 servicepackmajor;//The main version of the system patch package public            UInt16 Servicepackminor;  Minor version of the system patch package public            UInt16 Suitemask;   Identify the program group on the system public            Byte ProductType;    Identity system type Public            Byte Reserved;  Reserved, not used        }

As can be seen from the above definition, the structure defined in managed code has the following three aspects that are identical to those in unmanaged code:

    • Order of field declarations
    • Type of field
    • The size of the field in memory

And in the definition of the structure above, we used the StructLayout property, which belongs to the System.Runtime.InteropServices namespace (so you must add this extra namespace when using platform invoke technology). The function of this class is to allow the developer to explicitly specify the memory layout of the data fields in the struct or class, to ensure that the data fields in the struct are in the same order as they were defined in memory, so it is specified as layoutkind.sequential(which is also the default value). The following is a concrete look at the code that is called in managed code:

Using system;using system.componentmodel;using system.runtime.interopservices;namespace marshaling the struct body {class Program { To GetVersionEx a managed definition//In order to pass a pointer to a struct and pass the initialized information to the unmanaged code, you need to decorate the parameter with the REF keyword
The Out keyword cannot be used here, and if the Out keyword is used, the CLR does not initialize the parameter, which causes the call to fail

[DllImport ("Kernel32", charset=charset.unicode,entrypoint= "GetVersionEx")] private static extern Boolean Get Versionex_struct (ref osversioninfo OSVERSIONINFO); Because the Win32 GetVersionEx function parameter lpversioninformation is a data structure that points to the OSVERSIONINFO//So the struct is defined in managed code and the struct object as an unmanaged function parameter [St Ructlayout (Layoutkind.sequential,charset=charset.unicode)] public struct OSVERSIONINFO {public U Int32 osversioninfosize; The size of the structure, before calling the method to initialize the field public UInt32 majorversion; System major Version number public UInt32 minorversion; System This version number public UInt32 BuildNumber; System build number public UInt32 PlatformID; Platform supported by System//This property is used to indicate that it is marshaled to an inline array [MarshalAs (unmanagedtype.byvaltstr,sizeconst=128)] public String csdversion; Name of the system Patch pack public UInt16 servicepackmajor; The main version of the system patch package public UInt16 Servicepackminor; Minor version of the system patch package public UInt16 suitemask; Identify the program group on the system public Byte ProdUcttype; Identity system type public Byte Reserved; reserved, not used}//Get operating System Information private static string Getosversion () {//define a string to store version information String versionname = String. Empty; Initializes a struct object osversioninfo osversioninformation = new osVersionInfo (); Before calling the GetVersionEx method, you must use the sizeof method to set the Osversioninfosize member Osversioninformation.osversioninfosize = (UInt32) Ma in the struct body Rshal. SizeOf (typeof (osVersionInfo)); Call the Win32 function Boolean result = getversionex_struct (ref osversioninformation); if (!result) {//If the call fails, get the last error code int errorcode = Marshal.GetLastWin32Error (); Win32Exception win32exc = new Win32Exception (errorcode); Console.WriteLine ("The error message that the call failed is:" + win32exc.message); The call fails when returned as an empty string return string. Empty; } else {Console.WriteLine ("Call into"); Switch (osversioninformation.majorversion) {//Here is only a discussion of the major version number 6, as discussed in the other case Case 6:switch (osversioninformation.minorversion) { Case 0:if (Osversioninformation.producttype = = (Byte) 0) {versionname = "Microsoft Windows Vista"; } else {versionname = "M Icrosoft Windows Server 2008 "; Server version} break; Case 1:if (Osversioninformation.producttype = = (Byte) 0) { Versionname = "Microsoft Windows 7"; } else {versionname = "Microsoft Wi Ndows Server R2 "; } break; Case 2:versionname = "Microsoft Windows 8"; Break } break; Default:versionname = "Unknown operating system"; Break } return versionname; }} static void Main (string[] args) {string os=getosversion (); Console.WriteLine ("The current computer is installed with the operating system: {0}", OS); Console.read (); } }}

The result of the operation is:

Attach the Microsoft operating system name and version number of the corresponding relationship, you can refer to the following table for the above code for other discussion:

Operating system

Version number

Windows 8

6.2

Windows 7

6.1

Windows Server R2

6.1

Windows Server 2008

6.0

Windows Vista

6.0

Windows Server 2003 R2

5.2

Windows Server 2003

5.2

Windows XP

5.1

Windows 2000

5.0

V. Handling of the class of marshaling

The following is an example of marshaling a class directly through the GetVersionEx function, with the following code:

Using system;using system.componentmodel;using system.runtime.interopservices;namespace Marshaling class Processing {class Program {        Managed definitions for GetVersionEx//Because string is csdversion in the definition of a class, string is copied directly to the native struct type,//So the marshaler requires a copy operation. For unmanaged code to be able to get the initial value set in managed code (referring to the Osversioninfosize field, first initializing the value before calling the function),//So the [in] property must be added; When the function returns, in order to copy the result to the managed object, you must also add the [Ou T] attribute//cannot be used with the REF keyword, because OSVERSIONINFO is a class type, which is a reference type, if the REF keyword is a pointer passed in as a pointer, it will cause the call to fail
[DllImport ("Kernel32", CharSet = CharSet.Unicode, EntryPoint = "GetVersionEx")] private static extern Boole An getversionex_struct ([in, out] osversioninfo osversioninfo); Get operating System Information private static string Getosversion () {//define a string to store operating system information string Versionna Me = string. Empty; Initializes an object of class osversioninfo osversioninformation = new osVersionInfo (); Call the Win32 function Boolean result = Getversionex_struct (osversioninformation); if (!result) {//If the call fails, get the last error code int errorcode = Marshal.GetLastWin32Error (); Win32Exception win32exc = new Win32Exception (errorcode); Console.WriteLine ("The error message that the call failed is:" + win32exc.message); The call fails when returned as an empty string return string. Empty; } else {Console.WriteLine ("Call succeeded"); Switch (osversioninformation.majorversion{//Here is only the case where the major version number is 6, and the other case is the same as that discussed in 6:SWI TCH (osversioninformation.minorversion) {case 0: if (Osversioninformation.producttype = = (Byte) 0) { Versionname = "Microsoft Windows Vista"; } else {versionname = "M Icrosoft Windows Server 2008 "; Server version} break; Case 1:if (Osversioninformation.producttype = = (Byte) 0) { Versionname = "Microsoft Windows 7"; } else { Versionname = "Microsoft Windows Server R2"; } break; Case 2:versionname = "Microsoft Windows 8"; Break } break; Default:versionname = "Unknown operating system"; Break } return versionname; }} static void Main (string[] args) {string OS = Getosversion (); Console.WriteLine ("The current computer is installed with the operating system: {0}", OS); Console.read (); }} [StructLayout (layoutkind.sequential, CharSet = CharSet.Unicode)] public class osVersionInfo {Publi C UInt32 osversioninfosize = (UInt32) marshal.sizeof (typeof (osVersionInfo)); Public UInt32 MajorVersion = 0; Public UInt32 minorversion = 0; Public UIntBuildNumber = 0; Public UInt32 platformid = 0; This property is used to denote that it is marshaled to an inline array [MarshalAs (UnmanagedType.ByValTStr, SizeConst = $)] public string csdversion = null; Public UInt16 servicepackmajor = 0; Public UInt16 Servicepackminor = 0; Public UInt16 suitemask = 0; Public Byte producttype = 0; Public Byte Reserved; }}

The result of the operation is the same as the structure defined above, or it is attached:

Vi. Summary

This topic focuses on several types of data marshaling, one sentence for marshaling is to ensure that the data defined in managed code is laid out in memory in the same way as the memory layout in unmanaged code. Some simple types are also listed in the corresponding relationships defined in unmanaged and managed code, and can be defined in managed code using a universal IntPtr type for some pointer types or callback functions that are not listed. However, the topic is just a primer on data marshaling, To really master the data marshaling to consider a lot of other factors, this need everyone in the usual work accumulated.

Go C # Interoperability Primer Series (iii): data marshaling in platform invoke

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.