How to write data to an installer on the server?

2019-06-11 09:08发布

问题:

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.



标签: inno-setup