HandleRef のサンプル

更新 : 2007 年 11 月

このサンプルでは、アンマネージ関数が完了するまでの間、マネージ オブジェクトに関するガベージ コレクションを防ぐ方法を示します。また、関数オーバーロードを使用して、値型への参照の代わりに 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 の呼び出しが完了するまでの間、ガベージ コレクションが発生するのを防ぎます。

次のコード例のソース コードは、.NET Framework「プラットフォーム呼び出しの技術サンプル」で提供されています。

プロトタイプの宣言

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

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

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
   Overloads Declare Ansi Function ReadFile Lib "Kernel32.dll" ( _
      ByVal hndRef As HandleRef, _
      ByVal buffer As StringBuilder, _
      ByVal numberOfBytesToRead As Integer, _
      ByRef numberOfBytesRead As Integer, _
      ' Declares an int instead of a structure reference.
      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 'LibWrap
// 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 );
}

関数の呼び出し

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.Handle )
      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, 0 )
      Console.WriteLine( "Read with struct parameter: {0}", buffer )
      LibWrap.ReadFile2( hr, buffer, 5, read, Nothing )
      Console.WriteLine( "Read with class parameter: {0}", buffer )
   End Sub 'Main
End Class 'App
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.Handle );
      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, 0 );
      Console.WriteLine( "Read with struct parameter: {0}", buffer );
      LibWrap.ReadFile2( hr, buffer, 5, out read, null );
      Console.WriteLine( "Read with class parameter: {0}", buffer );
   }
}

参照

概念

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

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

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