A strange thing happened to me yesterday.  I went to install an OS X update to my iMac.  The update failed midway through, and this left my machine in an unbootable state.  The truth is that it could have been Windows, or a Windows product, that did the same thing, and it really reminded me why what we're doing is so important.

So far, you've only heard vague talk about Transactional NTFS.  Today I'd like to show you Transactional NTFS in action.  If you're in Microsoft, you can start using this today; if you're outside of Microsoft, I encourage you to take advantage of the upcoming Longhorn beta programs and try it out.

#include <windows.h>
#include <ktmw32.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>

int __cdecl _tmain(int argc, TCHAR ** argv)
{
HANDLE hTrans;
STARTUPINFO siStartupInfo;
PROCESS_INFORMATION piProcessInformation;
SECURITY_ATTRIBUTES saSecurityAttributes;

if (argc<2) {
_tprintf(_T("Usage: %s <appname>\n"), argv[0]);
return EXIT_FAILURE;
}

// Clear out the structure
memset (&saSecurityAttributes, 0, sizeof(saSecurityAttributes));

// Make the transaction inheritable, so child processes can use it.
saSecurityAttributes.bInheritHandle = TRUE;

// Create the transaction.
hTrans = CreateTransaction(&saSecurityAttributes, NULL, 0, 0, 0, 0, NULL);

if (hTrans == INVALID_HANDLE_VALUE) {
_tprintf(_T("Could not create transaction.\n"));
return EXIT_FAILURE;
}

// Currently, we're still operating as a normal, non-transacted
// thread. To use the transaction we've just created, call
// SetCurrentTransaction.
if (!SetCurrentTransaction(hTrans)) {
_tprintf(_T("Could not change transactions.\n"));
return EXIT_FAILURE;
}

memset(&siStartupInfo, 0, sizeof(siStartupInfo));
siStartupInfo.cb = sizeof(siStartupInfo);

// Start a child process, inheriting the transaction. Anything
// you do from this process will now be transactional! Neat, huh?
if (!CreateProcess(NULL,
argv[1],
NULL,
NULL,
TRUE, // Inherit transaction handle!
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&siStartupInfo,
&piProcessInformation)) {
_tprintf(_T("Could not run %s, GetLastError returns %i.\n"), argv[1],
GetLastError());
return EXIT_FAILURE;
}

// Wait for the child process to finish. This is really
// important, because we don't want to commit the transaction
// until the child process isn't using it anymore.
WaitForSingleObject(piProcessInformation.hProcess, INFINITE);

CloseHandle(piProcessInformation.hThread);
CloseHandle(piProcessInformation.hProcess);

// All of a sudden, changes the child process made to the
// filesystem become atomically visible to everybody else :)
if (!CommitTransaction(hTrans)) {
_tprintf(_T("Could not commit the transaction.\n"));
return EXIT_FAILURE;
}

// We're still running transactionally, but that transaction
// has just been committed. Any other file-based operations
// would fail now. So let's switch back to the normal,
// boring, non-transacted state.
if (!SetCurrentTransaction(NULL)) {
_tprintf(_T("Could not switch back to non-transactional state.\n"));
return EXIT_FAILURE;
}

// A transaction handle still needs to be closed, like everything
// else.
CloseHandle(hTrans);

return EXIT_SUCCESS;
}

What does it do? It allows you to transact any existing application you like. So I'd like to show now what happens when I use this program on Longhorn, and run 'cmd.exe'. The output on the left is using transactions; the output on the right is not.
N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:57 PM <DIR> .
04/25/2005 12:57 PM <DIR> ..
04/22/2005 02:01 PM 4,608 test.exe
1 File(s) 4,608 bytes
2 Dir(s) 3,081,523,200 bytes free

N:\temp>test cmd
Microsoft Windows [Version 6.0.5061]
(C) Copyright 1985-2005 Microsoft Corp.

N:\temp>echo foobar>file

N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:58 PM <DIR> .
04/25/2005 12:58 PM <DIR> ..
04/25/2005 12:58 PM 8 file
04/22/2005 02:01 PM 4,608 test.exe
2 File(s) 4,616 bytes
2 Dir(s) 3,081,523,200 bytes free

N:\temp>exit

N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:58 PM <DIR> .
04/25/2005 12:58 PM <DIR> ..
04/25/2005 12:58 PM 8 file
04/22/2005 02:01 PM 4,608 test.exe
2 File(s) 4,616 bytes
2 Dir(s) 3,081,523,200 bytes free

N:\temp>
N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:57 PM <DIR> .
04/25/2005 12:57 PM <DIR> ..
04/22/2005 02:01 PM 4,608 test.exe
1 File(s) 4,608 bytes
2 Dir(s) 3,081,523,200 bytes free







N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:58 PM <DIR> .
04/25/2005 12:58 PM <DIR> ..
04/22/2005 02:01 PM 4,608 test.exe
1 File(s) 4,608 bytes
2 Dir(s) 3,081,523,200 bytes free




N:\temp>dir
Volume in drive N is New Volume
Volume Serial Number is EE91-9028

Directory of N:\temp

04/25/2005 12:58 PM <DIR> .
04/25/2005 12:58 PM <DIR> ..
04/25/2005 12:58 PM 8 file
04/22/2005 02:01 PM 4,608 test.exe
2 File(s) 4,616 bytes
2 Dir(s) 3,081,523,200 bytes free

N:\temp>