Search code examples
inno-setuppascalscript

Writing binary file in Inno Setup


How does one write to a binary file in Inno Setup script? It's a configuration file I want to edit in the PrepareToInstall step. The problem is that I'm looking at the support functions:

TStream = class(TObject)
  function Read(Buffer: String; Count: Longint): Longint;
  function Write(Buffer: String; Count: Longint): Longint;
  function Seek(Offset: Longint; Origin: Word): Longint;
  procedure ReadBuffer(Buffer: String; Count: Longint);
  procedure WriteBuffer(Buffer: String; Count: Longint);
  function CopyFrom(Source: TStream; Count: Longint): Longint;
  property Position: Longint; read write;
  property Size: Longint; read write;
end;

And it seems even the most basic write function writes strings. Should I just do it in a batch script?


Solution

  • Consider the string in the TStream interface as a buffer of chars/bytes.

    It's bit more complicated with Unicode version of Inno Setup, where the string is an array of 2-byte chars (comparing to legacy Ansi version, as there one byte equals one char – Though as of Inno Setup 6, Unicode is actually the only version available anyway).

    See also Read bytes from file at desired position with Inno Setup.

    To convert a hex string to the actual binary data, you can use the CryptStringToBinary Windows API function.

    The following code works both in Ansi and Unicode version of Inno Setup.

    #ifndef Unicode
    const CharSize = 1;
    #define AW "A"
    #else
    const CharSize = 2;
    #define AW "W"
    #endif
    
    function CryptStringToBinary(
      sz: string; cch: LongWord; flags: LongWord; binary: string; var size: LongWord;
      skip: LongWord; flagsused: LongWord): Integer;
      external 'CryptStringToBinary{#AW}@crypt32.dll stdcall';
    
    const
      CRYPT_STRING_HEX = $04;
    
    procedure WriteHexToFile(Hex: string; FileName: string);
    var
      Stream: TFileStream;
      Buffer: string;
      Size: LongWord;
    begin
      Stream := TFileStream.Create(FileName, fmCreate);
    
      try
        SetLength(Buffer, (Length(Hex) div 2*CharSize) + CharSize - 1);
        Size := Length(Hex) div 2;
        if (CryptStringToBinary(
              Hex, Length(Hex), CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
           (Size <> Length(Hex) div 2) then
        begin
          RaiseException('Error decoding hex string');
        end;
    
        Stream.WriteBuffer(Buffer, Size);
      finally
        Stream.Free;
      end;
    end;
    

    Use it like:

    procedure WriteHexToFileTest;
    var
      Hex: string;
    begin
      Hex :=
        '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' + 
        '202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f' + 
        '404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f' + 
        '606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f' + 
        '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f' + 
        'a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf' + 
        'c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf' + 
        'e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff';
    
      WriteHexToFile(Hex, 'my_binary_file.dat');
    end;