In Wix#, how to avoid creating a physical folder on the target system, when deploying only registry entries?
Asked Answered
O

2

0

I have several related Windows registry entries that I want packaged inside an MSI, so that 1) there is an uninstall process, and 2) The fact that these registry entries have been applied to a given computer is documented by an Add/Remove Programs entry.

The problem is that I can get Wix# to do this just fine, but I can only get the MSI to build if all the registry entries are inside a "Dir" block, and that winds up actually creating a physical folder, which I don't want, on the target system.

As a temporary workaround, I wound up using Dir block, specifying a dummy "Temp" folder. The installer actually does create the folder, which I don't want; all I want is to have the registry entries applied.

WiX documentation describes its underlying construct, TargetDir, as essentially telling the installer to perform its actions on the target system. See http://wixtoolset.org/documentation/manual/v3/howtos/files_and_registry/write_a_registry_entry.html

In that native WiX XML example, it seems there will be no extraneous folder created on the target system; only the desired registry entries will be applied. What Wix# syntax construct can I use to have the registry entries applied, yet avoid having to create an actual folder on the target system?

All the Wix# samples I've seen thus far seem to have this side effect of creating an actual folder on the target system, whether you want one or not.

I know I could probably do this by taking .reg files of the registry entries, harvesting them to .wxs files with heat, and then building that to an msi with candle and light. I am really trying to keep this in the C#/Wix# world. C# is a well-understood skill set in my organization; WiX less so. (Acknowledging that Wix# is built on top of WiX features, and some degree of understanding of WiX and Windows Installer is essential; it's a comfort zone thing, being able to use C# instead of XML, not a fully logical thing.) Currently, we do a lot of these types of registry settings tasks manually, with no trail, and no simple, reliable uninstall.

/// <summary>
/// Configure the Event Log on a Windows (server) to have MyApplication Log settings and record an entry for it in Programs and Features.
/// Note that this program creates the Windows Installer MSI that accomplishes this.  
/// This program creates a WiX XML file that is then compiled by the WiX Toolkit (Candle and Light) into the MSI file.
/// </summary>
internal class Script
{
    public static void Main()
    {
        // Define a new Installer Project object
        var project = new Project("SetupMyApplicationEventLog" ,
        // Provide dummy "Temp" install directory to satisfy WiX# Syntactical requirement. There are no actual files being installed.
        new Dir(@"Temp"),
            /*
                * Event Log Registration Entries, translated from .reg file
            */
            // First, add the root level key of the tree of keys

            //[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log]
            //"EventMessageFile"="C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\EventLogMessages.dll"
            new RegValue(
                RegistryHive.LocalMachine,
                @"SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log",
                "",
                "") { AttributesDefinition = "Component:Win64=yes" },

            //[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log\STV.DSD.HQSYS.SERVICE2]
            //"EventMessageFile"="C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\EventLogMessages.dll"
            new RegValue(
                RegistryHive.LocalMachine,
                @"SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log\" + "STV.DSD.HQSYS.SERVICE2",
                "EventMessageFile",
                "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\EventLogMessages.dll") { AttributesDefinition = "Component:Win64=yes" },

            //[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log\STV.VFS.ONLINE]
            //"EventMessageFile"="C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\EventLogMessages.dll"
            new RegValue(
                RegistryHive.LocalMachine,
                @"SYSTEM\CurrentControlSet\Services\Eventlog\MyApplication Log\" + "STV.VFS.ONLINE",
                "EventMessageFile",
                "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\EventLogMessages.dll") { AttributesDefinition = "Component:Win64=yes" } );

        // Set the properties of the setup project

        // Set UI to minimal; there are no choices to be made here.
        project.UI = WUI.WixUI_ProgressOnly;
        project.Manufacturer = "STV";
        project.OutFileName = "SetupMyApplicationEventLog";
        project.GUID = new Guid("037C625A-609C-4C2C-9689-62A075B88AD7");
        // Assign version # to setup MSI property of type System.Version 
        project.Version = new Version(4, 0, 0, 0);

        // Add the Win64 attribute to the package, to force a 64-bit MSI to be created
        project.Package.AttributesDefinition = "Platform=x64";

        // Trigger the MSI file build
        Compiler.BuildMsi(project);

        //// Also create the .wxs file so we can see what was sent to WiX to build the MSI
        Compiler.BuildWxs(project);

        Console.WriteLine("productVersion=" + project.Version);
        Console.ReadLine();
    }
}

}

Outwit answered 25/10, 2014 at 2:58 Comment(0)
G
0

You can achieve what you want by adding the <RemoveFolder> tag to your component within the directory you do not wish to keep. See the reference.

The resulting MSI will be created by light with the warning LGHT1079 that you don't have any files, but can be ignored if this is the intent.

Simple example XML showing the directory with a component that deletes the folder on install/uninstall and creates the registry keys, forcing delete on uninstall.

<Directory Id="TARGETDIR" Name="SourceDir">
    <Component Id="ApplicationRegistry" Guid="YOUR-GUID-HERE">
         <RemoveFolder Id="RemoveTarget" Directory="TARGETDIR" On="both"/> 
         <RegistryKey Root="HKCU"
              Key="Software\Microsoft\[ProductName]"
              ForceDeleteOnUninstall="yes">
                <RegistryValue Type="integer" Name="SomeIntegerValue" Value="1" KeyPath="yes"/>
                <RegistryValue Type="string" Value="Default Value"/>
         </RegistryKey>
    </Component>
</Directory>

You'd simply either use this name for your directory, or replace the Directory="TARGETDIR" with the name of your directory.

The result after install is a registry key with no folder on the target machine. registry key path

You'd have to figure out how to get that tag into your generated XML, but that's the effect you'd need.

Grosz answered 25/10, 2014 at 18:54 Comment(5)
That looks very promising. There are at least two potential approaches with Wix# to getting that tag in the resulting .wxs file--1) Wix# syntax (I have to research/find examples) and 2) XML post-processing text substitution, e.g., hook up a delegate method to the Wix# event that fires when it has completed generating the .wxs file, but before the compile/link to MSI. I'll try and report back!Outwit
When I have this issue I just add a standard folder like CommonFilesFolder to the directory tree and have that be the directory. There's no need I can see to create an unwanted folder and remove it, just use one that's already on the system.Careaga
That would probably work too. I really wanted to solve it using the approach of injecting XML into the generated .wxs file, because that approach can apply to a variety of situations where Wix# doesn't generate quite exactly the .wxs XML I want.Outwit
@Ryan J, Your approach worked! I was able to find a way to get Wix# to generate .wxs file XML that matched up with the tag you provided, and posted that solution below. FWIW, I didn't have to go outside of Visual Studio and manually use light; the Wix# Visual Studio integration runs the candle and light processes for me using this approach.Outwit
The developer, Oleg Shilo, saw my comment on the CodeProject article and fixed this in v1.01.0 and emailed me within 24 hours. His response is here: codeproject.com/Articles/31407/…Outwit
O
0

Using the Wix XML code Ryan J provided, plus the example "InjectXML" from the Wix# samples code, I was successful in generating the wxs file and getting a working MSI, all while still staying within the Wix# coding environment in Visual Studio.

Here is the relevant .wxs XML that was being generated before, under the element:

<Directory Id="TARGETDIR" Name="SourceDir" >
  <Directory Id="INSTALLDIR" Name="%Temp%">
    <Component Id="INSTALLDIR.EmptyDirectory" Guid="037c625a-609c-4c2c-9689-62a075b88ae9">
      <CreateFolder />
    </Component>

So what needs to happen is: 1) Remove the "" element, which is under Product/Directory/Directory/Component. Ryan J's example clearly showed that it goes under the "Component" that is associated with the first "Directory" to be created, which is in turn under the "TARGETDIR", in the XML that Wix# generates.

2) Insert the element syntax, "", under the element Product/Directory/Directory/Component. While I believe it may also work to reference the Id "INSTALLDIR.EmptyDirectory"in this element, I used the Id "TARGETDIR" and it worked the way I wanted.

Here is the resulting Wix# code that injects the XML as supplied by Ryan J.

    internal class Script
    {
        public static void Main()
        {
            // Define a new Installer Project object
            var project = new Project("SetupMyApplicationEventLog" ,
            // Provide dummy "Temp" install directory to satisfy WiX# Syntactical requirement. There are no actual files being installed.
            new Dir(@"TempDeleteMe"),
...
            // Hook up a delegate to the "WixSourceGenerated" event, fires when .wxs file is fully created
            Compiler.WixSourceGenerated += InjectXMLElement;
            // Trigger the MSI file build
            Compiler.BuildMsi(project);
...

        /// Insert XML elements and attributes into the generated .wxs file
        static void InjectXMLElement(System.Xml.Linq.XDocument document)
        {
            // Remove the <CreateFolder /> tag from Directory element -- we don't want to create it
            var createFolderElement = document.Root.Select("Product/Directory/Directory/Component/CreateFolder");
            createFolderElement.Remove();
            // To cause the folder to not be created on the target system, add this element:
            // <RemoveFolder Id="RemoveTarget" Directory="TARGETDIR" On="both"/>
            var componentElement = document.Root.Select("Product/Directory/Directory/Component");
            
            componentElement.Add(new XElement("RemoveFolder",
                       new XAttribute("Id", "RemoveTarget"),
                       new XAttribute("Directory", "TARGETDIR"),
                       new XAttribute("On","both")));
        }
Outwit answered 28/10, 2014 at 3:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.