Passage de structures
De nombreuses fonctions non managées comptent sur vous pour passer, en tant que paramètre à la fonction, des membres de structures (types définis par l'utilisateur en Visual Basic) ou des membres de classes qui sont définis dans du code managé. Lorsque vous passez des structures ou des classes à du code non managé à l'aide de l'appel de code non managé, vous devez fournir des informations supplémentaires pour préserver la mise en forme et l'alignement d'origine. Cette rubrique présente l'attribut StructLayoutAttribute que vous utilisez pour définir des types mis en forme. Pour des structures et des classes managées, vous pouvez effectuer une sélection parmi plusieurs comportements prévisibles de mise en forme fournis par l'énumération LayoutKind.
Une différence importante entre les types structure et classe constitue le point central des concepts présentés dans cette rubrique. Les structures sont des types valeur et les classes sont des types référence ; les classes fournissent toujours au moins un niveau d'indirection de mémoire (un pointeur vers une valeur). Cette différence est importante parce que des fonctions non managées exigent souvent une indirection, comme le montrent les signatures contenues dans la première colonne du tableau suivant. Les déclarations de classes et de structures managées figurant dans les autres colonnes montrent le degré auquel vous pouvez ajuster le niveau d'indirection dans votre déclaration.
Signature non managée |
Déclaration managée : aucune indirection struct MyStruct(…); |
Déclaration managée : un niveau d'indirection. class MyStruct(…); |
---|---|---|
DoWork(MyStruct x); Exige aucun niveau d'indirection. |
DoWork(ByVal x As MyStruct) Ajoute aucun niveau d'indirection. |
Impossible parce qu'il y a déjà un niveau d'indirection. |
DoWork(MyStruct* x); Exige un niveau d'indirection. |
DoWork(ByRef x As MyStruct) Ajoute un niveau d'indirection. |
DoWork(ByVal x As MyStruct) Ajoute aucun niveau d'indirection. |
DoWork(MyStruct** x); Exige deux niveaux d'indirection. |
Impossible en raison de ByRef ByRef ne peut pas être utilisé. |
DoWork(ByRef x As MyStruct) Ajoute un niveau d'indirection. |
Le tableau décrit les instructions suivantes pour les déclarations d'appel de code non managé :
Utilisez une structure passée par valeur lorsque la fonction non managée n'exige aucune indirection.
Utilisez une structure passée par référence ou une classe passée par valeur lorsque la fonction non managée exige un niveau d'indirection.
Utilisez une classe passée par référence lorsque la fonction non managée exige deux niveaux d'indirection.
Déclaration et passage de structures
L'exemple suivant montre comment définir les structures Point et Rect dans du code managé et passer les types en tant que paramètres à la fonction PtInRect dans le fichier User32.dll. PtInRect possède la signature non managée suivante :
BOOL PtInRect(const RECT *lprc, POINT pt);
Notez que vous devez passer la structure Rect par référence, car la fonction nécessite un pointeur vers un type RECT.
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Structure Point
Public x As Integer
Public y As Integer
End Structure
Public Structure <StructLayout(LayoutKind.Explicit)> Rect
<FieldOffset(0)> Public left As Integer
<FieldOffset(4)> Public top As Integer
<FieldOffset(8)> Public right As Integer
<FieldOffset(12)> Public bottom As Integer
End Structure
Class Win32API
Declare Auto Function PtInRect Lib "user32.dll" _
(ByRef r As Rect, p As Point) As Boolean
End Class
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}
[StructLayout(LayoutKind.Explicit)]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}
class Win32API {
[DllImport("User32.dll")]
public static extern bool PtInRect(ref Rect r, Point p);
}
Déclaration et passage de classes
Vous pouvez passer les membres d'une classe à une fonction DLL non managée, à partir du moment où la classe possède une disposition de membre fixe. L'exemple suivant montre comment passer les membres d'une classe MySystemTime, qui sont définis dans un ordre séquentiel, à GetSystemTime dans le fichier User32.dll. GetSystemTime possède la signature non managée suivante :
void GetSystemTime(SYSTEMTIME* SystemTime);
Contrairement aux types valeur, les classes ont toujours au moins un niveau d'indirection.
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
<StructLayout(LayoutKind.Sequential)> Public Class MySystemTime
Public wYear As Short
Public wMonth As Short
Public wDayOfWeek As Short
Public wDay As Short
Public wHour As Short
Public wMinute As Short
Public wSecond As Short
Public wMiliseconds As Short
End Class
Public Class Win32
Declare Auto Sub GetSystemTime Lib "Kernel32.dll"(sysTime _
As MySystemTime)
Declare Auto Function MessageBox Lib "User32.dll"(hWnd As IntPtr, _
txt As String, caption As String, Typ As Integer) As Integer
End Class
Public Class TestPlatformInvoke
Public Shared Sub Main()
Dim sysTime As New MySystemTime()
Win32.GetSystemTime(sysTime)
Dim dt As String
dt = "System time is:" & ControlChars.CrLf & _
"Year: " & sysTime.wYear & _
ControlChars.CrLf & "Month: " & sysTime.wMonth & _
ControlChars.CrLf & "DayOfWeek: " & sysTime.wDayOfWeek & _
ControlChars.CrLf & "Day: " & sysTime.wDay
Win32.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0)
End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
class Win32API {
[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(MySystemTime st);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd,
string text, string caption, int options);
}
public class TestPlatformInvoke
{
public static void Main()
{
MySystemTime sysTime = new MySystemTime();
Win32API.GetSystemTime(sysTime);
string dt;
dt = "System time is: \n" +
"Year: " + sysTime.wYear + "\n" +
"Month: " + sysTime.wMonth + "\n" +
"DayOfWeek: " + sysTime.wDayOfWeek + "\n" +
"Day: " + sysTime.wDay;
Win32API.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);
}
}