OutArrayOfStructs 範例
更新:2007 年 11 月
這個範例示範如何將含有當做 Out 參數之整數和字串的結構陣列,傳遞給 Unmanaged 函式。此範例原始程式碼包含在平台叫用技術範例之中。
這個範例示範如何使用 Marshal 類別 (Class) 和 unsafe 程式碼來呼叫原生函式。
這個範例使用一個包裝函式 (Wrapper Function),以及在 PinvokeLib.dll 中定義的平台叫用,此平台叫用也有在原始程式檔 (Source File) 中提供。此範例使用 TestOutArrayOfStructs 函式和 MYSTRSTRUCT2 結構。該結構包含下列元素:
typedef struct _MYSTRSTRUCT2
{
char* buffer;
UINT size;
} MYSTRSTRUCT2;
MyStruct 類別包含一個 ANSI 字元的字串物件。CharSet 欄位會指定 ANSI 格式。MyUnsafeStruct 是包含 IntPtr 型別 (而非字串) 的結構。
LibWrap 類別包含多載的 TestOutArrayOfStructs 原型方法。如果某個方法將指標宣告為參數,便應該以 unsafe 關鍵字來標記類別。因為 Visual Basic 2005 無法使用 Unsafe 程式碼,因此多載方法、不安全的修飾詞 (Modifier) 和 MyUnsafeStruct 結構都是不需要的。
App 類別會實作 UsingMarshal 方法,執行傳遞陣列時所需要的全部工作。以 out (在 Visual Basic 中為 ByRef) 關鍵字標記的陣列,表示資料從被呼叫端傳遞至呼叫端。此實作使用下列 Marshal 類別方法:
PtrToStructure 會將資料從 Unmanaged 緩衝區封送處理 (Marshal) 至 Managed 物件。
DestroyStructure 會釋放結構中保留給字串的記憶體。
FreeCoTaskMem 會釋放保留給陣列的記憶體。
如前所述,C# 允許使用 Unsafe 程式碼,而 Visual Basic 2005 則不允許使用 Unsafe 程式碼。在 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 );
}
}