Read/Write File Transacted

image_pdfimage_print
   
 
/*==
== Copyright : BlueCurve (c)
== Licence   : Gnu/GPL v2.x
== Author    : Teddy Albina
== Email     : bluecurveteam@gmail.com
== Web site  : http://www.codeplex.com/BlueCurve
*/
using System;
using System.IO;
using System.Text;
using System.Transactions;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;

namespace BlueCurve.Search.Common.IO
{
    /// <summary>
    /// Fournit des m&eacute;thodes de gestions du syst&egrave;me de fichier
    /// </summary>
    public class CommonStream
    {
        #region Import native methods

        /// <summary>
        /// M&eacute;thode native v&eacute;rifiant si un r&eacute;pertoire est vide
        /// </summary>
        /// <param name="pszPath">Chemin du r&eacute;pertoire &agrave; tester</param>
        /// <returns>bool</returns>
        [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
        private extern static bool PathIsDirectoryEmpty(string pszPath);

        #region &#039;Kernel transaction manager api management&#039;

        /// <summary>
        /// Represente un handle de transaction
        /// </summary>
        public sealed class SafeTransactionHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTransactionHandle()
                : base(true)
            {
            }

            public SafeTransactionHandle(IntPtr preexistingHandle, bool ownsHandle)
                : base(ownsHandle)
            {
                SetHandle(preexistingHandle);
            }

            public enum FileAccess
            {
                GENERIC_READ = unchecked((int)0x80000000),
                GENERIC_WRITE = 0x40000000
            }

            [Flags]
            public enum FileShare
            {
                FILE_SHARE_NONE = 0x00,
                FILE_SHARE_READ = 0x01,
                FILE_SHARE_WRITE = 0x02,
                FILE_SHARE_DELETE = 0x04
            }

            public enum FileMode
            {
                CREATE_NEW = 1,
                CREATE_ALWAYS = 2,
                OPEN_EXISTING = 3,
                OPEN_ALWAYS = 4,
                TRUNCATE_EXISTING = 5
            }

            [DllImport("Kernel32.dll", SetLastError = true)]
            private static extern bool CloseHandle(IntPtr handle);

            override protected bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }

        /// <summary>
        /// Importation de la fonction native CreateFileTransacted() permettant
        /// de cr&eacute;er une transaction ntfs
        /// </summary>
        /// <returns>SafeFileHandle</returns>
        [DllImport("Kernel32.Dll", EntryPoint = "CreateFileTransacted", CharSet = CharSet.Unicode, SetLastError = true)]
        protected static extern SafeFileHandle CreateFileTransacted(
               [In] String lpFileName,
               [In] SafeTransactionHandle.FileAccess dwDesiredAccess,
               [In] SafeTransactionHandle.FileShare dwShareMode,
               [In] IntPtr lpSecurityAttributes,
               [In] SafeTransactionHandle.FileMode dwCreationDisposition,
               [In] int dwFlagsAndAttributes,
               [In] IntPtr hTemplateFile,
               [In] SafeTransactionHandle txHandle,
               [In] IntPtr miniVersion,
               [In] IntPtr extendedOpenInformation
           );

        /// <summary>
        /// Importation de l&#039;interface KTM
        /// </summary>
        [ComImport]
        [Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        protected interface IKernelTransaction
        {
            void GetHandle(out SafeTransactionHandle ktmHandle);
        }

        #endregion

        #endregion


        /// <summary>
        /// Copie un stream dans un autre
        /// de fa&ccedil;on asynchrone
        /// http://msdn.microsoft.com/fr-fr/magazine/cc337900.aspx
        /// </summary>
        /// <param name="source">Stream source</param>
        /// <param name="destination">Stream destination</param>
        /// <param name="completed">Action()</param>
        public static void CopyStreamToStream(FileStream source, FileStream destination,
                                              Action<FileStream, FileStream, Exception> completed)
        {
            byte[] buffer = new byte[0x1024];
            System.ComponentModel.AsyncOperation asyncOp = System.ComponentModel.AsyncOperationManager.CreateOperation(null);
          
            Action<Exception> done = e =>
            {
                if (completed != null) asyncOp.Post(delegate
                {
                    completed(source, destination, e);
                }, null);
            };

            AsyncCallback rc = null;
            rc = readResult =>
            {
                try
                {
                    int read = source.EndRead(readResult);
                    if (read > 0)
                    {
                        destination.BeginWrite(buffer, 0, read, writeResult =>
                        {
                            try
                            {
                                destination.EndWrite(writeResult);
                                source.BeginRead(
                                    buffer, 0, buffer.Length, rc, null);
                            }
                            catch (Exception exc) { done(exc); }
                        }, null);
                    }
                    else done(null);
                }
                catch (Exception exc) { done(exc); }
            };
            source.BeginRead(buffer, 0, buffer.Length, rc, null);
        }


        /// <summary>
        /// V&eacute;rifie qu&#039;un r&eacute;pertoire est vide
        /// </summary>
        /// <param name="path">Chemin de fichier du r&eacute;pertoire</param>
        /// <returns>bool</returns>
        public static bool DirectoryIsEmpty(string path)
        {
            return PathIsDirectoryEmpty(path);
        }


        #region Transactional methods

        /// <summary>
        /// Ecrit un fichier de fa&ccedil;on transactionnel
        /// </summary>
        /// <param name="data">Donn&eacute;es &agrave; &eacute;crire</param>
        /// <param name="path">Chemin du fichier dans lequel &eacute;crire les donn&eacute;es</param>
        /// <returns>Statut de l&#039;op&eacute;ration</returns>
        public static bool WriteFileTransacted(object data, string path)
        {
            if (data == null)
                return false;

            SafeTransactionHandle txHandle = null;
            SafeFileHandle fileHandle = null;
            bool response = true;
            try
            {
                IKernelTransaction kernelTx = (IKernelTransaction)TransactionInterop.GetDtcTransaction(System.Transactions.Transaction.Current);
                kernelTx.GetHandle(out txHandle);

                fileHandle
                    = CreateFileTransacted(
                        path
                        , SafeTransactionHandle.FileAccess.GENERIC_WRITE
                        , SafeTransactionHandle.FileShare.FILE_SHARE_NONE
                        , IntPtr.Zero
                        , SafeTransactionHandle.FileMode.CREATE_ALWAYS
                        , 0
                        , IntPtr.Zero
                        , txHandle
                        , IntPtr.Zero
                        , IntPtr.Zero);

                if (fileHandle.IsInvalid)
                    throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

                using (FileStream stream = new FileStream(fileHandle, FileAccess.Write, 1024, false))
                {
                    BinaryFormatter writer = new BinaryFormatter();
                    writer.Serialize(stream, data);
                    stream.Close();
                }
            }
            catch
            {
                System.Transactions.Transaction.Current.Rollback();
                response = false;
            }
            finally
            {
                if (fileHandle != null &amp;&amp; !fileHandle.IsInvalid)
                {
                    fileHandle.Close();
                    fileHandle.Dispose();
                }

                if (txHandle != null &amp;&amp; !txHandle.IsInvalid)
                {
                    txHandle.Close();
                    txHandle.Dispose();
                }
            }
            return response;
        }


        /// <summary>
        /// Lit un fichier de fa&ccedil;on transactionnel
        /// </summary>
        /// <param name="path">Chemin du fichier &agrave; lire</param>
        /// <returns>Donn&eacute;es lu</returns>
        public object ReadFileTransacted(string path)
        {
            if (!File.Exists(path))
                return null;

            SafeTransactionHandle txHandle = null;
            SafeFileHandle fileHandle = null;
            object raw = null;
            try
            {
                IKernelTransaction kernelTx = (IKernelTransaction)TransactionInterop.GetDtcTransaction(System.Transactions.Transaction.Current);
                kernelTx.GetHandle(out txHandle);

                fileHandle
                    = CreateFileTransacted(
                        path
                        , SafeTransactionHandle.FileAccess.GENERIC_READ
                        , SafeTransactionHandle.FileShare.FILE_SHARE_READ
                        , IntPtr.Zero
                        , SafeTransactionHandle.FileMode.OPEN_ALWAYS
                        , 0
                        , IntPtr.Zero
                        , txHandle
                        , IntPtr.Zero
                        , IntPtr.Zero);

                if (fileHandle.IsInvalid)
                    throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

                using (FileStream stream = new FileStream(fileHandle, FileAccess.Read, 1024, false))
                {
                    BinaryFormatter reader = new BinaryFormatter();
                    raw = reader.Deserialize(stream);
                }
            }
            catch
            {
                raw = null;
            }
            finally
            {
                if (fileHandle != null &amp;&amp; !fileHandle.IsInvalid)
                {
                    fileHandle.Close();
                    fileHandle.Dispose();
                }

                if (txHandle != null &amp;&amp; !txHandle.IsInvalid)
                {
                    txHandle.Close();
                    txHandle.Dispose();
                }
            }
            return raw;
        }

        #endregion
    }
}

   
     


This entry was posted in File Stream. Bookmark the permalink.