Wix Custom Action works under Windows 8, Fails under Windows 7 - wix

I'm launching a custom action console application that can return 0 or -1.
On Windows 8 when it returns 0, the install continues.
On Windows 7 when it returns 0, the install ends prematurely.
<Property Id="QtExecCmdLine" Value=""$(var.SourceFiles)\MyProgram.exe""/>
<CustomAction Id="CheckForOld" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="check"/>
<InstallExecuteSequence>
<Custom Action="CheckForOld" After="AppSearch" />
</InstallExecuteSequence>
Does anyone know what can be done to resolve this?

What are the UAC levels/settings on the Windows 8 and Window 7 machines? If you have UAC disabled on your dev machine but enabled (default setting) on the test Win 7 you could get the EXE failing to run as a custom action.
In this case I also recommend migrating your code to a DLL, you could write a C# DLL, using WixToolset and DTF.
The DLL can then set a property with the result of this search, and you can use that property to define a new launch condition to stop the installation, if required.
From Tao of the Windows Installer, Part 5
Rule 53: Test thoroughly It is crucial that you test your packages thoroughly before deployment. A classic mistake is for
developers and package authors to test only on their own systems where
they have full administrator rights and then discover that normal
users cannot use their applications.

Is that a QT exe file? What is the EXE file doing / checking? What happens if you turn off error checking - does it run and perform its task?
I wouldn't recommend to run a custom action with an exe file if you write the code yourself. Write it as a dll and connect the debugger to the code to step through it using a debug build dll - you will see exactly what happens as you step through the code:
Insert code to show a message box from within the function you want to debug
Compile a debug dll file and insert into the MSI file with the necessary plumbing to hook things up. This isn't entirely trivial, but not that complicated. See link below.
Run the MSI and wait for the message box to show
Attach the debugger to the correct msiexec.exe - either the user context one or the system context one depending on how your custom action is sequenced
Set breakpoints and step through the code to determine what is happening
How to create a basic MSI dll:
http://www.codeproject.com/Articles/1747/MSI-Custom-Action-DLL

What is this EXE supposed to do? There are several code smells. First is that it's scheduled in the UI sequence only. It won't get run during a silent install. The second is it's an EXE scheduled as immediate. EXE's can't access the MSI handle and set properties or do anything useful so I assume the EXE is changing the configuration of the machine. This is inappropriate outside of the execute sequence transaction. Finally, QtExecCmdLine needs an absolute path to the EXE on the destination machine. I don't see how $(var.SourceFiles) could provide that. That's a preprocessor variable that's only going to tell you where the file exists on the build server.
You aren't testing on your own dev machine? I'm guessing you build this on a Win 8 box and then tested on the same box. It happens to work cause it can find the file. When you run it on another machine (Win 7) it can't find the file and fails because the file can't possibly be there... you haven't installed anything yet.

Related

WiX CustomAction fails to start program with "Application cannot be restarted - Application SID does not match Conductor SID" warning

I have a client application for tracking user status ("Gone for the day", "Out to lunch", etc.) that needs to be started as part of its installation process. I have set up a wix installer to handle the installation and in particular a CustomAction to launch the application once the installation is complete. Below is the xml
<CustomAction Id="Launch_StatusTracker" FileKey="StatusTracker" ExeCommand=""
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="Launch_StatusTracker" After="InstallFinalize">NOT Installed</Custom>
<Custom Action="WixCloseApplications" Before="InstallInitialize">Installed</Custom>
</InstallExecuteSequence>
The code above executes just fine when I manually run the generated msi on my machine. It installs the application and then starts it up. However, when the application is installed using an SCCM 2012 Application the program is installed but it does not startup. Here is the warning message that I see in the Windows Event Viewer:
Application 'C:\Program Files\StatusTracker\StatusTracker.exe' (pid 7216) cannot be restarted - Application
SID does not match Conductor SID.
I've looked online for this type of error but I haven't been able to find anything about it that relates to SCCM. As an alternative I tried to have it run a batch file instead that will startup the program but that will not work for me because I need the program to run in the context of the current user.
What context is SCCM running the installer in? Typically it's SYSTEM and that's probably causing problems trying to start the process in the interactive user context. I used to have some tricks to get around this but they are all hacks. You may just have to take a reboot and have it start on next logon.
You can use PSEXEC to launch a command prompt as SYSTEM. Test the install silent in that context to mimic SCCM behavior.

Running regsvr32 as part of a WIX installer

I have authored a COM component that is distributed through a WIX-generated MSI.
The COM component has rather complicated and non-static registration logic which means that embedding the registration information directly in the Windows Installer WXS file is not a feasible option - registration must be done using regsvr32 - and it's a 32-bit COM component, so it must use the 32-bit version of regsvr32.exe - that is %SystemRoot%\SysWow64\regsvr32.exe on 64-bit Windows or %SystemRoot%\System32\regsvr32.exe on x86 Windows.
I noticed two problems with WIX with this WXS XML:
<InstallExecuteSequence>
<Custom Action="COMRegister" After="InstallFinalize">NOT Installed</Custom>
<Custom Action="COMUnregister" After="InstallInitialize">Installed</Custom>
</InstallExecuteSequence>
<CustomAction Id="COMRegister" Directory="APPLICATIONROOTDIRECTORY" ExeCommand='regsvr32.exe /s "[APPLICATIONROOTDIRECTORY]Component.dll"' />
<CustomAction Id="COMUnregister" Directory="APPLICATIONROOTDIRECTORY" ExeCommand='regsvr32.exe /s /u "[APPLICATIONROOTDIRECTORY]Component.dll"' />
The wrong regsvr32.exe was being invoked. I noticed that the x64 version of resgvr32.exe was being run on 64-bit systems instead of the 32-bit version.
regsvr32.exe was being run without elevated permissions, so COM registration failed with E_ACCESSDENIED.
For 1. it works if I hardcoded the path to the regsvr32.exe executable using [WindowsFolder]\SysWOW64\regsvr32.exe, but this wouldn't work on a real 32-bit machine where SysWow64 doesn't exist.
For 2. I read online that changing After="InstallFinalize" toAfter="RemoveExistingProducts"would cause it to run with elevated permissions, however instead this just gives me errors aboutRemoveExistingProducts` being an unresolved symbol name.
How can I resolve these two issues?
Update
(After struggling with this problem for the past 2 hours, I'm convinced the authors of WIX are close relations of H.P. Lovecraft)
I've worked-around the first issue by writing my own intermediate-step program which is a 32-bit executable, so it will always run under a WOW context, so it will reliably invoke the 32-bit regsvr32.exe program.
I found out the issue with the second issue was these things: For a CustomAction to run with elevated permissions (well, in the same security context as the main installer job) these conditions must be true:
<Custom/>" must haveBefore="InstallFinalize", and **not**After=""any other values forBefore=""` won't work reliably as WIX or Windows Installer might rearrange the actions (wut).
<CustomAction /> have have these attributes explicitly set:
Execute="deferred"
Impersonate="off"
Even-so, I would like to not have to use my helper program to correctly resolve the 32-bit regsvr32.exe. What options are there?
I wouldn't advise using self registration at all it is not the really the right way to do it with Windows Installer, if you really must set File/#SelfRegCost to 1.
A much better way is to extract the registry values and write them with WiX - you can also use heat to generate the values.
This sounds like something that will cause you lots of problems later. Though it sounds like you are determined to use this self-registration, please read this whole post: Self-Registration considered harmful.
Software that is doing "odd things" during self-registration are really frowned upon when found in application packaging in larger companies. Sometimes it is reason enough to throw out the entire software.
If you have stuff you need done with admin rights before your application can run, you should do it as part of the installer, but not via self-registration (see a plethora of reasons in the linked post above). It is better almost any other way, including having your main application.exe run with a command line from a custom action to trigger the custom registration steps, and then provide a good log of what has actually been done. And you should revert to normal COM registration and extraction for the COM servers.
It would be interesting to hear what is unique about your COM servers? Is it the usual licensing issue?

WiX MSI behaves differently before and after removing CRT MSM

I've got an MSI built with WiX. It performs the following custom action:
<CustomAction Id='StartTray'
Directory='INSTALLDIR'
ExeCommand='[INSTALLDIR]\myapptray.exe'
Return='asyncNoWait'
Impersonate='no'
Execute='deferred' />
It is scheduled like so:
<Custom Action='StartTray' After='StartServices'>NOT Installed OR (TRAYWASRUNNING AND NOT REMOVE~="ALL")</Custom>
myapptray.exe happens to use impersonation to relaunch itself from its starting context of Local System (running from MSI context) as the user currently active on the desktop. This is not in my control and Impersonate='yes' does not work because the MSI may be invoked for an upgrade from the context of the system service, meaning Impersonate='yes' would still end up running the app as Local System.
I recently moved from including the VC9 CRT as a MSM in this MSI, to including it in a bootstrapper exe.
Doing this prevents the myapptray.exe custom action from running successfully. The impersonation fails in WTSQueryUserToken which returns ERROR_PRIVILEGE_NOT_HELD. This seems to imply that removing the MSM actually changed the user context in which the MSI runs, but that seems ridiculous. The only lines I removed from the wxs file are the <Merge> and <MergeRef> tags for the MSM, nothing else has changed.
What am I doing wrong?
I'd look more at what versions of the CRT your EXE was built against and if there are any policy rules saying what it can run against. Moving from an MSM to an EXE run before your MSI should generally be a good thing.
BTW, I did something really hacking like this once upon a time. We had to push an MSI out under the SYSTEM context using MSI. If a user was logged on we had to relaunch the application using the users desktop login session. I had a DCOM server installed configured to impersonate the interactive user to accomplish this. Really wierd, but there was a valid reason.
This was all before Restart Manager though.
I figured it out.
The CRT MSM was setting ALLUSERS=1 and the installer's behavior changed because it was not present in our base installer. The MSM's setting of ALLUSERS was inherited into the base installer as a result.
Setting ALLUSERS=1 in our wxs file fixed the problem!

WiX CloseApplication for exe and dll

I've created a WiX setup project based on the article WiX 3 Tutorial: Understanding main WXS and WXI file mainly because it gives the WiX needed to do an application shutdown.
However, I'm puzzled by the outcome. Here's the situation:
We have an executable which uses a dll and create a setup which installs the executable and the dll. We execute the setup.
CASE 1: Next, we change the executable and NOT the dll and create the setup again. Then we start the installed application and make sure also the dll is loaded. If we now execute the second setup, a dialog is shown asking the user to shutdown the executable just as we expected.
CASE 2: But if we do not change the application but only the dll and then execute the setup while the application is running and the dll is loaded, no dialog is shown. At the end of the setup a dialog appears asking if we want to restart the computer.
Is this expected behaviour and how can I force the application shutdown dialog of CASE 1 also when only a dll is changed as in CASE 2? I do not want the user having to restart the computer because the application is running on a server which cannot be restarted.
This is all determined by Windows Installer during the costing process. The installer decides which files need to be installed / updated and calculates how much disk space is needed and if there are any file locks. If there are file locks, it attempts to resolve the lock to a process with a window handle. If it can do this, you'll get the dialog. If it can't, you won't. This doesn't mean a reboot won't be needed, it just can't give you useful information on how to avoid it.
A few additional points:
Make sure you are versioning your EXE and DLL. If the old DLL is 1.0.0.0 and the new DLL is 1.0.0.0 costing will say "Nothing to do here".
How does the EXE use the DLL at runtime? It might simply not have a lock on it during the entire life of the process.
Understand that MSI's reboot behavior can be altered through the use of properties such as REBOOT=ReallySuppress
Here's some good articles to read:
InstallValidate
FileInUseDialog
System Reboots
I haven't checked the code but I think what is happening is the CloseApplication action is not running because it sees that the exe hasn't changed. As far as I am aware you cannot target a DLL with CloseApplication. If you run your install with logging you should be able to see if the action is triggered. I am assuming you have RemoveExistingProducts schedule late in the install, if you were to move it after InstallValidate it would remove the exe every time and therefore trigger the action.

WiX: Forcefully launch uninstall previous using CustomAction

I'm writing a new major upgrade of our product.
In my installer I start by finding configuration settings of the previous version, then I'd like to uninstall the previous version.
I have found several guides telling me how one should make a MSI suitable for such upgrades.
However, the previous was not an MSI.
It was not according to best practices.
It does, however, in registry HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{GUID} specify an UninstallString.
Using a RegistrySearch I can easy find the command below, which I store in UNINSTALL_CMD.
RunDll32 C:\PROGRA~1\COMMON~1\INSTAL~1\PROFES~1\RunTime\10\01\Intel32\Ctor.dll,LaunchSetup
"C:\Program Files\InstallShield Installation Information\{GUID}\setup.exe"
-l0x9 -removeonly 4:
I cannot get the hang of the CustomAction needed to perform the actual uninstall.
<CustomAction Id="ca.UninstPrev" Property="UNINSTALL_CMD" ExeCommand="" />
The MSI logs says:
Info 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: ca.UninstallPrevious, location: RunDll32 C:\PROGRA~1\COMMON~1\INSTAL~1\PROFES~1\RunTime\10\01\Intel32\Ctor.dll,LaunchSetup "C:\Program Files\InstallShield Installation Information{GUID}\setup.exe" -l0x9 -removeonly, command:
Anyone seeing what I am doing wrong here?
Regards
Leif
I did application repackaging for a couple years at Continental Airlines where I did SMS pushes to an 18,000 seat forest. I frequently had a legacy application in the wild that was not installed using MSI that I needed to get redeployed using MSI and once that was done I supported major upgrades going forward.
These previously deployed apps typically had very broken and misbehaved uninstallers. Instead of calling these, I would use SMS to query the forest to get all the deployed versions. I would then deploy those old packages to a integration lab and work out what it was each installer did to the machine and write my own aggregated "forced cleanup" custom action that was capable of wiping the various versions of the application off the machine.
I executed this custom action prior CostInitialize so that when the new MSI did it's costing it wouldn't be influenced by the crap that was no longer on the machine. This worked for me because I pushed packages as System and I didn't have to worry about elevation issues. If you want to be fully UAC compliant you would want to run this custom code from a prereq package and either have your users run it manually or wire it into a bootstrapper to run prior to your MSI.
After a good nights sleep I found my error.
If you really read http://wix.sourceforge.net/manual-wix3/wix_xsd_customaction.htm the answer was there.
I was trying to make a type 50 custom action, launch an executable already installed on system.
Property specifies the full path to an executable to launch
ExeCommand specifies the command line arguments for this executable above.
And my fault was that I did place the full exe+command line in the Property field.
/L