HandleRef のサンプル

このサンプルでは、アンマネージ関数が完了するまでの間、マネージ オブジェクトに関するガベージ コレクションを防ぐ方法を示します。 また、関数オーバーロードを使用して、値型への参照の代わりに null 参照 (Visual Basic では Nothing) を渡す方法も示します。

HandleRef のサンプルで使用するアンマネージ関数とその関数宣言を次に示します。

  • Kernel32.dll からエクスポートされる ReadFile

    BOOL ReadFile(
       HANDLE hFile, 
       LPVOID lpBuffer, 
       DWORD nNumberOfBytesToRead, 
       LPDWORD lpNumberOfBytesRead, 
       LPOVERLAPPED lpOverlapped);
    

関数に渡された元の構造体には、次に示す要素が含まれています。

typedef struct _OVERLAPPED { 
    ULONG_PTR  Internal; 
    ULONG_PTR  InternalHigh; 
    DWORD  Offset; 
    DWORD  OffsetHigh; 
    HANDLE hEvent; 
} OVERLAPPED; 

このサンプルでは、Overlapped 構造体および Overlapped2 クラスにはポインター型と HANDLE 型の代わりに IntPtr 型が含まれます。 各メンバーが出現する順番でメモリ内に順次配列されることを保証するために、StructLayoutAttribute 属性を設定します。

LibWrap クラスには、ReadFile メソッドと ReadFile2 メソッドのマネージ プロトタイプが含まれます。 ReadFile は、Overlapped 構造体をパラメーターの 1 つとして渡します。 ReadFile メソッドをオーバーロードすることにより、このサンプルは必要な場合に構造体への参照の代わりに null 参照 (Visual Basic では Nothing) を渡すことができます。 C# と Visual Basic 2005 のどちらでも、null 参照 (Nothing) を直接渡すことは許可されません。

ReadFile2 は Overlapped2 クラスを渡します。 参照型であるクラスは、既定では In パラメーターとして渡されます。 InAttribute 属性と OutAttribute 属性を宣言に対して適用すると、Overlapped2 が In/Out パラメーターとしてマーシャリングされるようになります。 このサンプルは、必要な場合にクラスの代わりに null 参照 (Nothing) を直接渡すことができます。クラスが参照型である場合は、クラスの代わりに null 参照 (Nothing) を渡すことが許されるからです。 App クラスは FileStream 用の HandleRef ラッパーを作成することで、ReadFile または ReadFile2 の呼び出しが完了するまでの間、ガベージ コレクションが発生するのを防ぎます。

プロトタイプの宣言

' Declares a managed structure for the unmanaged structure.
<StructLayout(LayoutKind.Sequential)> _
Public Structure Overlapped
    ' ...
End Structure

' Declares a managed class for the unmanaged structure.
<StructLayout(LayoutKind.Sequential)> _
Public Class Overlapped2
    ' ...
End Class

Public Class LibWrap
    ' Declares a managed prototypes for unmanaged functions.
    ' Because Overlapped is a structure, you cannot pass Nothing as a
    ' parameter. Instead, declares an overloaded method.
    Overloads Declare Ansi Function ReadFile Lib "Kernel32.dll" ( _
        ByVal hndRef As HandleRef, _
        ByVal buffer As StringBuilder, _
        ByVal numberOfBytesToRead As Integer, _
        ByRef numberOfBytesRead As Integer, _
        ByRef flag As Overlapped) As Boolean

    ' Declares an int instead of a structure reference for 'flag'
    Overloads Declare Ansi Function ReadFile Lib "Kernel32.dll" ( _
        ByVal hndRef As HandleRef, _
        ByVal buffer As StringBuilder, _
        ByVal numberOfBytesToRead As Integer, _
        ByRef numberOfBytesRead As Integer, _
        ByVal flag As IntPtr) As Boolean

    ' Because Overlapped2 is a class, you can pass Nothing as a parameter.
    ' No overloading is needed.
    Declare Ansi Function ReadFile2 Lib "Kernel32.dll" Alias "ReadFile" ( _
        ByVal hndRef As HandleRef, _
        ByVal buffer As StringBuilder, _
        ByVal numberOfBytesToRead As Integer, _
        ByRef numberOfBytesRead As Integer, _
        <[In], Out> ByVal flag As Overlapped2) As Boolean
End Class
// Declares a managed structure for the unmanaged structure.
[StructLayout(LayoutKind.Sequential)]
public struct Overlapped
{
    // ...
}

// Declares a managed class for the unmanaged structure.
[StructLayout(LayoutKind.Sequential)]
public class Overlapped2
{
    // ...
}

public class LibWrap
{
   // Declares managed prototypes for unmanaged functions.
   // Because Overlapped is a structure, you cannot pass null as a
   // parameter. Instead, declares an overloaded method.
    [DllImport("Kernel32.dll")]
    public static extern bool ReadFile(
        HandleRef hndRef,
        StringBuilder buffer,
        int numberOfBytesToRead,
        out int numberOfBytesRead,
        ref Overlapped flag );

    [DllImport("Kernel32.dll")]
    public static extern bool ReadFile(
        HandleRef hndRef,
        StringBuilder buffer,
        int numberOfBytesToRead,
        out int numberOfBytesRead,
        IntPtr flag ); // Declares an int instead of a structure reference.

    // Because Overlapped2 is a class, you can pass null as parameter.
    // No overloading is needed.
    [DllImport("Kernel32.dll", EntryPoint="ReadFile")]
    public static extern bool ReadFile2(
        HandleRef hndRef,
        StringBuilder buffer,
        int numberOfBytesToRead,
        out int numberOfBytesRead,
        Overlapped2 flag);
}
// Declares a managed structure for the unmanaged structure.
[StructLayout(LayoutKind::Sequential)]
public value struct Overlapped
{
    // ...
};

// Declares a managed class for the unmanaged structure.
[StructLayout(LayoutKind::Sequential)]
public ref class Overlapped2
{
    // ...
};

public ref class LibWrap
{
public:
    // Declares managed prototypes for unmanaged functions.
    // Because Overlapped is a structure, you cannot pass null as a
    // parameter. Instead, declares an overloaded method.
    [DllImport("Kernel32.dll")]
    static bool ReadFile(
        HandleRef hndRef,
        StringBuilder^ buffer,
        int numberOfBytesToRead,
        int numberOfBytesRead,
        Overlapped% flag );

    [DllImport("Kernel32.dll")]
    static bool ReadFile(
        HandleRef hndRef,
        StringBuilder^ buffer,
        int numberOfBytesToRead,
        int% numberOfBytesRead,
        IntPtr flag ); // Declares an int instead of a structure reference.

    // Because Overlapped2 is a class, you can pass null as parameter.
    // No overloading is needed.
    [DllImport("Kernel32.dll", EntryPoint="ReadFile")]
    static bool ReadFile2(
        HandleRef hndRef,
        StringBuilder^ buffer,
        int numberOfBytesToRead,
        int% numberOfBytesRead,
        Overlapped2^ flag);
};

関数の呼び出し

Public Class App
    Public Shared Sub Main()
        Dim fs As New FileStream("HandleRef.txt", FileMode.Open)
        ' Wraps the FileStream handle in HandleRef to prevent it
        ' from being garbage collected before the call ends.
        Dim hr As New HandleRef(fs, fs.SafeFileHandle.DangerousGetHandle())
        Dim buffer As New StringBuilder(5)
        Dim read As Integer = 0
        ' Platform invoke holds the reference to HandleRef until the
        ' call ends.
        LibWrap.ReadFile(hr, buffer, 5, read, IntPtr.Zero)
        Console.WriteLine("Read {0} bytes with struct parameter: {1}", read, buffer)
        LibWrap.ReadFile2(hr, buffer, 5, read, Nothing)
        Console.WriteLine("Read {0} bytes with class parameter: {1}", read, buffer)
    End Sub
End Class
public class App
{
    public static void Main()
    {
        FileStream fs = new FileStream("HandleRef.txt", FileMode.Open);
        // Wraps the FileStream handle in HandleRef to prevent it
        // from being garbage collected before the call ends.
        HandleRef hr = new HandleRef(fs, fs.SafeFileHandle.DangerousGetHandle());
        StringBuilder buffer = new StringBuilder(5);
        int read = 0;
        // Platform invoke holds a reference to HandleRef until the call
        // ends.
        LibWrap.ReadFile(hr, buffer, 5, out read, IntPtr.Zero);
        Console.WriteLine("Read {0} bytes with struct parameter: {1}", read, buffer);
        LibWrap.ReadFile2(hr, buffer, 5, out read, null);
        Console.WriteLine("Read {0} bytes with class parameter: {1}", read, buffer);
    }
}
public ref class App
{
public:
    static void Main()
    {
        FileStream^ fs = gcnew FileStream("HandleRef.txt", FileMode::Open);
        // Wraps the FileStream handle in HandleRef to prevent it
        // from being garbage collected before the call ends.
        HandleRef hr = HandleRef(fs, fs->SafeFileHandle->DangerousGetHandle());
        StringBuilder^ buffer = gcnew StringBuilder(5);
        int read = 0;
        // Platform invoke holds a reference to HandleRef until the call
        // ends.
        LibWrap::ReadFile(hr, buffer, 5, read, IntPtr::Zero);
        Console::WriteLine("Read {0} bytes with struct parameter: {1}", read, buffer);
        LibWrap::ReadFile2(hr, buffer, 5, read, nullptr);
        Console::WriteLine("Read {0} bytes with class parameter: {1}", read, buffer);
    }
};

参照

概念

各種のマーシャリングのサンプル

プラットフォーム呼び出しのデータ型

マネージ コードでのプロトタイプの作成