HandleRef 샘플
업데이트: 2007년 11월
이 샘플에서는 관리되지 않는 함수가 완료되기 전에 관리되는 개체가 가비지 수집되는 것을 방지하는 방법을 보여 줍니다. 함수 오버로드를 사용하여 값 형식에 대한 참조 대신 null 참조(Visual Basic에서는 Nothing)를 전달하는 방법도 보여 줍니다.
HandleRef 샘플에서는 다음의 관리되지 않는 함수를 사용합니다. 이 함수는 원래의 함수 선언과 함께 표시되어 있습니다.
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 클래스에는 포인터 형식 및 HANDLE 형식 대신 IntPtr 형식이 포함되어 있습니다. 멤버가 나타나는 순서에 따라 메모리에 순차적으로 정렬되도록 StructLayoutAttribute 특성이 설정됩니다.
LibWrap 클래스에는 ReadFile 및 ReadFile2 메서드에 대한 관리되는 프로토타입이 포함되어 있으며, 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 또는 ReadFile2에 대한 호출이 완료되기 전에 가비지 수집이 발생하는 것을 방지합니다.
다음 코드 예제의 소스 코드는 .NET Framework Platform Invoke 기술 샘플을 통해 제공됩니다.
프로토타입 선언
' 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 );
}
}