Friday, November 2, 2007

WiX: Writing your own WiX Extension Part 2: Preprocessor

The preprocessor in WiX allows extensibilty at a few levels. In this article I will describe how to add a PreprocessorExtension to your WixExtension and have it handle variables and functions you define in your own namespace.
 
[This sample assumes you have already gone through Part 1]
 
1. Add a new class to your project called SamplePreprocessorExtension.

2. If you added a new file for this class, make sure you add: using Microsoft.Tools.WindowsInstallerXml to you file.

3. Make your SamplePreprocessorExtension class implement PreprocessorExtension.
public class SamplePreprocessorExtension : PreprocessorExtension
 
4. Add your SamplePreprocessorExtension to your SampleWixExtension class and override the PreprocessorExtension property from the base class. This will make it so when WiX asks your extension for your preprocessor extension, you extension will know what to do.
private SamplePreprocessorExtension preprocessorExtension;
public override PreprocessorExtension PreprocessorExtension
{
    get      {         if (this.preprocessorExtension == null)
        {
            this.preprocessorExtension = new SamplePreprocessorExtension(); 
        }
        return this.preprocessorExtension;
    }
}

5. Now, back in your SamplePreprocessorExtension class, you need to specify what prefixes (or namespaces) your extension will handle. For example, if you want to be able to define a variable $(sample.ReplaceMe) then you need to specify that your extension will handle the "sample" prefix.

private static string[] prefixes = { "sample" };
public override string[] Prefixes { get { return prefixes; } }

6. Now that you have specified your prefixes, you now need to handle variables and functions that are passed to you from WiX. You do this by overriding the GetVariable and EvaluateFunction methods from the PreprocessorExtension base class.

public override string GetVariableValue(string prefix, string name)
{
     string result = null;
    // Based on the namespace and name, define the resulting string.
    switch (prefix)
    {
        case "sample":
            switch (name)
            {
                case "ReplaceMe":
                   // This could be looked up from any where you can access from your code.
                   result = "replaced";
                   break;
            }
            break;
    } 
    return result;

  
public override string EvaluateFunction(string prefix, string function, string[] args)
{
    string result = null;
    switch (prefix)
    {
        case "sample":
            switch (function) 
            {
                case "ToUpper":
                    if (0 < args.Length) 
                    {
                        result = args[0].ToUpper();
                    }
                    else
                    {
                        result = String.Empty; 
                    }
                    break
            } 
            break
    } 
    return result;
}

7. Build

8. With this you can now pass your extension on the command line to candle and expect variables and functions in your namespace to be passed to your extension and be evaluated. To prove this, try adding the following properties your WiX source.
    <Property Id="VARIABLETEST" Value="$(sample.ReplaceMe)" />
    <Property Id="FUNCTIONTEST" Value="$(sample.ToUpper(lowercase))" />
You resulting msi should have entries in the Property table with the values "replaced" and "LOWERCASE" in the property table.

2 comments:

  1. Very useful, thank you.

    I was looking for something to get the next build number at compile time and not install time, such as with a Custom Action.

    ReplyDelete
  2. I'd probably have something in my build system that knows the build number and pass it into candle on the command line with the -d switch. That lets you set a preprocessor variables value.

    ReplyDelete