2

I've created a very simple MSI which copies some files to the ProgramFiles directory and while installing calling to custom actions found in a binary written in C#.

While installing, I can easily call any custom action I want. For example I've created an installation step where the user should enter a license, and after confirming the license it is checked against a server using logic written inside C# custom action.

But, when uninstalling, every time I add a custom action (even if it does nothing but returning Success), I get error that the installation failed.

This is how I use the uninstalling step:

<InstallExecuteSequence>
  <Custom Action='TestUninstallation' After='MsiUnpublishAssemblies'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>

where TestUninstallation is defined as following:

<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="TestCustomAction" DllEntry="Uninstall" />

The property DllEntry equals Uninstall which is a C# method which only returns Success.

After installation is completed, I'm trying to uninstall and I'm getting the UserExit dialog defined inside the AdminUISequence with the property OnExit.

Any idea what am I missing?

SyndicatorBBB
  • 1,757
  • 2
  • 26
  • 44
  • No time to look at this right now, but [please skim this answer](https://stackoverflow.com/a/24360658/129130). And please generate a log file to check what is really going on with that custom action. You know how to step through the code with the debugger? There is an answer from me on that here on SO. – Stein Åsmul Mar 17 '19 at 12:26
  • So what does the installer log show? – Christopher Painter Mar 18 '19 at 01:40

1 Answers1

1

Debugging: Managed code is relatively easy to debug (native code is actually even easier). Here are some pointers:


Suggestions: I think you just have a broken reference to the dll export function - in other words an erroneous dll function name / reference:

<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no"
              BinaryKey="CustomActions" DllEntry="__ERRONEOUS FUNCTION REFERENCE__" />

Just check what the dll actually exports and match like this:

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

As always the real McCoy is the check of the dll itself to see if you have the right function name (the below screen shot from this prior answer, recommended read).

This is a native code C++ dll:

Dependency Walker

This is a DTF-packaged managed code dll:

Managed Code Custom Action

Notice that this is a native dll with the managed code stuff embedded. It yields a very different functions list, but you still have to find the function name in there that you refer to.

This is a straight-up managed code dll (no native wrapping):

Managed code dll, unwrapped

And finally: this is the straight-up managed code DLL without being wrapped in a native dll shell.


Un-Uninstallable Setup: When a custom action crashes or fails during uninstallation, you will have problems getting rid of the installation (it just rolls-back and you are stuck with it installed). There are several fixes or workarounds.

The overall fix - in my view - is to not fail custom actions on uninstall, or at least condition them so you can force an uninstall by setting a property via the command line:

Set in MSI property table: SUPPRESSERROR = 0. Then - when needed - on the command line set:

msiexec.exe /x {PRODUCT-GUID} SUPPRESSERROR="1"

Inside the MSI you condition the uninstall custom action with:

REMOVE="ALL" AND SUPPRESSERROR="0"

Now the custom action will not run if SUPPRESSERROR is anything but 0.

There is an older answer with several further options: I screwed up, how can I uninstall my program? (courtesy of Wim Coenen, with me messing up his answer with more suggestions).


Boilerplate: For quick use, let me just dump a boilerplate ad-hoc custom action test project here. This assumes a C# managed code custom action project called "CustomAction1" in the same Visual Studio solution and a reference added to it in your WiX source - like you already have obviously (this is for later when we have all forgotten what the problem was and need to test again):

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" Name="WiXCustomActionsTesting" Language="1033" Version="1.0.0.0"
           Manufacturer="test" UpgradeCode="PUT-GUID-HERE">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <UIRef Id="WixUI_Mondo" />
    <Property Id="SUPPRESSERROR" Value="0" Secure="yes" />

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

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

    <!--BEGIN CUSTOM ACTION SECTION-->

      <Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
      <CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="CustomActions" DllEntry="CustomAction1" />

      <InstallUISequence></InstallUISequence>

      <InstallExecuteSequence>
        <Custom Action='TestUninstallation' After='InstallInitialize'></Custom>
      </InstallExecuteSequence>

    <!--END CUSTOM ACTION SECTION-->

  </Product>

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

  <Fragment>

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

      <Component>
        <File Source="C:\Projects\MySetup\MyApp.exe">
        </File>
      </Component>

    </ComponentGroup>

  </Fragment>
</Wix>
  1. Create WiX project
  2. Copy paste the code, set a new Upgrade GUID
  3. Create CustomAction project, default name
  4. Add reference to custom action project from wix project
  5. Add reference to WiXUIExtension.dll
  6. Adjust path to file in component
  7. Compile
Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
  • That depends screnshot is what a C++ CA would look like. C# DTF wouldn't look anything like that. – Christopher Painter Mar 18 '19 at 01:41
  • Yes, true. I took it out of the linked answer. I will update. – Stein Åsmul Mar 18 '19 at 02:25
  • Added a couple of more screenshots, not the greatest explanations and not my comfort zone, but at least the overview is there. Do you use the wrapper dlls or the normal .NET dlls? I don't like the latter. – Stein Åsmul Mar 18 '19 at 02:35
  • It's totally in my comfort zone. the logfile is likely going to show the .net exception. It's kinda hard to help someone if they haven't even looked at the logfile. – Christopher Painter Mar 18 '19 at 11:53
  • Also for the DTF packaged DLL, you first have to rename the .DLL to ZIP and extract the assembly that is inside it. You can also then decompile it using Reflector, make any changes you need, recompile it and inject it back into the MSI. :) I've done this plenty of times for third party installs that were broken. – Christopher Painter Mar 18 '19 at 11:56