次の方法で共有


構造体の受け渡し

多くのアンマネージド 関数では、構造体のメンバー (Visual Basic ではユーザー定義型) またはマネージド コードで定義されたクラスのメンバーがパラメーターとして渡されることを期待しています。 プラットフォーム呼び出しを使って構造体またはクラスをアンマネージ コードに渡す場合は、元のレイアウトやアラインメントを保持するための追加情報を提供する必要があります。 このトピックでは、フォーマットされた型を定義するために使用する StructLayoutAttribute 属性について説明します。 マネージド構造体とクラスの場合、 LayoutKind 列挙型によって提供される予測可能なレイアウト動作から選択できます。

ここで説明する概念の中心は、構造体の型とクラスの型の相違点です。 構造体は値型であり、クラスは参照型です。クラスは常に最低 1 つのレベルのメモリの間接参照 (値へのポインター) を提供します。 アンマネージ関数は、次の表の第 1 列のシグネチャに示されているように、間接参照を必要とすることが多いため、この違いは重要です。 残りの列のマネージド構造体とクラス宣言は、宣言内の間接参照のレベルを調整できる度合いを示します。 宣言は、Visual Basic と Visual C# の両方に対して提供されます。

アンマネージ シグネチャ マネージド宣言
間接参照なし
Structure MyType
struct MyType;
マネージド宣言
1 レベルの間接参照
Class MyType
class MyType;
DoWork(MyType x);

0 レベルの間接参照を必要とします。
DoWork(ByVal x As MyType)
DoWork(MyType x)

0 レベルの間接参照を追加します。
既に 1 レベルの間接参照が存在するため調整できません。
DoWork(MyType* x);

1 レベルの間接参照を必要とします。
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

1 レベルの間接参照を追加します。
DoWork(ByVal x As MyType)
DoWork(MyType x)

0 レベルの間接参照を追加します。
DoWork(MyType** x);

2 レベルの間接参照を必要とします。
ByRef ByRefまたはref refを使用できないため、できません。 DoWork(ByRef x As MyType)
DoWork(ref MyType x)

1 レベルの間接参照を追加します。

この表は、プラットフォーム呼び出しの宣言について次のようなガイドラインを示しています。

  • アンマネージ関数が間接参照を必要としない場合は、値渡しによる構造体を使用します。

  • アンマネージ関数が 1 レベルの間接参照を必要とする場合は、参照渡しによる構造体または値渡しによるクラスのいずれかを使用します。

  • アンマネージ関数が 2 レベルの間接参照を必要とする場合は、参照渡しによるクラスを使用します。

構造体の宣言と受け渡し

次の例は、マネージド コードで Point 構造体と Rect 構造体を定義し、型をパラメーターとして 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);
}

クラスの宣言と受け渡し

クラスが固定したメンバー レイアウトを持っている場合に限り、クラスのメンバーをアンマネージ DLL 関数に渡すことができます。 次の例では、User32.dll ファイル内のMySystemTimeに、順番に定義されたGetSystemTime クラスのメンバーを渡す方法を示します。 GetSystemTime には、次のアンマネージ シグネチャがあります。

void GetSystemTime(SYSTEMTIME* SystemTime);

値型とは異なり、クラスは常に 1 レベルの間接参照を使用します。

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

関連項目