Wix# native interface can be extended to bring some WiX functionality otherwise unavailable in Wix#.

There are various ways of achieving this. XML Injection and generic attributes are obvious ones. However there is another more standardized approach for this. Wix# can be extended with user-defined types that map WiX entities not supported natively in WiX. A typical use-case for this is the integration of WiX extensions. The 'Wix# Samples\Extensions' sample demonstrates how to integrate WiX utils:RemoveFolderEx extension.

The concept of user defined entity is simple. Such an entity is a class that implements IGenericEntity interface:

public interface IGenericEntity
{
    void Process(ProcessingContext context);
}

The instance of such a class can be passed to the Dir constructor (will be extended in the future to support Project constructor as well). During the compilation of the Dir object Wix# compiler invokes IGenericEntity.Process so it can adjust the XML content being yielded by the compiler with the new WiX elements and their required decoration. This approach in some degree similar to the XML-Injection, except it is not a one off solution but rather something that can be reused again and again and in the way consistent with the WixEntities already supported by Wix#.

The following is the example of how a user-defined entity RemoveFolderEx is used with a typical setup definition:

new Project("CustomActionTest",
    new Dir(@"%ProgramFiles%\CustomActionTest",
        new RemoveFolderEx { On = InstallEvent.uninstall, 
                             Property = "DIR_PATH_PROPERTY_NAME" },
        new File("readme.txt")));


The code below is a complete implementation of the RemoveFolderEx:
For the details of the WiX RemoveFolderEx element consult WiX documantation

public enum InstallEvent
{
    install,
    uninstall,
    both
}

public class RemoveFolderEx : WixEntity, IGenericEntity
{
    [Xml]
    public InstallEvent? On;
    [Xml]
    public string Property;
    [Xml]
    public string Id;

    public void Process(ProcessingContext context)
    {
        context.Project.IncludeWixExtension(WixExtension.Util);

        var name = WixExtension.Util.ToXName("RemoveFolderEx");
        XElement element = this.ToXElement(name);

        context.XParent
               .FindSingle("Component")
               .Add(element);
    }
}

In the above implementation method Process does a few things:

Indicate that candle needs to use WixUtilExtension.dll.
context.Project.IncludeWixExtension(WixExtension.Util);

Serialize itself (with ToXElement Fluent extension method) into XML element of the appropriate XML namespace. The serialization is based on the custom XMLAttribute marking serializable members.
XElement element = this.ToXElement(WixExtension.Util.ToXName("RemoveFolderEx"));

Finds the Component element associated with the Directory element and inserts serialized content (XML element) into it. The XML Fluent extensions are described at the end of this section
context.XParent
       .FindSingle("Component")
       .Add(element);

All this yields the following XML for the setup definition from the start of this page:
<Directory Id="INSTALLDIR" Name="CustomActionTest">

  <Component Id="Component.readme.txt" Guid="24d6c1b0-2912-498f-8253-3b2f2dbe1255">
    <File Id="readme.txt" Source="readme.txt" />
    <RemoveFolderEx On="uninstall" 
                    Property="DIR_PATH_PROPERTY_NAME" 
                    xmlns="http://schemas.microsoft.com/wix/UtilExtension" />
  </Component>

</Directory>

In the case above the new element was required to be placed inside of the existing component. However depending on the nature of your extension you may need to create a new component for it. Or even associate the component with a new Feature element. This is how you can achieve this:

public void Process(ProcessingContext context)
{
    context.Project.IncludeWixExtension(WixExtension.Util);

    var name = WixExtension.Util.ToXName("RemoveFolderEx");
    XElement element = this.ToXElement(name);

    context.XParent
           .AddElement("Component", "Id=TestComponent;Guid=" + Guid.NewGuid())
           .Add(element); 

    context.XParent
           .Parent("Product")
           .AddElement("Feature", @"Id=TestFeature;
                                    Title=Test Feature;
                                    Absent=allow;
                                    Level=1")
           .AddElement("ComponentRef", "Id=TestComponent");
}

If you want to add your new component to the already existing feature then you can just add your component ID to the list of already defined feature components in ProcessingContext.FeatureComponents.
Note, RemoveFolderEx in this sample is chosen as a good candidate for demonstrating the integration technique for a WiX extension. RemoveFolderEx class can be reused in other projects without any limitations.

Though do not expect any specific behavior from this sample at runtime. The RemoveFolderEx use-case is very complicated and convoluted. In order for RemoveFolderEx to work you need to do way more than just place a new XML element correctly. You will need to schedule an extra custom action to create a property DIR_PATH_PROPERTY_NAME. Or read the property value from the registry. Or from the file. Or something else...

Thus RemoveFolderEx is here only to demonstrate the integration technique but not how to remove folders.

In fact ManagedProject events is by far a superior choice for a task like removing folders. The following is the ManagedProject event sample for removing the installation directory reliably during uninstall:

var project = new ManagedProject("CustomActionTest"...

project.AfterInstall += (e) =>
                        {
                            if (e.IsUninstalling)
                                try
                                {
                                    System.IO.Directory.Delete(e.InstallDir, true);
                                }
                                catch { /*log error if required*/ }
                        };

Last edited Mar 23, 2016 at 4:39 AM by oleg_s, version 7