How can I enable logging in my wix project and set MsiLogFileLocation?
Now I am running my install.msi with command line arguments:
msiexec /i install.msi /l*v InstallLog.log
I want to log my work always just running install.msi without any arguments. Are there any way to do that?
You can tell WiX to use verbose logging like this:
<Property Id="MsiLogging" Value="v" />
but you can't specify where to write the file - msiexec has already determined that by the time WiX gets to set the property. It'll default to %TEMP%\MSI{random chars}.LOG. You could offer the user a checkbox to determine if they want to open that log, like this (assuming you're using the built in InstallDir UI template - it'd be possible with others as well, but this is a pretty simple example):
<Property Id="MsiLogging" Value="v" />
<UI>
<UIRef Id="WixUI_InstallDir" />
<Publish Dialog="ExitDialog"
Control="Finish"
Event="DoAction"
Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="View Installation Log" />
<WixVariable Id="WixUILicenseRtf" Value="license.rtf"/>
<Property Id="WixShellExecTarget" Value="[MsiLogFileLocation]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
This would copy the log file to a directory about as late as possible in the game, but would miss at least some of the tail end of the log:
<Property Id="MsiLogging" Value="v!" />
<CustomAction Id="CopyLog" Execute="immediate"
ExeCommand="cmd /c copy [MsiLogFileLocation] C:\temp\log.txt"
Directory="TARGETDIR"
Impersonate="no"
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="CopyLog" OnExit="success" />
</InstallExecuteSequence>
Note that this won't work for the C: drive root, because that requires administrative privileges. If you're ok missing even more of the log, you could change OnExit='success' to After='InstallFinalize' and change the Execute attribute on the CustomAction to commit. That should allow it run as administrator.
See this discussion for a bit more: http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Setting-MsiLogFileLocation-property-td7374335.html
Related
I need to forcefully kill a process that is running in the background before attempting to delete any files, when running an Uninstall from an MSI created with Wix. The main application consist of a trayicon which reflects the status of the bg-process monitoring local windows services (made on C#, though this may not be so relevant going further).
I first tried the following:
<File Id='FooEXE' Name='Foo.exe' Source='..\Source\bin\Release\Foo.exe' Vital='yes' />
...
<InstallExecuteSequence>
<Custom Action="CloseTray" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="CloseTray" ExeCommand="-exit" FileKey="FooEXE" Execute="immediate" Return="asyncWait" />
The tray icon is immediately closed after confirming application-close dialog, but the Foo.Exe task still appears on the taskmgr after the uninstall completed. Also,the following error message was given:
Thats why, then I tried this:
<InstallExecuteSequence>
<Custom Action="Foo.TaskKill" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="Foo.TaskKill" Impersonate="yes" Return="asyncWait" Directory="WinDir" ExeCommand="\System32\taskkill.exe /F /IM Foo.exe /T" />
After obtaining the same result, tried:
<Property Id="QtExecCmdLine" Value='"[WinDir]\System32\taskkill.exe" /F /IM Foo.exe'/>
...
<InstallExecuteSequence>
<Custom Action="MyProcess.TaskKill" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="MyProcess.TaskKill" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="ignore"/>
Sample which I took from here: How to kill a process from WiX
lately when all else failed, I also tried this without any success:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
...
<InstallExecuteSequence>
<Custom Action="WixCloseApplications" Before="InstallValidate" />
</InstallExecuteSequence>
...
<util:CloseApplication Id="CloseFoo" CloseMessage="yes" Description="Foo is still running!" ElevatedCloseMessage="yes" RebootPrompt="yes" Target="Foo.exe" />
This one gave me a different error:
I'm thinking on building a statue in honor to this process that just refuses to die!!! ... either that or think a problem on the application side exists, where I should add something like Application.Exit(); or Environment.Exit(0); at some line inside Program.cs.
Is there any other thing I could do at either Wix or my application to attempt closing it successfully at Uninstall?
Thanks!
Personally I think the best option for you to go with is the in-built CloseApplication method rather than your previous options.
The error you are getting for that (Error code 2762) is because you are trying to schedule the action in the immediate sequence but have the ElevatedCloseMessage="yes" set which triggers it as a deferred action. Either remove this attribute or schedule it in the deferred sequence.
I developed a custom installer with WIX for a .NET application.
In this installer I set environment variable
<Environment Id="COMPLUS_Version" Action="set" Part="all" Name="COMPLUS_Version"
Permanent="yes" System="yes" Value="v4.0.30319" />
and after installation I run application (this environment variable need to be set for correct running apllication).
<UI Id="MyWixUI_Mondo">
<UIRef Id="WixUI_Advanced" />
<Publish Dialog="ExitDialog"
Control="Finish"
Event="DoAction"
Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
<Property Id="WixShellExecTarget" Value="[#Application]" />
<CustomAction Id="LaunchApplication"
BinaryKey="WixCA"
DllEntry="WixShellExec"
Impersonate="yes" />
But, sometimes it doesn't work. For example, in Windows 7 Enterprise x86 apllication run after installation with errors (only after installation).
UPD. This problem appear when UAC is disabled.
I want to execute a custom action in a Windows Installer (with WiX script) that makes symbolic links at the end of installation. mklink requires administrator privilege, as the installer restricts. This is what I wrote:
<CustomAction Id="mklink_cmdline" Property="QtExecCmdLine" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="ignore" />
...
<InstallExecuteSequence>
<Custom Action="mklink_cmdline" Before="InstallFinalize">
...
</Custom>
<Custom Action="mklink_exec" After="mklink_cmdline">
...
</Custom>
...
</InstallExecuteSequence>
This works perfectly if UAC is completely disabled. However, when enabling UAC in any level, this custom action fails with
CAQuietExec: You do not have sufficient privilege to perform this operation.
even if I allowed in the consent window. I tried to change Execute to deferred, Impersonate to no, or change package's InstallPrivileges to elevated, none of them works.
Any suggestion I can bypass? Thank you!
Edit: revised code with deferred custom action
<CustomAction Id="mklink_cmdline" Property="mklink_exec" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />
...
<InstallExecuteSequence>
<Custom Action="mklink_exec" Before="InstallFinalize">
...
</Custom>
<Custom Action="mklink_cmdline" Before="mklink_exec">
...
</Custom>
...
</InstallExecuteSequence>
Does it work when ran from an administrator command prompt? I assume it does.
From what I found the msi cannot raise the UAC level which is what you need here. I had to create a setup.exe that wrapped the msi as an embedded resource and executed it. The setup.exe includes the app.manifest requesting administrator execution level which raises the UAC level appropriately:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="Setup.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
I may just not understand WIX, custom actions and UAC enough, but this is what I ended up doing.
Are you scheduling it between InstallInitialize and InstallFinalize when you mark it for Deferred? Your Before and after looks a little wierd:
InstallFinalize
_cmdline before InstallFinalize
_mkline_exec after _cmdline
Sounds a little nondeterministic. You might find _cmdline occurring after InstallFinalize and deferred won't work there.
Try:
InstallFinalize
_exec before InstallFinalize
_cmldline before _exec
If it's actually mklink that is requiring elevation, you might try using SysInternals junction.exe instead.
I ended up bundling elevate.exe from wintellect, deploy it to some temp folder and supply it with a path to command-line script which created all symbolic links. Than it was invoked via the custom action.
Command line file in turn has some goodness inside to detect proper program files folder. or get it from the command line, if needed.
It appears that even though WiX correctly elevates the custom action, msi (or Windows Installer) itself doesn't grant it sufficient rights to properly run mklink command.
Also note that Impersonate="yes" in the CA. I believe that's what will let msi to show elevation dialog box when it executes the action.
command line file:
cd /D %~p0
IF EXIST "%PROGRAMFILES(x86)%" SET PROGFILES=%PROGRAMFILES(x86)%
IF "%PROGFILES%".=="". SET PROGFILES=%PROGRAMFILES%
SET INSTALLPATH=%PROGFILES%\MyGreatProduct
SET DATAPATH=%PROGRAMDATA%\MyGreatProduct
IF NOT "%~1."=="." SET INSTALLPATH=%~1
IF NOT "%~2."=="." SET DATAPATH=%~2
IF EXIST "%INSTALLPATH%" mklink "%INSTALLPATH%\veryimportant.ini" "%DATAPATH%\veryimportant.ini"
in the wxs file:
<Component Directory="TempFolder" Id='Comp_Temp_Makesymlinks' Guid='47a58219-1291-4321-4321-176987154921'>
<File Id='makesymlinks_cmd' Source='makesymlinks.cmd'>
<Permission User='Everyone' GenericAll='yes' />
</File>
<File Id='elevate_exe' Source='elevate.exe'>
<Permission User='Everyone' GenericAll='yes' />
</File>
</Component>
<SetProperty Id="CA_MakeSymLinksCmd" Before="CA_MakeSymLinksCmd" Sequence="execute"
Value=""[TempFolder]\elevate.exe" "[TempFolder]\makesymlinks.cmd"" />
<CustomAction Id="CA_MakeSymLinksCmd" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="CA_MakeSymLinksCmd" Before="InstallFinalize"><![CDATA[NOT Installed AND VersionNT >= 600 ]]></Custom>
</InstallExecuteSequence>
I have the following WiX snippet:
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
<CustomAction Id="StartAppOnExit"
FileKey="Configurator.exe"
ExeCommand=""
Execute="immediate"
Impersonate="yes"
Return="asyncNoWait" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
Value="Configure initial settings" />
<UI>
<Publish Dialog="ExitDialog"
Control="Finish"
Order="1"
Event="DoAction"
Value="StartAppOnExit"
>WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
Basically on the exit dialog I display a box that says: launch application. Note: this application requires elevation. This all works fine except for a snag. If UAC is enabled it seems that the MSI mucks around with the user token and strips its groups, so when it tries to launch the application that requires elevation it is no longer an option.
How do I string this together to work?
I tried chucking an Impersonate="no", but it's too late at that point for this to work.
The UI sequence is running as a limited user, and it launches applications with a call to CreateProcess. If you use something like a WixShellExec with [WixShellExecTarget] instead, it will act like Explorer and show a UAC prompt if the target requires elevation. Or you could modify your Configurator.exe to allow being launched without elevated privileges, detect that case, and relaunch itself with elevated privileges.
For example, this should work:
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
<CustomAction Id="StartAppOnExit" BinaryKey="WixCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes"/>
<Property Id="WixShellExecTarget" Value="[#Configurator.exe]"/>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Configure initial settings" />
<UI>
<Publish Dialog="ExitDialog" Control="Finish" Order="1" Event="DoAction" Value="StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
FYI, Immediate custom actions are ALWAYS impersonated (i.e. they always run as the user who executes the MSI).
I like Michael Urman's idea regarding making your Configurator.exe handle the elevation issue.
I wonder if you could also just include a manifest in your EXE so that the OS knows elevation is always required.
I am using WIX and have successfully used a custom action to execute installed file at the end of installer like this:
<CustomAction Id="LaunchAfterInstall" FileKey="foobar.exe" ExeCommand="parameters" Execute="immediate" Impersonate="yes" Return="asyncNoWait" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION"/>
<UIRef Id="WixUI_InstallDir" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch Foobar." />
<UI>
<Publish Dialog="ExitDialog" Control="Finish" Order="1" Event="DoAction" Value="LaunchAfterInstall">WIXUI_EXITDIALOGOPTIONALCHECKBOX</Publish>
</UI>
This works well when foobar.exe is in a component in the same wxs file. However what I really want is to execute a file that is installed by a merge module. How do I do this?
I can make changes in the merge module, if this helps things.
I changed the action to solve my problem:
<CustomAction Id="LaunchAfterInstall" Directory="INSTALLLOCATION" ExeCommand="[INSTALLLOCATION]\foobar.exe" Execute="immediate" Impersonate="yes" Return="asyncNoWait" />
You can also open the Merge Module in Orca or your MSI after the build is complete (i.e. the Merge Module has been merged in) and look up the File.Id. Then use the File.Id in the CustomAction.
Ideally though the MSI shouldn't refer to content inside the Merge Module since Merge Modules are supposed to be independent. I appreciate it doesn't always work out that way. :)