Compartir a través de


Ejemplo OutArrayOfStructs

Actualización: noviembre 2007

En este ejemplo se muestra cómo pasar una matriz de estructuras que contiene enteros y cadenas como parámetros Out a una función no administrada. El código fuente de ejemplo está incluido en Ejemplo de tecnología de invocación de plataformas.

En este ejemplo se indica cómo llamar a una acción nativa utilizando la clase Marshal y código no seguro.

En este ejemplo se utilizan invocaciones de funciones y de plataforma de un contenedor definidas en PinvokeLib.dll, que se proporcionan igualmente en los archivos de código fuente. Utiliza la función TestOutArrayOfStructs y la estructura MYSTRSTRUCT2. La estructura contiene los elementos siguientes:

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

La clase MyStruct contiene un objeto de cadena de caracteres ANSI. El campo CharSet especifica el formato ANSI. MyUnsafeStruct es una estructura que contiene un tipo IntPtr en lugar de una cadena.

La clase LibWrap contiene el método de prototipo TestOutArrayOfStructs sobrecargado. Si un método declara un puntero como parámetro, la clase se debe marcar con la palabra clave unsafe. Dado que en Visual Basic 2005 no se puede utilizar código no seguro, el método sobrecargado, el modificador no seguro y la estructura MyUnsafeStruct no son necesarios.

La clase App implementa el método UsingMarshal, que realiza todas las tareas necesarias para pasar la matriz. La matriz se marca con la palabra clave out (ByRef en Visual Basic) para indicar que se pasan datos del destinatario de la llamada al llamador. En la implementación se utilizan los siguientes métodos de la clase Marshal:

  • PtrToStructure para calcular referencias de datos que se pasan del búfer no administrado a un objeto administrado.

  • DestroyStructure para liberar la memoria reservada para las cadenas de la estructura.

  • FreeCoTaskMem para liberar la memoria reservada para la matriz.

Como se mencionó anteriormente, en C# se permite el uso de código no seguro, pero en Visual Basic 2005 no se permite. En el ejemplo de C#, UsingUnsafe es una implementación de método alternativa en la que se utilizan punteros en lugar de la clase Marshal para devolver la matriz que contiene la estructura MyUnsafeStruct.

Declaración de prototipos

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

Llamadas a funciones

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

Vea también

Conceptos

Calcular referencias de clases, estructuras y uniones

Tipos de datos de invocación de plataforma

Crear prototipos en código administrado