WiX Toolset - Is there a way to hide Util:XmlFile calls from the log? - wix

Even though I hide the WiX property that contains a password and the ExecXmlFile property...
<Property Id="MyApp_MyServer_constr" Hidden="yes" />
<Property Id="ExecXmlFile" Hidden="yes" />
<DirectoryRef Id='MYAPPDIR'>
<Component Id='UpdateMyAppMyServerConnectionString' Guid='MY-GUID' Win64="yes">
<CreateFolder />
<util:XmlFile Id='UpdateMyAppMyServerConnectionString'
Action="setValue"
ElementPath="[MyServer_elementpath]"
File="[MYAPPDIR]MyApp.exe.config"
Value="[MyApp_MyServer_constr]" />
</Component>
</DirectoryRef>
...password details are exposed in the MSI install log file.
MSI (s) (F0:7C) [20:27:56:613]: Executing op: ActionStart(Name=ExecXmlFile,,)
Action 20:27:56: ExecXmlFile.
MSI (s) (F0:7C) [20:27:56:613]: Executing op: CustomActionSchedule(Action=ExecXmlFile,ActionType=3073,Source=BinaryData,Target=ExecXmlFile,CustomActionData=20C:\Program Files\MyCompany\MyInstallerName\MyApp\MyApp.exe.config30//configuration/connectionStrings/add[#name='MyServer']/#connectionStringUser ID=sa;Password=wysiwyg;Initial Catalog=MyDatabase;Data Source=MACHINE-NAME;Application Name=MyShortAppName)
I've done a considerable amount of checking online, but there does seem to be a WiX way of hiding this information from an MSI log without doctoring things using Orca after creating an MSI.
I found this WiX bug http://wixtoolset.org/issues/3859/, but it seems that not allowing hiding of ExecXmlFile CustomAction calls was by design.
Alternately, is there another WiX way to accomplish what I need that doesn't expose password details in the log file?
Thanks

Related

How to use RemoveFolderEx to recursively remove folder in LocalAppData on install AND uninstall

Here's what I have (based on what I've gleaned from several other Stack Overflow posts and elsewhere:
<Property Id="CACHEFOLDER">
<RegistrySearch Key="SOFTWARE\SIL\Transcelerator" Root="HKCU" Type="raw"
Id="CacheFolderRegSearch" Name="CachePath" />
</Property>
<Directory Id="TARGETDIR" Name="SourceDir
<!-- Transcelerator's cache folder in LocalAppData: -->
<!-- C:\Users\<current user>\AppData\Local\SIL\Transcelerator -->
<!-- This needs to be saved to a registry key so it can be cleaned up on uninstall and also purged when there is a new install in order to ensure that reparsing occurs. -->
<?define AppCacheFolder = "SIL\Transcelerator" ?>
<Component Id="CacheCleanup" Guid="{6A45D61D-EA73-4A8C-8941-B49A881ABB49}">
<RegistryValue Root="HKCU" Key="Software\SIL\Transcelerator" Name="CachePath"
Type="string" Value="[LocalAppData]$(var.AppCacheFolder)"
KeyPath="yes" />
<util:RemoveFolderEx On="both" Property="CACHEFOLDER"/>
</Component>
</Directory>
<Feature Id="MainApplication" Title="App Name" Level="1" Absent="disallow" Display="expand" AllowAdvertise="no" InstallDefault="local">
<ComponentRef Id="CacheCleanup" />
</Feature>
Note: Eventually, I'll want to make the MainApplication feature hidden, but for now it's comforting to see it.
Here are what I think are the relevant excerpts from the WIX log file:
AppSearch: Property: CACHEFOLDER, Signature: CacheFolderRegSearch
MSI (c) (38:F0) [18:25:39:116]: PROPERTY CHANGE: Adding CACHEFOLDER property. Its value is 'SIL\Transcelerator'.
Action ended 18:25:39: AppSearch. Return value 1.
...
MSI (c) (38:F0) [18:25:45:594]: Switching to server: PARATEXT7="C:\Program Files (x86)\Paratext 7\" PARATEXT7TEST="C:\Program Files (x86)\ParatextDir7Test\" PARATEXT8="C:\Program Files (x86)\Paratext 8\" PARATEXT8TEST="C:\Program Files (x86)\ParatextDir8Test\" PARATEXT75100ORGREATER="C:\Program Files (x86)\Paratext 7\Paratext.exe" CACHEFOLDER="SIL\Transcelerator" TARGETDIR="C:\" INSTALLDIR7="C:\Program Files (x86)\Paratext 7\plugins\Transcelerator\" INSTALLDIR7TEST="C:\Program Files (x86)\ParatextDir7Test\plugins\Transcelerator\" INSTALLDIR8="C:\Program Files (x86)\Paratext 8\plugins\Transcelerator\" INSTALLDIR8TEST="C:\Program Files (x86)\ParatextDir8Test\plugins\Transcelerator\" PLUGINDIR7="C:\Program Files (x86)\Paratext 7\plugins\" PLUGINDIR7TEST="C:\Program Files (x86)\ParatextDir7Test\plugins\" PLUGINDIR8="C:\Program Files (x86)\Paratext 8\plugins\" PLUGINDIR8TEST="C:\Program Files (x86)\ParatextDir8Test\plugins\" CURRENTDIRECTORY="C:\Projects\Transcelerator" CLIENTUILEVEL="0" CLIENTPROCESSID="17976" SOURCEDIR="C:\Projects\Transcelerator\output\installer\" ACTION="INSTALL" EXE
...
MSI (s) (E4:44) [18:25:46:006]: PROPERTY CHANGE: Adding CACHEFOLDER property. Its value is 'SIL\Transcelerator'.
...
Action 18:25:46: WixRemoveFoldersEx.
Action start 18:25:46: WixRemoveFoldersEx.
MSI (s) (E4:00) [18:25:46:041]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI6019.tmp, Entrypoint: WixRemoveFoldersEx
MSI (s) (E4:78) [18:25:46:042]: Generating random cookie.
MSI (s) (E4:78) [18:25:46:044]: Created Custom Action Server with PID 18712 (0x4918).
MSI (s) (E4:54) [18:25:46:067]: Running as a service.
MSI (s) (E4:54) [18:25:46:069]: Hello, I'm your 32bit Impersonated custom action server.
WixRemoveFoldersEx: Recursing path: SIL\Transcelerator\ for row: wrfA9D8B049E87ACFF02034C5FFCFB64E42.
WixRemoveFoldersEx: Search path not found: SIL\Transcelerator*
Action ended 18:25:46: WixRemoveFoldersEx. Return value 1.
...
MSI (s) (E4:44) [18:25:46:267]: Executing op: ComponentRegister(ComponentId={6A45D61D-EA73-4A8C-8941-B49A881ABB49},KeyPath=01:\Software\SIL\Transcelerator\CachePath,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
1: {97A212AC-E01E-486A-A220-AF9BBBC79E87} 2: {6A45D61D-EA73-4A8C-8941-B49A881ABB49} 3: 01:\Software\SIL\Transcelerator\CachePath
...
MSI (s) (E4:44) [18:25:46:597]: Executing op: RegOpenKey(Root=-2147483647,Key=Software\SIL\Transcelerator,,BinaryType=0,,)
MSI (s) (E4:44) [18:25:46:597]: Executing op: RegAddValue(Name=CachePath,Value=SIL\Transcelerator,)
WriteRegistryValues: Key: \Software\SIL\Transcelerator, Name: CachePath, Value: SIL\Transcelerator
...
Property(S): CACHEFOLDER = SIL\Transcelerator
Nothing relevant seems to be getting added to the registry. (At one point, it seems it was adding something withe correct GUID to tell it to do an uninstall action, but now I can't figure out what I changed to make that go away.) And none of files or subfolders in C:\Users\bogle\AppData\Local\SIL\Transcelerator are getting removed either on install or uninstall. I also tried changing from On="both" to On="Uninstall" to see if I could get that to work, but no dice.
I ended up using a custom action because it turns out that what I really needed to do was clear the cached files for any/all users, not just the current user. This was especially true because the installer always runs under elevated privileges, so the current user quite typically would not be the one I actually care about. I will point out that the original problem remains unsolved, so if anyone can figure out the problem and post an alternate answer that might help someone else, that could be useful.
The name of the directory property is LocalAppDataFolder, not LocalAppData. That's not defined so it's an empty string and the path RemoveFolderEx is given isn't valid (hence the Search path not found: SIL\Transcelerator error).

How to use selfextract files in a wix install

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

Wix IniFile error on Uninstall

I'm using wix IniFile element to edit ini file on install. When I try to uninstall i get error 2343:
Начало действия 12:37:47: RemoveIniValues.
MSI (s) (7C:BC) [12:37:47:264]: Note: 1: 2343
DEBUG: Error 2343: Specified path is empty.
My wxs with ini editing is as follows:
<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Property Id="miktex_config_path" Hidden="yes"/>
<SetProperty Id="miktex_config_path" Value="[INSTALLLOCATION]miktex\miktex\config" After="InstallFiles" Sequence="execute">Not Installed</SetProperty>
<DirectoryRef Id="dirC060208F28327102C690BFF33C18B6C4">
<Component Id="miktex_config_file" Guid="4B9400C2-7EEF-4233-881D-5DFE6F80BB5B">
<CreateFolder />
<IniFile Directory="miktex_config_path" Id="common_install_path" Name="miktexstartup.ini" Action="addLine" Key="CommonInstall" Value="[INSTALLLOCATION]miktex" Section="Paths"/>
<IniFile Directory="miktex_config_path" Id="common_data_path" Name="miktexstartup.ini" Action="addLine" Key="CommonData" Value="[CommonAppDataFolder]miktex_data" Section="Paths"/>
<Condition><![CDATA[Not Installed]]></Condition>
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Why doesn't uninstaller take into account my condition element?
How can I force installer to ignore ini file editing during uninstall?
The condition is against the component, however the action that is running is RemoveIniValue. You can surpress this action by overriding InstallExecuteSequence as follows:
<InstallExecuteSequence>
<RemoveIniValues Suppress="yes" />
</InstallExecuteSequence>
It depends on the task you pursue. If you need to prevent INI value removal at all then suppressing the RemoveIniValues in the InstallExecuteSequence is the way to go as suggested by David Martin. However, that suppression (NO MATTER what you put as a condition for that), prevents the INI entry removals and for install and for uninstall (again, condition doesn't work, don't even try to put condition to suppress on uninstall only). But if you need to allow INI entry/tag removals during the install (IniFile declaration with remove action) but at the same time you need to prevent the rest of your INI settings from removal during the uninstall, then simply mark the Component where you keep your IniFile declarations as Permanent="yes". In that case your INI settings will not be removed on uninstall and your declarations to remove particular INI settings on install will work, And forget about the RemoveIniValues suppression at all.

How to run .reg files when running the msi?

I am trying to understand how my setup.msi inserts registry values when installing it. In Orca editor I am seeing like this,
After installing the msi, in the log file I am seeing like this,
MSI (s) (A8:B4) [16:27:28:674]: Executing op: ComponentRegister(ComponentId={45667B7F-9DC7-43B7-BE9E-3215ED1B1985},KeyPath=02:\SOFTWARE\myCompany\MySolution\Plugins\MyProduct\ProductCode,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
I want to do the reverse engineering of this mechanism, can any one help me to understand this? I want to recreate the same using WIX, so I just tried like below
<Component Id="RegistryEntries" Guid="*">
<RegistryKey Root="HKLM"
Key="Software\Microsoft\MyCompany"
Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="MyApp" Value="[INSTALLLOCATION]" KeyPath="yes"/>
<RegistryValue Type="string" Name="Configuration Files" Value="[INSTALLLOCATIONCONFIG]"/>
<RegistryValue Type="string" Name="Configuration Files1" Value="[INSTALLLOCATIONCONFIG1]"/>
</RegistryKey>
</Component>
When I built the msi and if edit it Orca, I am seeing like below,
What should I do to get the msi as shown in the previous image?
MSI expresses registry data in the Registry table. During the installation, the system determines which features and components are being installed therefore which registry adds, updates and deletes to perform. The actual work is carried out by the WriteRegistryValues and RemoveRegistryValues actions.
It is not a best practice to "execute a reg file" during an installation because it's out of process and MSI can't manage the changes. Instead use the WiX Heat tool to harvest the contents of the registry file into wix xml source for inclusion in your installer.

Remove registry keys under HKCU on a per machine installation

I build a perMachine installer using WiX 3.6 to install a software I had not developed. Unfortunately the software creates some registry keys under HKCU during execution.
On uninstall, the self created keys should also be removed. It seems not so easy to remove these keys. I am "fighting" with ICE57 and/or ICE38. Both complaining the mix between perUser and perMachine data.
Hopefully you can point me in the right direction on fixing this issue.
To overcome ICEs you should move Per-User registry to separate components and use some registry entry as keyPath for that component, i.e.:
<Component Id='PerUserRegistry' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPAth" KeyPath="yes" Root="HKCU" Key="Software\[Manufacturer]\[ProductName]\[ProductCode]\PerUserRegistry" Name="[PackageCode]" Value="[ProductVersion]" Type="string" />
<!--Other Per-user registry goes here-->
</Component>
I completely agree with Christopher: It is common practice to leave per-user data on uninstall, but if removal is necessary, then Active Setup is the only real option.
First I propose you to remove them on Install or Re-Install instead of uninstall, you just need add RemoveRegirty entry and Active Setup, i.e. with this WiX code:
<Component Id='ActiveSetup' Guid='*'>
<RegistryValue Id="ActiveSetup00" Root="HKLM" KeyPath="yes" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Name="StubPath" Value="msiexec /fup [ProductCode] /qb-!" Type="string" />
<RegistryValue Id="ActiveSetup01" Root="HKLM" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Value="[ProductName] [ProductVerion] Configuration" Type="string" />
</Component>
<Component Id='PerUserRegistryCleanup' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPath" Root="HKCU" KeyPath="yes" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Name="StubPath" Value="msiexec /fup [ProductCode] /qb-!" Type="string" />
<RemoveRegistryKey Id='PerUserRegCleanup' Root='HKCU' Action='removeOnInstall' Key='Key\To\Be\Removed'/>
</Component>
Note: [PackageCode] use in ActiveSetup is very recommended, so with each new version (build) of MSI package you add separate entry (also see my final note). I used per-user active setup registry as key-path on purpose, so you don't run it for current user twice.
As for removing them after uninstall,
Now, hopefully you need to remove entire key, and not just some values. In either case, I would create custom action to add Registry entry for Active Setup during uninstall (or if there are many such keys/values, create and deploy .CMD file with those and launch it on uninstall, before RemoveFiles action, to add all of them to registry).
Note: that I would strongly recommend adding deleting this registry during install, or you might end up removing per-user values when software is yet installed.
So here's WiX code for all of this:
<CustomAction Id="CA_UninstallRegistryCleanUp" Directory="SystemFolder" ExeCommand="REG.exe ADD "HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\MySoftName_CleanUp" /v StubPath /d "reg add ^"HKCU\Key\To\Be\Removed^" /va /f" /f" Return="ignore" />
<InstallExecuteSequence>
<Custom Action='CA_UninstallRegistryCleanUp' After='RemoveRegistryValues'>REMOVE~="ALL"</Custom>
</InstallExecuteSequence>
<Component Id='RegCleanup_Remover' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPAth" Root="HKLM" KeyPath="yes" Key="SOFTWARE\[Manufacturer]\[ProductName]\[ProductCode]\" Name="DummyKey" Value="[ProductVersion]" Type="string" />
<RemoveRegistryKey Id='RegCleanup_Remover' Root='HKLM' Action='removeOnInstall' Key='SOFTWARE\Microsoft\Active Setup\Installed Components\MySoftName_CleanUp'/>
</Component>
Final notes:
There just two small issues with all this Active Setup stuff: be careful on Windows Terminal Servers; and once active setup was run for one user for current .MSI, it will not run again if you decide to reinstall same package, unless you change its PackageConde or raise version under ActiveSetup registry key. These are topics for another day, let me know if need them clarified.
And don't forget to add all of above Components to some Feature.
The Windows Installer considers this user data and best practice is to not remove it. Either way, it's very difficult to try to remove it anyways since other user profiles are out of scope / context. It's theoretically possible to write a custom action to enumerate profiles and load registry hives but on some versions of Windows ( Vista ) that won't work due to restricted permissions granted to the windows installer service.
If you really, really must be able to remove custom action data on uninstall then take a look at:
Active Setup Explained
You are going to need to leave behind an program (exe for example ) by marking a component as permanent. Then you'll need a custom action to write a registry value during the uninstall (because Windows Installer doesn't support this).
The concept is during the install you lay down an EXE and during the uninstall you leave you. You then write to the ActiveSetup registry key telling it to run your EXE once for each subsequent user to logon to the machine. The EXE then deletes your registry values. Reboot (politely) if needed to unload the extensions from explorer.
But honestly, a better designed application wouldn't need all of this.