SIGN UP MEMBER LOGIN:    
ARTICLE

Creating Transacted Files

Posted by Mohammad Elsheimy Articles | COM Interop September 02, 2009
In this article we will learn how to create transactional files in Windows Vista and descendants.
Reader Level:

I previously wrote this article on my blog, Think Big!.

Introduction

Lastly but not last, and after a long while, Windows Vista introduced a way to create transacted files or even to write to registry.

While market grows remarkably in the last years, its requirements increase as well. And every day you face a new problem that you must overcome to accommodate market requirements.

Transacted operations are one of the commonly demanded requirements by market.

While it's true that you can easily do database operations transactionally, it's not enough. Sometimes you will need to write files or make changes to registry in a transactional way.

With previous versions of Windows, you weren't able to create a file transactionally without writing a huge amount of disorganized code to create it manually. You couldn't use normal transactions or even COM+ Enterprise Services to create this type of transactions.

With Windows Vista you can easily create transacted files the common way you used to create normal files.


Unfortunately, .NET 3.5 also does not supports creating transacted files. So you need to dive into API to create it.
Not Windows Vista only that supports this type of transactions, Windows Server 2008, and future versions of Windows of course, also supports it.

Implementation

CreateFileTransacted() Function

To create a transacted file you can use the Kernel23.dll new function CreateFileTransacted().

This function takes the same arguments as the normal CreateFile() function plus three more arguments that we are interested of the first one of them, the transaction handle that will be used while creating the file and writing to it.

The definition of this function in C is as follows:

HANDLE CreateFileTransacted(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile,
HANDLE hTransaction,
PUSHORT pusMiniVersion,
PVOID pExtendedParameter);
We are interested in 6 parameters of these 9.
  • lpFileName:
    The path of the file being created.
  • dwDesiredAccess:
    Defines how file will be accessed. Read only (0x80000000), right only (0x40000000), or both read and write (0xC0000000).
  • dwShareMode:
    Defines the sharing mode -during the operation- for the file. Enabling reading file contents (0x1), writing to it (0x2), reading and writing (0x3), or deleting the file (0x4).
  • dwCreationDisposition:
    Action to take if file exist or do not exist. This argument can take one of the values:
    • 0x1:
      Create the file and throw an error if it exists.
    • 0x2:
      Always create the file even if it exists.
    • 0x3:
      Open the file and throw an error if it doesn't exist.
    • 0x4:
      Open the file or create it if it doesn't exist.
    • 0x5:
      Open the file and clear its contents. Or throw an error if it doesn't exist.
  • dwFlagsAndAttributes:
    Any additional options. This can be a combination of file attributes like Archive, Hidden, and Encrypted. Also it supports combining options like deleting the file after closing it.
  • hTransaction:
    A handle to our created transaction that we wish to use it in this operation. To get the handle you may take a step further into COM.

Also there're three arguments that we are not interested on, so you can safely pass it a NULL value:

  • lpSecurityAttributes:
    A SECURITY_ATTRIBUTES object contains an optional security descriptor information for the file.
  • hTemplateFile:
    A handle to a file to read it's attributes and options to fill the arguments automatically.
  • pusMiniVersion:
    The miniversion to be opened. When creating transacted file ,by specifying the hTransaction argument, this must be NULL.
  • pExtendedParameter:
    Reserved.

For a complete discussion of this API function, see MSDN Library.

PInvoking CreateFileTransacted() Function

PInvoke is a service that enables you to call unmanaged functions in DLLs, such as those in the Win32 API like the CreateFileTransacted() API function last mentioned.


PInvoke stands for Platform Invokation.

In order to PInvoke a function you need to know some things:
  1. Where this function resides (which DLL).
  2. Function definition and order of arguments -if found.-
  3. How to marshal between unmanaged types in .NET types.

Marshaling in .NET is the process of creating a bridge between new .NET types and legacy COM types. This is done by finding equivalents for those legacy types in the new data types or creating it if needed.

Most of the COM data types have equivalents in .NET, also it's very easy to create your own.

For our function, we could write it in C# as following:

[DllImport("kernel32.dll")]
public static extern
IntPtr CreateFileTransacted(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile,
IntPtr hHandle,
IntPtr pusMiniVersion,
IntPtr pExtendedParameter);

Code explanation:

static extern modifiers are required for creating PInvoke functions.

DllImport attribute used to define the DLL that contains the function.

System.IntPtr is the managed type equivalent to unmanaged HANDLE.

LPCSTR can be easily replaced by the managed System.String.


In unmanaged code DWORD is a 32-bit unsigned integer, so we could safely replace it with System.UInt32.
Because we need to pass a NULL value to the LPSECURITY_ATTRIBUTES argument we marshaled it as IntPtr, so we can use IntPtr.Zero to pass NULL values. We did that too with the last two arguments.
For creating the PInvoke methods easily, you could use the Red Gate's PInvoke.NET Visual Studio add-in.
Also you should visit the interop wiki.

After we create our PInvoke method we can call it. But after that we need to create the transaction and get its handle to fill the arguments of the method.

To get a transaction handle we need to dive into COM and call a method to get the handle. This method is wrapped to .NET using COM Interop.

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


Don't forget adding a using System.Runtime.InteropServices statement.

Now we can call the method:


private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
private const uint GENERIC_WRITE = 0x40000000;
private const uint CREATE_ALWAYS = 0x2;
private const uint FILE_SHARE_NONE = 0x0;

static void Main()
{
using (TransactionScope scope = new TransactionScope())
using (FileStream stream = CreateTransactedFile("D:\\SampleFile.txt"))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.WriteLine("Hello, World!");

// To commit the transaction use the followind line
scope.Complete();
}
}

public static FileStream CreateTransactedFile(string fileName)
{
IKernelTransaction ktx =
(IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current);

IntPtr txHandle;
ktx.GetHandle(out txHandle);

IntPtr fileHandle = CreateFileTransacted(fileName,
GENERIC_WRITE, FILE_SHARE_NONE, IntPtr.Zero,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero, txHandle, IntPtr.Zero, IntPtr.Zero);

return new FileStream(fileHandle, FileAccess.Write);
}

Don't forget adding a reference to System.Transactions and a using statement.

Code explanation:

Constants define the input for the function arguments.

In method CreateTransactedFile() we get the handle of the ambient transaction created in method Main(). Then we call the CreateFileTransacted() function which returns a pointer to the create file. Lastly we created the FileStream object with the created the file handle and used it to create a StreamWriter object to write textual data to the file.


Calling Commit() of the transaction object ensures that all operations completed successfully and now we can apply it. Otherwise, nothing would be done. That's the main characteristic of transactions. All or nothing.

You don't need to close the transaction handle manually because when disposing the TransactionScope object it will close its handle automatically.

If you are interested in closing it manually you can use the .NET wrapper System.Runtime.InteropServices.Marshal.Release() method. Or try the difficult way using the CloseHandle unmanaged handle.

Transactional support not limited to creating a file only. Most file system operation now support transactions, some examples are CopyFileTransacted, CreateDirectoryTransacted, CreateHardLinkTransacted, DeleteFileTransacted, MoveFileTransacted, RemoveDirectoryTransacted, and SetFileAttributesTransacted.

Code Sample

Code sample is attached with the article.

Login to add your contents and source code to this article
share this article :
post comment
 
Nevron Gauge for SharePoint
Become a Sponsor
PREMIUM SPONSORS
  • ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications. Visit DynamicPDF here
    Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor