I have a complicated situation.
There is a downloadable link to my installer on the web.
Before anyone downloads the installer they have to fill out a form in the browser.
I would like to save the information filled out in the form in the installer itself.
How could I accomplish this?
*Compiling the installer on the server each time anyone fills out the form is not an option because the installer is very heavy and this process would take a lot of time.
*Creating the form in the installer is not an option.
*I only need to save a single number in my executable.
问题:
回答1:
Its an interesting question. I'm with TLama on this one. When the installer is already build, your only chance is to modify the application / executable resources.
A tool like ResourceHacker might help in automating this task. http://angusj.com/resourcehacker/resource_hacker.zip
On the server-side you might use PHP to accept the form data and then forward them to ResourceHacker executed with wine and from PHP, e.g.:
exec("wine ResourceHacker.exe -script ScriptFile");
.
You have some options on how to pass the data from the form to ResourceHacker: cli argument, from a file, etc. For automation on server-side, i suggest to use a ScriptFile.
A ScriptFile
could start like...
[FILENAMES]
Exe=Installer.exe
SaveAs=ModifiedInstaller.exe
Log=file.log
[COMMANDS]
-modify ResourceSrc, ResourceMask
In order to find the element to change, you can use the ResourceHacker GUI on a Windows system and play around until it works, then alter the script for automation on the server-side accordingly.
Ok, after explaining how to modify a resource in general lets go into details:
Like i pointed out, its also possible to use a language string for this and change it, but i will give step-by-step instructions for inserting a new field in the VERSION_INFO section of an executable. For testing purposes i work on
\innosetup\Examples\MyProg.exe
Our goal is to add a new VALUE "PrivateBuild" with kind of a serial number.
(According to https://msdn.microsoft.com/de-de/library/windows/desktop/aa381049(v=vs.85).aspx, there is also "Comments" and "SpecialBuild" to enter information.)
1. Extract Version Info from MyProg.exe into VersionInfo.rc
ResourceHacker.exe -extract MyProg.exe, VersionInfo.rc, versioninfo,,
The content of VersionInfo.rc
looks like this:
1 VERSIONINFO
FILEVERSION 1,5,0,0
PRODUCTVERSION 1,5,0,0
FILEOS 0x4
FILETYPE 0x0
{
BLOCK "StringFileInfo"
{
BLOCK "040904b0"
{
VALUE "CompanyName", "My Company"
VALUE "FileDescription", "My Program"
VALUE "FileVersion", "1.5.0.0"
VALUE "InternalName", "MyProg"
VALUE "LegalCopyright", "Copyright (C) My Company"
VALUE "OriginalFilename", "MyProg.exe"
VALUE "ProductName", "My Program"
VALUE "ProductVersion", "1.5"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409, 0x04B0
}
}
2. Modify Version Info
We add the following line to VersionInfo.rc
VALUE "PrivateBuild", "123-123-123"
(Later: modify the file with PHP. Probably preg_match for the line containing ProductVersion and append a new line followed by the value line.)
The new content of VersionInfo.rc looks like this:
1 VERSIONINFO
FILEVERSION 1,5,0,0
PRODUCTVERSION 1,5,0,0
FILEOS 0x4
FILETYPE 0x0
{
BLOCK "StringFileInfo"
{
BLOCK "040904b0"
{
VALUE "CompanyName", "My Company"
VALUE "FileDescription", "My Program"
VALUE "FileVersion", "1.5.0.0"
VALUE "InternalName", "MyProg"
VALUE "LegalCopyright", "Copyright (C) My Company"
VALUE "OriginalFilename", "MyProg.exe"
VALUE "ProductName", "My Program"
VALUE "ProductVersion", "1.5"
VALUE "PrivateBuild", "123-123-123"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409, 0x04B0
}
}
3. Compile VersionInfo
windres -i VersionInfo.rc -o VersionInfo.res -O res
Now the VersionInfo text is a resource again.
4. Insert resource into exe
ResourceHacker.exe -script ScriptFile.rh
where ScriptFile.rh
contains
[FileNames]
Exe=MyProg.exe
SaveAs=MyProgNew.exe
Log=MyProg.log
[Commands]
-delete VERSIONINFO,1,1033
-add VersionInfo.res, VERSIONINFO,1,1033
Let's check the log:
[08 Sep 2015, 11:21:33]
[FileNames]
Exe=MyProg.exe
SaveAs=MyProgNew.exe
Log=MyProg.log
[Commands]
-delete VERSIONINFO,1,1033
Deleted: VERSIONINFO,1,1033
-add VersionInfo.res, VERSIONINFO,1,1033
Added: VERSIONINFO,1,1033
Done.
Ok... new VERSIONINFO was inserted.
5. How to use or extract the value from InnoSetup?
InnoSetup provides only a preprocessor function called GetStringFileInfo() so one can not use
#define SERIAL GetStringFileInfo("path/to/MyProgNew.exe", "PrivateBuild")
And so we have to find a workaround to access the info and that works probably using the WinAPI. Here is one way to do it, which has some room for improvement. Its written by El Sanchez over at OsZone.net.
[Code]
#ifdef UNICODE
#define A "W"
#else
#define A "A"
#endif
function GetFileVersionInfoSize(lptstrFilename: String; lpdwHandle: Integer): Integer;
external 'GetFileVersionInfoSize{#A}@version.dll stdcall delayload';
function GetFileVersionInfo(lptstrFilename: String; dwHandle, dwLen: Integer; var lpData: Byte): Boolean;
external 'GetFileVersionInfo{#A}@version.dll stdcall delayload';
function VerQueryValue(var pBlock: Byte; lpSubBlock: String; var lplpBuffer: DWord; var puLen: Integer): Boolean;
external 'VerQueryValue{#A}@version.dll stdcall delayload';
function GetFileVerInfo(FileName, VerName: String): String;
//VerName:
//Comments, LegalCopyright, CompanyName, FileDescription, FileVersion, ProductVersion,
//InternalName, LegalTrademarks, OriginalFilename, ProductName, PrivateBuild, SpecialBuild
var
dwLen, puLen, i: Integer;
lpFileVerInfo: array of Byte;
lplpBufferCP, lplpBufferVN: DWord;
LangCodepage: String;
begin
Result := '';
if FileExists(FileName) then
begin
dwLen := GetFileVersionInfoSize(FileName, 0);
if dwLen > 0 then
begin
SetArrayLength(lpFileVerInfo, dwLen);
if GetFileVersionInfo(FileName, 0, dwLen, lpFileVerInfo[0]) then
begin
if VerQueryValue(lpFileVerInfo[0], '\VarFileInfo\Translation', lplpBufferCP, puLen) then
begin
LangCodepage := Format('%.2x%.2x%.2x%.2x', [lpFileVerInfo[(dwLen div 2)-5], lpFileVerInfo[(dwLen div 2)-6], lpFileVerInfo[(dwLen div 2)-3], lpFileVerInfo[(dwLen div 2)-4]]);
if VerQueryValue(lpFileVerInfo[0], Format('\%s\%s\%s', ['StringFileInfo', LangCodepage, VerName]), lplpBufferVN, puLen) then
begin
i := (dwLen div 2) + lplpBufferVN - lplpBufferCP - 6;
repeat
if lpFileVerInfo[i] <> 0 then
begin
SetLength(Result, Length(Result)+1);
Result[Length(Result)] := Chr(lpFileVerInfo[i]);
end;
i := i + 1;
#ifdef UNICODE
until i > (dwLen div 2) + lplpBufferVN - lplpBufferCP - 8 + puLen;
#else
until lpFileVerInfo[i] = 0;
#endif
end;
end;
end;
end;
end;
end;
then GetFileVerInfo(ExpandConstant('{srcexe}'), "PrivateBuild");
6. Sign the installer
Will updating my resources ruin my code signing of the installer?
Yes, the insert will change the executable. You will have to sign it after the modification. Use an unsigned installer beforehand, then insert, then sign (on the server).
--
You can execute all steps with PHP on server-side.
You need ResourceHacker.exe
and windres.exe
on the server and wine
for executing them.
回答2:
Rather than trying to hack/modify the compiled executable, how about writing whatever values from the web form that you want to include in the installer to an INI file, which you then include with your Setup.exe download as Setup.ini and use the GetIniString
function to read these in as strings in the installer?
Your INI file could be as simple as:
[SETUP]
Value=Number
Alternatively, you could use either the LoadStringFromFile
or LoadStringsFromFile
functions and any file format you want. Personally, I would go with the INI file and the GetIniString
function if you decide on this method.