Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   API de Windows (https://www.clubdelphi.com/foros/forumdisplay.php?f=7)
-   -   Cambiar datos (en runtime) de la versión de un ejecutable (https://www.clubdelphi.com/foros/showthread.php?t=84173)

dec 17-09-2013 21:55:49

Cambiar datos (en runtime) de la versión de un ejecutable
 
Hola a todos,

A ver si alguien que, sin duda, sepa más que yo de estos menesteres, es capaz de echarme una mano. Quisiera cambiar los datos de la versión de un ejecutable en tiempo de ejecución. No se trata de cambiar los datos del mismo ejecutable en ejecución sino de otro que utilizo a modo de "plantilla".

He buscado por internet bastante, y, al cabo he encontrado hasta tres posibles implementaciones, pero, por distintos motivos, ninguna de ellas me sirve. Y, como no entiendo muy bien los intríngulis del asunto, pues tampoco soy capaz de adaptar ninguna de estas implementaciones para lograr lo que quiero.

El código fuente que mejor parece funcionarme, por diversas razones, es el que puede encontrarse en este mensaje del Borland Newsgroup Archive:

Código:

type
  PLangAndCodePage = ^TLangAndCodePage;

  TLangAndCodePage = packed record
    wLanguage: Word;
    wCodePage: Word;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: String;
  Size, Unused: DWORD;
  VerData: PByte;
  Value: Pointer;
  Len, VerLen, I: UINT;
  Translate: PLangAndCodePage;
  KeyName: WideString;
  FileVersion: LPWSTR;
  hUpdate: THandle;
  Updated: BOOL;
begin
  FileName := 'C:\program\myprog.exe';
  Size := GetFileVersionInfoSize(PChar(FileName), Unused);
  if Size <> 0 then
  begin
    GetMem(VerData, Size);
    try
      if GetFileVersionInfo(PChar(FileName), 0, Size, VerData) then
      begin
        Value := nil;
        Len := 0;
        VerQueryValue(VerData, '\', Value, Len);

        with PVSFixedFileInfo(Value)^ do
        begin
          dwFileVersionMS := MAKELONG(1, 1);
          dwFileVersionLS := MAKELONG(1, 1);
        end;

        Value := nil;
        Len := 0;
        VerQueryValue(VerData, '\VarFileInfo\Translation', Value, Len);

        Translate := PLangAndCodePage(Value);
        for I := 0 to (Len div SizeOf(TLangAndCodePage)) - 1 do
        begin
          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\FileVersion';

          Value := nil;
          VerLen := 0;

          // calling VerQueryValueW() directly so that the returned pointer is
          // pointing at the original Unicode data and not an Ansi copy...
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            FileVersion := PWideChar(Value);
            ZeroMemory(FileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(FileVersion, '1.1');

            // I'll leave it as an exercise for you to figure out what to do when your
            // new string value is larger than VerLen says is available. You will have
            // to reallocate VerData to make room for the extra characters, and
            // reassign various length fields to account for new offsets and such ...
          end;

          Inc(Translate);
        end;

        hUpdate := BeginUpdateResource(PChar(FileName), FALSE);
        if hUpdate <> 0 then
        begin
          Updated := UpdateResource(hUpdate, RT_VERSION,
            MAKEINTRESOURCE(VS_VERSION_INFO), MAKELANGID(LANG_NEUTRAL,
            SUBLANG_NEUTRAL), VerData, Size);
          EndUpdateResource(hUpdate, not Updated);
        end;
      end;
    finally
      FreeMem(VerData);
    end;
  end;
end;

Como se ve, ahí se trata de cambiar el campo "FileVersion" de la "tabla" con información de la versión. De hecho el código funciona estupendamente, empero, al tratar de cambiar otro campo como "ProductName", comienzan los problemas. El propio autor del código lo dice en un comentario:

Cita:

I'll leave it as an exercise for you to figure out what to do when your
new string value is larger than VerLen says is available. You will have
to reallocate VerData to make room for the extra characters, and
reassign various length fields to account for new offsets and such ...
En efecto, si uno trata de cambiar el campo "ProductName", mal que bien, el código sigue funcionando, pero, ya entramos en lo que no llego a comprender, y, por lo tanto, a poco que la cadena que trate de poner en "ProductName" sea más larga que la existente, todo se va al garete, pues la tabla con información de la versión se "sobreescribe", llegando a quedar completamente inválida.

Claro está que eso es justamente lo que el autor del código fuente dice que pasará si se trata de asignar una cadena mayor a la existente, pero, ¿cómo hacer para conseguirlo? El autor explica más o menos lo que hay que hacer: "You will have to reallocate VerData to make room for the extra characters, and reassign various length fields to account for new offsets and such..."... pero yo no logro entender qué tengo que hacer, exactamente, ni dónde ni cómo hacerlo.

¿Alguien tiene alguna idea? Yo, de forma chapucera, he intentado asignar, en tiempo de diseño, cadenas de caracteres "largas" a todo los campos de la información de la versión. Sin embargo, no he conseguido que esto funcione. Tal vez tenga que ver con que se trate de cadenas "variables", es decir, igual hay que "recolocar la memoria" siempre que se trate de cadenas variables, porque, al fin y al cabo el ejemplo de arriba cambia un campo de "longitud fija" (creo).

¿Alguien tiene alguna idea y quiere compartirla aquí? :o

P.D. Digo que la de arriba es la implementación que más me ha convencido porque es la más autocontenida. Las otras dos (código fuente de InnoSetup y las "utilidades para recursos" de Colin Wilson) no he podido hacerlas funcionar, por no decir que su código está más repartido en diferentes unidades, etc. Sin embargo todo esto es debido a mi desconocimiento, sin duda, puesto que tanto InnoSetup como XN Resource Editor (de Colin Wilson) son capaces de cambiar la información de la versión de un ejecutable...

javier14 26-10-2023 14:55:18

Conseguiste solucionarlo???

dec 26-10-2023 17:09:47

Hola a todos,

Sí; utilizando la siguiente función, si bien, probándola, he visto que todo parece ir bien excepto el "FileVersion"... parece que esa "clave" en particular no se cambia y no sé bien porqué ahora mismo... en fin... espero que te pueda servir.

Código Delphi [-]
(*
  Important about this method:

  This method can update the executable version information WHICH IS ALREADY
  SET FROM THE EXECUTABLE'S PROJECT'S OPTIONS!!!!

  What we do is to set a dummy 255 empties characters in every version
  information key, so, we can update it here up to 255 characters that keys.

  This is the only way in which we get this working!!
*)
function UpdateExeInfo(const ExePath, CompanyName, FileDescription,
     FileVersion, InternalName, LegalCopyright, LegalTrademarks, OriginalFileName,
      ProgramID, ProductName, ProductVersion, Comments: string): Boolean;
type
  PLangAndCodePage = ^TLangAndCodePage;
  TLangAndCodePage = packed record
    wLanguage: Word;
    wCodePage: Word;
  end;
var
  Size, Unused: DWORD;
  VerData: PByte;
  Value: Pointer;
  Len, VerLen, I: UINT;
  Translate: PLangAndCodePage;
  KeyName: WideString;
  AFileVersion: LPWSTR;
  hUpdate: THandle;
  Updated: BOOL;
begin
  Result := False;
  if not FileExists(ExePath) then
    Exit;

  Size := GetFileVersionInfoSize(PChar(ExePath), Unused);
  if Size <> 0 then
  begin
    GetMem(VerData, Size);
    try
      if GetFileVersionInfo(PChar(ExePath), 0, Size, VerData) then
      begin
        Value := nil;
        Len := 0;
        VerQueryValue(VerData, '\', Value, Len);

        Value := nil;
        Len := 0;
        VerQueryValue(VerData, '\VarFileInfo\Translation', Value, Len);

        Translate := PLangAndCodePage(Value);
        for I := 0 to (Len div SizeOf(TLangAndCodePage)) - 1 do
        begin

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'CompanyName';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(CompanyName));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'FileDescription';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(FileDescription));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'FileVersion';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(FileVersion));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'InternalName';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(InternalName));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'LegalCopyright';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(LegalCopyright));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'LegalTrademarks';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(LegalTrademarks));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'OriginalFileName';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(OriginalFileName));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'ProgramID';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(ProgramID));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'ProductName';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(ProductName));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'ProductVersion';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(ProductVersion));
          end;

          KeyName := '\StringFileInfo\' + IntToHex(Translate^.wLanguage, 4) +
            IntToHex(Translate^.wCodePage, 4) + '\' + 'Comments';
          Value := nil;
          VerLen := 0;
          if VerQueryValueW(VerData, PWideChar(KeyName), Value, VerLen) then
          begin
            AFileVersion := PWideChar(Value);
            ZeroMemory(AFileVersion, VerLen * SizeOf(WideChar));
            wsprintfW(AFileVersion, PWideChar(Comments));
          end;

          Inc(Translate);
        end;

        hUpdate := BeginUpdateResource(PChar(ExePath), False);
        if hUpdate <> 0 then
        begin
          Updated := UpdateResource(hUpdate, RT_VERSION,
            MAKEINTRESOURCE(VS_VERSION_INFO), MAKELANGID(LANG_NEUTRAL,
            SUBLANG_NEUTRAL), VerData, Size);
          EndUpdateResource(hUpdate, not Updated);
        end;
      end;
    finally
      FreeMem(VerData);
    end;
  end;
end;

javier14 26-10-2023 17:11:25

:eek: Solo quería cambiar el FileVersion, pero gracias

dec 26-10-2023 17:40:24

Hola a todos,

Pues... como te he comentado... he visto que la función, precisamente, no cambia la clave "FileVersion"... sí todo lo demás... pero, precisamente, "FileVersion", no... cosa que no llego a entender ahora mismo.

Si puedes probarlo... y comentar aquí si a ti te cambia "FileVersion"... o sino lo consigues tampoco...


La franja horaria es GMT +2. Ahora son las 09:23:53.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi