Question

I'm looking to change the build number (low-order of VersionLS) in the version info of an executable. So, I should read the VS_VERSIONINFO structure, change the build number and then update it back to the PE.

I'm working with this code as base: https://stackoverflow.com/a/7999813/1970843. This code works really well to change the VS_FIXEDFILEINFO data, but it doesn't changes (nor access) the StringFileInfo information.

I'm pretty sure I should include something in the VERSIONHEADER packed record to add the Children entry of VS_VERSIONINFO, but I don't know exactly how to do this. This is what I have so far:

type
    StringStruc = Packed Record
        wLength: Word;
        wValueLength: Word;
        wType: Word;
        //szKey: ?;
        //Value: ?;
    End;

    StringTable = Packed Record
        wLength: Word;
        wValueLength: Word;
        wType: Word;
        szKey: Array[0..8] Of WideChar;
        Children: StringStruc;
    End;

    StringFileInfo = Packed Record
        wLength: Word;
        wValueLength: Word;
        wType: Word;
        szKey: Array[0..14] Of WideChar;   // 'STRINGFILEINFO'
        Children: StringTable;
    End;

    VERSIONHEADER = Packed Record
        wLength: Word;
        wValueLength: Word;
        wType: Word;
        szKey: Array[0..16] Of WideChar;   // 'VS_VERSION_INFO'
        Version: VS_FIXEDFILEINFO;
        Children: StringFileInfo;
    End;

...

var VersionHandle, VersionRes: THandle;
    VersionSize: Cardinal;
    Version: Array Of AnsiChar;
    Ver: ^VERSIONHEADER;
Begin
    VersionSize := GetFileVersionInfoSize(PChar(sExe), VersionHandle);

    SetLength(Version, VersionSize);
    Ver := Pointer(Version);
    GetFileVersionInfo(PChar(sExe), 0, VersionSize, Ver);

So, the information seems to be coming correctly up to the first StringStruc. But since both szKey and Value aren't fixed size, I don't know how to correctly define my Packed Record (is it even possible?) to get those values. I'm also having troubles with the arrays... how can I define them? The way I'm doing, I'm just getting the first Children on each Struc. Notice that I'm ignoring the paddings... is this ok?

Any help is appreciated. Most of what I've done here was by trial and error, so I don't really understand what's going on.

PS: I'm still working on this, so I might update this post frequently.

Was it helpful?

Solution

I appreciate your attention and help. I found a ready solution here on SO. In fact, it's in the comments to the very question I've linked to (shame on me!).

It's based on a library by Colin Wilson. It uses pointer arithmetic to extract and write the information, so, the hard (maybe only) way. There's also a nice example on how to use the library by Jason Penny: SetVersion. Since I'm using D7, I downloaded Colin Wilson's library from here (under Resource Utilities), but a more recent version, using UnicodeString and nicer pointer arithmetic, is available here.

This is the actual implementation I have now:

uses ..., unitResourceVersionInfo, unitPEFile;

...

var VersionInfo: TVersionInfoResourceDetails;
    PEResModule: TPEResourceModule;
    VersionNumber: ULARGE_INTEGER;
    sVersion: String;
    I: Integer;
Begin
    PEResModule := TPEResourceModule.Create;
    Try
        PEResModule.LoadFromFile(sExe);

        For I := 0 To PEResModule.ResourceCount - 1 Do Begin
            If PEResModule.ResourceDetails[I] Is TVersionInfoResourceDetails Then Begin
                VersionInfo := (PEResModule.ResourceDetails[I] As TVersionInfoResourceDetails);
                Break;
            End;
        End;

        VersionNumber.LowPart := MakeLong(NewBuildNumber, HiWord(VersionInfo.FileVersion.LowPart));
        VersionNumber.HighPart := VersionInfo.FileVersion.HighPart;
        VersionInfo.FileVersion := VersionNumber;
        VersionInfo.ProductVersion := VersionNumber;

        VersionInfo.CodePage := $04e4;

        sVersion := Format('%d.%d.%d.%d', [HiWord(VersionInfo.FileVersion.HighPart), LoWord(VersionInfo.FileVersion.HighPart), HiWord(VersionInfo.FileVersion.LowPart), LoWord(VersionInfo.FileVersion.LowPart)]);
        VersionInfo.SetKeyValue('FileVersion', sVersion);

        PEResModule.SaveToFile(ChangeFileExt(sExe, '.exe2'));

    Finally
        FreeAndNil(PEResModule);
    End;
End;

The codepage line is due to a bug (I believe it is) in the library. It doesn't reads the codepage (comes 0), so, when you save back, it appears as 0.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top