OutArrayOfStructs のサンプル

更新 : 2007 年 11 月

このサンプルでは、整数および文字列を含む構造体の配列を、Out パラメータとしてアンマネージ関数に渡す方法を示します。サンプルのソース コードは、「プラットフォーム呼び出しの技術サンプル」で提供されています。

このサンプルでは、Marshal クラスおよびアンセーフ コードを使用してネイティブ関数を呼び出す方法を示します。

このサンプルでは、ソース ファイルにも含まれている、PinvokeLib.dll に定義されたラッパー関数とプラットフォーム呼び出しを使用します。使用するのは、TestOutArrayOfStructs 関数と MYSTRSTRUCT2 構造体です。この構造体には次の要素が含まれます。

typedef struct _MYSTRSTRUCT2
{
   char* buffer;
   UINT size; 
} MYSTRSTRUCT2;

MyStruct クラスには ANSI 文字の文字列オブジェクトが含まれます。CharSet フィールドによって ANSI 形式を指定します。MyUnsafeStruct は、文字列の代わりに IntPtr 型を格納している構造体です。

LibWrap クラスには、オーバーロードされた TestOutArrayOfStructs プロトタイプ メソッドが含まれます。メソッドがパラメータとしてポインタを宣言する場合、そのクラスに unsafe キーワードでマークを付ける必要があります。Visual Basic 2005 では保証されないコードを使用できないため、オーバーロードされたメソッド、保証されない修飾子、および構造体 MyUnsafeStruct は不要になります。

App クラスは UsingMarshal メソッドを実装します。このメソッドは、配列を渡すために必要なタスクをすべて実行します。配列には、呼び出し先から呼び出し元にデータを渡すことを示す out キーワード (Visual Basic では ByRef) でマークが付けられます。この実装は、次に示す Marshal クラス メソッドを使用します。

  • PtrToStructure は、アンマネージ バッファからマネージ オブジェクトへとデータをマーシャリングします。

  • DestroyStructure は、構造体に含まれる文字列用に予約したメモリを解放します。

  • FreeCoTaskMem は、配列用に予約したメモリを解放します。

前述のように、C# では保証されないコードが使用されますが、Visual Basic 2005 では使用されません。C# のサンプルの UsingUnsafe は、Marshal クラスの代わりにポインタを使用する代替メソッド実装であり、MyUnsafeStruct 構造体を含む配列を返します。

プロトタイプの宣言

' Declares a class member for each structure element.
< StructLayout( LayoutKind.Sequential, CharSet:=CharSet.Ansi )> _
Public Class MyStruct 
   Public buffer As String 
   Public someSize As Integer
End Class 'MyStruct

Public Class LibWrap
' Declares a managed prototype for the unmanaged function.
   Declare Sub TestOutArrayOfStructs Lib "..\\LIB\\PinvokeLib.dll" ( _
      ByRef arrSize As Integer, ByRef outArray As IntPtr )
End Class 'LibWrap
// Declares a class member for each structure element.
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public class MyStruct 
{
   public String buffer;
   public int size;
}
// Declares a structure with a pointer.
[ StructLayout( LayoutKind.Sequential )]
public struct MyUnsafeStruct 
{
   public IntPtr buffer;
   public int size;
}

public unsafe class LibWrap
{
   // Declares managed prototypes for the unmanaged function.
   [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern void TestOutArrayOfStructs( out int size, 
      out IntPtr outArray );
   [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern void TestOutArrayOfStructs( out int size, 
   MyUnsafeStruct** outArray );
}

関数の呼び出し

Public Class App
   Public Shared Sub Main()
      Console.WriteLine( ControlChars.CrLf & "Using marshal class" & _
ControlChars.CrLf )
      UsingMarshal()
      'Visual Basic 2005 cannot use unsafe code.
   End Sub 'Main
   
   Public Shared Sub UsingMarshal()   
      Dim arrSize As Integer
      Dim outArray As IntPtr
      LibWrap.TestOutArrayOfStructs( arrSize, outArray )
      Dim manArray(arrSize - 1) As MyStruct
      Dim current As IntPtr = outArray
      Dim i As Integer
      
      For i = 0 To arrSize - 1
      
         manArray(i) = New MyStruct()
         Marshal.PtrToStructure( current, manArray(i))
         
         Marshal.DestroyStructure( current, GetType( MyStruct )) 
         current = IntPtr.op_explicit( current.ToInt64() _
           + Marshal.SizeOf( manArray(i) ))
         
         Console.WriteLine( "Element {0}: {1} {2}", i, manArray(i)._
           buffer, manArray(i).someSize )
      Next i
      Marshal.FreeCoTaskMem( outArray )
   End Sub 'UsingMarshal
End Class 'App
public class App
{
   public static void Main()
   {
      Console.WriteLine( "\nUsing marshal class\n" );
      UsingMarshal();
      Console.WriteLine( "\nUsing unsafe code\n" );
      UsingUnsafe();
   }
   
   public static void UsingMarshal()   
   {
      int size;
      IntPtr outArray;
      LibWrap.TestOutArrayOfStructs( out size, out outArray );
      MyStruct[] manArray = new MyStruct[ size ];
      IntPtr current = outArray;
      for( int i = 0; i < size; i++ )
      {
         manArray[ i ] = new MyStruct();
         Marshal.PtrToStructure( current, manArray[ i ]);
         
         //Marshal.FreeCoTaskMem( (IntPtr)Marshal.ReadInt32( current ));
         Marshal.DestroyStructure( current, typeof(MyStruct) );
         current = (IntPtr)((long)current + 
            Marshal.SizeOf( manArray[ i ] ));
         
         Console.WriteLine( "Element {0}: {1} {2}", i, 
            manArray[ i ].buffer, manArray[ i ].size );
      }
      Marshal.FreeCoTaskMem( outArray );
   }
   
   public static unsafe void UsingUnsafe()
   {
      int size;
      MyUnsafeStruct* pResult;
      LibWrap.TestOutArrayOfStructs( out size, &pResult );
      MyUnsafeStruct* pCurrent = pResult;
      for( int i = 0; i < size; i++, pCurrent++ )
      {
         Console.WriteLine( "Element {0}: {1} {2}", i, 
            Marshal.PtrToStringAnsi( pCurrent->buffer ), pCurrent->size );
         Marshal.FreeCoTaskMem( pCurrent->buffer );
      }
      Marshal.FreeCoTaskMem( (IntPtr)pResult );
   }
}

参照

概念

クラス、構造体、および共用体のマーシャリング

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

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