Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DotNETFrameworkNotesForProfessionals.pdf
Скачиваний:
32
Добавлен:
20.05.2023
Размер:
1.82 Mб
Скачать

Chapter 27: Platform Invoke

Section 27.1: Marshaling structs

Simple struct

C++ signature:

typedef struct _PERSON

{

int age;

char name[32];

} PERSON, *LP_PERSON;

void GetSpouse(PERSON person, LP_PERSON spouse);

C# definition

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct PERSON

{

public int age;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string name;

}

[DllImport("family.dll", CharSet = CharSet.Auto)]

public static extern bool GetSpouse(PERSON person, ref PERSON spouse);

Struct with unknown size array fields. Passing in

C++ signature

typedef struct

{

int length; int *data;

} VECTOR;

void SetVector(VECTOR &vector);

When passed from managed to unmanaged code, this

The data array should be defined as IntPtr and memory should be explicitly allocated with

Marshal.AllocHGlobal() (and freed with Marshal.FreeHGlobal() afterwords):

[StructLayout(LayoutKind.Sequential)] public struct VECTOR : IDisposable

{

int length; IntPtr dataBuf;

public int[] data

{

set

{

FreeDataBuf();

GoalKicker.com – .NET Framework Notes for Professionals

94

if (value != null && value.Length > 0)

{

dataBuf = Marshal.AllocHGlobal(value.Length * Marshal.SizeOf(value[0])); Marshal.Copy(value, 0, dataBuf, value.Length);

length = value.Length;

}

}

}

void FreeDataBuf()

{

if (dataBuf != IntPtr.Zero)

{

Marshal.FreeHGlobal(dataBuf); dataBuf = IntPtr.Zero;

}

}

public void Dispose()

{

FreeDataBuf();

}

}

[DllImport("vectors.dll")]

public static extern void SetVector([In]ref VECTOR vector);

Struct with unknown size array fields. Receiving

C++ signature:

typedef struct

{

char *name; } USER;

bool GetCurrentUser(USER *user);

When such data is passed out of unmanaged code and memory is allocated by the unmanaged functions, the managed caller should receive it into an IntPrt variable and convert the bu er to a managed array. In case of strings there is a convenient Marshal.PtrToStringAnsi() method:

[StructLayout(LayoutKind.Sequential)] public struct USER

{

IntPtr nameBuffer;

public string name { get { return Marshal.PtrToStringAnsi(nameBuffer); } }

}

[DllImport("users.dll")]

public static extern bool GetCurrentUser(out USER user);

Section 27.2: Marshaling unions

Value-type fields only

C++ declaration

typedef union

{

char c;

GoalKicker.com – .NET Framework Notes for Professionals

95

int i;

} CharOrInt;

C# declaration

[StructLayout(LayoutKind.Explicit)] public struct CharOrInt

{

[FieldOffset(0)] public byte c; [FieldOffset(0)] public int i;

}

Mixing value-type and reference fields

Overlapping a reference value with a value type one is not allowed so you cannot simply use the FieldOffset(0) text; FieldOffset(0) i; will not compile for

typedef union

{

char text[128]; int i;

} TextOrInt;

and generally you would have to employ custom marshaling. However, in particular cases like this simpler technics may be used:

[StructLayout(LayoutKind.Sequential)] public struct TextOrInt

{

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] public byte[] text;

public int i { get { return BitConverter.ToInt32(text, 0); } }

}

Section 27.3: Calling a Win32 dll function

using System.Runtime.InteropServices;

class PInvokeExample

{

[DllImport("user32.dll", CharSet = CharSet.Auto)]

public static extern uint MessageBox(IntPtr hWnd, String text, String caption, int options);

public static void test()

{

MessageBox(IntPtr.Zero, "Hello!", "Message", 0);

}

}

Declare a function as static extern stting DllImportAttribute with its Value property set to .dll name. Don't forget to use System.Runtime.InteropServices namespace. Then call it as an regular static method.

The Platform Invocation Services will take care of loading the .dll and finding the desired finction. The P/Invoke in most simple cases will also marshal parameters and return value to and from the .dll (i.e. convert from .NET datatypes to Win32 ones and vice versa).

GoalKicker.com – .NET Framework Notes for Professionals

96

Section 27.4: Using Windows API

Use pinvoke.net.

Before declaring an extern Windows API function in your code, consider looking for it on pinvoke.net. They most likely already have a suitable declaration with all supporting types and good examples.

Section 27.5: Marshalling arrays

Arrays of simple type

[DllImport("Example.dll")] static extern void SetArray(

[MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] byte[] data);

Arrays of string

[DllImport("Example.dll")]

static extern void SetStrArray(string[] textLines);

GoalKicker.com – .NET Framework Notes for Professionals

97