Recipe12.19.Opening a File Stream with Just a File Handle


Recipe 12.19. Opening a File Stream with Just a File Handle

Problem

When interoperating with unmanaged code, you encounter a situation in which 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 use a FileStream object for writing data, then flush and close the unmanaged file handle. This setup is illustrated by the UsingAnUnmanagedFileHandle method shown in Example 12-8.

Example 12-8. UsingAnUnmanagedFileHandle method

 public static void UsingAnUnmanagedFileHandle( ) {     IntPtr hFile = IntPtr.Zero;     // Create a file using unmanaged code.     hFile = 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.         // Wrap our file handle in a safe handle wrapper object.         Microsoft.Win32.SafeHandles.SafeFileHandle safeHFile =             new Microsoft.Win32.SafeHandles.SafeFileHandle(hFile, true);         // Open a FileStream object using the passed in safe file handle.         using (FileStream fileStream =                new FileStream(safeHFile, FileAccess.ReadWrite))         {             // 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);         }         // Remove the file.         File.Delete("data.txt");     } } 

In the UsingAnUnmanagedFileHandle method, we wrap the file handle in a SafeFileHandle object and pass it as the first parameter, in a FileStream. Once we have the file stream, we use its capabilities to write to the file handle. We get the bytes from a string in ASCII-encoding format and call Write on the file stream, as shown here:

 byte[] bytes = Encoding.ASCII.GetBytes(line); fileStream.Write(bytes,0,bytes.Length); 

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 in the FileInteropFunctions class shown in Example 12-9. 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.

Example 12-9. FileInteropFunctions class

 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. The FileStream constructors in Version 2.0 of the .NET Framework have been enhanced to accept a Microsoft.Win32.SafeHandles. SafeFileHandle object instead of an IntPtr for the file handle. The SafeFileHandle wraps the IntPtr file handle and allows the system to handle the releasing of this file handle automatically. To automatically release this wrapped file handle, you must pass true as the second argument to the SafeFileHandle constructor. Microsoft recommends letting the system handle the releasing of this wrapped file handle.

Keep your code short when opening a file using a file handle. Call the FileStream. Close method as soon as possible or use the using statement as in the Solution for this recipe. One 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.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net