這個範例示範如何將含有當做 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 類別會實作 UsingMarshaling 方法,執行傳遞陣列時所需要的全部工作。 以 out (在 Visual Basic 中為 ByRef) 關鍵字標記的陣列,表示資料從被呼叫端傳遞至呼叫端。 此實作使用下列 Marshal 類別方法:
PtrToStructure 會將資料從 Unmanaged 緩衝區封送處理 (Marshal) 至 Managed 物件。
DestroyStructure 會釋放結構中保留給字串的記憶體。
FreeCoTaskMem 會釋放保留給陣列的記憶體。
如前所述,C# 允許使用 Unsafe 程式碼,而 Visual Basic 2005 則不允許使用 Unsafe 程式碼。 在 C# 範例中,UsingUnsafePointer 是可以選擇的方法實作,會使用指標代替 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);
}
// Declares a class member for each structure element.
[StructLayout(LayoutKind::Sequential, CharSet=CharSet::Ansi)]
public ref class MyStruct
{
public:
String^ buffer;
int size;
};
// Declares a structure with a pointer.
[StructLayout(LayoutKind::Sequential)]
public value struct MyUnsafeStruct
{
public:
IntPtr buffer;
int size;
};
public ref class LibWrap
{
public:
// Declares managed prototypes for the unmanaged function.
[DllImport("..\\LIB\\PInvokeLib.dll")]
static void TestOutArrayOfStructs(int% size,
IntPtr% outArray);
[DllImport("..\\LIB\\PInvokeLib.dll")]
static void TestOutArrayOfStructs(int% size,
MyUnsafeStruct** outArray);
};
呼叫函式
Public Class App
Public Shared Sub Main()
Console.WriteLine( vbNewLine + "Using marshal class" + vbNewLine)
UsingMarshaling()
'Visual Basic 2005 cannot use unsafe code.
End Sub 'Main
Public Shared Sub UsingMarshaling()
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");
UsingMarshaling();
Console.WriteLine("\nUsing unsafe code\n");
UsingUnsafePointer();
}
public static void UsingMarshaling()
{
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 UsingUnsafePointer()
{
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);
}
}
public ref class App
{
public:
static void Main()
{
Console::WriteLine("\nUsing marshal class\n");
UsingMarshaling();
Console::WriteLine("\nUsing unsafe code\n");
UsingUnsafePointer();
}
static void UsingMarshaling()
{
int size;
IntPtr outArray;
LibWrap::TestOutArrayOfStructs(size, outArray);
array<MyStruct^>^ manArray = gcnew array<MyStruct^>(size);
IntPtr current = outArray;
for (int i = 0; i < size; i++)
{
manArray[i] = gcnew MyStruct();
Marshal::PtrToStructure(current, manArray[i]);
Marshal::DestroyStructure(current, MyStruct::typeid);
//current = (IntPtr)((long)current + Marshal::SizeOf(manArray[i]));
current = current + Marshal::SizeOf(manArray[i]);
Console::WriteLine("Element {0}: {1} {2}", i, manArray[i]->buffer,
manArray[i]->size);
}
Marshal::FreeCoTaskMem(outArray);
}
static void UsingUnsafePointer()
{
int size;
MyUnsafeStruct* pResult;
LibWrap::TestOutArrayOfStructs(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);
}
};