WiX unable to check property that is set using CustomAction - wix

I want to include different dll in the installation based on certain values. So, I am trying to load a component based on a property that is set using custom action.
In wxs file:
...
<Property Id="PropDllVersion" Value="0" />
...
<CustomAction Id="CheckPropDllVersion" BinaryKey="CustomAction1.dll" DllEntry="GetPropVersion" Return="ignore" Execute="immediate"/>
...
<InstallExecuteSequence>
<Custom Action="CheckPropDllVersion" After="ValidateProductID" />
</InstallExecuteSequence>
...
<Component Id="Test"
Guid="B81F832D-2D96-4169-9BD0-8D77098FEC60">
<Condition><![CDATA[PropDllVersion = "19"]]></Condition>
<File Id="File15"
Name="xyz.dll"
Vital="yes"
KeyPath="yes"
AssemblyManifest="File5"
AssemblyApplication="File5"
Assembly=".net"
DiskId="1"
/>
</Component>
...
Then in the custom action file:
[CustomAction]
public static ActionResult GetPropVersion(Session session)
{
session["PropDllVersion"] = "19";
}
I can see in the msi log file that this property is changed to 19, however the xyz.dll is not included in the installation. It looks like the PropDllVersion is not set at the condition level or am I doing something wrong... I tried to sequence it at many other places still it is not working...
If I use a global property in the condition instead of my property it works!

Please note that private properties (its name contains lowercase letters) use their default values in InstallExecuteSequence. So you should use a public property, for example PROP_DLL_VERSION.

Where else have you tried to sequence your custom action? You need to sequence it before the CostInitialize action.
Also, call the FileCost action following CostInitialize and the CostFinalize action then.
You can check out the documentation of the CostInitialize action here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa368050%28v=vs.85%29.aspx

Related

Wix Custom Action - session empty and error on deferred action

I am using Wix 3.11.1 with VS2017 Extension. I set a property from a control on a custom Dialogue and then try to execute an immediate custom action. When I try to read the session, it is always empty.
As recommended I changed my action to differed execution and used an immediate action to set my property. When I run my installer I get the error: "DEBUG: Error 2896: Executing action [ActionName] failed."
In CustomDialog.wxs
<Control Id="ConnId" Type="Edit" X="60" Y="110" Height="17" Width="300" Property="CONN"/>
<Control Id="installButton" Type="PushButton" Text="Continue" Height="15" Width="60" X="240" Y="260">
<Publish Event="DoAction" Value="RegistrationInfoCustomAction">1</Publish>
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
<Fragment>
<Binary Id="CustomActionBinary" SourceFile="..\..\CustomActions\bin\Debug\CustomActions.CA.dll"/>
<CustomAction Id="SetPropertyForShowProperty" Property="RegistrationInfoCustomAction" Execute="immediate" Value="[CONN]" Return="check" />
<CustomAction Id="RegistrationInfoCustomAction" BinaryKey="CustomActionBinary" DllEntry="SaveUserInfo" Execute="deferred" Return="check" HideTarget="no"/>
</Fragment>
In Product.wxs
<InstallExecuteSequence>
<Custom Action="SetPropertyForShowProperty" After="InstallInitialize"/>
<Custom Action="RegistrationInfoCustomAction" Before="InstallFinalize"/>
</InstallExecuteSequence>
In CustomActions.cs
[CustomAction]
public static ActionResult SaveUserInfo(Session session)
{
Debugger.Launch();
CustomActionData data = session.CustomActionData;
session.Log("Begin SaveUserInfo");
var connectionString = data["CONN"];
session.Log($"content: {connectionString}");
session.Log("End SaveUserInfo");
return ActionResult.Success;
}
The custom action works when it contains only logging statements but adding any other code make it fail. Also, the session is always empty.
In Installer Log:
MSI (c) (88:34) [16:30:21:255]: Invoking remote custom action. DLL: C:\Users\Andre\AppData\Local\Temp\MSIF1A3.tmp, Entrypoint: SaveUserInfo
MSI (c) (88:F8) [16:30:21:256]: Cloaking enabled.
MSI (c) (88:F8) [16:30:21:256]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (88:F8) [16:30:21:256]: Connected to service for CA interface.
Action ended 16:30:41: RegistrationInfoCustomAction. Return value 3.
DEBUG: Error 2896: Executing action RegistrationInfoCustomAction failed.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2896.
The arguments are: RegistrationInfoCustomAction, ,
Action ended 16:30:41: SetupDialog. Return value 3.
MSI (c) (88:8C) [16:30:41:911]: Doing action: FatalError
Similar Answers: I want to add some linkes to previous answers on the topic of deferred mode custom actions. There are links to github-samples in these answers - including one sample which uses the DTF class CustomActionData to easily send properties to deferred mode (once you are properly set up):
How to hide the value of customactiondata in logs of MSI?
Pass ConnectionString to Custom Action in WiX Installer (escape semicolon)
UPDATE: It is late, I didn't see this on first sight, but only immediate mode custom actions can be run from the setup GUI. Make a new, immediate mode custom action to set a value to your PUBLIC property CONN, and then set the value of CONN via a type 51 custom action to be assigned to the Id of the deferred mode custom action - as described below.
SecureCustomProperties: Add the property you specify to SecureCustomProperties by setting the Secure="yes" attribute:
<Property Id="MYPROPERTY" Secure="yes">Send this text to deferred mode</Property>
Name Match: the property name you assign the value to must match the deferred mode custom action Id. Looks OK in your source.
More Technical: the Property attribute's value of the type 51 action must be identical to the Id of the custom action that is consuming CustomActionData:
<!-- Declare property with value -->
<Property Id="MYPROPERTY" Secure="yes">Send this text to deferred mode</Property>
<!-- Type 51 CA: Send value of MYPROPERTY to the deferred mode CA named MyAction -->
<CustomAction Id="MyAction.SetProperty" Return="check" Property="MyAction" Value="[MYPROPERTY]" />
<!-- The deferred mode custom action -->
<CustomAction Id="MyAction" Return="check" Execute="deferred" BinaryKey="CAs" DllEntry="MyAction" />
<!-- ... -->
<!-- Inserting the CAs in sequence -->
<InstallExecuteSequence>
<Custom Action="MyAction.SetProperty" After="InstallInitialize" />
<Custom Action="MyAction" Before="InstallFinalize" />
</InstallExecuteSequence>
Here are some resources:
How to pass CustomActionData to a CustomAction using WiX?
https://www.firegiant.com/wix/tutorial/events-and-actions/at-a-later-stage/
How to access installer properties from deferred custom actions
Accessing or Setting Windows Installer Properties Through Deferred, Commit, and Rollback Custom Actions
Getting CustomActionData in deferred custom action
Just for debugging reference. And you can use: string data = session["CustomActionData"];
Tell you what, let me slide in the code to test using VBScript so you can use message boxes. Should not be necessary, just in case you have a debugging issue:
VBScript "Simple.vbs" (save as file):
MsgBox Session.Property("CustomActionData")
WiX markup change:
<Binary Id='Simple.vbs' SourceFile='Simple.vbs' />
<CustomAction Id="MyAction" Return="check" Execute="deferred" BinaryKey="Simple.vbs" VBScriptCall='' />
Just in case that is easier. Recommend you use VBScript for debugging only. I like VBScript to get a "heartbeat" when I want to eliminate all other error sources.

How to keep a config file when major upgrade in wix v3.8?

I want to keep a config file when the msi installer does a major upgrade. For the config file, I make a change when installing. The code is as follows:
<Component Id="MODIFYCONFIG" Guid="6A1D7762-B707-4084-A01F-6F936CC159CE" Win64="yes">
<File Id="Application.config" Name="Application.config" Vital="yes" KeyPath="yes" Source="Resource\Application.config"></File>
<util:XmlFile Id="SetValueIP" Action="setValue" Permanent="yes" File="[#Application.config]"
ElementPath="/configuration/applicationSettings/Application.Properties.Settings/setting[\[]#name='IpAddress'[\]]/value" Value="[IPADDRESS]" Sequence="1"/>
<util:XmlFile Id="SetValuePort" Action="setValue" Permanent="yes" File="[#Application.config]"
ElementPath="/configuration/applicationSettings/Application.Properties.Settings/setting[\[]#name='IpPort'[\]]/value" Value="[PORT]" Sequence="2"/>
<Condition>Not Installed</Condition>
</Component>
<Component Id="KEEPCONFIG" Guid="F7F173AA-C2FD-4017-BFBC-B81852A671E7" Win64="yes">
<RemoveFile Id="ApplicationConfig" Name="Application.config" On="uninstall"/>
<Condition>(REMOVE=ALL) AND (NOT UPGRADINGPRODUCTCODE)</Condition>
</Component>
But when a major upgrade occurs the file is not preserved. How can I preserve the modified file?
This solved it for me... config file is preserved with minor/major upgrade, and completely removed on uninstall.
Ref: Aaron Stebner: How to retain user-customized files during a Windows Installer major upgrade
EDIT: Summarized info from the linked page...
Each config file shall have it's own component, where the config file is marked as the keypath of the component. Unversioned file replacement logic will be used by the Windows Installer.
Add "RemoveExistingProducts" action after the "InstallFiles" action. New versions of all components are installed before removing the old MSI. When it's done in this sequence, the components will have their reference count incremented to 2, but the config files will not be replaced unless they are unmodified (because of unversioned file replacement logic). When the old MSI is removed, the reference count will be decremented back to 1, but the files will not be removed because the reference count are not 0.
You have 3 options when upgrading:
Make the config file component permanent. This will not un-install it, and you will be able to upgrade it, but removing it will be very difficult.
Use the Remember property pattern to store the config settings for the IP and PORT in the registry.
As part of the install, write the config file to a temporary filename and then use a CopyFile command to create the destination file. On upgrade check for the file using a FileSearch, and if it exists then don't copy. Only issue here is if the config file has changed you won't get the updated sections.
The best option is the remember me property as this has the least problems.
It took me a while, but here is how I solved it myself. It's probably a variation of caveman_dick's third option.
1) Add new action into UISequence to back up your current config file. You can do it with the magic of custom action and ComponentSearch to actually locate the file.
2) Restore the file later in ExecuteSequence.
<Binary Id="CustomActions.CA.dll" SourceFile="..\CustomActions\bin\$(var.Configuration)\CustomActions.CA.dll" />
<CustomAction Id="BackupConfigFile"
Return="check"
BinaryKey="CustomActions.CA.dll"
DllEntry="BackupFile" />
<CustomAction Id="RestoreConfigFile"
Return="check"
Execute="deferred"
Impersonate="no"
BinaryKey="CustomActions.CA.dll"
DllEntry="RestoreFile" />
<CustomAction Id="PropertyDelegator"
Property="RestoreConfigFile"
Value="MYTARGET=[MYTARGET];FILENAME_TO_BACKUP=[FILENAME_TO_BACKUP]" />
<Property Id="FILENAME_TO_BACKUP" Value="test.exe.config" />
<Property Id="PREVIOUS_PATH">
<ComponentSearch Id="evSearch" Guid="{010447A6-3330-41BB-8A7A-70D08ADB35E4}" />
</Property>
and here is quick CustomAction.cs I wrote:
[CustomAction]
public static ActionResult BackupFile(Session session)
{
try
{
// check out if the previous installation has our file included
// and if it does,
// then make copy of it.
var previousInstallationPath = session["PREVIOUS_PATH"];
var fileToBackup = session["FILENAME_TO_BACKUP"];
if (!string.IsNullOrEmpty(previousInstallationPath) && !string.IsNullOrEmpty(fileToBackup))
{
var absolutePath = Path.Combine(previousInstallationPath, fileToBackup);
if (File.Exists(absolutePath))
{
var destinationPath = Path.Combine(Path.GetTempPath(),
string.Concat(fileToBackup, _MODIFIER));
File.Copy(absolutePath, destinationPath);
}
}
}
catch (Exception e)
{
session.Log("Couldn't backup previous file: {0}", e);
}
return ActionResult.Success;
}
[CustomAction]
public static ActionResult RestoreFile(Session session)
{
try
{
// check if our CustomAction made backup of file,
// and if it indeed exists in temp path, then
// we basically copy it back.
var currentInstallationPath = session.CustomActionData["MYTARGET"];
var fileToRestore = session.CustomActionData["FILENAME_TO_BACKUP"];
var fileOriginalContentPath = Path.Combine(Path.GetTempPath(),
string.Concat(fileToRestore, _MODIFIER));
if (File.Exists(fileOriginalContentPath))
{
var destinationFile = Path.Combine(currentInstallationPath, fileToRestore);
if (File.Exists(destinationFile))
File.Delete(destinationFile);
File.Move(fileOriginalContentPath, destinationFile);
}
}
catch (Exception e)
{
session.Log("Couldn't restore previous file: {0}", e);
}
return ActionResult.Success;
}
to actually define sequences:
<InstallUISequence>
<Custom Action="BackupConfigFile" After="AppSearch"></Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="PropertyDelegator" Before="RestoreConfigFile" />
<Custom Action="RestoreConfigFile" After="InstallFiles"></Custom>
</InstallExecuteSequence>
haven't tested it thoroughly, but seems to do the job for now. Caveat: Temp folder might change?!
Alternatively there is this one that I found from Internet, but haven't tested it.
<!-- Support Upgrading the Product -->
<Upgrade Id="{B0FB80ED-249E-4946-87A2-08A5BCA36E7E}">
<UpgradeVersion Minimum="$(var.Version)"
OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
<UpgradeVersion Minimum="0.0.0"
Maximum="$(var.Version)" IncludeMinimum="yes"
IncludeMaximum="no"
Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>
<Property Id="OLDERVERSIONBEINGUPGRADED" Secure="yes" />
<!-- Action to save and Restore the Config-File on reinstall
-->
<!-- We're using CAQuietExec to prevent DOS-Boxes from
popping up -->
<CustomAction Id="SetQtCmdLineCopy" Property="QtExecCmdLine"
Value=""[SystemFolder]cmd.exe" /c copy
"[INSTALLDIR]MyApp.exe.config"
"[INSTALLDIR]config.bak"" />
<CustomAction Id="QtCmdCopy" BinaryKey="WixCA"
DllEntry="CAQuietExec" Execute="immediate" />
<CustomAction Id="SetQtCmdLineRestore"
Property="QtCmdRestore" Value=""[SystemFolder]cmd.exe" /c move
/Y "[INSTALLDIR]config.bak"
"[INSTALLDIR]MyApp.exe.config"" />
<CustomAction Id="QtCmdRestore" Execute="commit"
BinaryKey="WixCA" DllEntry="CAQuietExec" />
<!-- These actions will run only for a major upgrade -->
<InstallExecuteSequence>
<Custom Action="SetQtCmdLineCopy"
After="InstallInitialize"> NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
<Custom Action="QtCmdCopy"
After="SetQtCmdLineCopy">NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
<Custom Action="SetQtCmdLineRestore"
Before="InstallFinalize">NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
<Custom Action="QtCmdRestore"
After="SetQtCmdLineRestore">NOT (OLDERVERSIONBEINGUPGRADED =
"")</Custom>
</InstallExecuteSequence>
There is another option, but it may not be applicable to your scenario - it all depends on who is initially running your installer...
If your app is downloaded over the web for example, then we usually go with caveman_dick's remember property pattern.
However, we have a couple of suites of products that are always installed by our own installation staff who visit a clients site. In this instance, simply do not include the config file in the installer at all!
Put simply - if the installer doesn't know about a file, then it won't uninstall it!
In this case you have the option of your installation team creating and configuring the config file, or your app creating it when it doesn't exist, and asking the user for the values.
As stated this won't be an option in some scenarios, but it works fine for ours.
Add Schedule="afterInstallExecuteAgain" in the MajorUpgrade
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecuteAgain" />
It work for me

Custom property is empty

I have custom action which stores the file location into property at immediate phase.
Then I have another action at deffered phase, which reads this property and creates the file:
String configFilePath = session.CustomActionData["configPath"];
String configFileName = session.CustomActionData["configFile"];
...
Everything works so far.
At uninstall I would like to remove this file, so I have another deffered action, which executes after removing files. Action works, the problem is that property is empty(session.CustomActionData is empty).
Why? I have setted it at immediate phase also when uninstall is happening. And if I can read it at custom setUpConfig action, why I can't read it at custom removeConfig action?
I guess one option is to store this value into registry at installing phase and then read it from there when uninstalling. But I would like to know why i can set the property when installing the program, and why it is not setted at uninstalling. The action, which sets the proeprty is executed in both cases.
<CustomAction Id="ConfigFileLocation" Property="setUpConfig" Execute="immediate"
Value="configPath=[WEBSITE];configFile=config_template.asp" />
<CustomAction Id="setUpConfig" BinaryKey="MyCustomAction" DllEntry="configFile"
Execute="deferred" Impersonate="no" Return="check" />
<CustomAction Id="removeConfig" BinaryKey="MyCustomAction"
DllEntry="removeCustomFile" Execute="deferred" Impersonate="no" Return="check" />
<InstallExecuteSequence>
<Custom Action="ConfigFileLocation" After="LaunchConditions" />
<Custom Action="setUpConfig" Before="InstallFinalize"><![CDATA[NOT REMOVE="ALL"]] />
<Custom Action="removeConfig" After="RemoveFiles"><![CDATA[REMOVE="ALL"]] />
</InstallExecuteSequence>
To pass data to a deferred custom action you must set a Property with a name that matches the CustomAction/#Id. You are doing that correctly for the setUpConfig custom action via the immediate ConfigFileLocation custom action.
However, the removeConfig custom action does not have an immediate custom action setting a Property named removeConfig.
Ergo the removeConfig custom action's CustomActionData property is blank.

WIX: Giving Permissions to a folder

I've read all related topics and haven't found a full answer to my problem.
I would like to give full permissions to SYSTEM and Read & Execute permissions to Users group to a folder under Program Files. Nothing more, nothing less.
I know there are 3 ways to give permissions to a folder using WIX, none of them are really good for me and I'll explain why:
1) Regular Permission element:
<CreateFolder Directory="Test">
<Permission User="SYSTEM" GenericAll="yes"/>
<Permission User="Users" Domain="[LOCAL_MACHINE_NAME]"
GenericRead="yes" Read="yes" GenericExecute="yes" ChangePermission="yes"/>
</CreateFolder>
Problem: It fails on foreign OS since it doesn't knows the "Users" keyword. I tried it with SID as well. Beside that I need to place the Permission element under each file in the Test directory (but if this was the only case, I would have managed)
2) WixUtilsExtension PermissionEx element:
<CreateFolder Directory="Test">
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Users" Domain="[LOCAL_MACHINE_NAME]"
GenericRead="yes" Read="yes" GenericExecute="yes" ChangePermission="yes"/>
</CreateFolder>
Problem: The folder also keeps the default permissions of the Program Files folder. I can not allow that.
3) PermissionEx with Sddl:
Problem: This element is only available when installing with MSI 5.0. I'm using installer 3.01.
I'll be happy to get any solution, including solutions with custom actions...
I had this exact same issue and talked to Rob M about it. I was going to do Christian G's answer (https://stackoverflow.com/a/5296967/18475), but Rob suggested using WixQueryOsWellKnownSID (http://wix.sourceforge.net/manual-wix3/osinfo.htm) to get around non en-US locales.
In the .wxs file you add the following:
<PropertyRef Id="WIX_ACCOUNT_LOCALSYSTEM" />
<PropertyRef Id="WIX_ACCOUNT_USERS" />
And further down in the .wxs file where you want to apply the permissions it's just like this:
<Permission GenericAll="yes" User="[WIX_ACCOUNT_LOCALSYSTEM]" />
<Permission GenericRead="yes" GenericExecute="yes" User="[WIX_ACCOUNT_USERS]" />
Now when you run light, you just need to link WixUtilExtension.
light -ext WiXUtilExtension ...
NOTE: Depending on your version of WiX, this may not be fully supported. If it doesn't work for you, there may be other options you can use to translate SIDs.
Use the following code to accomplish this without a custom action. I've verified this works (also on child folders). Also the User Everyone is mapped on localized windows operating systems.
<CreateFolder>
<Permission User="Everyone" GenericAll="yes" ChangePermission="yes"/>
</CreateFolder>
Another option would be to have a simple CA that will just translate a msi property that contains the SID to the actual name of the group from the localized OS. The CA doesn't have to be deferred and it's not doing the actual work of setting the permissions.
Below is a sample of CA that reads the value of PROPERTY_TO_BE_TRANSLATED msi property and translates the msi property indicated by it. In this way you can run the CA to translate different msi properties.
[CustomAction]
public static ActionResult TranslateSidToName(Session session)
{
var property = session["PROPERTY_TO_BE_TRANSLATED"];
if (String.IsNullOrEmpty(property))
{
session.Log("The {0} property that should say what property to translate is empty", translateSidProperty);
return ActionResult.Failure;
}
var sid = session[property];
if (String.IsNullOrEmpty(sid))
{
session.Log("The {0} property that should contain the SID to translate is empty", property);
return ActionResult.Failure;
}
try
{
// convert the user sid to a domain\name
var account = new SecurityIdentifier(sid).Translate(typeof(NTAccount)).ToString();
session[property] = account;
session.Log("The {0} property translated from {1} SID to {2}", property, sid, account);
}
catch (Exception e)
{
session.Log("Exception getting the name for the {0} sid. Message: {1}", sid, e.Message);
return ActionResult.Failure;
}
return ActionResult.Success;
}
In WiX you define the properties to be translated using the SID for the accounts:
<Property Id="AdminAccount" Value="S-1-5-32-544" />
<Property Id="EveryoneAccount" Value="S-1-1-0" />
Create the CA that will set the PROPERTY_TO_BE_TRANSLATED property and then call the CA doing the translation:
<CustomAction Id="TranslateAdmin_SetProperty" Property="PROPERTY_TO_BE_TRANSLATED" Value="AdminAccount"/>
<CustomAction Id="TranslateAdmin" BinaryKey="CommonCustomActions" DllEntry="TranslateSidToName" Impersonate="no" />
<CustomAction Id="TranslateEveryone_SetProperty" Property="PROPERTY_TO_BE_TRANSLATED" Value="EveryoneAccount" />
<CustomAction Id="TranslateEveryone" BinaryKey="CommonCustomActions" DllEntry="TranslateSidToName" Impersonate="no" />
Don't forget to use the msi properties when setting the permissions:
<CreateFolder>
<Permission GenericAll="yes" User="[AdminAccount]" />
<Permission GenericRead="yes" GenericExecute="yes" User="[EveryoneAccount]" />
</CreateFolder>
Finally, schedule the CA before CreateFolder
<InstallExecuteSequence>
<Custom Action='TranslateAdmin_SetProperty' Before='TranslateAdmin' />
<Custom Action='TranslateAdmin' Before='CreateFolders' />
<Custom Action='TranslateEveryone_SetProperty' Before='TranslateEveryone' />
<Custom Action='TranslateEveryone' Before='CreateFolders' />
</InstallExecuteSequence>
In this way the CA is doing only some simple work, leaving the setting of permissions to the WiX element.
As the <Permission> element clears the inheritance of permissions from parent folders, you could try using a single <Permission> element for users "Everyone" or "Administrators" followed by <util:PermissionEx> elements to set permissions for user names that are not supported by the <Permission> element, for example:
<Permission User="Everyone" GenericRead="no" />
<util:PermissionEx User="Users" Domain="[LOCAL_MACHINE_NAME]" GenericRead="yes" Read="yes" GenericExecute="yes" ChangePermission="yes" />
It is not needed to explicitly set permission for SYSTEM, as these are added automatically by the installer.
you need to implement deferred custom action for changing permissions. c# custom action example:
[CustomAction]
public static ActionResult SetFolderPermission(Session session)
{
string folder = session.CustomActionData["Folder"].Trim('\"');
string sid = session.CustomActionData["SID"].Trim('\"');
System.Security.Principal.SecurityIdentifier sidID = new System.Security.Principal.SecurityIdentifier(sid);
System.Security.AccessControl.DirectorySecurity ds = System.IO.Directory.GetAccessControl(folder);
ds.AddAccessRule(new System.Security.AccessControl.FileSystemAccessRule(sidID
, System.Security.AccessControl.FileSystemRights.Write
, System.Security.AccessControl.InheritanceFlags.ObjectInherit
, System.Security.AccessControl.PropagationFlags.NoPropagateInherit
, System.Security.AccessControl.AccessControlType.Allow));
System.IO.Directory.SetAccessControl(folder , ds);
return ActionResult.Success;
}
you may port that on C++, custom action must be deferred - than you must access your session properties by CustomActionData

WiX: Passing Data to managed custom action does not work

i got a problem regarding wix and managed custom actions: I already managed it to create an managed custom action and got it called from my installer (verified it in the installation log files). My Problem is now that i did't manage it to pass data to the custom action. Afaik if i choose to run the custom action in such a late stage i must mark the action as "deferred". With this limitation it's only possible to access the CustomActionData Property right? Why does this not work in my case? (As far as i can see i adoptet everything correctly from the samples?)
Here is what i already tried:
Custom Action:
public class CustomActions
{
[CustomAction]
public static ActionResult RegisterDflHelp(Session session)
{
session.Log("Begin CustomAction1");
session.Log("Before Access to customactiondata");
//string helpdir = session["HELP_DIR"];
string cad = session["CustomActionData"];
Debugger.Break();
session.Log("Help dir is: " + helpdir);
session.Log("Custom Action Data is: " + cad);
return ActionResult.Success;
}
}
Merge Module which calls custom action:
<CustomAction Id='RegisterDflHelp' BinaryKey='RegisterDflHelpDll' DllEntry='RegisterDflHelp' Execute='deferred' />
<CustomAction Id="HELP_DIR.SetProperty" Return="check" Property="HELP_DIR" Value="Teeest" />
<Property Id='HELP_DIR' Secure='yes'/>
<InstallExecuteSequence>
<Custom Action='HELP_DIR.SetProperty' After='InstallFiles' />
<Custom Action='RegisterDflHelp' After='HELP_DIR.SetProperty' />
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
</Directory>
<ComponentGroupRef Id="HelpGroup"/>
Product which includes Help-Merge Module:
<Product....>
<Package....>
...
<Directory>
<!--Directory which gets the help folder--!>
<Merge Id ="DflHelpInstaller" SourceFile="DflHelpInstaller.msm" Language="1033" DiskId="1" />
Anyone an idea? Thanks in advance!!
Daniel
Btw: What would also be interesting to know: At which stage in the installationprocess must an action marked as deferred? And if i managed it to call my custom action before this stage. Whats the advantage?
You are still setting the HELP_DIR property instead of the custom action data. If you are trying to set the CustomActionData for the RegisterDfHelp action to "Teeest", then I think you should do this:
<CustomAction Id="SetRegisterDflHelpCustomActionData" Return="check"
Property="RegisterDflHelp" Value="Teeest" />
<InstallExecuteSequence>
<Custom Action='SetRegisterDflHelpCustomActionData' After='InstallFiles' />
<Custom Action='RegisterDflHelp' After='SetRegisterDflHelpCustomActionData' />
</InstallExecuteSequence>
somethimes using your head really helps;-)
the reason why the sample above does not work is quite simple:
Instead of:
CustomAction Id="HELP_DIR.SetProperty" Return="check" Property="HELP_DIR" Value="Teeest" />
I head to set the Property Attribute to the Action which should get the custom data!! here:
<CustomAction Id="HELP_DIR.SetProperty" Return="check" Property="RegisterDflHelp" Value="$(var.HelpSourceDir)" />
Hope that helps others to save the time i spend to solve this silly problem;-)