Bagikan melalui


Struktur Penerusan

Banyak fungsi yang tidak dikelola mengharapkan Anda untuk meneruskan, sebagai parameter ke fungsi, anggota struktur (jenis yang ditentukan pengguna dalam Visual Basic) atau anggota kelas yang ditentukan dalam kode yang dikelola. Saat meneruskan struktur atau kelas ke kode yang tidak dikelola menggunakan pemanggilan platform, Anda harus memberikan informasi tambahan untuk mempertahankan tata letak dan perataan asli. Topik ini memperkenalkan atribut StructLayoutAttribute, yang Anda gunakan untuk menentukan jenis format. Untuk struktur dan kelas terkelola, Anda dapat memilih dari beberapa perilaku tata letak yang dapat diprediksi yang disediakan oleh enumerasi LayoutKind.

Inti dari konsep yang disajikan dalam topik ini adalah perbedaan penting antara struktur dan jenis kelas. Struktur adalah jenis nilai dan kelas adalah jenis referensi—kelas selalu menyediakan setidaknya satu tingkat memori tidak langsung (penunjuk ke suatu nilai). Perbedaan ini penting karena fungsi yang tidak terkelola sering menuntut tingkat tidak langsung, seperti yang ditunjukkan oleh tanda tangan di kolom pertama tabel berikut. Struktur terkelola dan deklarasi kelas di kolom yang tersisa menunjukkan sejauh mana Anda dapat menyesuaikan tingkat tidak langsung dalam deklarasi Anda. Deklarasi disediakan untuk Visual Basic dan Visual C#.

Tanda tangan tidak terkelola Deklarasi terkelola:
tidak ada tingkat tidak langsung
Structure MyType
struct MyType;
Deklarasi terkelola:
satu tingkat tidak langsung
Class MyType
class MyType;
DoWork(MyType x);

Menuntut tidak ada tingkat tidak langsung.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Menambahkan tidak ada tingkat tidak langsung.
Tidak dimungkinkan karena sudah ada satu tingkat tidak langsung.
DoWork(MyType* x);

Menuntut satu tingkat tidak langsung.
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Menambahkan satu tingkat tidak langsung.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Menambahkan tidak ada tingkat tidak langsung.
DoWork(MyType** x);

Menuntut dua tingkat tidak langsung.
Tidak dimungkinkan karena ByRef ByRef atau ref ref tidak dapat digunakan. DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Menambahkan satu tingkat tidak langsung.

Tabel tersebut menjelaskan panduan berikut untuk deklarasi pemanggilan platform:

  • Gunakan struktur yang diteruskan dengan nilai ketika fungsi yang tidak dikelola tidak menuntut tingkat tidak langsung.

  • Gunakan struktur yang diteruskan dengan referensi atau kelas yang diteruskan dengan nilai ketika fungsi yang tidak dikelola menuntut satu tingkat tidak langsung.

  • Gunakan kelas yang diteruskan dengan referensi ketika fungsi yang tidak dikelola menuntut dua tingkat tidak langsung.

Struktur Penerusan dan Deklarasi

Contoh berikut menunjukkan cara mendefinisikan struktur Point dan Rect dalam kode terkelola, dan meneruskan jenis sebagai parameter ke fungsi PtInRect di file User32.dll. PtInRect memiliki tanda tangan tidak terkelola berikut:

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

Perhatikan bahwa Anda harus melewatkan struktur Rect dengan referensi, karena fungsi mengharapkan penunjuk ke jenis 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);  
}  

Kelas Penerusan dan Deklarasi

Anda dapat meneruskan anggota kelas ke fungsi DLL yang tidak dikelola, selama kelas tersebut memiliki tata letak anggota yang tetap. Contoh berikut menunjukkan cara meneruskan anggota kelas MySystemTime, yang ditentukan secara berurutan, ke GetSystemTime di file User32.dll. GetSystemTime memiliki tanda tangan tidak terkelola berikut:

void GetSystemTime(SYSTEMTIME* SystemTime);  

Tidak seperti jenis nilai, kelas selalu memiliki setidaknya satu tingkat tidak langsung.

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

Lihat juga