Friday 9 November 2012

Converting a Visual Studio Setup Project to Wix


Anyone who’s using Visual Studio 2012 and either opened up an old solution with a Setup Project (.vdproj) or created a new solution and looked for the setup project under project types will know it’s no longer there and no longer supported. This is not actually that surprising since TFS Build Server and MSBuild doesn’t support the legacy .vdproj files any way.

Wix is the recommended alternative; itis very powerful, but quite complicated to work with and there is currently no GUI.

The .vdproj can’t be converted directly, however one of the wix tools called dark can be used to decompile msi files into wxs xml format.

This is a step by step procedure for converting an MSI with an exe, a number of DLLs and files into a Wix project.

1. If you haven’t got Wix, install it and add a Wix project to your solution.

2. Decompile the msi with the following command line:

c:\Program Files (x86)\WiX Toolset v3.6\bin>dark c:\temp\setup.msi -o c:\temp\setup.wxs

Dark will output results like this, there will be a load of warnings like this:

Windows Installer Xml Decompiler version 3.6.3303.0
Copyright (C) Outercurve Foundation. All rights reserved.

setup.msi
c:\temp\setup.msi : warning DARK1060 : The _VsdLaunchCondition table is being decompiled as a custom table.
dark.exe : warning DARK1065 : The AdvtUISequence table is not supported by the Windows Installer XML toolset because it has been deprecated by the Windows Installer team.  Any information in this table will be left out of the decompiled output.
c:\temp\setup.msi : warning DARK1062 : The ModuleSignature table can only be represented in WindowsInstaller XML for merge modules.  The information in this table will be left out of the decompiled output.
c:\temp\setup.msi : warning DARK1062 : The ModuleComponents table can only be represented in Windows Installer XML for merge modules.  The information in this table will be left out of the decompiledoutput.
c:\temp\setup.msi : warning DARK1066 : The MsiPatchHeaders table is added to the install package bya transform from a patch package (.msp) and not authored directly into an install package (.msi). The information in this table will be left out of the decompiled output.
c:\temp\setup.msi : warning DARK1066 : The Patch table is added to the install package by a transform from a patch package (.msp) and not authored directly into an install package (.msi). The information in this table will be left out of the decompiled output.
c:\temp\setup.msi : warning DARK1066 : The PatchPackage table is added to the install package by a transform from a patch package (.msp) and not authored directly into an install package (.msi). The information in this table will be left out of the decompiled output.
dark.exe : warning DARK1058 : The AdvtExecuteSequence table contains an action 'MsiUnpublishAssemblies' which is not allowed in this table.  If this is a standard action then it is not valid for this table, if it is a custom action or dialog then this table does not accept actions of that type. This action will be left out of the decompiled output.

3. Export binaries using dark.exe:
c:\Program Files (x86)\WiX Toolset v3.6\bin>dark c:\temp\setup.msi -x c:\temp

4. Rename anything that sounds like an icon or bitmap in the outputted ‘Binary’ folder to have a .bmp suffix, I had:
  • DefBannerBitmap
  • NewFldrBtn
  • UpFldrBtn

These should be .ibd files:
  • MSVBDPCADLL
  • VSDNETCFG

The icon in the ‘Icon’ folder can be renamed .ico

Copy these folders into the WIX project, include them and set as resources.

5. Fix binary paths from

<Binary Id="MSVBDPCADLL" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />
        <Binary Id="VSDNETCFG" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />
        <Binary Id="DefBannerBitmap" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />
        <Binary Id="UpFldrBtn" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />
        <Binary Id="NewFldrBtn" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />

To:

<Binary Id="MSVBDPCADLL" SourceFile="Binary\MSVBDPCADLL.ibd" />
    <Binary Id="VSDNETCFG" SourceFile="Binary\VSDNETCFG.ibd" />
    <Binary Id="DefBannerBitmap" SourceFile="Binary\DefBannerBitmap.bmp" />
    <Binary Id="UpFldrBtn" SourceFile="Binary\UpFldrBtn.bmp" />
    <Binary Id="NewFldrBtn" SourceFile="Binary\NewFldrBtn.bmp" />

And

<Icon Id="_7BBE63EC1B23A31B9D3734.exe" SourceFile="FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES" />

To:

<Icon Id="_7BBE63EC1B23A31B9D3734.exe" SourceFile="Icon\_7BBE63EC1B23A31B9D3734.ico" />

6. Fix project references by adding references to the required projects in the solution like you would in any normal project and then fix the binary components like this (DLLs are similar to EXEs):

<Component Id="C__EFA30D8EEF7020387C8D684F42EDADC6" Guid="{BA9BC921-7529-B2AF-D64D-ABF58AAF77AD}">
                <File Id="_EFA30D8EEF7020387C8D684F42EDADC6" Name="MyApplication.exe" KeyPath="yes" ShortName="MYAPPL~1.EXE" Assembly=".net" AssemblyManifest="_EFA30D8EEF7020387C8D684F42EDADC6" AssemblyApplication="_EFA30D8EEF7020387C8D684F42EDADC6" DiskId="1" Source="SourceDir\File\_EFA30D8EEF7020387C8D684F42EDADC6" />
                <Shortcut Id="_1EE6E64055BE47E2AD4588E6B6AC87EC" Directory="_380EF96A89404298A858064DE3EC207A" Name="MyApplication" ShortName="MYAPPLICATIONL~1" Icon="_7BBE63EC1B23A31B9D3734.exe" IconIndex="0" Show="normal" WorkingDirectory="TARGETDIR" Advertise="yes" />
            </Component>

To:

<Component Id="C__EFA30D8EEF7020387C8D684F42EDADC6" Guid="{BA9BC921-7529-B2AF-D64D-ABF58AAF77AD}">
        <File Id="_EFA30D8EEF7020387C8D684F42EDADC6" Name="MyApplication.exe" KeyPath="yes" ShortName="MYAPPL~1.EXE" Assembly=".net" AssemblyManifest="_EFA30D8EEF7020387C8D684F42EDADC6" AssemblyApplication="_EFA30D8EEF7020387C8D684F42EDADC6" DiskId="1" Source="$(var.MyApplication.TargetPath)" />
        <Shortcut Id="_1EE6E64055BE47E2AD4588E6B6AC87EC" Directory="_380EF96A89404298A858064DE3EC207A" Name="MyApplication" ShortName="MYAPPL~1" Icon="_7BBE63EC1B23A31B9D3734.ico" IconIndex="0" Show="normal" WorkingDirectory="TARGETDIR" Advertise="yes" />
      </Component>

7. Fix file components like this:

<Component Id="C__0BDFAFE635E591014F851368D952F145" Guid="{BE51E0B1-FFFA-CB90-0B48-D6A70E66312E}">
                <File Id="_0BDFAFE635E591014F851368D952F145" Name="Settings.csv" KeyPath="yes" ShortName="SETTINGS.CSV" DiskId="1" Source="SourceDir\File\_0BDFAFE635E591014F851368D952F145" />
            </Component>
To:

<Component Id="C__0BDFAFE635E591014F851368D952F145" Guid="{BE51E0B1-FFFA-CB90-0B48-D6A70E66312E}">
        <File Id="_0BDFAFE635E591014F851368D952F145" Name="Settings.csv" KeyPath="yes" ShortName="SETTINGS.CSV" DiskId="1" Source="$(var.MyApplication.TargetDir)" />
      </Component>

8. Now build and fix any errors. Installers can contain many different components and features so will vary in their complexity to get working. 


9 comments:

  1. Geoff - I'm getting everything resolved with my errors after running, but now i'm getting ICE30 errors saying my TargetFile Interfaces.dll is used by two different components on an LFN system, this breaks component reference counting.

    Do you know how to resolve?

    ReplyDelete
  2. Have you got the same dll listed twice? If so try taking one out?

    ReplyDelete
  3. Geoff - That looks like the issue, however some of the dll's that are included are 32bit and some 64bit with same name. I found a solution to this by taking out the "Name" attribute on the File Element since the wix documentation said that is optional and if not specified it will use the source name.

    http://wix.sourceforge.net/manual-wix3/wix_xsd_file.htm

    Thanks for the help, its not compiling!

    ReplyDelete
  4. Yes, small typo.

    One last thing. Do you know how I can replace the old source file's it created in the FILES directory with the actual Source location of the referenced files in my solution that i'm still using?

    Old:
    Source="c:\CDS_Trunk\Main\Testing\SetupProject1\File\_1A182947CFF1124D2FC07F04434C9077"

    New: (I think this can work?)
    I assume I could just give it a relative path like...
    Source="..\Interfaces.dll"

    I see it extracted all the old binaries and put them in the FILES directory and gave them names like _31474CA2ED575510B8CF82C2C90CB5BE, which should just be the dll's i'm using for the setup correct?

    ReplyDelete
  5. Excellent B-)

    Try this:

    Source="$(var.MyApplication.TargetPath)"

    ReplyDelete
  6. Alright, getting closer. When defining my pre-processor variable for the location of the referenced dll from the project I have this below, where CDS.Management.Task.Client is the project name in my solution.

    SourceLib=CDS.Management.Task.Client\bin\Debug

    My .wxs file contains:
    Source="$(var.SourceLib)\CDS.Core.dll"

    But my Error windows says:
    The system cannot find the file 'CDS.Management.Task.Client\bin\Debug\CDS.Core.dll'

    Am i missing someting to define the SourceLib path such as a ..\ ?

    ReplyDelete
  7. Ok, sorry I fixed it. Realized i wasn't giving it a proper file path to the dll.....

    This was my fix for the pre-processor var:
    SourceLib=$(SolutionDir)Components\Task\CDS.Management.Task.Client\bin\Debug

    ReplyDelete
  8. After setting the pre-processor variables, this worked like a charm. Thank you very much for sharing.

    ReplyDelete