34

I've seen others' questions on this matter, but I can't make it work for me. I'm trying to get used to Wix so we can migrate our vdproj's (I feel like we've taken 1 step forward and 4 steps back here...the most basic of things have become completely non-trivial with Wix...but I do see value in having a fully fledged declarative markup for building installers).

I have the following wxs in a wixproj in SharpDevelop.

Install works. Uninstall does nothing and leaves the install folder and dll in place. What's the problem?

Files.wxs:

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="TARGETDIR">
            <Directory Id="ProgramFilesFolder" Name="PFiles">
                <Directory Id="INSTALLDIR" Name="Client">
                    <Component Id="InteropDll" Guid="AD09F8B9-80A0-46E6-9E36-9618E2023D66" DiskId="1">
                        <File Id="Interop.dll" Name="Interop.dll" Source="..\Interop\bin\$(var.Configuration)\Interop.dll" KeyPath="yes" />
                        <RemoveFile Id="RemoveInterop.dll" Name="Interop.dll" On="uninstall" />
                    </Component>
                </Directory>
            </Directory>
        </DirectoryRef>
    </Fragment>
</Wix>

Setup.wxs:

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*"
        Name="Client Setup"
        Language="1033"
        Version="1.0.0.0"
        UpgradeCode="4A88A3AD-7CB6-46FB-B2FD-F4EADE0218F8"
        Manufacturer="Client Setup">
        <Package Description="#Description"
            Comments="Comments"
            InstallerVersion="200"
            Compressed="yes"/>
        <!--
            Source media for the installation. 
            Specifies a single cab file to be embedded in the installer's .msi. 
        -->
        <Media Id="1" Cabinet="contents.cab" EmbedCab="yes" CompressionLevel="high"/>

        <!-- Installation directory and files are defined in Files.wxs -->
        <Directory Id="TARGETDIR" Name="SourceDir"/>

        <Feature Id="Complete"
                 Title="Client Setup"
                 Description="Client Setup"
                 Level="1">
            <ComponentRef Id="InteropDll" />
        </Feature>

        <!-- 
            Using the Wix UI library

            WixUI_InstallDir does not allow the user to choose 
            features but adds a dialog to let the user choose a 
            directory where the product will be installed
        -->
        <Property Id="WIXUI_INSTALLDIR">INSTALLDIR</Property>

        <UI Id="WixUI_InstallDir">
            <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
            <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
            <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

            <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
            <Property Id="WixUI_Mode" Value="InstallDir" />

            <DialogRef Id="BrowseDlg" />
            <DialogRef Id="DiskCostDlg" />
            <DialogRef Id="ErrorDlg" />
            <DialogRef Id="FatalError" />
            <DialogRef Id="FilesInUse" />
            <DialogRef Id="MsiRMFilesInUse" />
            <DialogRef Id="PrepareDlg" />
            <DialogRef Id="ProgressDlg" />
            <DialogRef Id="ResumeDlg" />
            <DialogRef Id="UserExit" />

            <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
            <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>

            <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>

            <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg">NOT Installed</Publish>
            <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>

            <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
            <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
            <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
            <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
            <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
            <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
            <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>

            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1">NOT Installed</Publish>
            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">Installed AND PATCH</Publish>

            <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>

            <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
            <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
            <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>

            <Property Id="ARPNOMODIFY" Value="1" />
        </UI>

        <UIRef Id="WixUI_Common" />
    </Product>
</Wix>
Jeff
  • 35,755
  • 15
  • 108
  • 220
  • The configuration seems ok. Did you try creating a verbose uninstall log to see what happens? http://setupanddeployment.com/debugging/msi-log . Search for InstallValidate and RemoveFiles actions and see what they do. – Cosmin Sep 23 '11 at 21:12
  • Not sure how, but this seems to have randomly started working... – Jeff Sep 24 '11 at 04:29

6 Answers6

57

Try to change the GUID of the components which are not uninstalling. I tried the same and it worked for me. It may be because the GUID is already registered in the registry by some other product.

The cause is generally messed up component reference count in the registry - often it happens on dev-boxes during development. Can also often happen to Installshield packages due to their use of the SharedDllRefCount concept (legacy, non-MSI reference counting).

Some technical details: Change my component GUID in wix? Test on a clean virtual to verify the problem is real and not a dev-box issue. Changing component GUIDs can have repercussions (patching problems etc...).

Community
  • 1
  • 1
Jony Lalwani
  • 2,189
  • 2
  • 16
  • 14
  • You are the man. I have been struggling with this issue for few hours. The GUID on that specific componenet was empty. I added a GUID and it worked. – coder Jul 23 '12 at 18:14
  • 2
    Do you have to generate a new Guid each time you make a separate build to ensure uninstall works – TheWommies Nov 25 '13 at 23:38
  • This solution worked for me above a bunch that I tried.. Thanks! – gab Dec 05 '13 at 21:51
  • If you are using the heat.exe/Harvest[http://wixtoolset.org/documentation/manual/v3/overview/heat.html] WIX tool to generate the component listing then you may want to look into the '-ag' and '-gg' switches which control GUID definition for components. Using -ag will auto-generate *consistent* guids based on file location, however -gg seems to generate new GUIDs each time. – Armin Sadeghi May 04 '15 at 06:16
  • I had a valid guid, but I have a feeling the guid got messed up with some previous version of the installer when I was still fiddling with it. In any case, just using '*' worked great. – neminem Jan 20 '16 at 01:36
  • 2
    Like neminem says guids can get messed up during development with frequent setup builds that may fail in some fashion. Changing the guid breaks the link to the erronous one and will work in most cases. It is a good idea to also change the file name for the key path to ensure patching will work correctly if attempted. Here is a quick explanation of the guid / key path concept and when you should change your guid and not: http://stackoverflow.com/questions/1405100/change-my-component-guid-in-wix/1422121#1422121 – Stein Åsmul Aug 28 '16 at 17:36
  • Thank you! It's not every day that you find an answer to a Windows Installer issue that solves the problem. – Robson William Apr 06 '22 at 18:56
9

Make sure that no other MSI packages are keeping your components installed.

Specifically, go into Control Panel / Programs and Features, and make sure that there isn't an "old" version of your program still installed.

Dave Mackersie
  • 1,033
  • 10
  • 14
5

Would be worth checking the following registry key to see if your files are listed. This can cause the uninstaller to ignore the components as it believes them to be shared.

HKLM\Software\Microsoft\Windows\CurrentVersion\SharedDlls
Armin Sadeghi
  • 766
  • 12
  • 19
  • 1
    This was found to be the case with our DLL. It was sitting right here in this SharedDlls registry location. We are using the SharedDllRefCount="yes" for our DLL file Component. We changed it to "no" and deleted the entry for our DLL out of this reg key and now it correctly removes the DLL when we uninstall! – Josh P Mar 16 '22 at 16:44
4

I somehow got my project in the state where every single one of my components couldn't be uninstalled. I have no idea how. I wrote a program that will take a .wixproj file and change all of the component GUIDs to new GUIDs and that solved the issue (after I manually deleted the files). This is based off of user593287's answer.

The argument should be the path to your project file. An example of running this from the command line would be:

GuidFixer.exe MyProject.csproj

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace GuidFixer
{
    public class Program
    {
        public static void Main(string[] args)
        {
            string projectFileName = args[0];
            string path = Path.GetDirectoryName(projectFileName);
            List<string> files = new List<string>();

            XmlDocument projectDocument = new XmlDocument();            
            projectDocument.Load(projectFileName);
            XmlNamespaceManager manager = new XmlNamespaceManager(projectDocument.NameTable);
            manager.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");

            // Finds all of the files included in the project.
            XmlNodeList nodes = projectDocument.SelectNodes("/msbld:Project/msbld:ItemGroup/msbld:Compile", manager);
            foreach (XmlNode node in nodes)
            {
                string fileName = Path.Combine(path, node.Attributes["Include"].Value);
                files.Add(fileName);
            }

            foreach (string fileName in files)
            {
                // Lets only do .wxs files
                if (!Path.GetExtension(fileName).Equals(".wxs", StringComparison.CurrentCulture))
                {
                    continue;
                }

                // This will only update files that aren't readonly, make sure
                // you check out your files from source control before running.
                FileAttributes attributes = File.GetAttributes(fileName);
                if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                {
                    continue;
                }

                bool modified = false;

                XmlDocument doc = new XmlDocument();
                doc.PreserveWhitespace = true; // space inside tags are still lost
                doc.Load(fileName);

                foreach (XmlNode node in doc.GetElementsByTagName("Component"))
                {
                    Guid guid = Guid.NewGuid();
                    string value = guid.ToString("B").ToUpper();

                    node.Attributes["Guid"].Value = value;
                    modified = true;
                }

                // Only update files that were modified, to preserve formatting.
                if (modified)
                {
                    doc.Save(fileName);
                }
            }
        }        
    }
}

I made some changes to it without testing it, so good luck, it's pretty straightforward though.

Joel McBeth
  • 1,278
  • 16
  • 21
  • Helpful. I will note for future users, that while it doesn't handle *.wxs as a file arg, it does work if you spell out the file name explicitly. (using *.wxs resulting in an 'illegal characters in path' System.IO.Path exception.) – Pauli Price Apr 02 '13 at 20:13
1

I encountered a similar problem, which appeard not to be present any more when I converted all guids into uppercase (as required in some specification for compatiblity issues). Did not test exensively whether this was really the solution to the problem. Maybe this is the same as the previous answer.

0

I had a similar issue. I did all the above steps, but the files would just not be uninstalled and the log would not indicate that anything was wrong.

Only after I removed

<!--DEBUG: use C:\Laser-Wix\ as base path for now-->
<SetDirectory Id="LaserDir" Value="[WindowsVolume]Laser-WIX" />

from my wxs (which I used to retarget the install location to a different folder for development purposes) the files were actually removed.

Hope this helps someone.

uceumern
  • 877
  • 10
  • 14