WiX newbie here. I'm curious if this approach is possible using WiX.
Problem statement...
I am packaging some SQL files that I want to execute against some parameters that the user will enter at run time. Think connection string information. One of the parameters that the user can enter is the directory where they want the DB to be installed.
Current solution (doesn't work).....
To do this i'm packaging these files using heat. When it sucks in this files one of my SQL files has some tokens in them that a custom action looks for to basically do a find replace in the file. The problem is thatwhile it is indeed doing a find replace it's doing them against the source files that heat sucked in and not the files that exist in the .msi file.
Question 1...
Within the WiX workflow is there a way that via a custom action I can do processing on the files that are stored within the .cab or .msi file? If that is possible can someone show me an example of this?
Question 2 . . .
If question 1 isn't possible the other idea I had was to break the find replace SQL piece and the file install piece into separate msi file. So the first step would be to explode all the files I need into the install directory via one msi. The next msi would execute the SQL piece as at that point the files exist on the file system for me to do editing on. Does this sound like a sane approach to the problem? It could very well be that I'm trying to work around WiX and not with WiX here.
Current code snippets
<Product>
<!-- This is where this will be installed-->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLFOLDER" Name="ZOLLData">
<Directory Id="SQLINSTALLFOLDER" Name="Sql" />
</Directory>
</Directory>
<!-- CUSTOM ACTION DEFINITIONS-->
<CustomAction Id="FindReplaceZEDSTextCA"
Return="check"
BinaryKey="GemstoneInPremiseInstallerCustomActions.CA.dll"
Execute="immediate"
DllEntry="FindReplaceText" />
<!-- Database Information-->
<util:User Id="SQLUser" Name="[SQLUSERNAME]" Password="[SQLPASSWORD]"/>
<sql:SqlDatabase Id="MasterDatabase" Database="master"
Server="[SQLSERVERNAME]"
User="SQLUser"/>
<InstallUISequence>
<Show Dialog="SqlConnectionDlg" After="CostFinalize" />
<Show Dialog="ProgressDlgCustom" After="SqlConnectionDlg"/>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="FindReplaceZEDSTextCA" After="InstallFiles">
</Custom>
</InstallExecuteSequence>
</Product>
<Fragment>
<DirectoryRef Id="INSTALLFOLDER" />
</Fragment>
<Fragment>
<ComponentGroup Id="SqlComponents">
<Component Id="CreateDatabase.sql" Directory="INSTALLFOLDER" Guid="6A2C6088-9302-451C-A01B-02D618D4AC27">
</Component>
</ComponentGroup>
</Fragment>
Thanks in advance.
The problem was the my custom action was set with Execute="immediate". This should have been Execute="deferred".
Related
Firstly I should clarify that I am a novice and have been struggling to understand the WIX formatting, but by cobbling together examples found on-line, I now have the files installing fine so I next need to register my DLL.
I used the example here as a starting point: How to deploy a SharpShell-based shell extension via WiX? but it seems that the SharpShell tool srm.exe may not be getting called at installation.
If I manually call srm.exe as follows, it works as hoped i.e. the DLL is registered and my shell extension works.
srm install MyExtension.dll -codebase
I can also see that the registration has been successful via the Server Manager application that comes with SharpShell.
I can also manually uninstall with the following - not that this is particularly relevant to my problem but it at least confirms that the manual methods work:
srm uninstall MyExtension.dll
Here is a fragment of my WXS file. When I run the resultant MSI, the files are installed but the DLL is not being registered; confirmed via SharpShell's Server Manager. Where am I going wrong?
</Component>
<Component Id="SRMexe" Guid="C17BB61F-6471-46F9-AA87-2D14D2456632">
<File Id='srm' Name='srm.exe' DiskId='1' Source='..\MyExtension\packages\SharpShellTools.2.2.0.0\lib\srm.exe' KeyPath='yes'>
</File>
</Component>
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</ComponentGroup>
</Fragment>
<Fragment>
<CustomAction Id="InstallShell" FileKey="srm"
ExeCommand='install "[INSTALLFOLDER]\MyExtension.dll" -codebase'
Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="UninstallShell" FileKey="srm"
ExeCommand='uninstall "[INSTALLFOLDER]\MyExtension.dll"'
Execute="deferred" Return="check" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="InstallShell"
After="InstallFiles">
NOT Installed
</Custom>
<Custom Action="UninstallShell"
Before="RemoveFiles">
(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>
</Fragment>
It doesn't look like you have any references to the Fragment with the CustomAction definitions so they are not linked into your final output MSI.
Add a CustomActionRef from your Product element to create the reference.
Using WiX installer, I install an application in C:/ProgramFiles/App folder. In addition to that I want to mark a random file at "C:/User/public/Abc/HiddenFile.txt" as hidden.
My code:
<SetDirectory Id="UserPublicAbcFolder" Value="C:\Users\Public\Abc" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="UserPublicAbcFolder" Name="UserPublicAbcFolder" />
</Directory>
<DirectoryRef Id="UserPublicAbcFolder">
<Component Id="HiddenFile">
<File Name="HiddenFile.txt" Hidden="yes" />
</Component>
</DirectoryRef >
<Feature Id="SomeId" Title="AppName" Level="1">
<ComponentRef Id="HiddenFile" />
</Feature>
I get compilation error "System cannot find the file 'HiddenFile.txt'".
The "HiddenFile.txt" is a random file that always be present in my "C:\Users\Public\Abc" folder.
I just want to mark it hidden as part of the installation.
Please help me to achieve it. Thank you.
I did it using a Custom action. As WiX allows us to write Custom Action as C# method, it comes very handy.
Steps I followed:
Create a WiX CustomAction project.
Write a Action method in C# to update the file permissions.
In Product.wxs, under "Fragment" element create a "Binary" element to point to the .CA.dll.
Then create a "CustomAction" element under "Fragment" to specify the Action method name to call.
Insert the CustomAction into "InstallExecuteSequence" by creating a "Custom" element and specify appropriate value for Before/After attribute.
I am really new using WIX, my only experience to build a complete install was with INNO, and WIX has been decided the way to go for an MSI
The installation includes a bunch of examples and templates, 427 files, which seems crazy to enumerate even with an utility like HEAT.EXE
So we went for a self-extracting utility. WIX install includes this custom utility and the compressed cabinet file, and then that extracts files and folders via custom action.
Problem is when we try to delete the compressed file and the extractor utility. I haven't found the way to delete them after the extraction takes place. RemoveFile seems to not work, and deleting the file within the extraction utility fires the self-healing mechanism in every run of the application.
Leaving the files in the install is dangerous, it is 200MB and it would reset all the installed examples if user runs the self-extraction utility by accident.
If it is of use, the WIX snippet in charge of this is like that:
<Fragment>
<ComponentGroup Id="App_Files">
<Component Id="cmp_Files_Dat" Directory="INSTALLCOMMONFOLDER" Guid="{8BFED6C2-4D4F-48BB-xxxx-C171F624C90B}">
<File Id="fil_Files_Dat" Source="appfiles\files.dat" />
<RemoveFile Id="rem_Files_Dat" Name="files.dat" On="install" />
</Component>
<Component Id="cmp_UnpackFiles_exe" Directory="INSTALLCOMMONFOLDER" Guid="{5722B5E0-C6E8-4C71-yyyy-61EC0ACA0D72}">
<File Id="fil_UnpackFiles_exe" Source="appfiles\UnPackFiles.exe" Checksum="yes" />
<RemoveFile Id="rem_UnpackFiles_exe" Name="UnPackFiles.exe" On="install" />
</Component>
</ComponentGroup>
<CustomAction Id="action_UnPackFiles" FileKey="fil_UnpackFiles_exe" ExeCommand="[INSTALLCOMMONFOLDER]" Execute="commit" Return="check" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="action_UnPackFiles" After="InstallFiles" > NOT (REMOVE="ALL") </Custom>
</InstallExecuteSequence>
</Fragment>
The net result of the code above leaves the files, does not remove them.
Thanks in advance
Josep
I am writing a WXS file for a package I want to install. For the sake of simplicity, let's say I want to install 1 file and then I want to execute a command against it (in my case, it is a public GPG key and I want to import it after the installation is done). Here are the relevant parts of my WXS file:
<CustomAction Id="ImportKey" Directory="INSTALLDIR"
ExeCommand="[SystemFolder]cmd.exe /C gpg --import keyfile.key"
Return="check" />
<!-- Install file keyfile.key into C:\GnuPG -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="GnuPG">
<Component Id="GnuPGConfiguration" Guid="E9469F1C-A875-1014-A3B3-DEF3264B13C4">
<File Name="keyfile.key" Id="KeyfileKey" />
</Component>
</Directory>
</Directory>
<Feature Id="GnuPGConfiguration" Level="1" Title="GnuPG Configuration">
<ComponentRef Id="GnuPGConfiguration" />
</Feature>
<!-- Run custom action after files are installed -->
<InstallExecuteSequence>
<Custom Action="ImportKey" After="InstallFiles">NOT Installed AND NOT PATCH</Custom>
</InstallExecuteSequence>
I can successfully build the MSI. When installing, I use msiexec and turn on logging. There it says that installation fails on the custom action and the correct command is found in the log. Running it manually works. If I comment out execution of the command, the file is installed in the correct location (C:\GnuPG\keyfile.key exists after installation).
Instead of running my GPG command, I tried running dir ant redirected its output to a file. Viewing it, I can see that keyfile.key is not among the files in C:\GnuPG. It seems that the command is run before the file is installed.
Any ideas on what I am doing wrong?
You need to read and understand:
Installation Phases and In-Script Execution Options for Custom Actions in Windows Installer
You will find yourself considering needing
<CustomAction ... Execute="deferred" and Impersonate="no" ... />
Also you are likely to need to qualify the location of the .key file as your current directory isn't going to be what you think it is.
I want my msi installer to generate a set of folders in a particular location and put a dummy file in each directory.
Currently I have the following CustomActions:
<CustomAction Id="SMC_SetPathToCmd" Property="Cmd" Value="[SystemFolder]cmd.exe"/>
<CustomAction Id="SMC_GenerateMovieFolders" Property="Cmd" ExeCommand="for /f "tokens=* delims= " %a in ([MBSAMPLECOLLECTIONS]movies.txt) do (echo %a)" />
<CustomAction Id="SMC_CopyDummyMedia" Property="Cmd" ExeCommand="for /f "tokens=* delims= " %a in ([MBSAMPLECOLLECTIONS]movies.txt) do (copy [MBSAMPLECOLLECTIONS]dummy.avi "%a"\"%a".avi)" />
These are called in the InstallExecuteSequence:
<Custom Action="SMC_SetPathToCmd" After="InstallFinalize"/>
<Custom Action="SMC_GenerateMovieFolders" After="SMC_SetPathToCmd"/>
<Custom Action="SMC_CopyDummyMedia" After="SMC_GenerateMovieFolders"/>
The custom actions seem to start, but only a blank command prompt window is shown and the directories are not generated.
The files needed for the customaction are copied to the correct directory:
<Directory Id="WIX_DIR_COMMON_VIDEO">
<Directory Id="MBSAMPLECOLLECTIONS" Name="MB Sample Collections" />
</Directory>
<DirectoryRef Id="MBSAMPLECOLLECTIONS">
<Component Id="SampleCollections" Guid="C481566D-4CA8-4b10-B08D-EF29ACDC10B5" DiskId="1">
<File Id="movies.txt" Name="movies.txt" Source="SampleCollections\movies.txt" Checksum="no" />
<File Id="series.txt" Name="series.txt" Source="SampleCollections\series.txt" Checksum="no" />
<File Id="dummy.avi" Name="dummy.avi" Source="SampleCollections\dummy.avi" Checksum="no" />
</Component>
</DirectoryRef>
What's wrong with these Custom Actions or is there a simpler way to do this?
The obvious problems are that the way these custom actions are sequenced they won't properly support managed (elevated/UAC) instals or have any concept of servicing by MSI from a repair, rollback, uninstall, upgrade perspective.
It's hard to give exact advice without knowing exactly what you are doing. I know recently I had a customer who had an SDK with some "samples" that he wanted to instal per-user. I convinced this customer to just distribute ZIP files and write a simple winforms app that could handle asking the user a few questions and extracting these files to a directory of the users choice. This way they could deploy multiple instances of the samples, play with them, delete them, whatever without stepping on MSI's toes.