Custom action of Wix failed to call vb script - wix

I have one VB script in the package itself. I need to call it using CMD, the default way of calling the script taking too much time, so I am trying to call it with CMD and CSCRIPT but the installer raises the error while installation.
I am using the following code which is not working as expected. I searched a lot but didn't find the solution.
<Binary Id="ServiceInstall" SourceFile="..\..\..\AddVirDir.vbs" />
<CustomAction Id="InstallService" BinaryKey ="ServiceInstall"
ExeCommand="CMD /C "[#ServiceInstall]""
Execute="immediate" Return="check" HideTarget="no" Impersonate="no"/>

WiX IIS Elements: If this is all IIS, I would avoid scripting and custom actions whenever possible and use WiX's built-in IIS elements. Here is a Web-installer sample from Rainer Stropek available on github.com.
Look for <iis:WebVirtualDir ... /> et al. Find the WiX Documentation here. I believe you should be able to accomplish what you need without too many custom actions.
DISM.exe: Stropek himself uses custom actions in another sample source to set up IIS using DISM.exe. Not sure I would do it like this (no other suggestions though), but it is a sample of custom actions and IIS.
Need-For-Speed: As to your installation performance issues. Maybe you need to suppress the creation of a restore point and limit file costing? The Windows Installer engine allows this - see link below. I doubt it will be very effective though. I think there must be something else wrong in your installer. Some timeout issue? It could relate to other custom actions, slow network, or some other issue. Can you elaborate on your deployment scenario?
In any case, here is some documentation on speeding up MSI installations in general. Essentially the MSIFASTINSTALL property is the only one I would recommend. DISABLEROLLBACK can cause genuine problems.
Logging: I normally recommend that setup developers enable default verbose MSI logging - as described in the "Globally for all setups on a machine" section, to always have a log file ready when you need one. It is created with a random name for every MSI operation in the TEMP folder and you sort by modify to get the latest one. The log file might give clues as to why the installation is slow - just determine what is really going on. Apologies if this is just obvious trifles and you have this set up already.
Manual log file creation:
msiexec.exe /i C:\Path\Your.msi /L*v C:\Your.log
Interpreting an MSI log: interpreting a log file can be challenging sometimes. Here is an answer with some links to help with this.

Service Installation & Control: You should not install services with scripts. There are built-in mechanisms in MSI that are vastly superior. You simply use the ServiceInstall and ServiceControl WiX XML Elements and "declare" how the service should be registered and when and how the service should be started and stopped:
<Component>
<File Source="$(var.SourceDir)\WindowsService.exe" />
<ServiceInstall Name="MyService" ErrorControl="normal" Start="auto" Type="ownProcess" />
<ServiceControl Id="MyService" Name="MyService" Start="install" Stop="both" Remove="uninstall" Wait="yes" />
</Component>
Look! No custom actions! :-) - Just MSI auto-magic. There is no need to use any custom actions for this. MSI is full-featured and reliable provided your service executable behaves as it is supposed to.
Let me link to a similar sample on github in case the above is not clear. It is more complete and elaborate.
VBScript: I wrote this before I saw you dealt with services. I'll just throw it in: to call a script that doesn't have a function you can try something like this:
<!-- The VBScript file -->
<Binary Id='Sample.vbs' SourceFile='Sample.vbs' />
<!-- The Custom Action -->
<CustomAction Id='Sample.vbs' VBScriptCall='' BinaryKey='Sample.vbs'
Execute='immediate' Return='ignore'/>
<!-- And Insert Into Installation Sequence -->
<InstallExecuteSequence>
<Custom Action='Sample.vbs' After='AppSearch'/>
</InstallExecuteSequence>
That should work for a script like this (Sample.vbs - no funtions, just an implicit main function):
MsgBox(Session.Property("ProductName"))
There is an answer on the topic of VBScript custom actions here: WIX installer execute vbscript from CustomAction.

Related

Invoking Data Protection API from within a CustomAction

I am trying to run some unmanaged code from a C# ca within WIX. I have a wrapper around the CryptProtectData function, this code works fine if run from an admin prompt. I have scheduled the ca as deferred hoping it would run during the elevated session however it fails in this context.
The scheduling is as follows:
<CustomAction Id="SetCustomActionDataValue" Property="EncryptValues" Value="foo=bar;...;" />
<CustomAction Id="EncryptValues" BinaryKey="InstallerCustomAction.CA.dll" DllEntry="EncryptValues" Execute="deferred" Impersonate="no" HideTarget="no" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetCustomActionDataValue" After="InstallFiles" />
<Custom Action="EncryptValues" After="SetCustomActionDataValue" />
</InstallExecuteSequence>
What needs to be done to allow something like this to run during the installer, preferably after the files and registry entries are committed to the system?
Do you have any logging in your CA? Can you confirm that your CustomActionData made it over?
One thing you might try is to wire this up as a simple console app and use PSExec to invoke a cmd.exe prompt as SYSTEM.
I've never used this API but the comment "Typically, only a user with the same logon credential as the user who encrypted the data can decrypt the data." makes me think that this might not run well as SYSTEM. This article seems to confirm that. http://www.obviex.com/samples/dpapi.aspx
FWIW, I'm not sure what you are trying to achieve but I'd consider moving it from the installer to the application if possible. Installers are generally best kept simple and reliable.

WiX/MSI: Redirect stdout to file, type 50 custom action

I have a WiX installer all rigged up to run SqlPackage.exe to deploy some installed .dacpac-packaged SQL applications to a database. Actually deploying the database files as follows will succeed:
<Property Id="CONNSTRING" Value="Data Source=localhost;Integrated Security=True;Initial Catalog=MPQS-DACPAC" />
<Property Id="SQLPACKAGEPATH" Value="C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe" />
<CustomAction Id="DeployDatabase" Property="SQLPACKAGEPATH"
ExeCommand='/Action:Publish /tcs:"[CONNSTRING]" /sf:"[#My.Database.dacpac]" /p:BackupDatabaseBeforeChanges=True /p:RegisterDataTierApplication=True'
Return="check" Execute="deferred" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="DeployDatabase" After="DuplicateFiles">NOT REMOVE</Custom>
</InstallExecuteSequence>
...and I can watch the output in the presented console window, mid-install.
However, it doesn't always succeed; for example, CONNSTRING can be specified in a dialog and may be incorrect. If there are errors, they appear for an instant, then the console closes and I get a 1722 error in the logs.
To capture the console output, I have tried:
<CustomAction Id="DeployDatabase" Property="SQLPACKAGEPATH"
ExeCommand='/Action:Publish /tcs:"[CONNSTRING]" /sf:"[#My.Database.dacpac]" /p:BackupDatabaseBeforeChanges=True /p:RegisterDataTierApplication=True > "[DBLogs]test.log"'
Return="check" Execute="deferred" Impersonate="yes" />
The > "[DBLogs]test.log" on the end should (theoretically) redirect output to a file at that location, but instead, the installer fails out at the moment the console window is shown. It appears that no text is displayed in the console in the instant it is presented.
The kicker is: I can copy the command that is logged with the error (with the > properly resolved to >), paste it into my own cmd window, and it will execute and log.
What am I doing wrong?
And more importantly: what can I do to execute this command and save stdout+stderr to a logfile?
Note: I have also tried this with the type 34 syntax (this way resolves to a type 50). Both exhibit the same behavior.
EXE custom actions have a number of concerns. Read:
Integration Hurdles for EXE Custom Actions
To address several of these issues, including stderr/stdout, Wix includes the Quiet Execution Custom Action.
I have had the same problem where I wanted to log the output of SqlPackage.exe during a WIX MSI Install, so I created a WIX binary extension that handles getting the standard output/error and logging it to a file for sqlpackage.exe.
Check it out at https://wixdacpacinstaller.codeplex.com/.
I made it free and open source.
Quick Snippet from the docs to show how to use it:
<!-- first, add the binary extension. Be sure to specify the source file as WixDacPacExtension.CA.dll. -->
<Binary
Id="WixDacPacExtensionBinary"
SourceFile="<Path to your file>\WixDacPacExtension.CA.dll"/>
<!-- Create a custom action to run first and set up all the parameters that are -->
<!-- passed to the Wix DacPac Extension. The property name MUST MATCH -->
<!-- the name of the custom action that executes the binary defined above. -->
<!-- The parameters in the Value property are semi-colon delimited. -->
<CustomAction
Id="SetupDacPacWIXDacPacInstallerExampleCustomAction"
Property="DacPacWIXDacPacInstallerExampleCustomAction"
Value="ShowUI=True;SqlPackagePath=c:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\SqlPackage.exe;DacPacPath=[INSTALLFOLDER]WIXDacPacInstallerExample.dacpac;LogFilePath=[TempFolder]\WIXDacPacInstallerExample.dacpac.log;TargetServerName=[DATABASESERVER];TargetDatabaseName=WIXDacPacInstallerExample;OtherParameters=/p:RegisterDataTierApplication=True /p:BlockWhenDriftDetected=False /p:BlockOnPossibleDataLoss=False"
/>
<!--
This custom action will execute the extension with the parameters from Step #1.
NOTE: the Id of this custom action matches the Property of the custom action
from Step #1.
-->
<CustomAction
Id="DacPacWIXDacPacInstallerExampleCustomAction"
BinaryKey="WixDacPacExtensionBinary"
DllEntry="Execute"
Execute="deferred"
Return="check"
/>
I believe there is something about SQLPackage.exe which routes the output (both standard and error) in a non-standard way. I encountered difficulties when running SQLPackage.exe from PowerShell and also had difficulties. No matter what I did, I could not get PowerShell to capture the output from SQLPackage.exe. Ultimately I was able to resolve the problem by using Start-Process cmdlet instead of Invoke-Expression to run SQLPackage.exe, and pass in -RedirectStandardOutput $out and -RedirectStandardError $errorLog. In this way I was at least able to capture the output, but I did notice that even when an error occurred, it wasn't redirected along with the Error redirect, but rather it was redirected to the standard output stream. I don't know exactly why this happens, but it seems like this would be relevant to the results you had in WiX.
I would love to see more about how you were able to incorporate SQLPackage into a WiX installation. Do you have any further information you can share, or resources on how you approached this?

'wix execute batch before uninstall with administrator

My WiX XML file installs app that contains windows-service named OLOLO_SERVICE (for example). I want to stop this service when installing/reinstalling my app.
I use CustomAction with ExeCommand='sc stop OLOLO_SERVICE'.
<CustomAction Id='EnsureThatServiceIsStopped' Directory='INSTALLLOCATION'
Impersonate="no" Execute="immediate" ExeCommand="sc
stop OLOLO_SERVICE" Return="ignore" />
Inside <InstallExecuteSequence> tag is action
<Custom Action='EnsureThatServiceIsStopped' Before='InstallValidate' />
But this doesn't works, uninstaller shows this window "For uninstallation continue you should stop following executables" (maybe not 100% correct, because in my russian Windows 7 it is written in russian).
I think that possible reasons for this are
script runs before admin rights taken (and stopping service fails because it needs admin privilegies)
script runs after validation (and validation fails when checking installed executables)
Plesae help me, I want to stop service using batch 'sc stop OLOLO_SERVICE'
PS. I decided to simplify a question: I want my WiX to execute 'sc stop OLOLO_SERVICE' with administrator privilegies and before checking for running applications
You don't need to do this in a batch file, you can use the ServiceControl element:
<ServiceControl Id="ServiceControl_OloService"
Name="OLOLO_SERVICE"
Stop="both"
Remove="uninstall"
Wait="yes" />

Set environment variable before running a custom action in WiX

I have to build an MSI-based installer using WiX and I need to set environment MY_HOME before running a command action.
I have a component:
<Component Id="SEMYHOME"
Guid="*my guid*">
<CreateFolder />
<Environment Id="MY_HOME"
Action="set"
Part="all"
Name="MY_HOME"
Permanent="no"
System="yes"
Value="[APPLICATIONPATH]myapp"/>
</Component>
Then I have a custom action:
<CustomAction Id="InstallMyService"
Directory="INSTALLDIR"
ExeCommand='"[INSTALLDIR]myapp\install_service.bat" install'
Execute="immediate"
Return="ignore"/>
<InstallExecuteSequence>
<Custom Action="InstallMyService"
After="InstallFinalize"/>
</InstallExecuteSequence>
NOTE: This action need the MY_HOME variable to be set before running.
When install this MSI, I got a log showing that the MY_HOME variable is set before running the custom action "InstallMyService", but the command to install my service still fails. I found that the cause is when command called, MY_HOME still not set.
After an install is finished, MY_HOME was set as expected, but the custom action fails :(
How can I fix this problem?
Windows Installer and Custom Actions are hosted via the Service Control Manager which has a long history of not respecting broadcast messages that are sent announcing Environment changes. So even if you fix the immeadiate / deferred problem that Yan mentions you'll find that your custom action still doesn't have the environment variable.
Why don't just just pass "[APPLICATIONPATH]myapp" to your .bat file and fetch it in as %2?
BTW I also don't reccomend calling batch files from an installer. It's fragile and embarrassing to see installs that run popping up little black windows.
You CA is immediate. This means that it runs immediately when Windows Installer is processing your MSI package. And this obviously happens before the component containing <Environment/> is installed. Modify it to be deferred (Execute="deferred") and schedule before InstallFinalize.

WiX - trying to figure out install sequences

I'm installing a large app, and part of it is a custom written tool called "DbUpdateManager" to mass execute SQL scripts against our target database.
Right now, the WiX 2.x install works - but it has one flaw: during install, I also install a couple of Windows services, which can be optionally started right away. Those however will fail, if the DbUpdateManager hasn't been run yet.
So what I'm trying to accomplish is this:
Install DbUpdateManager and my services from my MSI
Run DbUpdateManager BEFORE any of the services start up
My current WiX source looks something like this:
<Directory Id='INSTALLDIR' Name='DbUpdMgr' LongName='DbUpdateManager' >
<!-- DbUpdateManager component with the necessary files -->
<Component Id='DbUpdateManagerComponent' Guid='...' DiskId='1'>
<File Id='DbUpdateManagerFile' LongName='DbUpdateManager.Wizard.exe'
Name='DbUmWz.exe' src='DbUpdateManager.Wizard.exe' KeyPath='no' />
</Component>
<!-- Component to install one of my Windows services -->
<Component Id='InstallServiceComponent' Guid='...' DiskId='1'>
<File Id='InstallServiceFile' LongName='MyService.exe'
Name='MyServic.exe' src='MyService.exe' KeyPath='yes'/>
<ServiceInstall Id='InstallMyService' Name='MyService'
Description='My Service' ErrorControl='normal'
Start='auto' Type='ownProcess' Vital='yes' />
<ServiceControl Id='UninstallMyService' Name='MyService'
Remove='uninstall' Wait='yes' />
</Component>
<!-- Feature for the DbUpdateManager referencing the above component -->
<Feature Id='DbUpdateManager' ConfigurableDirectory='INSTALLDIR'
AllowAdvertise='no' Description='DbUpdateManager' Level='1'
Title='Database Update Manager'>
<ComponentRef Id='DbUpdateManagerComponent'/>
</Feature>
<!-- Custom action for running DbUpdateManager -->
<CustomAction Id='RunDbUpdateManagerAction' FileKey='DbUpdateManagerFile'
ExeCommand='' Return='asyncWait' />
<!-- Calling the custom action in the install sequence -->
<InstallExecuteSequence>
<RemoveExistingProducts After='InstallInitialize' />
<Custom Action='RunDbUpdateManagerAction'
After='InstallFinalize'>&DbUpdateManager=3</Custom>
I inherited this WIX, and it works - but as I said - the DbUpdateManager gets called too late in the process (only "After=InstallFinalize") and thus the services will fail to start up properly at first (the run fine the second time around when you restart them manually after DbUpdateManager has run).
I poked around the MSI documentation a bit and found a nice step called "StartServices", so my hunch was to just change my calling the custom action to this:
<InstallExecuteSequence>
<Custom Action='RunDbUpdateManagerAction'
Before='StartServices'>&DbUpdateManager=3</Custom>
Unfortunately, in this case, nothing at all happens - DbUpdateManager NEVER gets called....
Any ideas why? Debugging the MSI/WiX stuff is really really tricky, and I can't seem to see the forest for the trees anymore....
Thanks!
Marc
EDIT: The "RunDbUpdateManagerAction" is placed in the right position in the InstallExecuteSequence table in my MSI - right AFTER InstallServices and just BEFORE StartServices - and yet it doesn't work.... DbUpdateManager (a Winforms utility) does not show up during installation :-(
EDIT 2: now my action appears to be executed and at the right time - unfortunately, I'm just not seeing my wizard :-( What I'm seeing is an error code "return value 1631" which means something like "MSI Service could not be started" - wtf ???
MSI (s) (2C:D8) [20:53:36:383]: Doing action: RunDbUpdateManagerAction
Action 20:53:36: RunDbUpdateManagerAction.
Action started at 20:53:36: RunDbUpdateManagerAction.
MSI (s) (2C:D8) [20:53:36:383]: Doing action: StartServices
Action 20:53:36: StartServices. Services are being started
Action started at 20:53:36: StartServices.
Action finished at 20:53:36: RunDbUpdateManagerAction. Return value 1631.
Well, I finally got it working - with a bit of help from everyone who responded, and by consulting some of the WiX tutorials and help pages out there on the web. MSI installer stuff isn't easy to figure out and learn......
Basically, I changed execution of my custom action to "deferred" (as suggested by Rob) and I moved the point in the sequence where it gets executed to "After=InstallFiles". I also changed the condition in the <Custom> tag to "NOT Installed" which seems to work just fine in my scenario.
Contrary to Rob's fear, the Db Update Manager and its UI come up quite nicely this way, and the process of updating our database is now completed before any of our services (that depend on the database) get started.
Looking forward to a full RTM release of WiX 3.0 (and its future) !
Thanks to everyone - unfortunately, I could only accept one answer - all would have deserved it.
Marc
It appears that your CustomAction depends on the 'DbUpdateManagerFile' being installed. That means that your CustomAction needs to be scheduled after InstallFiles executes. Remember there are two passes to the InstallExecuteSequence. First, the "immediate" (or "scheduled" or "script generation") actions are executed to build the transaction log (aka: "install script"). Second, the "deferred" actions in the transaction log are executed.
Right now your CustomAction is "immediate" (the default) so it is trying to run before the files are actually copied to the machine. The InstallFiles action is in the script before your CustomAction but it hasn't been executed yet.
So, you need to mark your CustomAction "deferred" to get it to run after your files are installed.
Note: you are not probably not able to show UI from a deferred CA. I wasn't sure if this tool of yours was expected to show UI.
PS: sorry if I didn't explain that well, it's been a long day.
Try getting a log file of the Installation, and look for the sequence order in there and the value of the condition to perform the Custom Action
Use this in the command line:
msiexec /i [msiname] /l*v [filename]
EDIT: After reading your comment have a look at this page here you could try to add NOT INSTALLED in the condition
EDIT2: I found this page Search for your error Number 1631
You can open the .msi in Orca and look at the InstallExecuteSequence table to see what order things are actually happening in. This may give you a good idea of what's actually happening when.