A Vista update: integration between System.Transactions and the transacted file system and registry

I have written earlier (here and here) about how to integrate the transactional file and registry services in Vista with System.Transactions.  With the upcoming Vista RC update, there are a number of changes in the APIs for TxF and TxR that impact how to do this integration.

First, TxF and TxR have changed from APIs that automatically  picked up the ambient kernel transaction and joined it, to one where the developer needs to explicitly show an intention to participate in the transaction.  This is accomplished through the addition of several new APIs, such as CreateFileTransacted.  These new APIs are explicitly transaction-aware.  All existing file and registry APIs return to being explicitly transaction-unaware.

(For details on the new transaction-aware APIs, see  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/win32_functions_changed_by_transactions.asp.)

All of the new APIs take a kernel transaction handle as an explicit parameter.  This means that if I want to use these new routines from inside a System.Transactions TransactionScope I will need to do some work.  That is what I want to concentrate on here in the form of a partial example.

Let's start by setting up a couple of helper classes and declarations.

First, we know that we'll be manipulating a kernel transaction handle, so we'll specialize a SafeHandle appropriately:

using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;

namespace TxFile
{
    [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)]
      private static extern bool CloseHandle (IntPtr handle);

      [ResourceExposure(ResourceScope.Machine)]
      [ResourceConsumption(ResourceScope.Machine)]
      override protected bool ReleaseHandle ()
      {
          return CloseHandle (handle);
      }
    }
}

We also need to declare the new CreateFileTransacted API.  This looks a lot like CreateFile, but with a couple of extra parameters.  The new declaration is:

[DllImport("Kernel32.Dll",
          
CallingConvention=CallingConvention.StdCall,
           CharSet = CharSet.Unicode)]
internal static extern SafeFileHandle CreateFileTransacted(
    String lpFileName,
    int dwDesiredAccess,
    int dwShareMode,
    IntPtr lpSecurityAttributes,
    int dwCreationDisposition,
    int dwFlagsAndAttributes,
    SafeFileHandle hTemplateFile,
    SafeTransactionHandle txHandle,
    IntPtr miniVersion,
    IntPtr extendedOpenInformation
   );

Our final declaration is to define the new COM interface that DTC supports, IKernelTransaction.  This interface is available off MSDTC's ITransaction objects.  It's declaration looks like:

[ComImport]
[Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IKernelTransaction
{
    void GetHandle( out SafeTransactionHandle ktmHandle );
}

With these helpers and declarations out of the way, we can look at what to do in the mainline code to use CreateFileTransacted in a TransactionScope.  We know that we need the transaction handle, that we can get that from IKernelTransaction, and we know that we can get that from a DTC ITransaction.  That turns into:

SafeTransactionHandle txHandle;

IKernelTransaction kernelTx =
   (IKernelTransaction) TransactionInterop.GetDtcTransaction
    (Transaction.Current);
kernelTx.GetHandle (txHandle);

Putting it all together, we would get a TransactionScope pattern that looks like:

using (TransactionScope s = new TransactionScope ())
{
  SafeTransactionHandle txHandle;

  IKernelTransaction kernelTx =
   (IKernelTransaction) TransactionInterop.GetDtcTransaction
    (Transaction.Current);
  kernelTx.GetHandle( txHandle );
  ...

  // Do the Win32 file operation such as...
  SafeFileHandle fileHandle = CreateFileTransacted(..., txHandle, null );

  s.Complete ();
}

 


Posted Aug 31 2006, 11:03 AM by jim-johnson
Filed under:

Comments

Managed World - Jason Olson's Blog wrote Using TxF in Vista RC1
on 09-05-2006 2:32 PM
Adventures in Server Land wrote Using TxF in Vista RC1
on 09-05-2006 2:33 PM
Jim Johnson has a new post about how to use TxF with the new model in Vista RC1. You will obviously need...
Jeff Thorm wrote re: A Vista update: integration between System.Transactions and the transacted file system and registry
on 09-07-2006 1:24 PM
Gee! Naively, I thought that the new transaction classes were easy and should bring transaction to the masses. I was apparently wrong. Reading this article, with the amount of lines of code, just to do a simple transactional file operation -- this look unecessarily complex (and therefore unusable)
Managed World - Jason Olson's Blog wrote Transaction Resources List
on 09-08-2006 1:08 PM
DiegoV wrote re: A Vista update: integration between System.Transactions and the transacted file system and registry
on 11-21-2006 7:18 PM
Jim,

I just posted something to the Channel9 thread in which Surendra Verma explains TxF/TxR. I found his justification of the API changes completely convincing, although, I am kind of sad that this wasn't as simple as I expected.

I have an idea for a possible solution, though maybe it is a stupid one. This in an exerpt of my post on Channel9:

http://channel9.msdn.com/ShowPost.aspx?PostID=259679#259679

What I have in mind is:

1. The original version of the API supports an ambient transaction and you mentioned that you could still expose that transaction handle at the thread level. I am not sure where things landed in the final Vista build.

2. Apparently, all the Win32, COM+ and .NET APIs that could take advantage of (optional) transactional semantics, take a file name (or key name for TxR) as a parameter at some point, and ultimately call a Win32 API with a file (key) name as a parameter.

So, what if you could define a special, “transactional” moniker that you would need to preppend to the file name or registry key parameter in order to get “implicit” transactional semantics? I can imagine something like “txfile:”, but it could be something else.

I think this would have a few significant effects:

1. The non-transactional semantics of existing code would be preserved even in the presence of an ambient transaction, because hardcoded and existing strings would be lacking the moniker.

2. You would get back the possibility of using transactions with any existing API that takes a file name (or a key name) from Win32 through the .NET Framework while requiring minimal changes.

3. If you included something like this in a later version of Windows, you could still mix explicit, Vista-like calls, with implicit, moniker based, calls in the same transaction.

Do you think it makes some sense?

Add a Comment

(required)  
(optional)
(required)  
Remember Me?