Wix Toolset - File for admin not appearing when installer is an admin - wix

I am trying to use a component condition for an admin manual pdf. If the installer is in the administrators group I want the admin manual to be installed. Here's how I'm setting this up but it's not being installed even if installer is an administrator. What am I missing?
Requirement:
InstallScope="perUser" />
<Condition>Privileged</Condition>
Find below:
<Component Id="cmp_ManualForAdmins.pdf" Guid="4C28B047-74D2-4642-A180-0039B4C2C5BC">
<File Id="fil_ManualForAdmins.pdf" Name="ManualForAdmins.pdf" Source="$(var.WindowsFormsApp1_TargetDir)ManualForAdmins.pdf">
<Shortcut Id="startMenuAdminManual" Directory="ProgramMenuSubFolder" Name="AdminManual"></Shortcut>
</File>
<Condition>Privileged</Condition>
</Component>

I just spent an hour investigating this and there really aren't any good answers. Because the MSI is invoked from a standard user process and doesn't require elevation, MSI never knows the user can elevate so the Privileged property isn't set.
I figured a custom action might help to work around this but searching up C# Detect Admin revealed various classes and API calls that all had the same behavior.
If I install a protoype MSI from an elevated command prompt the condition evaluates to true and the 'admin.txt' file is installed. From a non elevated it is not installed.
So what would I do? One of two things:
1) Make a second Docs MSI that is a permachine install that requires elevation
or
2) Build and deploy a docs.exe which is manifested to require admin. If the program successfully elevates then have it extract the PDF from an embedded resource to the temp directory and do a ShellExecute to launch the default PDF viewer with that file.

Administrative Installation: Admins typically perform an administrative installation (file extraction) of a setup - at least if they work in big companies that do application packaging. Hence I tend to try to make files like that easily visible on the extracted source media - instead of installing them during normal installation (or both or either, doesn't matter which).
Sample administrative installation (glorified file extraction):
msiexec /a Test.msi TARGETDIR=D:\ExtractedFiles\
More about Administrative Installations.
Admin.pdf: Here is a quick hack that I haven't tested extensively. The admin.pdf will show up during file extraction and not during installation:
<..>
<Feature Id="MainApplication" Title="MainApplication" Level="1">
<Feature Id="SomeFiles" Title="SomeFiles" Level="1" />
<!-- Remove "Display" attibute to show Admin feature in normal setup GUI -->
<Feature Id="Admin" Title="Admin" Level="1001" Display="hidden" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Component Id="AdminManual" Feature="Admin" Guid="{00000000-0000-0000-0000-0000DBFB0000}">
<File Source="D:\Admin.pdf" />
</Component>
<..>
Running administrative installation will extract the admin.pdf to the top level extraction directory:
msiexec /a Test.msi TARGETDIR=D:\ExtractedFiles\
The Admin feature is hidden from the normal installation GUI. Change the attribute "Display" to change this. Just remove it for example - for testing purposes.

Related

How do I stop removal of files during uninstallation using Wix

When uninstalling my application, I'd like to configure the Wix setup to NOT to remove few files that were added as part of the installation. It seems like the uninstaller removes all the files that were originally installed from the MSI file. How do I do that?
Here are my files which I wish to keep it forever
<Binary Id="RootCABinary" SourceFile="Assets\Certificates\RootCA.cer" />
<Binary Id="SubCABinary" SourceFile="Assets\Certificates\SubCA.cer" />
I have used WixIIsExtension.dll to install these certificates to the windows store.
Overwrite: Is it important that the file never gets overwritten? If so, add
"Never Overwrite" to your component. WiX attribute: NeverOverwrite="yes". Remember to test upgrade scenarios!
Permanent Component: As stated by Pavel, you can set a component permanent:
<Component Permanent="yes">
<File Source="License.rtf" />
</Component>
Blank GUID: Another way to do it is to set a blank component GUID. It essentially means "install and then leave alone". No repair or uninstall should be done (remember to add NeverOverwrite="yes" if the file should never be overwritten):
<Component Guid="" Feature="MainApplication">
<File Source="SubCA.cer" KeyPath="yes" />
</Component>
Read-Only Copy: I sometimes install files to a per-machine path (for example somewhere under program files) and then copy them to a per-user location (somewhere in the user-profile) on application launch, and then do operations on them that entail that the files should not be deleted. This is good in cases where you want to do something that change the files in question (they need to be writable). Then it is good to "untangle" them from deployment concerns (files will not be meddled with by installer operations at all - the installer has no knowledge of them). The original read-only copy installed to program files can be uninstalled though (no longer needed?).
Other approaches: You can also create such files using custom actions during installation (usually text files only), you can download the file in question from your web site on application launch (makes the file easy to manage and update / replace? Vulnerable to connection problems - firewalls, etc...) and the application can create the file using "internal defaults" in the main executable on launch. And there are no doubt further approaches that I can't recall.
Put your binaries in a separate WiX component and make it permanent. Have a look at this thread as well

How to include inherited permissions when specifying permissions for a file installed by Wix / Windows Installer?

The Wix source code that I feed to the Wix compiler to build an MSI package for my application, contains the following PermissionEx directive, part of a file component which Windows Installer should install with additional (to those that should be inherited by default) permissions:
<PermissionEx Sddl="D:AR(A;;FW;;;BU)" />
As you can surmise, I intend to install the file with inherited permissions ("AR") included in its ACL and on top of that allow members of the Built-in Users group ("BU") to be allowed ("A") to write to the file ("FW").
The code above does not have the desired effect -- the file is installed, but only that single explicit ACE is listed, none of the ACEs that are supposed to be inherited from parent folder.
In contrast, if I subsequently remove all permissions from the file and run cacls file /S:D:AR(A;;FW;;;BU), i.e. specify exactly the same SDDL string, it does work as intended -- the permissions from parent are inherited and form part of the ACL, together with the explicit non-inherited ACE.
I am using Wix 3.11.1.2318 and the Windows Installer version is 5.0.16299.611, all running on Windows 10 Enterprise 64-bit. Orca tells me the MsiLockPermissionsEx table embedded in my built MSI file is populated with the intended SDDL record. So why is the file created without inheriting permissions from its containing folder?
I tried to use "AI" in place of "AR", and both strings together, but none of it had any effect either.
Is this some known limitation or a quirk with Windows Installer? I know that people were talking a while back how the old LockPermissions table (the one specified for Windows Installer versions earlier than 5) was inadequate in this specific regard -- inherited permissions, namely -- but they also said Microsoft was out to address this very issue with the new table feature.
Otherwise what am I doing wrong?
Given your knowledge in this field, you probably have already tried this. It would also be much better to eliminate the need for permissioning, but two snippets for you - notice the Append attribute:
Create a WiX project in Visual Studio. Add the Util namespace to the WiX element:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
In Visual Studio project, right click References and add reference to "%ProgramFiles(x86)%\WiX Toolset v3.11\bin\WixUtilExtension.dll".
Permission Folder:
<Component Feature="ProductFeature" Id="Test.exe" Guid="PUT-GUID-HERE">
<File Source="C:\Test.exe" />
<CreateFolder>
<util:PermissionEx User="Power Users" GenericWrite="yes" />
</CreateFolder>
</Component>
Permission File:
<Component>
<File Source="C:\Test2.exe">
<util:PermissionEx Append="yes" User="Users" GenericWrite="yes" />
</File>
</Component>
Take a look at WiX's custom PermissionEx in the Util extension.
http://wixtoolset.org/documentation/manual/v3/xsd/util/permissionex.html

SelectionTree Hide options

I read lots about how to show or remove installation options for features in
the SelectionTree.
Will be installed on local hard drive.
Entire feature will be unavailable.
I don't have any subfeatures, still option 2 i.e. Entire feature will be unavailable. is getting displayed.
For e.g.
<Feature Id="ABC" Title="ABC ShortCut" Level="1">
<ComponentRef Id="ApplicationShortcut" />
</Feature>
Is there any way to hide option 2, show only "Will be installed on local hard drive".
I have checked this thread, but not get any proper answer there.
You can't if you deploy as .MSI
The only option is to deploy with an .EXE (that calls MsiSetExternalUI and takes care of the UI) like Burn.

How to uninstall with msiexec using product id guid without .msi file present

I'm trying to automate the uninstallation of packages created using WiX for the purposes of changing the installed software stack & configuration without reprovisioning a whole OS. Eventually I'll use powershell scripting to do this but at the moment I can't seem to get my test package to uninstall interactively with cmd.
If I run:
msiexec /x '{A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}'
msiexec /x A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8
I get:
"The installation package could not be opened. Verify that the package
exists and that you can access it, or contact the application vendor
to verify that this is a valid Windows Installer Package."
If I run:
msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
I get:
"This action is only valid for products that are currently installed"
I've looked at the windows installer guide, the WiX documentation, msiexec documentation and used orca to go over the .msi myself but I've not really found anything that gives a clear picture of how an uninstall is processed. Is the .msi file required and if not then why does windows installer seem to think it is when given a GUID?
The WiX code for the .msi installer is:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='htp://schemas.microsoft.com/wix/2006/wi' >
<!--DO NOT COPY / PASTE THE PRODUCT ID GUID BELOW TO YOUR OWN WIX SOURCE -->
<Product Id='A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8' Language='2057'
Manufacturer='COMPANYNAME IT-Operations'
Name='COMPANYNAMEServerListener' Version='1.0.0'
UpgradeCode='PUT-GUID-HERE'>
<Package Id='*' Manufacturer='COMPANYNAME IT-Operations' Compressed='yes' />
<Media Id='1' Cabinet='COMPANYNAMEServerListener.cab' EmbedCab='yes' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='COMPANYNAME' Name='COMPANYNAME'>
<Directory Id='INSTALLDIR' Name='COMPANYNAMEServerListener'>
<Component Id='MainExecutable' Guid='*' >
<File Id='COMPANYNAMEServerListener.exe'
Source='COMPANYNAMEServerListener.exe' Vital='yes'
KeyPath='yes' />
<ServiceInstall
Id='COMPANYNAMEServerListenerInstall'
DisplayName='COMPANYNAMEServerListener'
Description='Accepts and discards TCP connections on port 28028 to indicate that this server is alive and ready to be controlled'
Name='COMPANYNAMEServerListener'
Account='NT AUTHORITY\LocalService'
ErrorControl='normal'
Start='auto'
Type='ownProcess'
Vital='yes'
>
<ServiceDependency Id='tcpip'/>
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="COMPANYNAMEServerListener" Wait="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id='Complete' Level='1' >
<ComponentRef Id='MainExecutable' />
</Feature>
<CustomTable Id ="COMPANYNAMEMetadata">
<Column Id="Property" Type="string" Category="Identifier" PrimaryKey="yes"/>
<Column Id="Value" Type="string"/>
<Row>
<Data Column="Property">InstallString</Data>
<Data Column="Value">/qn</Data>
</Row>
</CustomTable>
</Product>
</Wix>
"Reference-Style" Answer: This is an alternative answer to the one below with several different options shown. Uninstalling an MSI file from the command line without using msiexec.
The command you specify is correct: msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
If you get "This action is only valid for products that are currently installed" you have used an unrecognized product or package code, and you must find the right one. Often this can be caused by using an erroneous package code instead of a product code to uninstall - a package code changes with every rebuild of an MSI file, and is the only guid you see when you view an msi file's property page. It should work for uninstall, provided you use the right one. No room for error. If you want to find the product code instead, you need to open the MSI. The product code is found in the Property table.
UPDATE, Jan 2018:
With all the registry redirects going on, I am not sure the below registry-based approach is a viable option anymore. I haven't checked properly because I now rely on the following approach using PowerShell: How can I find the product GUID of an installed MSI setup?
Also check this reference-style answer describing different ways to uninstall an MSI package and ways to determine what product version you have installed:
Uninstalling an MSI file from the command line without using msiexec
Legacy, registry option:
You can also find the product code by perusing the registry from this base key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall . Press F3 and search for your product name. (If it's a 32-bit installer on a 64-bit machine, it might be under HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall instead).
Legacy, PowerShell option: (largely similar to the new, linked answer above)
Finally, you can find the product code by using PowerShell:
get-wmiobject Win32_Product | Format-Table IdentifyingNumber, Name
Similar post: WiX - Doing a major upgrade on a multi instance install (screenshot of how to find the product code in the MSI).
msiexec.exe /x "{588A9A11-1E20-4B91-8817-2D36ACBBBF9F}" /q
The good thing is, this one is really easily and deterministically to analyze:
Either, the msi package is really not installed on the system or you're doing something wrong.
Of course the correct call is:
msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
(Admin rights needed of course- With curly braces without any quotes here- quotes are only needed, if paths or values with blank are specified in the commandline.)
If the message is: "This action is only valid for products that are currently installed", then this is true. Either the package with this ProductCode is not installed or there is a typo.
To verify where the fault is:
First try to right click on the (probably) installed .msi file itself. You will see (besides "Install" and "Repair") an Uninstall entry. Click on that.
a) If that uninstall works, your msi has another ProductCode than you expect (maybe you have the wrong WiX source or your build has dynamic logging where the ProductCode changes).
b) If that uninstall gives the same "...only valid for products already installed" the package is not installed (which is obviously a precondition to be able to uninstall it).
If 1.a) was the case, you can look for the correct ProductCode of your package, if you open your msi file with Orca, Insted or another editor/tool. Just google for them. Look there in the table with the name "Property" and search for the string "ProductCode" in the first column. In the second column there is the correct value.
There are no other possibilities.
Just a suggestion for the used commandline: I would add at least the "/qb" for a simple progress bar or "/qn" parameter (the latter for complete silent uninstall, but makes only sense if you are sure it works).
There's no reason for the {} command not to work. The semi-obvious questions are:
You are sure that the product is actually installed! There's something in ARP/Programs&Features.
The original install is in fact visible in the current context. It looks as if it might have been a per-user install, and if you are logged in as somebody else now then it won't know about it - you'd need to log in under the same account as the original install.
If the \windows\installer directory was damaged the cached file would be missing, and that's used to do the uninstall.
Thanks all for the help - turns out it was a WiX issue.
When the Product ID GUID was left explicit & hardcoded as in the question, the resulting .msi had no ProductCode property but a Product ID property instead when inspected with orca.
Once I changed the GUID to '*' to auto-generate, the ProductCode showed up and all works fine with syntax confirmed by the other answers.
Try this command
msiexec /x {product-id} /qr
you need /q at the end
MsiExec.exe /x {2F808931-D235-4FC7-90CD-F8A890C97B2F} /q

How to fix ICE57.Per-User installation

Our application writes some settings to the registry into the HKCU hive during runtime. I want to delete this settings during uninstall. Here is code:
<Fragment>
<DirectoryRef Id="INSTALLLOCATION" DiskId="1" FileSource="$(var.SourceDirProject)\">
<Component Id="DeleteHkcuManufacturerHive" Guid="GUID">
<Condition>REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE</Condition>
<CreateFolder/>
<RemoveRegistryKey Action="removeOnUninstall"
Id="HKCUkey" Root="HKCU" Key="Software\$(var.Manufacturer)"/>
</Component>
</DirectoryRef>
</Fragment>
ICE57: Component 'DeleteHkcuManufacturerHive' has both per-user and per-machine data with a per-machine KeyPath.
Why I'm getting ICE57? Installation is per-User. Thank's in advance.
UPD: Where is here the per-machine element? May be it is an INSTALLLOCATION=Program Files\ManufacturerDirectory?
You are operating on the HKCU hive which is only available to the current user.
MSDN states:
ICE57 validates that individual components do not mix per-machine and
per-user data. This ICE custom action checks registry entries, files,
directory key paths, and non-advertised shortcuts. Mixing per-user and
per-machine data in the same component could result in only partial
installation of the component for some users in a multi-user
environment.
The ICEs are validations on your installation package. As stated above, ICE57 is to make sure you don't mix up per-machine and per-user constructs. If you must remove entries to HKCU during uninstall (and the software is installed per-machine) then you can turn off that specific validation in Visual Studio in Properties > Tool Settings as shown in the screenshot below:
However, you may want to think about the root cause of your issue. If you are doing a per-machine install, your installer or application should probably not be writing to HKCU as it is only available to the current user, whereas your app is installed for all users.
I've got an answer on wix-users mailing list. Here is Peter Shirtcliffe's answer:
ProgramFiles is a per-machine location. You can only access it when elevated. If you want to install program code in a per-user installation, you should install to %LocalAppData%\Programs.
Remove the condition entirely. The component will be installed but will have no effect until you uninstall the application. At that point, when the component is removed, the registrykey will be removed also.