This is not an answer to my question because it does not explain (in all three answers) how to deal with incompatibility of @Property and @BinaryKey. Moreover it answers to the "reverse" question: How to retrieve the property not how to "put" it.
I have created a C++ program that replaces in the given file one string by another.
I try to call it from a WiX installer:
<Directory Id="ConfigDir" Name="configs">
<Directory Id="GenesisDir" Name="genesis" />
</Directory>
...
<Binary Id="SetupDLLId" SourceFile="windows\custom.dll" />
<CustomAction
Id="SubstituteString"
BinaryKey="SetupDLLId"
DllEntry="MyCustomAction"
Execute="deferred"
Return="check"
Property="[ConfigDir]config.cfg;[GenesisDir]" />
<InstallExecuteSequence>
<Custom Action="SubstituteString" Before="InstallFinalize" Property="Subst" />
</InstallExecuteSequence>
but something does not work (WiX tools compilation fails). I don't know which attribute to use in place of erroneous Property
.
C:\blockchain-installer\windows\my.wxs(96) : error CNDL0022 : The CustomAction/@Property attribute cannot coexist with a previously specified attribute on this element. The CustomAction element may only have one of the following source attributes specified at a time: BinaryKey, Directory, FileKey, Property, or Script.
C:\blockchain-installer\windows\my.wxs(105) : error CNDL0004 : The Custom element contains an unexpected attribute 'Property'.
C++ source:
#include <windows.h>
#include <cstdlib>
#include <stdio>
extern "C" _declspec(dllexport) UINT __stdcall MyCustomAction(MSIHANDLE hInstall);
extern "C" UINT __stdcall MyCustomAction(MSIHANDLE hInstall)
{
DWORD dwSize=0;
UINT ret1 = MsiGetProperty(hInstaller, TEXT("CustomActionData"), TEXT(""), &dwSize);
if(ret1 != ERROR_MORE_DATA && ret1 != ERROR_SUCCESS) {
std::exit(1);
}
++dwSize; // include the terminating null character
TCHAR data = new TCHAR[dwSize];
UINT ret2 = MsiGetProperty(hInstaller, TEXT("CustomActionData"), data, &dwSize);
if(ret2 != ERROR_SUCCESS) {
std::exit(1);
}
size_t pos = data.find(';');
std::string filename = data.substr(0, pos);
std::string directory = data.substr(pos + 1);
std::string content;
std::ifstream file(filename);
std::string contents((std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>()));
std::string needle = "%GenesisDir%";
size_t pos2 = contents.find(needle);
contents.replace(pos2, needle.length(), directory);
std::ofstream file2(filename);
file2 << contents << std::flush;
delete[] data;
return ERROR_SUCCESS;
}
Please, describe the correct code in details.
If it is impossible to do with DLL, then guide how to do it with .exe
instead.