HandleRef 範例
更新:2007 年 11 月
這個範例示範如何在 Unmanaged 函式完成之前,防止 Managed 物件上的記憶體回收。這個範例也會示範如何使用函式多載化 (Function Overloading) 傳遞 Null 參考 (在 Visual Basic 中為 Nothing),而非實值型別 (Value Type) 的參考。
HandleRef 範例使用下列 Unmanaged 函式,顯示其原始函式宣告:
從 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 類別包含 IntPtr 型別,而非指標型別 (Pointer Type) 和 HANDLE 型別。設定 StructLayoutAttribute 屬性以確保成員是按照出現的順序,循序配置於記憶體中。
LibWrap 類別包含 ReadFile 和 ReadFile2 方法的 Managed 原型。ReadFile 會傳遞 Overlapped 結構做為其中一個參數。必要時,將 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 or 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 );
}
}