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.
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); }
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.