Dela via


Skicka strukturer

Många ohanterade funktioner förväntar sig att du skickar medlemmar i strukturer (användardefinierade typer i Visual Basic) som en parameter till funktionen eller medlemmar i klasser som definieras i hanterad kod. När du skickar strukturer eller klasser till ohanterad kod med plattformsanrop måste du ange ytterligare information för att bevara den ursprungliga layouten och justeringen. Det här avsnittet introducerar attributet StructLayoutAttribute som du använder för att definiera formaterade typer. För hanterade strukturer och klasser kan du välja bland flera förutsägbara layoutbeteenden som tillhandahålls av LayoutKind-uppräkningen .

Centralt för de begrepp som presenteras i det här avsnittet är en viktig skillnad mellan struktur- och klasstyper. Strukturer är värdetyper och klasser är referenstyper – klasser ger alltid minst en nivå av indirekt minne (en pekare till ett värde). Den här skillnaden är viktig eftersom ohanterade funktioner ofta kräver indirektion, vilket visas av signaturerna i den första kolumnen i följande tabell. Den hanterade strukturen och klassdeklarationerna i de återstående kolumnerna visar i vilken grad du kan justera indirektionsnivån i deklarationen. Deklarationer tillhandahålls för både Visual Basic och Visual C#.

Ohanterad signatur Hanterad deklaration:
ingen indirektion
Structure MyType
struct MyType;
Hanterad deklaration:
en indirekt nivå
Class MyType
class MyType;
DoWork(MyType x);

Kräver noll indirekta nivåer.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Lägger till noll nivåer av indirektion.
Inte möjligt eftersom det redan finns en indirekt nivå.
DoWork(MyType* x);

Kräver en indirekt nivå.
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Lägger till en indirekt nivå.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Lägger till noll nivåer av indirektion.
DoWork(MyType** x);

Kräver två indirekta nivåer.
Inte möjligt eftersom ByRef ByRef eller ref ref inte kan användas. DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Lägger till en indirekt nivå.

Tabellen beskriver följande riktlinjer för plattformsanropsdeklarationer:

  • Använd en struktur som skickas av ett värde när den ohanterade funktionen inte kräver någon indirektion.

  • Använd antingen en struktur som skickas av en referens eller en klass som skickas av ett värde när den ohanterade funktionen kräver en indirekt nivå.

  • Använd en klass som skickas med referens när den ohanterade funktionen kräver två indirekta nivåer.

Deklarera och skicka strukturer

I följande exempel visas hur du definierar strukturerna Point och Rect i den hanterade koden och skickar typerna som parameter till funktionen PtInRect i User32.dll-filen. PtInRect har följande ohanterade signatur:

BOOL PtInRect(const RECT *lprc, POINT pt);  

Observera att du måste skicka Rect-strukturen efter referens, eftersom funktionen förväntar sig en pekare till en RECT-typ.

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  
  
Friend Class NativeMethods
    Friend 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;  
}
  
internal static class NativeMethods
{  
    [DllImport("User32.dll")]  
    internal static extern bool PtInRect(ref Rect r, Point p);  
}  

Deklarera och skicka klasser

Du kan skicka medlemmar i en klass till en ohanterad DLL-funktion, så länge klassen har en fast medlemslayout. I följande exempel visas hur du skickar medlemmar i MySystemTime klassen, som definieras i sekventiell ordning, till GetSystemTime i filen User32.dll. GetSystemTime har följande ohanterade signatur:

void GetSystemTime(SYSTEMTIME* SystemTime);  

Till skillnad från värdetyper har klasser alltid minst en indirekt nivå.

Imports System.Runtime.InteropServices  
  
<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  
  
Friend Class NativeMethods  
    Friend Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (
        sysTime As MySystemTime)  
    Friend Declare Auto Function MessageBox Lib "User32.dll" (
        hWnd As IntPtr, lpText As String, lpCaption As String, uType As UInteger) As Integer  
End Class  
  
Public Class TestPlatformInvoke
    Public Shared Sub Main()  
        Dim sysTime As New MySystemTime()  
        NativeMethods.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  
        NativeMethods.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;
}  
internal static class NativeMethods
{  
    [DllImport("Kernel32.dll")]  
    internal static extern void GetSystemTime(MySystemTime st);  
  
    [DllImport("user32.dll", CharSet = CharSet.Auto)]  
    internal static extern int MessageBox(
        IntPtr hWnd, string lpText, string lpCaption, uint uType);  
}  
  
public class TestPlatformInvoke  
{  
    public static void Main()  
    {  
        MySystemTime sysTime = new MySystemTime();  
        NativeMethods.GetSystemTime(sysTime);  
  
        string dt;  
        dt = "System time is: \n" +  
              "Year: " + sysTime.wYear + "\n" +  
              "Month: " + sysTime.wMonth + "\n" +  
              "DayOfWeek: " + sysTime.wDayOfWeek + "\n" +  
              "Day: " + sysTime.wDay;  
        NativeMethods.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);  
    }  
}  

Se även