Recipe 11.19 Opening a File Stream with just aFile Handle
Problem
When interoperating with
unmanaged code, you encounter a situation where you are provided a
file handle and no other information. This file handle must be used
to open its corresponding file.
Solution
In order to use an unmanaged file handle to access a file, use the
FileStream class. The unmanaged file handle could
have been generated using P/Invoke to open a file and get the file
handle. The code would then pass it to the
WriteToFileHandle managed method for writing data,
then flush and close the unmanaged file handle. This setup is
illustrated in the following
code:
public void UsingAnUnmanagedFileHandle( )
{
IntPtr hFile = IntPtr.Zero;
// create a file using unmanaged code
hFile = (IntPtr)FileInteropFunctions.CreateFile("data.txt",
FileInteropFunctions.GENERIC_WRITE,
0,
IntPtr.Zero,
FileInteropFunctions.CREATE_ALWAYS,
0,
0);
if(hFile.ToInt64( ) > 0)
{
// write to the file using managed code
WriteToFileHandle(hFile);
// close the file
FileInteropFunctions.CloseHandle(hFile);
// remove the file
File.Delete("data.txt");
}
}
In order to write to the file handle, we wrap it in a
FileStream, passing the file handle as the first
parameter. Once we have the file stream, we use the capabilities of
the FileStream to write to the file handle by
getting the bytes from a string in ASCII encoding format and calling
Write on the file stream, as shown
here:
public static void WriteToFileHandle(IntPtr hFile)
{
// Open a FileStream object using the passed in file handle
// pass false so that the stream doesn't own the handle, if this was true,
// closing the filestream would close the handle
FileStream fileStream = new FileStream(hFile, FileAccess.ReadWrite, false);
// flush before we start to clear any pending unmanaged actions
fileStream.Flush( );
// Operate on file here...
string line = "Managed code wrote this line!";
// write to the file
byte[] bytes = Encoding.ASCII.GetBytes(line);
fileStream.Write(bytes,0,bytes.Length);
// just close the file stream
fileStream.Close( );
}
In order to perform the unmanaged functions of creating, flushing,
and closing the file handle, we have wrapped the unmanaged Win32 API
functions for these functions. The DllImport
attribute says that these functions are being used from
kernel32.dll and the
SetLastError attribute is set to
true, so that we can see if anything went wrong. A
few of the #defines used with file creation have
been brought over from unmanaged code for readability:
class FileInteropFunctions
{
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint GENERIC_EXECUTE = (0x20000000);
public const uint GENERIC_ALL = (0x10000000);
public const uint CREATE_NEW = 1;
public const uint CREATE_ALWAYS = 2;
public const uint OPEN_EXISTING = 3;
public const uint OPEN_ALWAYS = 4;
public const uint TRUNCATE_EXISTING = 5;
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr CreateFile(
String lpFileName, // filename
uint dwDesiredAccess, // access mode
uint dwShareMode, // share mode
IntPtr attr, // Security Descriptor
uint dwCreationDisposition, // how to create
uint dwFlagsAndAttributes, // file attributes
uint hTemplateFile); // handle to template file
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool FlushFileBuffers(IntPtr hFile);
}
Discussion
You can open a
file using one of the overloaded constructors of the
FileStream class and passing a file handle into
it. When opening a file handle, determine whether this object should
be able to close this file's handle. If the
unmanaged code creating the file intends to hand off ownership to the
managed code, the
object should set the ownsHandle parameter to
true. The ownsHandle parameter
is the third parameter on the constructor used with an existing
handle. In many cases, this instance should not be allowed to close
this file's handle. Instead, let the code that
initially opened the file also close the file. If in doubt, set this
parameter to false.
Keep your code short when opening a file using a file handle. Call
the FileStream.Close method as soon as possible.
The reason for this recommendation is that another object might also
have this file open, and operating on that file through both
FileStream objects can corrupt the data in the
file.
See Also
See the "DllImport Attribute,"
"File Class," and
"FileStream Class" topics in the
MSDN documentation.
|