May 19, 2008

Data Driven Managed Custom Actions made easy with DTF

One of the shortcomings of Windows Installer is no built in ability to perform math functions. Fortunately this is made easy in C# thanks to the AxLibrary project from Ron Ayoub found on The Code Project. Now with DTF's ability to easily and reliably write Manged Custom Actions creating a simple math evaluation pattern is easy. Consider this database snippet in WiX:


(Note: I originally wrote the table using InstallShield but decided to re implement it in WiX since it's easier to read without visualization. But it sure is more difficult to author. In InstallShield it only took a few clicks and fill in the form typing in the schema wizard. )

The goal is to query the Math table and process math statements with conditions that evaluate to true. Using C#, DTF and AXLibrary it's as simple as this block of code:





The result is a log file like this:




Wow, is that easy or what? I'd love it if someone would write the same functionality in C++ and show what it would look like. BTW, even though DTF comes from WiX, this CA was actually executed via InstallShield. After all, it is just a Type 1 exported stdcall function.

1 comment:

Michael said...

Excluding the boilerplate for EXPORTS, return codes, etc., the C++ would look something like the code below. Checking return codes would probably add about one line per ::Msi call, although it may be hidden in a macro.

UINT ProcessMath(MSIHANDLE hInstall)
{
PMSIHANDLE hDatabase = ::MsiGetActiveDatabase(hInstall);
PMSIHANDLE hView, hRec;

::MsiOpenView(_T("SELECT `Property`, `Condition`, `Expression` FROM `Math`"), &hView);
::MsiViewExecute(hView, NULL);
while (ERROR_SUCCESS == ::MsiViewFetch(hView, &hRec))
{
TCHAR szProperty[BUF], szCondition[BUF], szExpression[BUF];
DWORD cchBuf;
::MsiRecordGetString(hRec, 1, szProperty, &(cchBuf = BUF));
::MsiRecordGetString(hRec, 2, szCondition, &(cchBuf = BUF));
::MsiRecordGetString(hRec, 3, szExpression, &(cchBuf = BUF));
if (::MsiRecordIsNull(hRec, 2) || MSICONDITION_TRUE == ::MsiEvaluateCondition(hInstall, szCondition))
{
TCHAR szResult[BUF];
AXParser parser;
parser.Parse(szExpression);
ltot(parser.Evaluate(), szResult, 10);
::MsiSetProperty(hInstall, szProperty, szResult);
}
}
return ERROR_SUCCESS;
}

So algorithmically it's no worse, but as usual there's a lot more line noise.