Udostępnij za pośrednictwem


Przekazywanie struktur

Wiele niezarządzanych funkcji oczekuje, że przekażesz jako parametr do funkcji elementy struktur (typów zdefiniowanych przez użytkownika w Visual Basic) lub elementy klas zdefiniowanych w kodzie zarządzanym. Podczas przekazywania struktur lub klas do niezarządzanego kodu przy użyciu wywołania funkcji platformy należy podać dodatkowe informacje, aby zachować oryginalny układ i wyrównanie. W tym temacie wprowadzono atrybut StructLayoutAttribute używany do definiowania sformatowanych typów. W przypadku zarządzanych struktur i klas można wybrać jedno z kilku przewidywalnych zachowań układu dostarczanych przez wyliczenie LayoutKind.

Kluczową kwestią dla pojęć przedstawionych w tym temacie jest ważna różnica między typami struktury i klas. Struktury to typy wartości, a klasy są typami referencyjnymi — klasy zawsze zapewniają co najmniej jeden poziom pośredniej pamięci (wskaźnik do wartości). Ta różnica jest ważna, ponieważ funkcje niezarządzane często wymagają pośredniości, jak pokazano w podpisach w pierwszej kolumnie poniższej tabeli. Struktura zarządzana i deklaracje klas w pozostałych kolumnach pokazują stopień, do którego można dostosować poziom pośrednictwa w deklaracji. Deklaracje są dostarczane dla języka Visual Basic i Visual C#.

Podpis niezarządzany Deklaracja zarządzana:
brak pośredni
Structure MyType
struct MyType;
Deklaracja zarządzana:
jeden poziom pośredni
Class MyType
class MyType;
DoWork(MyType x);

Wymaga zerowego poziomu pośrednictwa.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Dodaje zero poziomów pośrednictwa.
Nie można, ponieważ istnieje już jeden poziom pośredni.
DoWork(MyType* x);

Wymaga jednego poziomu pośrednictwa.
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Dodaje jeden poziom pośredni.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Dodaje zero poziomów niepośredniości.
DoWork(MyType** x);

Wymaga dwóch poziomów pośredniości.
Nie można tego zrobić, ponieważ nie można użyć ByRefByRef lub refref. DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Dodaje jeden poziom pośredni.

W tabeli opisano następujące wytyczne dotyczące deklaracji wywoływania platformy:

  • Użyj struktury przekazywanej przez wartość, gdy niezarządzana funkcja nie wymaga żadnej pośredniości.

  • Użyj struktury przekazanej przez odwołanie lub klasy przekazanej przez wartość, gdy niezarządzana funkcja wymaga jednego poziomu pośredniego.

  • Użyj klasy przekazywanej przez referencję, gdy niezarządzana funkcja wymaga dwóch poziomów pośrednictwa.

Deklarowanie i przekazywanie struktur

W poniższym przykładzie pokazano, jak zdefiniować struktury Point i Rect w kodzie zarządzanym oraz przekazać te typy jako parametry do funkcji PtInRect w pliku User32.dll. PtInRect ma następujący niezarządzany podpis:

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

Zwróć uwagę, że należy przekazać strukturę rect przez referencję, ponieważ funkcja oczekuje wskaźnika do typu 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  
  
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);  
}  

Deklarowanie i przekazywanie klas

Można przekazać elementy członkowskie klasy do niezarządzanej funkcji DLL, o ile klasa ma stały układ składowy. W poniższym przykładzie pokazano, jak przekazać składowe MySystemTime klasy zdefiniowanej w kolejności sekwencyjnej do metody GetSystemTime w pliku User32.dll. Funkcja GetSystemTime ma następujący podpis niezarządzany:

void GetSystemTime(SYSTEMTIME* SystemTime);  

W przeciwieństwie do typów wartości klasy zawsze mają co najmniej jeden poziom pośredni.

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);  
    }  
}  

Zobacz też