How do I add C# methods to an existing large wix script
Asked Answered
B

1

3

We have an existing wix script that is pretty complex & long. All the CustomActions are performed with inline vbscript.

I want to switch some of those actions from vbscript to C#. All the examples everywhere start with "create a wix project in VisualStudio...". Is there any example out there about how to add in C# code to an existing wix project? One where it is built using the old school wix command line apps?

Bhutan answered 24/2, 2020 at 21:6 Comment(4)
Does this answer your question? How to create custom actions in c# and bind it on a wix setup project You can create a new project with wix custom actions and refer to it in main (existing) wix projectEspousal
OK, added a description below. Might have been better to make a youtube.com video - but there it is now. Looked for youtube videos, and there is this one from Advanced Installer. This uses the WiX templates for C# custom actions, but Advanced Installer to include the DLL.Cahoon
WiX videos: Painter. Mesta Automation (whatever that is). Keith Reichert. Angel Six. Neither seems to deal with custom action projects.Cahoon
First understand WiX / Windows Installer isn't "script" it's a declarative programming language not an imperative language. Avoid custom actions at all cost and only add them carefully.Foolproof
L
6

A shameless promotion of C++ custom actions first! :-).

And: "WiX Quick Start" (some pointers to good WiX and MSI resources).


Step-By-Step: I'll give it a try, please try this (you might want to skip to the bottom source if you are sort of done with these preliminary steps - this is step-by-step for real and very slow to get to the action - you might get what you need straight from the WiX source):

  1. In WiX Visual Studio solution, right click solution node at top => Add => New Project...

  2. Expand WiX Toolset node, select v3 (provided that is the version of WiX you use)

  3. Double click "C# Custom Action Project for WiX v3"

  4. Right click "References" in WiX project (not in C# project) => Add Reference...

  5. Go "Projects" and add a reference to the C# project (double click and OK)

  6. Do a test build. Provided there were no errors before there should be none now.

  7. You should see something like "CustomAction1.CA.dll" in the build Output window. The suffix *.CA.dll is added to a win32 wrapper dll for the original managed code dll. All of this is handled by WiX itself - or actually the Votive Visual Studio integration for WiX - just know the difference:

    • "CustomAction1.dll" - managed code dll.
    • "CustomAction1.CA.dll" - native win32 wrapper dll containing the native one and several other components. Include this version in your MSI.
  8. Add the following snippet:

     <Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
    
  9. The above should compile the actual C# dll into the MSI. You can open the MSI in Orca and see in the Binary table.

  10. It is not great, but I like to add a reference to System.Windows.Forms and use a MessageBox.Show to show a dialog from within the custom action to ensure it is running as expected. I also add the application debugger launch command for dlls built in debug mode. That way Visual Studio will be automatically invoked (if all works correctly) so the code can be stepped through.

  11. Add the reference to "System.Windows.Forms" by right clicking the C# project's Reference node and then add "System.Windows.Forms". Also add "using System.Windows.Forms;" to the top of the source file - see full source below. The key is to remember to reference "System.Windows.Forms" at a project level.

  12. Now add this as test code to the custom action project's "CustomAction1" custom action code snippet (see code section towards bottom for full source):

       // will launch the debugger for debug-build dlls
       #if DEBUG
         System.Diagnostics.Debugger.Launch();
       #endif
    
       MessageBox.Show("hello world");
    
  13. To get a standard setup GUI (for others who read this), add a reference to WiXUIExtension as explained here (that is a step-by-step for creating a basic WiX project that compiles and has a GUI), and then inject this into your source:

     <UIRef Id="WixUI_Mondo" />
    
  14. I like to change <MediaTemplate /> to <MediaTemplate EmbedCab="yes" /> to avoid external source cab files (with this change cabs are compiled into the MSI).

  15. If you don't have any components added, you can add this to include notepad.exe in your MSI for test installation under directory INSTALLFOLDER (just a trick to install something without having source files available - a source path that should resolve on any machine) - replace the whole "TODO" section - see full source below:

     <Component Feature="ProductFeature">
       <File Source="$(env.SystemRoot)\notepad.exe" />
     </Component>
    
  16. Now we need to declare the actual custom action and insert it into an installation sequence. Let's add this underneath the previous <Binary> element:

     <CustomAction Id="CA1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
    
     <InstallUISequence>
       <Custom Action="CA1" After="CostFinalize" />
     </InstallUISequence>
    
     <InstallExecuteSequence>
       <Custom Action="CA1" After="CostFinalize" />
     </InstallExecuteSequence>
    
  17. Now build and test run the MSI. You should get numerous "hello world" messages.

  18. That is the overall "heartbeat" of a C# / managed code custom action - the way I sometimes use them.


WiX Source Actual: And now, the synthesis - remember to replace all GUIDs!:

The construct: $(env.SystemRoot) - in the WiX source below - gets the environment variable %SystemRoot% - which resolves to C:\ on most systems (to list environment variables open a cmd.exe and type set and press Enter). The below source should hence compile on all systems without modifications:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" Name="DemoCA" Language="1033" Version="1.0.0.0" Manufacturer="test" UpgradeCode="0adf972a-5562-4a6f-a552-dd1c16761c55">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
        <MediaTemplate EmbedCab="yes" />
    
    <UIRef Id="WixUI_Mondo" />

 <!-- START CUSTOM ACTION CONSTRUCTS -->
    
    <Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />

    <CustomAction Id="CA1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>

    <InstallUISequence>
      <Custom Action="CA1" After="CostFinalize" />
    </InstallUISequence>

    <InstallExecuteSequence>
      <Custom Action="CA1" After="CostFinalize" />
    </InstallExecuteSequence>

 <!-- END CUSTOM ACTION CONSTRUCTS -->

    <Feature Id="ProductFeature" Title="AddingCSharpCustomActions" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
               <Directory Id="INSTALLFOLDER" Name="AddingCSharpCustomActions"/>
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">

         <Component Feature="ProductFeature">
           <File Source="$(env.SystemRoot)\notepad.exe" />
         </Component>

        </ComponentGroup>
    </Fragment>
</Wix>

Steps-in-brief: short summary of changes needed:

  1. Set manufacturer field to something.
  2. Add and modify the custom action constructs as indicated above.
  3. Add the Component / File elements towards bottom replacing the "TODO" section there.
  4. Set the MediaTemplate to use embedded cabs as described above (optional, not necessary for the sample to work).

Custom Action Code: And finally the actual C# custom action test code - updated with Debugger.Launch which will launch the debugger for a debug-build DLL. You can then attach the debugger to the correct source project and step-through the code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.Windows.Forms;

namespace CustomAction1
{
    public class CustomActions
    {
        [CustomAction]
        public static ActionResult CustomAction1(Session session)
        {

#if DEBUG
            System.Diagnostics.Debugger.Launch();
#endif

            MessageBox.Show("hello world");

            session.Log("Begin CustomAction1");

            return ActionResult.Success;
        }
    }
}

Links:

Longhair answered 24/2, 2020 at 22:18 Comment(5)
First off, thanks for the detailed post. But... I can't find "WiX Toolset node" in Visual Studio 2019. Not in the "install other types" part either. Is it this? wixtoolset.org/releasesBhutan
I haven't tried 2019, wonder if this works?. Maybe you can try on a virtual? I installed just the GUI for Visual Studio on a virtual once, and a couple of other components and it was enough to run Wix.Cahoon
Here is what I installed according to a note I found: 1. .NET Framework 3.5 Development Tools, 2. .NET Compiler Platform SDK, 3. Several dependency packages comes along: "C# and Visual Basic" and ".NET Compiler Platform SDK", etc... 4. .NET3.5 was already present on Windows 7. 5. Install WiX and WiX VS Integration.Cahoon
Ok, I created "C# Custom Action Project for WiX v3" and I got one project - a C# one with a CustomAction method. But no wix project. How do I create that?Bhutan
I would just insert the one you already have? That is what you wanted right? To use your existing source? If so, please right click your Visual Studio's solution top node and go Add => Existing Project...Cahoon

© 2022 - 2024 — McMap. All rights reserved.