共用方式為


傳遞結構

許多非管理式函式需要您將結構的成員(即 Visual Basic 中的使用者定義型別)或以管理程式碼定義的類別的成員,作為參數傳遞給函式。 使用平台叫用將結構或類別傳遞至 Unmanaged 程式碼時,您必須提供其他資訊來保留原始配置和對齊方式。 本主題介紹用來定義格式化類型的 StructLayoutAttribute 屬性。 針對 Managed 結構和類別,您可以從 LayoutKind 列舉所提供的數個可預測配置行為中進行選取。

本主題中所呈現概念的中心是結構與類別類型之間的重要差異。 結構是值類型,而類別是參考類型 — 類別總是提供至少一層記憶體間接(指向值的指標)。 這項差異十分重要,因為 Unmanaged 功能通常需要間接引用,如下表第一欄中的描述所示。 其餘欄位中的 Managed 結構和類別宣告顯示了您可以在宣告中調整間接層次的程度。 Visual Basic 和 Visual C# 都會提供宣告。

未管理的簽章 管理的宣告:
無間接取值
Structure MyType
struct MyType;
管理的宣告:
一層間接性
Class MyType
class MyType;
DoWork(MyType x);

要求無間接取值。
DoWork(ByVal x As MyType)
DoWork(MyType x)

不增加任何間接層級。
因為已經存在一層間接取值,所以無法進行。
DoWork(MyType* x);

要求一層間接取值。
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

新增一層間接層次。
DoWork(ByVal x As MyType)
DoWork(MyType x)

無增加間接層級。
DoWork(MyType** x);

要求兩層間接取值。
不可行,因為無法使用 ByRefByRefrefref DoWork(ByRef x As MyType)
DoWork(ref MyType x)

增加一層間接性。

該表格描述下列平台叫用宣告指引:

  • Unmanaged 函式未要求間接取值時,請使用以傳值方式傳遞的結構。

  • 當非受控函式需要一個間接層時,請使用以引用方式傳遞的結構或以值方式傳遞的類別。

  • 當非受管控的函式需要兩層間接取址時,請使用以參考方式傳遞的類別。

宣告和傳遞結構

下列範例示範如何使用 Managed 程式碼定義 PointRect 結構,並將類型當成參數傳遞至 User32.dll 檔案中的 PtInRect 函式。 PtInRect 具有下列非管理簽章:

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

請注意,您必須以傳址方式傳遞 Rect 結構,因為此函式需要有 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);  
}  

宣告和傳遞類別

只要類別具有固定成員配置,您就可以將類別的成員傳遞至 Unmanaged DLL 函式。 下列範例示範如何以定義的循序順序將 MySystemTime 類別成員傳遞至 User32.dll 檔案中的 GetSystemTimeGetSystemTime 具有下列非受控簽章:

void GetSystemTime(SYSTEMTIME* SystemTime);  

與實值型別不同,類別一律會有至少一層間接取值。

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

另請參閱