Reputation: 91
I am deploying a product that contains encrypted fields in some config xml files. During installation, I am creating a Self-Signed Certificate on the target machine. Then I use it to encrypt some strings and finally I need to store those in a config file.
In order to create the Certificate I am bringing makecert.exe with me to the target installation directory. So, I need to wait until all files are copied to their destination before I can run my CustomAction which creates the Certificate. After this some other CustomAction is creating the encrypted values that need to be stored in the configs.
This works great, but at this point I would like to store the new values in my config files, but I have already missed the stage at which XmlFile/XmlConfig were executed. In the MSI log I see that the "ExecXmlConfig" action is performed right after the files are copied:
MSI (s) (6C:CC) [02:12:10:898]: Source for file 'makecert.exe' is compressed
InstallFiles: File: makecert.exe, Directory: C:\Program Files (x86)\MyProduct\InstanceFolder\, Size: 60240
MSI (s) (6C:CC) [02:12:10:900]: Executing op: SetTargetFolder(Folder=C:\Program Files (x86)\MyProduct\Nhibernate\)
MSI (s) (6C:CC) [02:12:10:900]: Executing op: SetSourceFolder(Folder=1\o1lebnnf\vemzkq_g\|MyProduct\Nhibernate\)
MSI (s) (6C:CC) [02:12:10:901]: Executing op: FileCopy(SourceName=nw4bpvhi.xml|hibernate.cfg.xml,SourceCabKey=nhibernate.config,DestName=hibernate.cfg.xml,....
MSI (s) (6C:CC) [02:12:10:902]: File: C:\Program Files (x86)\MyProduct\Nhibernate\hibernate.cfg.xml; Won't Overwrite; Won't patch; Existing file is unversioned but modified
MSI (s) (6C:CC) [02:12:10:902]: Executing op: SetTargetFolder(Folder=C:\Program Files (x86)\MyProduct\InstanceFolder\)
MSI (s) (6C:CC) [02:12:10:902]: Executing op: SetSourceFolder(Folder=1\o1lebnnf\|MyProduct\)
MSI (s) (6C:CC) [02:12:10:902]: Executing op: FileCopy(SourceName=7z64.dll,SourceCabKey=Seven7z64.dll,DestName=7z64.dll,Attributes=512,...
MSI (s) (6C:CC) [02:12:10:903]: File: C:\Program Files (x86)\MyProduct\InstanceFolder\7z64.dll; To be installed; Won't patch; No existing file
MSI (s) (6C:CC) [02:12:10:903]: Source for file 'Seven7z64.dll' is compressed
InstallFiles: File: 7z64.dll, Directory: C:\Program Files (x86)\MyProduct\InstanceFolder\, Size: 1484800
MSI (s) (6C:CC) [02:12:10:929]: Executing op: CacheSizeFlush(,)
MSI (s) (6C:CC) [02:12:10:929]: Executing op: ActionStart(Name=ExecXmlConfigRollback,,)
Action 02:12:10: ExecXmlConfigRollback.
MSI (s) (6C:CC) [02:12:10:948]: Executing op: CustomActionSchedule(Action=ExecXmlConfigRollback,ActionType=3329,Source=BinaryData,Target=ExecXmlConfigRollback,...
MSI (s) (6C:CC) [02:12:10:949]: Executing op: ActionStart(Name=ExecXmlConfig,,)
Action 02:12:10: ExecXmlConfig.
MSI (s) (6C:CC) [02:12:10:951]: Executing op: CustomActionSchedule(Action=ExecXmlConfig,ActionType=3073,Source=BinaryData,Target=ExecXmlConfig,CustomActionData=1?C:\Program Files (x86)\MyProduct\Nhibernate\hibernate.cfg.xml?3?0?/hibernate-configuration/session-factory/property[@name='connection.connection_string']????0)
So I'm stuck in between... If I configure my CustomAction to run After='InstallFiles'
it tries to execute long before the files are copied. (am I missing the right event?).
On the other hand, If I configure my Actions to run After='InstallFinalize'
- it's too late, because XmlConfig already fired and didn't write anything to the config file.
Ideally, I would like to run all of this in the very end of the installation.
It seems silly that I should need to write a CustomAction that does exactly the same operation like ExecXmlConfig just to call it at another time...
Here is my InstallExecuteSequence:
<InstallExecuteSequence>
<!-- Create and Register Certificate on Install -->
<Custom Action='GenerateProductCertificate' After='InstallFiles'><![CDATA[REMOVE<>"ALL"]]></Custom>
<Custom Action='RegisterProductServiceCertificate' After='GenerateProductCertificate'><![CDATA[REMOVE<>"ALL"]]></Custom>
<!-- Create and NHibernate Certificate -->
<Custom Action='GenerateNHibernateCertificate' After='RegisterProductServiceCertificate'><![CDATA[REMOVE<>"ALL"]]></Custom>
<!-- Configure NHibernate XML with Encrypted ConnectionString -->
<Custom Action='GenerateSecureConnectionString' After='GenerateNHibernateCertificate'><![CDATA[(REMOVE<>"ALL")]]></Custom>
</InstallExecuteSequence>
I am using WiX 3.7 (v3.7.1022.0, Monday, October 22, 2012)
My Questions are:
EDIT:
After a few more attempts and little more research I can define my problem more accurately. What I want to find out is: How to set an Output Value from a Deferred Action that will be available to the next Deferred Action?
My scenario forces me to run my Custom Actions in deferred mode, so I cannot access the Session to store any new Variables. But I am sure that more deferred actions are scheduled, so what I am trying to do is find a way to pass some variables to them.
Upvotes: 1
Views: 965
Reputation: 111
The same question arose with the msi that I am currently building.
How to set an Output Value from a Deferred Action that will be available to the next Deferred Action?
Bottom line is that I was unable to do this exactly. However, I did find a way around it with additional custom actions.
My situation is slightly different, but hopefully helpful. For this case, there are some registry entries that must be updated and I wish to save the previous values in case of a rollback. I cannot use the standard msi registry mechanism because the value names will be installation dependent (I do know the range of possible values).
My custom actions are c++ based code in a binary dll. My first thought was based on the assumption that once the dll was loaded it would remain and all calls in the deferred Execute Sequence would have access to any created data structures. So, I simply created a global variable and saved the necessary data to it. However, during the rollback call the struct was empty. It seems that before each call into the dll the dll is reloaded. I did not confirm this completely though I investigated it enough to see that DllMain was called multiple times.
The solution I finally came to was to create two additional custom actions that ran in the script generation round of the Execute Sequence, one each for install and for rollback. They scanned the registry and generated a delimited string that was saved as the CustomActionData for the respective install and rollback custom actions. The deferred custom actions where the work is done were rewritten to just do what they are told by through the CustomActionData. All the "logic" (well, almost all) is in the code that created the delimited strings.
This does seem to work. The only slight concern I have is now there is some delay and disassociation between the scan of the registry and the updating of the registry, which for aesthetic purposes I guess I was trying to avoid.
Not sure if you can do anything similar in your case. One thought would be to also use a binary dll to manage all of the work. It would also have custom actions that generate CustomActionData based on simulated output of the your first custom action that can then be read by your second one. Since your custom actions involve calling an external exe, you can do so with ShellExecute, System, CreateProcess, or similar. The question whether is can you get enough information without actually running the first custom action.
Upvotes: 1