Convert a FileStream opened in synchronous mode to one in asynchronous mode, in C#

Raymond Chen published an interesting blog post today explaining how to convert a synchronous file handle to an asynchronous one, via the ReOpenFile Windows API function.

But that function is not implemented in the .Net BCL. Thus I wrote a managed wrapper for it quickly – an extension method on FileStream. (I’m just sitting around waiting for us to start our sprint planning meeting anyway.)

Actually I have not tested this at all, but I imagine it works. Anyway, here’s the code:

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Romy.Core.Extensions
{
    public static class Example
    {
        private const uint FILE_FLAG_OVERLAPPED = 0x40000000;

        public static FileStream Reopen(this FileStream source, FileAccess access, FileShare share, int bufferSize, bool useAsync)
        {
            var fileAccess = EFileAccess.GenericWrite;

            switch (access)
            {
                case FileAccess.Read:
                    fileAccess = EFileAccess.GenericRead;
                    break;
                case FileAccess.ReadWrite:
                    fileAccess = EFileAccess.GenericRead | EFileAccess.GenericWrite;
                    break;
                case FileAccess.Write:
                    fileAccess = EFileAccess.GenericWrite;
                    break;
            }

            var fileShare = EFileShare.Read | EFileShare.Write;

            switch (share)
            {
                case FileShare.Delete:
                    fileShare = EFileShare.Delete;
                    break;
                case FileShare.Inheritable:
                    fileShare = source.CanRead ? EFileShare.Read : source.CanWrite ? EFileShare.Write : EFileShare.None;
                    break;
                case FileShare.None:
                    fileShare = EFileShare.None;
                    break;
                case FileShare.Read:
                    fileShare = EFileShare.Read;
                    break;
                case FileShare.ReadWrite:
                    fileShare = EFileShare.Read | EFileShare.Write;
                    break;
                case FileShare.Write:
                    fileShare = EFileShare.Write;
                    break;
            }

            return new FileStream(NativeMethods.ReOpenFile(source.SafeFileHandle, (uint)fileAccess, (uint)fileShare, useAsync ? FILE_FLAG_OVERLAPPED : 0), access, bufferSize, useAsync);
        }

        #region Interop

        private static class NativeMethods
        {
            [DllImport("kernel32", SetLastError = true)]
            public static extern SafeFileHandle ReOpenFile(SafeFileHandle hOriginalFile, uint dwAccess, uint dwShareMode, uint dwFlags);
        }

        [Flags]
        private enum EFileAccess : uint
        {
            // Standard Section
            AccessSystemSecurity = 0x1000000,   // AccessSystemAcl access type
            MaximumAllowed = 0x2000000,     // MaximumAllowed access type

            Delete = 0x10000,
            ReadControl = 0x20000,
            WriteDAC = 0x40000,
            WriteOwner = 0x80000,
            Synchronize = 0x100000,

            StandardRightsRequired = 0xF0000,
            StandardRightsRead = ReadControl,
            StandardRightsWrite = ReadControl,
            StandardRightsExecute = ReadControl,
            StandardRightsAll = 0x1F0000,
            SpecificRightsAll = 0xFFFF,

            FILE_READ_DATA = 0x0001,        // file & pipe
            FILE_LIST_DIRECTORY = 0x0001,       // directory
            FILE_WRITE_DATA = 0x0002,       // file & pipe
            FILE_ADD_FILE = 0x0002,         // directory
            FILE_APPEND_DATA = 0x0004,      // file
            FILE_ADD_SUBDIRECTORY = 0x0004,     // directory
            FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
            FILE_READ_EA = 0x0008,          // file & directory
            FILE_WRITE_EA = 0x0010,         // file & directory
            FILE_EXECUTE = 0x0020,          // file
            FILE_TRAVERSE = 0x0020,         // directory
            FILE_DELETE_CHILD = 0x0040,     // directory
            FILE_READ_ATTRIBUTES = 0x0080,      // all
            FILE_WRITE_ATTRIBUTES = 0x0100,     // all

            // Generic Section
            GenericRead = 0x80000000,
            GenericWrite = 0x40000000,
            GenericExecute = 0x20000000,
            GenericAll = 0x10000000,

            SPECIFIC_RIGHTS_ALL = 0x00FFFF,
            FILE_ALL_ACCESS =
            StandardRightsRequired |
            Synchronize |
            0x1FF,

            FILE_GENERIC_READ =
            StandardRightsRead |
            FILE_READ_DATA |
            FILE_READ_ATTRIBUTES |
            FILE_READ_EA |
            Synchronize,

            FILE_GENERIC_WRITE =
            StandardRightsWrite |
            FILE_WRITE_DATA |
            FILE_WRITE_ATTRIBUTES |
            FILE_WRITE_EA |
            FILE_APPEND_DATA |
            Synchronize,

            FILE_GENERIC_EXECUTE =
            StandardRightsExecute |
              FILE_READ_ATTRIBUTES |
              FILE_EXECUTE |
              Synchronize
        }

        [Flags]
        private enum EFileShare : uint
        {
            None = 0x00000000,
            /// <summary>
            /// Enables subsequent open operations on an object to request read access.
            /// Otherwise, other processes cannot open the object if they request read access.
            /// If this flag is not specified, but the object has been opened for read access, the function fails.
            /// </summary>
            Read = 0x00000001,
            /// <summary>
            /// Enables subsequent open operations on an object to request write access.
            /// Otherwise, other processes cannot open the object if they request write access.
            /// If this flag is not specified, but the object has been opened for write access, the function fails.
            /// </summary>
            Write = 0x00000002,
            /// <summary>
            /// Enables subsequent open operations on an object to request delete access.
            /// Otherwise, other processes cannot open the object if they request delete access.
            /// If this flag is not specified, but the object has been opened for delete access, the function fails.
            /// </summary>
            Delete = 0x00000004
        }

        #endregion

    }
}
Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Programming and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s