Transactions with Windows Vista


You can write a custom durable resource manager that works with the File and Registry classes. A file-based durable resource manager can copy the original file and write changes to the temporary file inside a temporary directory to make the changes persistent. When committing the transaction, the original file is replaced by the temporary file. Writing custom durable resource managers for files and the registry is no longer necessary with Windows Vista. Windows Vista supports native transactions with the file system and with the registry. For this, Windows Vista has new API calls such as CreateFileTransacted, CreateHardLinkTransacted, CreateSymbolicLinkTransacted, CopyFileTransacted, and so on. What these API calls have in common is that they require a handle to a transaction passed as an argument; they do not support ambient transactions. The transactional API calls are not available from .NET 3.0, but you can create a custom wrapper by using Platform Invoke.

Tip 

Platform Invoke is discussed in more detail in Chapter 23, “COM Interoperability.”

When invoking native methods, the parameters of the native methods must be mapped to .NET data types. Because of security issues, .NET 2.0 introduced the class SafeHandle to map a native HANDLE type. SafeHandle is an abstract type that wraps operating system handles and supports critical finalization of handle resources. Depending on the allowed values of a handle, the derived classes SafeHandleMinusOneIsInvalid and SafeHandleZeroOrMinusOneIsInvalid can be used to wrap native handles. SafeFileHandle itself derives from SafeHandleZeroOrMinusOneIsInvalid. To map a handle to a transaction, the class SafeTransactionHandle is defined.

  using System; using Microsoft.Win32.SafeHandles; using System.Runtime.Versioning; using System.Runtime.InteropServices; using System.Runtime.ConstrainedExecution; using System.Security.Permissions; namespace Wrox.ProCSharp.Transactions {    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]    public sealed class SafeTransactionHandle : SafeHandleZeroOrMinusOneIsInvalid    {       private SafeTransactionHandle()             : base(true) { }       public SafeTransactionHandle(IntPtr preexistingHandle, bool ownsHandle)             : base(ownsHandle)       {          SetHandle(preexistingHandle);       }       [DllImport("Kernel32.dll", SetLastError = true)]       [ResourceExposure(ResourceScope.Machine)]       [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]       [return: MarshalAs(UnmanagedType.Bool)]       private static extern bool CloseHandle(IntPtr handle);       [ResourceExposure(ResourceScope.Machine)]       [ResourceConsumption(ResourceScope.Machine)]       protected override bool ReleaseHandle()       {          return CloseHandle(handle);       }    } } 

The interface IKernelTransaction is used to get a transaction handle and pass it to the transacted Windows API calls. This is a COM interface and must be wrapped to .NET by using COM Interop attributes as shown. The attribute GUID must have exactly the identifier as you can see in the interface definition, as this is the identifier used with the definition of the COM interface.

Tip 

COM Interop is explained in Chapter 23, “COM Interoperability.”

  using System; using System.Runtime.InteropServices; namespace Wrox.ProCSharp.Transactions {    [ComImport]    [Guid("")]    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]    public interface IKernelTransaction    {       void GetHandle(out SafeTransactionHandle ktmHandle);    } } 

The class TransactedFile wraps the Windows API CreateFileTransacted(). The parameters defined with the Windows API call are mapped to .NET data types. The parameter txHandle represents a handle to a transaction and is of the previously defined type SafeTransactionHandle. The attribute DllImport defines that the Windows API call is implemented in the native DLL Kernel32.dll.

  using System; using System.IO; using System.Transactions; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; namespace Wrox.ProCSharp.Transactions {    public static class TransactedFile    {       [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall,             CharSet = CharSet.Unicode)]       internal static extern SafeFileHandle CreateFileTransacted(             String lpFileName,             uint dwDesiredAccess,             uint dwShareMode,             IntPtr lpSecurityAttributes,             uint dwCreationDisposition,             int dwFlagsAndAttributes,             SafeFileHandle hTemplateFile,             SafeTransactionHandle txHandle,             IntPtr miniVersion,             IntPtr extendedParameter); 

To make the Windows API call easy to use from .NET applications, the class TransactedFile defines the method GetTransactedFileStream(). This method requires a file name as parameter and returns a System.IO.FileStream.

TransactionInterop.GetDtcTransaction() creates an interface pointer of the IKernelTransaction to the ambient transaction that is passed as an argument to GetDtcTransaction(). Using the interface IKernelTransaction, the handle of type SafeTransactionHandle is created. This handle is then passed to the wrapped API call CreateFileTransacted(). With the returned file handle, a new FileStream instance is created and returned to the caller.

        internal const short FILE_ATTRIBUTE_NORMAL = 0x80;       internal const short INVALID_HANDLE_VALUE = -1;       internal const uint GENERIC_READ = 0x80000000;       internal const uint GENERIC_WRITE = 0x40000000;       internal const uint CREATE_NEW = 1;       internal const uint CREATE_ALWAYS = 2;       internal const uint OPEN_EXISTING = 3;       public static FileStream GetTransactedFileStream(string filename)       {          IKernelTransaction ktx = (IKernelTransaction)                TransactionInterop.GetDtcTransaction(Transaction.Current);          SafeTransactionHandle txHandle;          ktx.GetHandle(out txHandle);          SafeFileHandle fileHandle = TransactedFile.CreateFileTransacted(                filename, GENERIC_WRITE, 0,                IntPtr.Zero, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,                null,                txHandle, IntPtr.Zero, IntPtr.Zero);          return new FileStream(fileHandle, FileAccess.Write);       }    } } 

Now it’s very easy to use the transactional API from .NET code. You can create an ambient transaction with the TransactionScope class and use the TransactedFile class within the context of the ambient transaction scope. If the transaction is aborted, the file is not written. If the transaction is committed, you can find the file in the temp directory.

  using System; using System.Transactions; using System.IO; namespace Wrox.ProCSharp.Transactions {    class Program    {       static void Main()       {          using (TransactionScope scope = new TransactionScope())          {             FileStream stream =                   TransactedFile.GetTransactedFileStream("c:/temp/sample.txt");             StreamWriter writer = new StreamWriter(stream);             writer.WriteLine("Write a transactional file");             writer.Close ();             if (!Utilities.AbortTx())                scope.Complete();          }       }    } } 

Now you can use databases, volatile resources, and files within the same transaction.




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

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