Defining private properties in Wix Installer - wix

First, let me define what I mean by saying private property. Normally, properties can be set using scripting such as follows:
<Property Id="CHECKREGISTRY">
<RegistrySearch Id="CheckRegistryKey"
Root="HKLM"
Key="SOFTWARE\Foo"
Name="Bar"
Type="raw" />
</Property>
<Condition Message="You don't have the required permission to install this tool.">
<![CDATA[Installed OR CHECKREGISTRY]]>
</Condition>
But the problem is that you can bypass this check by simply running a script like this:
msiexec.exe /i FooInstaller.msi /quiet CHECKREGISTRY="#1"
and that is against the very first idea that you should have the registry key to be able to do what you want to do.
As you see, a private property -- if exists -- would prevent this and allow being able to set only from within the MSI installer itself.
I was thinking to go to CustomAction route, but for a very simple thing, it is overly complicated. Is there any simple solution to this problem, or how can we define private properties in the first place?

You can create a private property by naming it with a lowercase letter. Public properties are all-uppercase.
However, you can't use AppSearch to set private properties.
Remember that MSI databases can be inspected so there's no real security. Best you can get is to use custom actions to obfuscate.

By definition, a property consisting only of uppercase letters is public. To make it private use some lowercase characters.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa371245(v=vs.85).aspx
If you are worried about a public property being set in the command line, just explicitly set it to an empty value before AppSearch.

Related

How to achieve authoring a single package for per-user or per-machine installation

I tried all the scenarios for creating a single MSI for per-user and per-machine. Below is my explanation: According to https://blogs.msdn.microsoft.com/windows_installer_team/2009/09/02/authoring-a-single-package-for-per-user-or-per-machine-installation-context-in-windows-7/
<Property Id='ALLUSERS' Value='2' />
<Property Id='MSIINSTALLPERUSER' Value='{}' />
Results in a per-machine installation and the value of MSIINSTALLPERUSER is "1" results in per-user installation.
Now I am running a custom action which determines the user is whether admin or not and so changing the property value to be {} or 1.
My problem is the value of "ALLUSER" is changed to 1 Before logging during installation. The log created shows:
"PROPERTY CHANGE: Modifying ALLUSERS property. Its current value is '2'. Its new value: '1'."
But When i run by directly giving MSIPERUSER Value as "1" The log shows:
"PROPERTY CHANGE: Deleting ALLUSERS property. Its current value is '2'."
So please help me how can i develop a single installer for both admin user and normal user.
Thanks in advance.
Basically you follow the rules here:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd408068(v=vs.85).aspx
The point here is that Windows decides how the install works based on the user's privileges, not you with a custom action. That's why the property values change. Follow those rules and it just works.

Wix Bundle Upgrade Without Resupplying Password

I have authored an MSI that requires an account and password to be supplied to install and start a Windows Service, so I've added a couple of properties to my 'Product' element for that. I have a requirement that these properties should not need to be resupplied to perform an upgrade and since one of those properties is a password I don't want to persist it's value to the registry (or anywhere). I have achieved this with
<MajorUpgrade ... Schedule="afterInstallExecute" />
Now I'm authoring an exe bootstrapper to bundle this MSI with it's prerequisite, similarly the exe will need to receive values for the properties and pass them to the MSI, so I've added some 'Variable' elements to my bundle and passed them to my 'MsiPackage' element with child 'MsiProperty' elements. And this works great during first install when the values are supplied, but now when I want to upgrade the bundle without supplying values for the properties the bootstrapper passes empty values to the MSI. Something equivalent to...
msiexec /i MyMsi.msi ACCOUNT= PASSWORD=
Which breaks the upgrade. The new version of the Windows Service is attempting to start with an empty value for account and password.
Is there a way to conditionally pass variable values to MSI's as property values?
What happens when both the 'Variable' element attributes 'Hidden' and 'Persisted' are set? Will the password really be hidden?
Is there another pattern I don't know about / haven't thought of?
Something like this doesn't feel like it should require a custom action.
On upgrades you can disable the <InstallServices> standard action.
In one of the products I work with I have the following:
<!-- http://stackoverflow.com/questions/15965539/how-to-only-stop-and-not-uninstall-windows-services-when-major-upgrade-in-wix don't change service config on upgrade -->
<DeleteServices>NOT UPGRADINGPRODUCTCODE</DeleteServices>
<InstallServices>NOT WIX_UPGRADE_DETECTED OR V6INSTALLED</InstallServices>
Because I didn't want to reset the start types of the services is the user has decided to start them manually instead of automatically (it's an option in our product to set this).
By doing this, it should leave the service already installed as is when upgrading instead of trying to re-add it with empty parmeters for the login user/pass
An alternative is to make a salted hash of the password and store the user and salted&hashed password into the registry. On upgrades you can read these values decode the password and use those values.

Ignoring disallowed property IISMAJORVERSION and 'EnableUserControl'?

My installer package works on my machine but failed on my colleague's machine. Looking at the log, I can see a few properties values are not persisted during ExecuteAction phrase: there are few Ignoring disallowed property lines in his installer log file and those properties are set with the right value during UISequence. The strange thing is that we are both Admin user on local machines he is and Domain Admin as well while I am not, and we are both on Windows 7 64bit. So I don't think it is because he don't have Administrator rights.
The exact entries in the log file:
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property IISMAJORVERSION
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property IISMINORVERSION
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property WCF_SRV_INSTALL_FOLDER
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property TARGETDIR
A easy fix would be mark all those properties as Secure, but properties such as IISMAJORVERSION are not defined in my code. I discovered that I can set EnableUserControl to 1 to make Ignoring disallowed property go away. Another solution would be create my own security properties and copy whatever ignored property value I need into my own properties and read them instead of the ignored property.
After that I discovered there is an entry Machine policy value 'EnableUserControl' is 0 in his installer log, but not in my log, which seems point to set EnableUserControl to 1 is probably what I need. In that case, the question is why there are different values from those two machines?
So my number one question would be: is set EnableUserControl a good fix for my solution, or there are probably better/safer solutions given I only seems found the symptoms but not the cause?
Or if EnableUserControl seems like a reasonable fix given the information, any suggestion could help me track down the cause of the problem (a registry key value change by the Administrator, probably?).
I don't think there is anything special about my installer, but in case of anyone want to see more details:
<Package Id="*" InstallerVersion="200" Description="Web service installer" Compressed="yes" InstallScope="perMachine" />
Edit:
As pointed out by PhilDW, those properties probably should be marked as Secure to begin with. But then shouldn't all properties be marked as Secure because of UAC, I don't think is make much sense to define a property to be not Secure if it might be used by others?
I'm pretty sure it doesn't matter if you defined them or not - try marking them Secure.

Wix installer (3.0) - how do I write to the Registry after success or fail of the installation?

I have an application that will be installed by another program (basically a wrapper that installs multiple applications and reports pass/fail for each). The requirement from the wrapper development team is that my application must write either Success or Fail to a specific registry key after the installation completes.
For my solution, I was thinking I could initilize the registry key to Success when the installtion begins, and update the value to Fail only if the installation fails (or the other way around).
Based on reading examples, browsing through the Wix Help, and searching for similar issues, I'm pretty certain I need to use a Custom Action, something like
<Custom Action="SetInstallationStatusFail" After="InstallFinalize">NOT Installed</Custom>
The place I'm stuck at now is that I don't know what code I need to write for the SetInstallationStatusFail in order to update the registry key. I'm also not sure what Parent element to stck it under. I think it should be something simple like this
<RegistryValue Action="write" Root="HKLM" Key="SOFTWARE\$(var.RegistryCompanyName)\$(var.RegistryProductName)\InstallStatus" Name="install" Type="string" Value="Fail" />
If you think I'm trying to do something that isn't valid, or if you know of a better solution for wrting to the registry after install, please let me know. Thank you.
It's a bad requirement because the registry value would be "orphaned" with nothing to clean it up. MSI already provides a way to determine success or failure: the return code. See "Error Codes" in the MSI SDK for a list.
MSI doesn't provide a way to write to the HKLM registry after failure, either natively or via custom action. (There are actions that can be triggered on error but they don't have privileges to write to HKLM.)
Have a chat to the wrapper guys and see if they are willing to consider other options - even if they can't accept return codes and want to check somewhere else there are still other ways to do this using the Windows Installer API.
For example, here's a really simple VBScript to loop through the currently installed products looking to see if an application is installed by name.
productName = "My Application"
Set installer = Wscript.CreateObject("WindowsInstaller.Installer")
For Each productCode In installer.Products
If LCase(installer.ProductInfo(productCode, "ProductName")) = LCase(productName) Then Exit For
Next
If IsEmpty(productCode) Then
Wscript.Echo "Couldn't find " & productName
Else
Wscript.Echo "Found " & productName
End If
Another option would be to test if the value in the registry exists.
Your installer writes a value to the registry, the value can be Success. If the value exists in the registry, then installation was successful; if it does not, then installation failed.
Here is a thought. You can write registry value in both cases in the success case or failure case. Write a custom action to write success value. Write and schedule a rollback custom action to write failed value. If install fails it will be rolled back and the rollback custom action will write failed. If it succeeds you write value success in the custom action with condition not installed.
In either case These registry values can be deleted during uninstall. This may not be an elegant solution but if this is your requirement then you can fulfill it.
Perhaps Bob Arnson can comment upon this solution.

How can I get WiX to call a method in a .NET assembly as part of the installation process?

I'm migrating some existing products to use WiX 3.5 (I'm using the Votive VS integration). Some of the items I'm installing need to be registered with a third-party framework. The requirement is that I must call a Register() method in a third party .NET assembly to inform it of the presence of the items I'm installing. It expects a COM ProgID.
I can't figure out how to get WiX to do this. I thought about creating a binary Custom Action, but I can't find a way of passing a parameter (a string containing the ProgID) into that custom action. I don't want to hard-code it because I need this to be re-usable code. I can't see a way to do this declaratively because the Register() function is a 'black box'.
Man this is a steep learning curve. What's my best approach here?
Look at the Deployment Tools Foundation (DTF) for WIX. There is a DTF.chm file with the WIX installation with lots of information.
Assuming you installation process is something like
Setup installation, input parameters/ProgID, do validation, etc.
Begin actual installation of files
Call registration methods
You'll need two Custom actions (ignoring rollback and uninstallation)
SetupRegistration
DoRegistration
SetupRegistration should be an immediate custom action fired either from the UI or late in the setup phase. It grabs the ProgID and any other data needed, uses a CustomActionData object and assigns that to a property named "DoRegistration" (Important, the property name must be the same as the second custom action)
The DoRegistration is a deferred custom action and needs to be scheduled in the InstallExecuteSequence probably after InstallFiles, but that depends. It pulls the Session.CustomActionData property and gets the ProgID out, then calls whatever registration method you need.
Am using a sort of what you have described.
I use to call CustomAction(events) when required. Like on clicking button you can call a method which will do work for you.
Calling custom action like:
<Custom Action="ActionName" After="InstallFinalize">CONDITION = "1"</Custom>
Or calling custom action based on specific button click:
<CustomAction Id="TestConnection" BinaryKey="SetupCustomActions" DllEntry="TestConnection" Execute="immediate" Return="check" />