How to open file from installation directory? - wix

Passing the PATH to custom action
<Property Id="ModifyConfig" Value="PATH=[PATH]"/>
<CustomAction Id='ModifyConfig'
BinaryKey="WebAppCA"
DllEntry="ModifyConfig"
Execute="deferred"
Return="check"
HideTarget="no"
Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action="ModifyConfig" After="InstallFiles"></Custom>
</InstallExecuteSequence>
My custom action
[CustomAction]
public static ActionResult ModifyConfig(Session session)
{
string PATH = session.CustomActionData["PATH"];
FileInfo file = new FileInfo(PATH + "appsettings.json");
session.Log("PATH: " + PATH);
session.Log("Search for configuration file " + file.FullName);
}
When custom action executes I get following in log:
PATH: C:\MyPath\
Search for configuration file C:\WINDOWS\Installer\MSIE38D.tmp-\C:\MyPath\appsettings.json
How it can be and how I can get the file?
EDIT: This works good - Execute="immediate" and After="InstallFinalize"
<CustomAction Id='ModifyConfig'
BinaryKey="WebAppCA"
DllEntry="ModifConfig"
Execute="immediate"
Return="check"/>
<InstallExecuteSequence>
<Custom Action="ModifyConfig" After="InstallFinalize"></Custom>
</InstallExecuteSequence>

Related

Custom Action to set INSTALLFOLDER path

So I created this customAction that gets me the path of another app using Registries
string value = Registry.GetValue(userRoot, key, -1).ToString();
session["INSTALLLOCATION"] = value;
And it works. The problem is how I send it back to the .wxs file and set the installation path of something to be that string value. I have this thing in the Wix file:
<CustomAction Id="CustomAction" Property="CustomAction2" Value="path=[INSTALLLOCATION]" />
<Binary Id="CustomActionBinary" SourceFile="$(var.ProAdmin_TargetDir)ExtractRegistryPath\bin\Debug\ExtractRegistryPath.CA.dll"/>
<CustomAction Id="CustomAction2" Impersonate="no" BinaryKey="CustomActionBinary" DllEntry="CustomAction1" Return="check" Execute="deferred"/>
<InstallUISequence>
<Custom Action="CustomAction" Before="CustomAction2" />
<Custom Action='CustomAction2' Before="ExecuteAction" />
</InstallUISequence>
This thing throws an 2762 error code.
You're getting error 2762 because you can't have deferred actions in the InstallUISequence. The custom action that populates the INSTALLLOCATION property should be called at the beginning of both the InstallUISequence and InstallExecuteSequence as an 'immediate' action - and - INSTALLLOCATION should be a folder in the Directory table.

Custom Actions not been called

I have some problems with managed code custom actions. I have 3 custom actions but only one of them is working. They are called at different times in InstallExecuteSequence but moving them makes no difference. I know there not getting very far because if I place a message box at the beginning of the routine(for debugging) it never get called . Where am I going wrong ? The actions are created like so.
<Binary Id="CA" SourceFile="$(var.ca.Custom.Actions.TargetDir)$(var.ca.Custom.Actions.TargetName).CA.dll" />
<CustomAction Id="WriteRemoveArpEntry" Property="CustomActionData" Value="PNAME=$(var.ProductName)" HideTarget="yes" />
<CustomAction Id="RemoveArpEntry" BinaryKey="CA" DllEntry="RemoveProductFromARP" Return="ignore" />
<CustomAction Id="SetValueforProductFolder" Property="CustomActionData" Value="SDIR=[INSTALLDIR];TDIR=[MANUDIR]\backup\$(var.ProductName)\$(var.VersionNumber)" HideTarget="yes" />
<CustomAction Id="Backup_Product_DIR" BinaryKey="CA" DllEntry="BackupDIR" Return="ignore" />
<CustomAction Id="WriteInstallAttemptData" Property="CustomActionData" Value="PRODUCTNAME=$(var.ProductName);APPVERSION=$(var.VersionNumber)" HideTarget="yes" />
<CustomAction Id="WriteInstallAttempt" BinaryKey="CA" DllEntry="WriteXMLServer" Return="ignore" />
I then call them here
<Custom Action="SetValueforProductFolder" Before="Backup_Product_DIR">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="Backup_Product_DIR" Before="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteRemoveArpEntry" Before="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="RemoveArpEntry" After="WriteRemoveArpEntry">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteInstallAttemptData" After="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteInstallAttempt" After="WriteInstallAttemptData">NOT Installed AND NOT REMOVE</Custom>
</InstallExecuteSequence>
The headers for the routines look like this
public static ActionResult BackupDIR(Session session)
{
public static ActionResult RemoveProductFromARP(Session session)
{
Public static ActionResult WriteXMLServer(Session session)
{
However only WriteXMLServer works. In the log file I get the following .
MSI (s) (BC:9C) [07:23:45:562]: Invoking remote custom action. DLL:
C:\Windows\Installer\MSI2E2A.tmp, Entrypoint: BackupDIR CustomAction
Backup_Product_DIR returned actual error code 1154 but will be
translated to success due to continue marking
In the one that works I get
MSI (s) (BC:A0) [07:24:25:994]: Invoking remote custom action. DLL:
C:\Windows\Installer\MSICC20.tmp, Entrypoint: WriteXMLServer SFXCA:
Extracting custom action to temporary directory:
C:\Windows\Installer\MSICC20.tmp-\ SFXCA: Binding to CLR version
v4.0.30319
The answer was very simple I forgot the
[CustomAction]
For the other two routines.

How to use a same DllEntry multiple times in custom action?

I have a CustomAction written in C#. I want to call the DllEntry twice in my wix installer code. How can I achieve this?
I am doing this in the following manner. But this is working for me. Is there any other fair way to do this?
C# Code:
[CumtomAction]
public static ActionResult SymbolicLink(Session session)
{
string s1=session.CustomActionData["value1"];
string s2=session.CustomActionData["value2"];
//mycode;
}
WixCode:
<CustomAction Id="ca" Property="dllCA" Value="value1='one';value2='two'" />
<CustomAction Id="dllCA" BinaryKey="InstallerLibrary" DllEntry="SymbolicLink" Execute="deferred"/>
<CustomAction Id="ca1" Property="dllCA1" Value="value1='three';value2='four'" />
<CustomAction Id="dllCA1" BinaryKey="InstallerLibrary" DllEntry="SymbolicLink" Execute="deferred"/>
<InstallExecuteSequence>
<Custom Action="ca" Before="InstallFinalize"></Custom>
<Custom Action="dllCA" After="ca"></Custom>
<Custom Action="ca1" Before="InstallFinalize"></Custom>
<Custom Action="dllCA1" After="ca1"></Custom>
</InstallExecuteSequence>
Defining it once should be suffice, ensuring you are setting the property correctly before you make the call to the SymbolicLink custom action:
<CustomAction Id="ca" Property="SymbolicLink" Value="value1='one';value2='two'" />
<CustomAction Id="ca1" Property="SymbolicLink" Value="value1='three';value2='four'" />
<CustomAction Id="dllCA" BinaryKey="InstallerLibrary" DllEntry="SymbolicLink" Execute="deferred"/>
<InstallExecuteSequence>
<Custom Action="ca" Before="InstallFinalize"></Custom>
<Custom Action="dllCA" After="ca"></Custom>
<Custom Action="ca1" Before="InstallFinalize"></Custom>
<Custom Action="dllCA" After="ca1"></Custom>
</InstallExecuteSequence>

can't pass properties to WiX custom action

I've read How do I pass msiexec properties to a WiX C# custom action? , but that didn't answer my question, or maybe I just don't see what I am doing wrong.
My install package fails to install, and the logs say that my property wasn't found in the custom actions collection. My code is:
<CustomAction Id="SetCustomActionDataValue" Return="check" Property="Itp.Configurator.WixCustomAction" Value="G=G2" />
<CustomAction Id="CreateDatabase" BinaryKey="Binary1" DllEntry="CreateDatabase" Execute="deferred" Return="check" />
<InstallExecuteSequence>
<Custom Action='SetCustomActionDataValue' After="InstallFiles"/>
<Custom Action='CreateDatabase' After="SetCustomActionDataValue">
NOT Installed AND NOT PATCH
</Custom>
</InstallExecuteSequence>
And code inside the custom action is:
string Property1 = session.CustomActionData["G"];
The name of the property in your first element must be exactly the same as the name of the deferred custom action you'd like to pass the value to. So, if the deferred action is CreateDatabase, then the first element should look like this:
<CustomAction Id="SetCustomActionDataValue" Return="check" Property="CreateDatabase" Value="G=G2" />

How do I pass msiexec properties to a WiX C# custom action?

I have an MSI file being created with Wxs 3.0. My MSI references a C# custom action, written using the new C# Custom Action project.
I want to pass an argument to msiexec that gets routed to my custom action - for example:
msiexec /i MyApp.msi ENVIRONMENT=TEST#
In my .wxs file, I refer to my custom action like this:
<Property Id="ENVIRONMENT"/>
<Binary Id="WixCustomAction.dll" SourceFile="$(var.WixCustomAction.Path)" />
<CustomAction Id="WixCustomAction" BinaryKey="WixCustomAction.dll" DllEntry="ConfigureSettings"/>
<InstallExecuteSequence>
<Custom Action="WixCustomAction" After="InstallFiles"></Custom>
</InstallExecuteSequence>
My C# custom action is set up like this:
[CustomAction]
public static ActionResult ConfigureSettings(Session session)
{
}
I was expecting to be able to access the property like this:
string environmentName = session.Property["ENVIRONMENT"];
but this doesn't seem to work.
How do I access the property I passed to msiexec in my custom action?
If instead of
<CustomAction Id="SetCustomActionDataValue"
Return="check"
Property="Itp.Configurator.WixCustomAction"
Value="[ENVIRONMENT],G2,[CONFIGFILE],[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />
you write this:
<CustomAction Id="SetCustomActionDataValue"
Return="check"
Property="Itp.Configurator.WixCustomAction"
Value="Environment=[ENVIRONMENT];G=G2;ConfigFile=[CONFIGFILE];TargetDir=[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />
then you will be able to reference your variables like this:
string env=session.CustomActionData["Environment"];
Just for completeness; using the method described by Jeremy Lew, in the blog above allows for the following:
Calling:
msiexec /i ITP.Platform.2.msi ENVIRONMENT=QA CONFIGFILE=EnvironmentConfig.xml
With this in the .wxs file:
<Property Id="ENVIRONMENT" Secure="yes" />
<Property Id="CONFIGFILE" Secure="yes" />
<Binary Id="Itp.Configurator.WixCustomAction.dll"
SourceFile="$(var.Itp.Configurator.WixCustomAction.Path)" />
<CustomAction Id="SetCustomActionDataValue"
Return="check"
Property="Itp.Configurator.WixCustomAction"
Value="[ENVIRONMENT],G2,[CONFIGFILE],[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />
<CustomAction Id="Itp.Configurator.WixCustomAction"
Return="check"
Execute="deferred"
BinaryKey="Itp.Configurator.WixCustomAction.dll"
DllEntry="ConfigureItpBrandSettings" />
<InstallExecuteSequence>
<Custom Action="SetCustomActionDataValue" After="InstallFiles"></Custom>
<Custom Action="Itp.Configurator.WixCustomAction" After="SetCustomActionDataValue"></Custom>
</InstallExecuteSequence>
With a custom action:
/// <summary>
/// CustomAction keys should be Environment,BrandId,ConfigPath,itpBasePath
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
[CustomAction]
public static ActionResult ConfigureItpBrandSettings(Session session)
{
string[] arguments = GetCustomActionDataArguments(session);
string environmentName = arguments[0];
string brandId = arguments[1];
string configPath = arguments[2];
string itpBasePath = arguments[3];
//Do stuff
return ActionResult.Success;
}
private static string[] GetCustomActionDataArguments(Session session)
{
string[] keys = new string[session.CustomActionData.Keys.Count];
session.CustomActionData.Keys.CopyTo(keys,0);
return keys[0].Split(',');
}
works.
Parsing the CustomActionData arguments is pretty ugly, but it does work. Hopefully someone knows a more elegant way to do this.
Here is my working code:
<Binary Id="MyCA" SourceFile="..\bin\ChainerRun.CA.exe" />
<CustomAction Id="SetCustomActionDataValue" Return="check" Property="CustomActionData" Value="TARGETDIR=[TARGETDIR];AA=Description;" />
<CustomAction Id="ReadAndSet"
BinaryKey="MyCA"
DllEntry="ReadAndSet"
Execute="immediate"
HideTarget="no"
Return="check" />
<InstallExecuteSequence>
<Custom Action="SetCustomActionDataValue" Before="InstallFiles" />
<Custom Action="ReadAndSet" After="SetCustomActionDataValue" />
</InstallExecuteSequence>
In the C# custom action function:
[CustomAction]
public static ActionResult ReadAndSet(Session session)
{
ActionResult retCode = ActionResult.NotExecuted;
System.Diagnostics.Debug.Assert(false);
session.Log("ReadAndSet() begins ...");
string installLocation = session.CustomActionData["TARGETDIR"];
string hostName = session.CustomActionData["AA"];
...
}
Your custom action needs to be a deferred custom action in order to run after InstallFiles. Deferred custom actions do not have access to properties, but they do have access to CustomActionData. See this blog post for a discussion on how to get what to do about it. (This example is a VBScript custom action, but you will be able to retrieve the value through the session.CustomActionData collection.)
If we're talking about Wix Sharp (and not plain Wix with its XML stuff), adding a custom property is a piece of cake. All you have to do is to set UsesProperties property for your managed action.
For example, if you want to add a custom property named "MYPROP", just define your action like this:
new ElevatedManagedAction(nameof(CustomActions.MyCustomAction))
{
Condition = Condition.Installed,
When = When.Before,
Step = Step.RemoveFiles,
Return = Return.check,
Execute = Execute.deferred,
UsesProperties = "MYPROP"
}
Set the property value via msiexec command line:
msiexec /i my.msi MYPROP=MYVALUE
And then you'll be able to access it from your custom action:
[CustomAction]
public static ActionResult MyCustomAction(Session session)
{
session.Log("MYPROP VALUE: " + session.CustomActionData["MYPROP"]);
return ActionResult.Success;
}
When property is not set via command line the default value will be an empty string.