I am replacing an old installer with a new one using Wix 3.8.
The old installer inserts a line in the file %SYS32%\Drivers\Etc\Services and that seems simple. However I have not found any easy way to add new information to a text file using WiX. So I am thinking that maybe there is some other way to do this - i.e. not to use the services file, but to register the port using some other setting in Windows, for example to Write to the Registry which is simple with WiX.
Anyone know how to do this?
Otherwise, any tip on any Custom "FileWriter" for Wix? The only one I have found can only write using a template file and that is not what I want. I could of course write my own, but this feels like reinventing the wheel...
Thanks!
/Tomas
For writing to a file, you can create a dynamic-link library that includes the method which will do the things you want to be done (editing files...etc.). And then you can create a Type 1 custom action which calls a method from a dll file. Technically, a Type 1 action means authoring an unmanaged C/C++ dll (Windows Installer does not natively support .NET actions), but you can use C# Custom Action project (that comes with WiX extension) which creates a C/C++ dll from managed C# code.
aCustomAction.cs
using System;
using Microsoft.Deployment.WindowsInstaller;
namespace someLibrary
{
public class CustomActions
{
[CustomAction]
public static ActionResult MyFunction(Session session)
{
// do something
return ActionResult.Success;
}
}
}
ActionResult return type notifies the installer whether the action succeeded or failed. Please note that you can use 128 custom actions per CustomAction project (16 before Wix 3.6).
When the project is compiled, you will have a .dll and a .CA.dll file. You should reference the second one (which includes unmanaged code) in your wix project by adding the following lines to Product.wxs:
<Binary Id="aCustomActionDLL" SourceFile=".\aCustomAction.CA.dll" />
or you can directly reference from the project using $var.
Finally, you can use CustomAction element's BinaryKey and DllEntry attributes to specify method to call:
<CustomAction Id="CA_aCustomAction"
BinaryKey="aCustomActionDLL"
DllEntry="MyFunction"
Execute="deferred"
Return="Check" />
Then you schedule it to run:
<InstallUISequence>
<Custom Action="CA_aCustomAction" After="CostFinalize" />
</InstallUISequence>
The dll files will not be installed to user's computer; they perform their duty yet they stay inside the MSI package.
As an alternative you can create a full executable, then run it as a custom action (Type 2, Type 18 or Type 34). I recommend type 2 since it embeds the exe file into MSI:
<Binary Id="yourProgramEXE" SourceFile="source.exe" />
<CustomAction Id=""CA_ProgramExe"
BinaryKey= "yourProgramEXE"
Impersonate="yes"
Execute="deferred"
ExeCommand=""
Return=""check />
Impersonate tells the installer whether to impersonate the user who launched the installer or not. If it is no, you action will run as LocalUser. Immediate actions do not need impersonation and you will get an ICE68 warning if you set it to no in an immediate action. However, I strongly suggest to set them deferred so that if the installation fails. they can be rolled back.
Another alternative for very simple actions is a little embedded vbscript. Just the "CustomAction" and "InstallSequence" nodes needed, with the CustomAction looking like this:
<CustomAction Id="RestoreBackupDbFile" Script="vbscript" >
<![CDATA[
Set fso = CreateObject("Scripting.FileSystemObject")
path = fso.BuildPath(Session.Property("DATAFOLDER"), "Default")
if fso.FolderExists(path) then
dbp = fso.BuildPath(path, "TEST.db.bak")
if fso.FileExists(dbp) then
fso.CopyFile dbp, fso.BuildPath(path, "TEST.db"), true
fso.DeleteFile dbp
end if
end if
]]>
</CustomAction>
Note the use of Session.Property to get values from the executing MSI.
Related
I am using WIX Installer in which i have a custom action as below.
<CustomAction Id="ConfigureBiodentifyServer" FileKey="Biodentify.InstallUtil.exe" Execute="deferred" ExeCommand="/configurebiodentify /metadata="DefaultSetup.xml" /iskiosk="false"/databaseType="SQLServer" /sqlserver="[$(var.SqlServerName)]" /sqluser="" /sqlpw="" /domainName="[$(var.ComputerDomain)]" /domainPk="[$(var.DomainPk)]" " Impersonate="yes" Return="check" />
<InstallExecuteSequence><Custom Action="ConfigureBiodentifyServer" After="StartServices">NOT Installed And $(var.InstallType) = "SERVER" </Custom> </InstallExecuteSequence>
But the ExeCommand is giving error when i installed my Installer?
There are several levels of refactoring you should be considering. First, you are calling InstallUtil which means you are calling an Installer class custom action. This custom action should be refactored to WiX Deployment Tools Foundation (DTF) to take advantage of a better hosting pattern ( for example you can Get/Set Windows Installer properties instead of passing command line arguments ).
Also WiX has SQL extensions to handle SQL Scripts so you might be reinventing the wheel here.
Since I migrated to WiX I only can run custom actions from binaries that are inserted in the Binary table
<Binary Id="SetupActions.CA.dll"
src="..\SetupActions\bin\Release\SetupActions.CA.dll" />
But Visual Studio Setup Project used to use the installed binaries as the container of custom actions.
Is there any way to use the old way in WiX?
You mean that you want to run a custom action that references a function within a dll that is installed with the package? In this case use the custom action type 17. Or in WiX:
<CustomAction Id="myCAfromInstalledDLL" FileKey="IdOfFile.dll" ExeCommand="EntryPointInDll" />
I suppose you could try the following:
Create a custom action binary that you embed in your installer.
Your custom action binary can act as a wrapper and determine the location of the installed binaries and call the appropriate methods \ custom actions. A benefit is that you can check for the existence of the files and take appropriate action if they are missing.
Something like this:
CustomAction Id='FooAction' BinaryKey='FooBinary' DllEntry='FooEntryPoint' Execute='immediate'
Return='check'/
Binary Id='FooBinary' SourceFile='foo.dll'
with the Xml angle brackets edited out for SO.
It's that binarykey that means it gets extracted from the Binary table to be called.
Im using wix at the moment and have developed an installer. This installer calls a C++ custom action DLL. Both the DLL and the setup are building successfully but when i go to install it the installation ends with "The Installer Wizard ended Prematurely because of an error" Anyone know a possible answer?
This is the C++ Dll function:
extern "C" UINT __stdcall StopOrcService(MSIHANDLE hInstall)
Then i continue in under it with the code for the function. I then exported it using the def file.
<CustomAction Id='StopOrcService' BinaryKey='StopOrcService' DllEntry='StopOrcService' Execute='immediate' Return='check'/>
<InstallExecuteSequence>
<Custom Action='StopOrcService' After='ProcessComponents' />
</InstallExecuteSequence>
<Binary Id='StopOrcService' SourceFile='SetupDLL.dll' />
That is my wix code.
There are dozens of possible answers and it's hard to say without seeing your C++ and your WiX code.
Things to consider:
Was the C++ dll purposefully built as a custom action? ( Does it export a Type1 stdcall MsiCustom Action? )
What's the name of that function?
Is that function name correct in your WiX code?
Have you put any logging in your function to see if it got executed?
Update: You shouldn't be using a custom action to do something that the installer can do natively. Also when creating CA's that require elevation and/or change the state of the machine, they should always be scheduled as deferred with no impersonation not immediate. You have a bunch of reading to do on installer best practices to fix your strategic problems rather than bandied your tactical problem.
I have realized that i forgot to add the following line which made my code work perfectly. Hopefully this helps others.:)
#pragma comment(linker, "/EXPORT:StopOrcService=_StopOrcService#4")
I have an MSI which is going to install/update a driver. However I want to detect if the driver is running and shut it down before starting the install. I need to do this silently as the customers are using active directory to deploy to computers.
Using the WIX install scripts, how do I do this?
Not sure if I am missing something here but if you need to shut down a driver before installing, then a custom action would be the best approach, in my opinion.
You can write custom actions either by using VB scripts or calling into managed assemblies. I like the latter more (personal preference of C# instead of VB). The custom action in WiX source file would look like that:
Declare the binary element which represents the assembly to be called into:
<Binary Id="BIN_DriverManagement" SourceFile=".\DriverManagement.CA.dll />
Then define the custom action, calling the method of the assembly:
<CustomAction Id="CACT_ShutDownDriverAction" BinaryKey="BIN_DriverManagement" DllEntry="ShutDownDriver" />
The last step would be schedulling the custom action into the installation sequence:
<InstallExecuteSequence>
<Custom Action="CACT_ShutDownDriverAction" After="LaunchConditions" />
</InstallexecuteSequence>
Actually, I am not sure when such custom action should be scheduled, it depends on the requirements.
The custom action function, implemented in C# should be prepended with [CustomAction] attribute from the Microsoft.Deployment.WindowsInstaller namespace, so it might look like this:
[Microsoft.Deployment.WindowsInstaller.CustomAction]
public static ActionResult ShutDownDriver(Session session)
{
// Shut down the driver here.
}
I have a situation where I have been asked to re-implement and InstallShield installer in Wix. I am having difficulty with examples on the Internet trying to implement the exact behavior. What I need to do is the following:
When the user clicks the Install button on the VerifyReadyDlg (I am using Mondo) and the ProgressDlg comes up:
Call a .exe in the install location (if exists from a previous install)
Stop a service in the install location (if exists from a previous install)
Unregister the said service (if exists from a previous install)
Copy files from the MSI, including a copy of the service .exe to install location
Read data from some Edit controls on a previous dialog and write the data to some of the copied .xml configuration files that are now in the install location
Register the service again
On clicking the Finish button on the very final dialog (ExitDlg) I need to:
Start the service again
Call an .exe (different to the first one) in the install location
I can copy files and write data to some of the files after they have been copied...though Wix just does this...I don't tell it when to go and do it. The problem is I need to schedule the first .exe call, Service Stop and Unregister before the Copy action starts and I can only Register again AFTER the .xml config files have been updated.
I have been trying to use InstallExecuteSequence and ServiceInstall elements and tying them together with CustomActions, but it's not coming together and I think I am totally approaching this the wrong way...it's not worth posting what I have done.
How should I go about this? This is a common pattern that I have seen in InstallSheild scripts, yet I can't find any Wix examples doing this sequence.
Thanks a HUGE bunch for any help on this.
Michael.
You can control when your actions are scheduled using Before or After attributes of Custom element or the elements for standard actions.
Is the current installation made with MSI? If yes, you may want to do upgrade installation and schedule RemoveExistingProducts after you copy existing config but before other install actions are performed. This way the previous package will be removed with its own uninstall logic, and then you can install the updated package.
To save configuration from the previous version, you have to run an action before RemoveExistingProducts is executed.
You may wish to use a Custom Action that you define in C# from a project you create that outputs to a DLL file:
public static class CustomActions
{
[CustomAction]
public static ActionResult DoSomething(Session session) {
// Put C# code here that does what you describe, such as run an exe
// from the command prompt.
return ActionResult.Success;
}
}
Import this DLL into your Wix installer:
<!-- The custom action DLL itself.-->
<Binary Id="WebAppCA" SourceFile="$(var.SolutionDir)..\WebAppInstallCustomActions\bin\Debug\WebAppInstallCustomActions.CA.dll" />
<!-- The custom action that does something that you can use. -->
<CustomAction Id="DoSomething"
BinaryKey="WebAppCA"
DllEntry="DoSomething"
Execute="immediate"
Return="check" />
If you wanna check out a complete solution, see this thread on John Robbins' Blog (link)...he has a Wix installer that installs an IIS site but when you start it, it uses a Custom Action defined in C# to enumerate a list of web sites.