Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 28-02-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Cómo determinar el tamaño original de un archivo ejecutable

Hola a todos,

A ver si podéis echarme una mano con el siguiente problema. En uno de mis proyectos necesito adjuntar ciertos datos en un archivo ejecutable, esto es, en realidad adjunto un archivo a otro archivo, este último un ejecutable generado también con Delphi. Todo funciona bien, excepto si además cambio el icono de dicho archivo ejecutable. Para conseguir el tamaño original de un archivo ejecutable, me baso en el siguiente código de Angus Johnson:

Código Delphi [-]
{$IFDEF VER100}
{TImageDosHeader isn't defined in Delphi 3 so here's an an abbreviated structure definition...}
type

  PImageDosHeader = ^TImageDosHeader;
  TImageDosHeader = packed record
    e_magic         : WORD;
    e_ignore        : packed array [0..28] of WORD;
    _lfanew        : Longint;
  end;
{$ENDIF}

function GetExeSize: cardinal;
var
  p: pchar;
  i, NumSections: integer;
begin
  result := 0; // if error then result = 0
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p,sizeof(TImageFileHeader)+ sizeof(TImageOptionalHeader));
  for i := 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData+SizeOfRawData > result then
        result := PointerToRawData+SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

La idea del anterior código es determinar el tamaño original de un archivo ejecutable, sin contar con el archivo que hemos adjuntado al mismo, puesto que este se "añade" al ejecutable, pero, no forma parte de sus "cabeceras" (no sé si me explico, porque, aquí me pierdo un poco, tengo que reconocerlo). Pues bien, en teoría esto ha de funcionar, sin embargo, el anterior código siempre retorna "0" en Windows NT (Windows 10, por ejemplo). Como se trata un código para Delphi 3, yo lo he "sabido" modificar un poco de este modo:

Código Delphi [-]
function GetExeSize(): Int64;
var
  P: pchar;
  I, NumSections: Integer;
begin
  Result := 0;
  P := pointer(HInstance);
  Inc(P, SizeOf(DWORD));
  NumSections := PImageFileHeader(P).NumberOfSections;
  Inc(P, SizeOf(TImageNtHeaders));
  for I := 1 to NumSections do
  begin
    if PImageSectionHeader(P)^.PointerToRawData > Result then
    begin
      Result := PImageSectionHeader(P)^.PointerToRawData +
                 PImageSectionHeader(P)^.SizeOfRawData;
    end;
    Inc(P, SizeOf(TImageSectionHeader));
  end;
end;

En efecto, el código modificado que muestro arriba no retorna "0", sin embargo, no tiene en cuenta el cambio del icono del ejecutable. Es decir, supongamos que el archivo ejecutable original tiene un tamaño de 100 bits. Ahora pueden darse dos casos: que el tamaño del icono a usar sea mayor o menor que el del ejecutable original: sea como sea, la función anterior retornará 100, es decir, no tendrá en cuenta que se ha cambiado el icono del ejecutable.

Como para saber la cantidad de datos a leer del archivo ejecutable se determina por la fórmula: Tamaño Actual - Tamaño Original, dicha fórmula falla, en el sentido de que, por ejemplo, si se usado un icono de menor tamaño que el original, es posible que "Tamaño Actual" sea menor que "Tamaño Original", de modo que estaríamos leyendo una cantidad de datos negativa, o sea, que no podremos leer correctamente dichos datos.

¿Qué es lo que necesito?

Tengo dudas sobre si los cambios que he introducido a la función de Angus Johnson son del todo correctos o no, de modo que esta función, o sea, la que determina el tamaño original del archivo ejecutable, la que esté causando los problemas: puesto que no tenga en cuenta el posioble cambio del icono. Hay que decir, que, si no se cambia el icono, todo funciona como se espera, pero, el cambio del icono es una opción para el usuario y no se puede simplemente eliminar.

He probado varias cosas, por ejemplo, en la función original tenemos esta línea:

Código Delphi [-]
inc(p,sizeof(TImageFileHeader)+ sizeof(TImageOptionalHeader));

Como puede verse, en la modificación que yo he hecho dicha línea se ha transformado en esta otra:

Código Delphi [-]
Inc(P, SizeOf(TImageNtHeaders));

Ahora bien, si trato de hacer lo siguiente, para tratar de ajustar más:

Código Delphi [-]
Inc(P, SizeOf(TImageNtHeaders) + SizeOf(TImageOptionalHeader));

... dicha cambio implica que la función retorne de nuevo siempre "0".

¿Otras posibles causas del problema?

Creo que el problema está determinado porque la función anterior no es capaz de tener en cuenta el cambio de icono en el archivo ejecutable. Pareciera que sigue "leyendo" el tamaño original del icono y así el resultado que ofrece está equivocado, tanto en el caso de que el icono a cambiar sea mayor o menor que el original.

Pero, por otro lado, me queda también la duda de que pueda ser, precisamente, la función encargada de cambiar el icono, la que esté causando el problema, porque, de alguna forma no "actualize" el tamaño al del nuevo "icono/recurso". Sin embargo, la función encargada de cambiar el icono, originalmente escrita por Jordan Russell para Inno Setup, funciona por lo demás correctamente.

Código Delphi [-]
procedure TExeUtils.ChangeIcon(const FileName, IcoFileName: string);

  procedure Error(const Msg: String);
  begin
    raise Exception.Create('Resource update error: ' + Msg);
  end;

  procedure ErrorWithLastError(const Msg: String);
  begin
    Error(Msg + ' (' + IntToStr(GetLastError) + ')');
  end;

  function EnumLangsFunc(hModule: Cardinal; lpType, lpName: PAnsiChar; wLanguage: Word; lParam: Integer): BOOL; stdcall;
  begin
    PWord(lParam)^ := wLanguage;
    Result := False;
  end;

  function GetResourceLanguage(hModule: Cardinal; lpType, lpName: PChar; var wLanguage: Word): Boolean;
  begin
    wLanguage := 0;
    EnumResourceLanguages(hModule, lpType, lpName, @EnumLangsFunc, Integer(@wLanguage));
    Result := True;
  end;

type
  PIcoItemHeader = ^TIcoItemHeader;
  TIcoItemHeader = packed record
    Width: Byte;
    Height: Byte;
    Colors: Byte;
    Reserved: Byte;
    Planes: Word;
    BitCount: Word;
    ImageSize: DWORD;
  end;
  PIcoItem = ^TIcoItem;
  TIcoItem = packed record
    Header: TIcoItemHeader;
    Offset: DWORD;
  end;
  PIcoHeader = ^TIcoHeader;
  TIcoHeader = packed record
    Reserved: Word;
    Typ: Word;
    ItemCount: Word;
    Items: array [0..MaxInt shr 4 - 1] of TIcoItem;
  end;
  PGroupIconDirItem = ^TGroupIconDirItem;
  TGroupIconDirItem = packed record
    Header: TIcoItemHeader;
    Id: Word;
  end;
  PGroupIconDir = ^TGroupIconDir;
  TGroupIconDir = packed record
    Reserved: Word;
    Typ: Word;
    ItemCount: Word;
    Items: array [0..MaxInt shr 4 - 1] of TGroupIconDirItem;
  end;

  function IsValidIcon(P: Pointer; Size: Cardinal): Boolean;
  var
    ItemCount: Cardinal;
  begin
    Result := False;
    if Size < Cardinal(SizeOf(Word) * 3) then
      Exit;
    if (PChar(P)[0] = 'M') and (PChar(P)[1] = 'Z') then
      Exit;
    ItemCount := PIcoHeader(P).ItemCount;
    if Size < Cardinal((SizeOf(Word) * 3) + (ItemCount * SizeOf(TIcoItem))) then
      Exit;
    P := @PIcoHeader(P).Items;
    while ItemCount > Cardinal(0) do begin
      if (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) < Cardinal(PIcoItem(P).Offset)) or
         (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) > Cardinal(Size)) then
        Exit;
      Inc(PIcoItem(P));
      Dec(ItemCount);
    end;
    Result := True;
  end;

var
  H: THandle;
  M: HMODULE;
  R: HRSRC;
  Res: HGLOBAL;
  GroupIconDir, NewGroupIconDir: PGroupIconDir;
  I: Integer;
  wLanguage: Word;
  F: TFileStream;
  Ico: PIcoHeader;
  N: Cardinal;
  NewGroupIconDirSize: LongInt;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Error('Only supported on Windows NT and above');

  Ico := nil;

  try
    { Load the icons }
    F := TFileStream.Create( IcoFileName, fmOpenRead or fmShareDenyWrite );
    try
      N := F.Size;
      if Cardinal(N) > Cardinal($100000) then  { sanity check }
        Error('Icon file is too large');
      GetMem(Ico, N);
      F.ReadBuffer(Ico^, N);
    finally
      F.Free;
    end;

    { Ensure the icon is valid }
    if not IsValidIcon(Ico, N) then
      Error('Icon file is invalid');

    { Update the resources }
    H := BeginUpdateResource(PChar(FileName), False);
    if H = 0 then
      ErrorWithLastError('BeginUpdateResource failed (1)');
    try
      M := LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
      if M = 0 then
        ErrorWithLastError('LoadLibraryEx failed (1)');
      try
        { Load the 'MAINICON' group icon resource }
        R := FindResource(M, 'MAINICON', RT_GROUP_ICON);
        if R = 0 then
          ErrorWithLastError('FindResource failed (1)');
        Res := LoadResource(M, R);
        if Res = 0 then
          ErrorWithLastError('LoadResource failed (1)');
        GroupIconDir := LockResource(Res);
        if GroupIconDir = nil then
          ErrorWithLastError('LockResource failed (1)');

        { Delete 'MAINICON' }
        if not GetResourceLanguage(M, RT_GROUP_ICON, 'MAINICON', wLanguage) then
          Error('GetResourceLanguage failed (1)');
        if not UpdateResource(H, RT_GROUP_ICON, 'MAINICON', wLanguage, nil, 0) then
          ErrorWithLastError('UpdateResource failed (1)');

        { Delete the RT_ICON icon resources that belonged to 'MAINICON' }
        for I := 0 to GroupIconDir.ItemCount-1 do begin
          if not GetResourceLanguage(M, RT_ICON, MakeIntResource(GroupIconDir.Items[i].Id), wLanguage) then
            Error('GetResourceLanguage failed (2)');
          if not UpdateResource(H, RT_ICON, MakeIntResource(GroupIconDir.Items[i].Id), wLanguage, nil, 0) then
            ErrorWithLastError('UpdateResource failed (2)');
        end;

        { Build the new group icon resource }
        NewGroupIconDirSize := 3*SizeOf(Word)+Ico.ItemCount*SizeOf(TGroupIconDirItem);
        GetMem(NewGroupIconDir, NewGroupIconDirSize);
        try
          { Build the new group icon resource }
          NewGroupIconDir.Reserved := GroupIconDir.Reserved;
          NewGroupIconDir.Typ := GroupIconDir.Typ;
          NewGroupIconDir.ItemCount := Ico.ItemCount;
          for I := 0 to NewGroupIconDir.ItemCount-1 do begin
            NewGroupIconDir.Items[i].Header := Ico.Items[i].Header;
            NewGroupIconDir.Items[i].Id := I+1; //assumes that there aren't any icons left
          end;

          { Update 'MAINICON' }
          for I := 0 to NewGroupIconDir.ItemCount-1 do
            if not UpdateResource(H, RT_ICON, MakeIntResource(NewGroupIconDir.Items[i].Id), 1033, Pointer(DWORD(Ico) + Ico.Items[i].Offset), Ico.Items[i].Header.ImageSize) then
              ErrorWithLastError('UpdateResource failed (3)');

          { Update the icons }
          if not UpdateResource(H, RT_GROUP_ICON, 'MAINICON', 1033, NewGroupIconDir, NewGroupIconDirSize) then
            ErrorWithLastError('UpdateResource failed (4)');
        finally
          FreeMem(NewGroupIconDir);
        end;
      finally
        FreeLibrary(M);
      end;
    except
      EndUpdateResource(H, True);  { discard changes }
      raise;
    end;
    if not EndUpdateResource(H, False) then
      ErrorWithLastError('EndUpdateResource failed');
  finally
    FreeMem(Ico);
  end;
end;

¿Alguna conclusión?

Quiero pensar que, puesto que estoy tratando de traducir un código escrito en Delphi 3, que, además no comprendo muy bien, la función problemática no es la encargada de cambiar el icono, sino "GetExeSize", que, no consigue determinar bien el tamaño original del archivo ejecutable, es decir, no tiene en cuenta el posible cambio del icono.

Pero la verdad es que no puedo concluir que ahí esté el problema... y por eso necesito de vuestra ayuda, puesto que tal vez alguno de vosotros esté más puesto que yo en estos asuntos y pueda ofrecer algo de luz al respecto. Por favor, si necesitáis cualquier otra información que se me haya olvidado, decídmelo y trataré de proporcionarla.

¡Muchas gracias a tod@s!
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 28-02-2017 a las 11:45:51.
Responder Con Cita
  #2  
Antiguo 28-02-2017
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Pregunta tonta: ¿por qué no dejas el icono fuera?
Responder Con Cita
  #3  
Antiguo 28-02-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Cita:
Empezado por Casimiro Notevi Ver Mensaje
Pregunta tonta: ¿por qué no dejas el icono fuera?
De pregunta tonta nada... Gracias por responder Casimiro. Pero, ¿qué significa dejar el icono "fuera"?
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #4  
Antiguo 28-02-2017
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Quiero decir, según he entendido, que si el usuario puede cambiar el icono del programa ¿por qué no lo cargas el ficherito desde disco?
Responder Con Cita
  #5  
Antiguo 28-02-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

He pensado algo parecido, desde luego, pero, la idea es que el icono "luzca" en el propio archivo ejecutable, es decir, no es tanto que dicho icono aparezca en el formulario u otro lugar, pero, en el mismo icono del ejecutable. En todo caso estaríamos obligando a distribuir el icono en cuestión junto con el ejecutable... que tendría un icono "original"... no sé... Casimiro... de veras que he pensado que simplemente decir, "no se puede cambiar el icono", pero, entiendo que no debo tomar dicho camino, pues, por mejorar algo, vamos a empeorar otra cosa.

Yo creo que la función "GetExeSize" puede mejorarse, es decir, no está del todo bien adaptada por mí desde el código original de Angus Johnson, de modo que por ahí pueden ir los tiros... dicha función debería devolver el tamaño original de un ejecutable, teniendo en cuenta si se cambia el icono del mismo... que es lo que parece que no hace... aunque, como he dicho arriba, tal vez la función hace su trabajo bien (lo dudo, porque, yo mismo modifiqué dicha función para ser válida en Windows NT) y sea la función que actualiza el icono quien deja algo "sin actualizar"...
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #6  
Antiguo 28-02-2017
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
¿Pero lo de cambiar el icono y el tamaño del ejecutable es por alguna protección?
Responder Con Cita
  #7  
Antiguo 28-02-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Gracias por contestar Casimiro. No; en mi proyecto uno de los ejecutables actúa como un "stub", como un "autoextraible", es decir, en dicho ejecutable se adjunta una aplicación del usuario. Cuando el ejecutable se inicia "muestra" la aplicación del usuario. El problema es que tal como lo hago ahora (actualizando cierto recurso del ejecutable que sirve de "stub") el tamaño del mismo se limita a unos 500 MB, que, no está mal, pero, con esta segunda forma que estoy intentando es posible eliminar dicha limitación, empero, no funciona... pues con la primera forma, aun con dicha limitación en el tamaño del ejecutable final, al menos sí puedo cambiar el icono sin problema.
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #8  
Antiguo 28-02-2017
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Esta es una de esas preguntas que escafandra seguramente sabe contestar (a ver si al oir su nombre se acerca ) Yo sólo puedo comentar dos cosas:

1. ¿Tendrá algo que ver con el paso de 32 a 64 bits?
2. Has probado algo más sencillo como lo que comentan aquí?

LineComment Saludos
Responder Con Cita
  #9  
Antiguo 28-02-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Gracias por contestar Román. He probado de todo majo... de hecho hace años que me peleo con este asunto. El método que muestras en tu enlace, por ejemplo, y, si no recuerdo mal... falla en el momento en que se "firme" un ejecutable. Y esto es necesario, es decir, dar la posibilidad de firmar los ejecutables/aplicaciones del usuario. Respecto de que sea cuestión del paso de 32 a 64 bits, no estoy muy seguro, porque, también aquí ando perdido.

En principio yo no estoy intentándolo en una aplicación de 64 bits, sino en una de 32 bits. Sin embargo, es cierto que, probablemente, no sé traducir correctamente la función de Angus Johnson, que, seguramente, funcionaría bien antes de Windows NT. Creo que aquí está el problema: en el paso de Windows 9x a Windows NT. Tal vez haya más, pero, por ejemplo, esta línea que he remarcado arriba:

Código Delphi [-]
Inc(p, SizeOf(TImageFileHeader) + SizeOf(TImageOptionalHeader));

... y que yo trato de traducir como:

Código Delphi [-]
Inc(p, SizeOf(TImageNtHeaders));

... probablemente no pueda ser traducida así sin más. Pero, como he dicho arriba, en el momento en que la traduzco por esto otro:

Código Delphi [-]
Inc(p, SizeOf(TImageNtHeaders) + SizeOf(TImageOptionalHeader));

... la función retorna siempre "0". Pero, claro, seguramente, la traducción primera que estoy haciendo está mal, puesto que "reduce" dicha línea, y tal vez ahí esté el meollo del asunto.

Por otro lado, según escribo esto, se me hace raro que la función traducida por mí retorne el tamaño del ejecutable original... es cierto que no tiene en cuenta el cambio de icono (lo que la hace inservible) pero, al fin y al cabo está retornando el tamaño del ejecutable original... o sea que tal vez la función que actualiza el icono también tenga algo que ver después de todo... tal vez "se olvida" de actualizar el tamaño del ejecutable teniendo en cuenta el nuevo icono...

Como digo, llevo años intentando que algo así funcione: que se pueda adjuntar un archivo de cualquier tamaño a un ejecutable, permitiendo a su vez cambiar su icono, y, por supuesto, que permanezca siendo válido para su firma. Esto me abriría más puertas además de mejorar el proyecto del que trato aquí: podría acaso pensar en nuevos proyectos también.
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 01-03-2017 a las 08:56:46.
Responder Con Cita
  #10  
Antiguo 01-03-2017
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
No sé sobre eso
Responder Con Cita
  #11  
Antiguo 01-03-2017
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.911
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
Y porque no tomas el tamaño antes de adjuntarlo? Porque tienes que medirlo como recurso?

Y si mides el archivo antes de adjuntarlo, lo adjuntas y luego restas? Y me refiero a solo mirar el tamaño del archivo tal cual, sin entrar a su estructura interna.
__________________
El malabarista.
Responder Con Cita
  #12  
Antiguo 01-03-2017
bitbow bitbow is offline
Miembro
 
Registrado: jul 2006
Posts: 366
Poder: 18
bitbow Va camino a la fama
Coincido con mamcx, por que simplemente no haces el calculo del tamaño del exe de forma separada con el calculo del tamaño del icono y luego restas sumas o lo que tengas que hacer?

Saludos.
__________________
¡Ni como ayudarte Niño!!
bitbow
Responder Con Cita
  #13  
Antiguo 01-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Cita:
Empezado por Casimiro Notevi Ver Mensaje
No sé sobre eso
¡Ya somos unos cuantos!

Cita:
Empezado por mamcx Ver Mensaje
Y porque no tomas el tamaño antes de adjuntarlo? Porque tienes que medirlo como recurso?

Y si mides el archivo antes de adjuntarlo, lo adjuntas y luego restas? Y me refiero a solo mirar el tamaño del archivo tal cual, sin entrar a su estructura interna.
Cita:
Empezado por bitbow Ver Mensaje
Coincido con mamcx, por que simplemente no haces el calculo del tamaño del exe de forma separada con el calculo del tamaño del icono y luego restas sumas o lo que tengas que hacer?

Saludos.
Bueno; he probado tantas cosas,... pero, todavía no me atrevo a decir que no a nada. Ciertamente, sabemos el tamaño del archivo ejecutable "original". Pero, ojo, porque, en el momento que dicho ejecutable sea "firmado" (digitalmente) su tamaño cambiará, y, por tanto ya no podremos usar la referencia del tamaño original de dicho ejecutable.

El tamaño del icono, por otro lado, es desconocido, es decir, lo escoje el propio usuario. Es cierto que podemos conocerlo, por supuesto, pero, por ejemplo, el tamaño del icono elegido puede hacer que el tamaño original del ejecutable varíe a más o a menos, puesto que el archivo ejecutable tiene ya un icono "por defecto" incorporado, que no tiene porqué coincidir (sería raro que lo hiciese) con el que elija el usuario.

He pensado (pero no lo he llegado a intentar por mero aburrimiento, es decir, como una especie de decir basta ya) en utilizar un recurso del ejecutable sólo para guardar el tamaño del icono, de modo que después podamos hacer los cálculos necesarios. Es posible que funcionase... y además guardar dicho tamaño en un recurso del ejecutable no sería para nada problemático: ahora mismo guardo así hasta 500 MB.

De hecho los cálculos no necesitan ser perfectos. Es decir, afortunadamente, lo que se guarda en el ejecutable es un archivo "zip", y, a la hora de descomprimir, digamos que la herramienta de descompresión (los componentes Abbrevia) se muestra tolerante, por ejemplo, si el tamaño del archivo "zip" original resulta "mayor" (dado el incremento del tamaño del icono) no hay problema aún así para descompromir el "stream zip" en cuestión.

En fin, voy a tratar de pararme en esto, al menos para comprobar si pudiera funcionar... y os avisaré si logro hacerlo o si al cabo la cosa se complica de una forma no esperada. ¡Muchas gracias a todos!

P.S. No obstante, ciertamente la función "GetExeSize" acaso debería funcionar tal como lo hacía antaño en Windows 9x. Seguramente mi traducción esté mal (apuesto por esto al 99%) o bien dicha función no funcionaría nunca en Windows NT... cosa que dudo muchísimo...
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #14  
Antiguo 01-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Bueno, pues, a ver si soy capaz de explicarme, pero, vaya por delante que al final parece que lo he/mos conseguido. Esta mañana quise probar lo que Mario y bitbow propusieron. Pues bien, el caso es que los resultados obtenidos no eran los esperados, esto es, la cosa funcionaba bien para determinados iconos, pero, no para otros. Ya había escrito aquí un mensaje describiendo esta situación, cuando, después de enviarlo (algunos lo habréis acaso recibido) he decidido borrarlo, puesto que algo no me cuadraba...

En primer lugar he querido descubrir que la suma TAMAÑO EJECUTABLE + TAMAÑO ICONO, no coincidía con lo que obteníamos al medir el tamaño del ejecutable, una vez cambiado el icono. Yo esto lo he atribuido al hecho de que el icono se guarde en un determinado recurso del ejecutable: tal vez, entonces, el tamaño del icono no sea exactamente igual al tamaño final que tenga el recurso que alberga el icono. No estoy seguro de este párrafo en concreto, pero, el caso es que he borrado mi mensaje, y es que...

¿Por qué funcionaba con unos iconos pero no con otros? Y entonces he descubierto que el icono problemático, tenía en su interior varios tamaños de imagen, partiendo de un tamaño de 512 píxeles. He intentado abrir entonces el archivo ejecutable generado con el programa XN Resource Editor, y, en efecto, no he podido acceder al recurso "MAINICON", por un error "Pixel Format Not Valid for Icons or Cursors".

Entonces he considerado que, aunque podía cambiarse dicho icono sin errores, tal vez dicho icono estaba jorobándolo todo, porque, de hecho el icono original del ejecutable tiene un tamaño máximo de 256 píxeles, y, si no me equivoco, Delphi XE2 no soporta (en Windows) iconos de más resolución que esa. De modo que ahora parecía que el problema no venía dado porque el TAMAÑO EJECUTABLE + TAMAÑO ICONO no coincidiese con el tamaño del ejecutable generado.

El problema parecía causarlo el icono de tamaño de 512 píxeles, y, en efecto, al quitar la imagen de dicho tamaño del icono, o usando iconos de un tamaño máximo (no parece importar el mínimo) de 256 píxeles, la cosa parece funcionar como se espera... y ahora puedo obtener ejecutables "que funcionan" mayores de 1,5 GB. O sea, ¿prueba superada? Parece que sí, pero, me quedan algunas dudas al respecto y son las siguientes:

1º Sigo pensando que, en efecto, el TAMAÑO EJECUTABLE + TAMAÑO ICONO no coincide con el tamaño que al cabo el ejecutable tiene. Resulta que cuando se cambia el icono de un ejecutable, no se trata de sumar al tamaño del ejecutable el tamaño del archivo del icono, sino que este último se alberga en un recurso del ejecutable que, digamos, tiene su propio tamaño. Luego lo que yo estoy guardando ahora mismo, es decir, TAMAÑO EJECUTABLE + TAMAÑO ICONO, parece funcionar, sí, pero, aunque sin estar seguro, me atrevo a decir que funciona porque la descompresión del archivo tiene cierta "tolerancia", de modo que no importa si el "stream" que se lee es mayor que el original: la descompresión se realiza sin problemas.

2º Creo que la función "GetExeSize" debería seguir funcionando en Windows NT (no diré ya en Windows 64 bits, porque, aunque funcionase, tal vez el código tampoco tendría nada que ver). Si esta función hiciese su trabajo como lo hacía en los tiempos de Windows 9x, seguramente todo sería más sencillo, mejor dicho, más exacto, puesto que estaríamos obteniendo el tamaño del ejecutable original ya con el icono incluido... y teniendo en cuenta que el tamaño de este puede no tener que ver con el tamaño que ocupe el icono en el recurso correspondiente.

3º Tengo que tomar el camino del primer punto. Una vez descubierto que el causante del problema era un determinado icono (de tamaño excesivo) he querido probar con "GetExeSize", pero, no funciona. Es decir, de momento, tengo que tomar el camino sugerido por Mario y bitbow, puesto que, aunque no sea "exacto" (vamos, si no es que estoy completamente equivocado) parece funcionar correctamente, siempre que el icono no supere el tamaño máximo, pero, esto ya no es problema de la solución, sino de que iconos de más tamaño no parecen soportarse.

En fin, voy a hacer unas cuantas pruebas más, pero, como digo, parece que el asunto se puede dar por solucionado... al menos mi proyecto puedo ahora "compilar" aplicaciones de más de 500 MB, y, además hacerlo de una forma más rápida y segura en todo caso, puesto que antes actualizaba un recurso del ejecutable "base", y, esto causaba problemas no sólo con archivos grandes, a veces también con los pequeños. Esta otra opción de "adjuntar" al ejecutable un archivo es más rápida y eficiente.

¡Muchas gracias a todos! ¡Comentaré por aquí cómo acaba la cosa!
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 01-03-2017 a las 15:52:15.
Responder Con Cita
  #15  
Antiguo 01-03-2017
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.195
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por dec Ver Mensaje
Hola a todos,

A ver si podéis echarme una mano con el siguiente problema. En uno de mis proyectos necesito adjuntar ciertos datos en un archivo ejecutable, esto es, en realidad adjunto un archivo a otro archivo, este último un ejecutable generado también con Delphi. Todo funciona bien, excepto si además cambio el icono de dicho archivo ejecutable. Para conseguir el tamaño original de un archivo ejecutable, me baso en el siguiente código de Angus Johnson:

Código Delphi [-]
{$IFDEF VER100}
{TImageDosHeader isn't defined in Delphi 3 so here's an an abbreviated structure definition...}
type

  PImageDosHeader = ^TImageDosHeader;
  TImageDosHeader = packed record
    e_magic         : WORD;
    e_ignore        : packed array [0..28] of WORD;
    _lfanew        : Longint;
  end;
{$ENDIF}

function GetExeSize: cardinal;
var
  p: pchar;
  i, NumSections: integer;
begin
  result := 0; // if error then result = 0
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p,sizeof(TImageFileHeader)+ sizeof(TImageOptionalHeader));
  for i := 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData+SizeOfRawData > result then
        result := PointerToRawData+SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

La idea del anterior código es determinar el tamaño original de un archivo ejecutable, sin contar con el archivo que hemos adjuntado al mismo, puesto que este se "añade" al ejecutable, pero, no forma parte de sus "cabeceras" (no sé si me explico, porque, aquí me pierdo un poco, tengo que reconocerlo). Pues bien, en teoría esto ha de funcionar, sin embargo, el anterior código siempre retorna "0" en Windows NT (Windows 10, por ejemplo). Como se trata un código para Delphi 3, yo lo he "sabido" modificar un poco de este modo:

Código Delphi [-]function GetExeSize(): Int64; var P: pchar; I, NumSections: Integer; begin Result := 0; P := pointer(HInstance); Inc(P, SizeOf(DWORD)); NumSections := PImageFileHeader(P).NumberOfSections; Inc(P, SizeOf(TImageNtHeaders)); for I := 1 to NumSections do begin if PImageSectionHeader(P)^.PointerToRawData > Result then begin Result := PImageSectionHeader(P)^.PointerToRawData + PImageSectionHeader(P)^.SizeOfRawData; end; Inc(P, SizeOf(TImageSectionHeader)); end; end;

El código funciona bien en Todos los WIndows, el problema es el compilador. En delphi 7 no hay problema pero los que priorizan unicode, la fastidian, como siempre. Al usar PCHAR el compilados asume (malamente) un PWCHAR con lo que al incrementar el puntero se hace en múltiplos de 2 bytes. Esta es la modificación que debes hacer:

Código Delphi [-]
function GetExeSize: cardinal;
var
  p: PBYTE;  // USAR PBYTE EN LUGAR DE PCHAR (PWCHAR)
  i, NumSections: integer;
begin
  Result := 0; // if error then result = 0
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p, sizeof(TImageFileHeader) + sizeof(TImageOptionalHeader));
  for i:= 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData+SizeOfRawData > Result then
        Result := PointerToRawData + SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

El código expuesto calcula el tamaño del ejecutable actual, pero puede modificarse para calcular el tamaño teórico de cualquier fichero ejecutable conociendo su ruta.

El formato PE de los ejecutables de Windows, agrandes rasgos consta de:

- Cabecera DOS 'MZ' : IMAGE_DOS_HEADE: 64 bytes
- DOS Stub o subprograma encargado de avisar del error si no se ejecuta en Windows: 448 bytes
- Firma PE: 'PE00': 4 Bytes.
- Cabecera PE: IMAGE_FILE_HEADER: 20 Bytes.
- Cabecera opcional: IMAGE_OPTIONAL_HEADER: 224 Bytes. (tamaño variable)
- Cabeceras de sección: IMAGE_SECTION_HEADER: 40 Bytes cada una, número variable
- Cuerpos de sección: código, datos importados y exportados, relocations, recursos... Tamaño indeterminado.

Además, todo esto lleva una alineación con lo que la afirmación TamFile + TamIcono = NewTamfile es falsa.

la siguiente clase es un fragmento de otra más completa para el manejo del PE que escribí hace ya algunos años con la funcionalidad de eliminar secciones de un ejecutable. El tema está aquí. Dado que es un foro restringido, voy a publicar un fragmento que permite calcular el tamaño teórico de un archivo ejecutable que no se está ejecutando. Si no coincide con el real, "algo se ha añadido sin permiso oficial al ejecutable" (aunque un buen "añadidor", ya se encargará de modificar todo el PE para oficializarlo).

Código Delphi [-]
unit PEFile_;

interface

uses
  Windows;

type
  PIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;
  PIMAGE_FILE_HEADER = ^IMAGE_FILE_HEADER;
  PIMAGE_OPTIONAL_HEADER = ^IMAGE_OPTIONAL_HEADER;
  PIMAGE_SECTION_HEADER = ^IMAGE_SECTION_HEADER;

  ASectionHeaders = array [0..0] of IMAGE_SECTION_HEADER;
  PASectionHeaders = ^ASectionHeaders;

  TPEFile = class;
  PTPEFile = ^TPEFile;

  TPEFile = class
  private
   pImageFile: Pointer;
   SizeOfFile: integer;

   procedure IniPE;
  public
   DosHeader:      PIMAGE_DOS_HEADER;
   DosStub:        Pointer;
   PESignature:    PDWORD;
   PEHeader:       PIMAGE_FILE_HEADER;
   OptionalHeader: PIMAGE_OPTIONAL_HEADER;
   SectionHeaders: PASectionHeaders; //PIMAGE_SECTION_HEADER;

   constructor  Create;
   constructor  CreateFromFile(const FileName: PCHAR);
   destructor   Destroy; override;
   function     GetSizeOfFile: DWORD;

   function  ReadFile(FileName: PCHAR): boolean;

   function  ToOffset(pData: Pointer): Cardinal;
   function  ToPointer(Offset: Cardinal): Pointer;

   function  Signature(): boolean;
  end;

implementation


//------------------------------------------------------------------------------
//  El constructor por defecto.
constructor TPEFile.Create;
begin
 inherited Create;
 pImageFile:= nil;
 DosHeader:= nil;
 PESignature:= nil;
 PEHeader:= nil;
 OptionalHeader:= nil;
 SectionHeaders:= nil;
end;

constructor TPEFile.CreateFromFile(const FileName: PCHAR);
begin
 inherited Create;
 pImageFile:= nil;
 ReadFile(FileName);
end;

destructor  TPEFile.Destroy;
begin
  if pImageFile <> nil then VirtualFree(pImageFile, 0, MEM_RELEASE);
  inherited Destroy;
end;

// Inicializa los punteros de un objeto
procedure TPEFile.IniPE;
begin
   if pImageFile = nil then exit;

   DosHeader      := PIMAGE_DOS_HEADER(pImageFile);
   PESignature    := PDWORD(DosHeader._lfanew + DWORD(pImageFile));
   PEHeader       := PIMAGE_FILE_HEADER(DosHeader._lfanew + 4 + DWORD(pImageFile));
   OptionalHeader := PIMAGE_OPTIONAL_HEADER(DWORD(PEHeader) + sizeof(IMAGE_FILE_HEADER));
   SectionHeaders := PASectionHeaders(DWORD(OptionalHeader) + PEHeader.SizeOfOptionalHeader);
end;

// Lee un archivo PE e inicializa el objeto TPEFile
function TPEFile.ReadFile(FileName: PCHAR): boolean;
var
   Hi: DWORD;
   hFile: THandle;
   BytesRead: integer;
begin
   if pImageFile <> nil then VirtualFree(pImageFile, 0, MEM_RELEASE);

   hFile      := CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
   SizeOfFile := GetFileSize(hFile, @Hi);
   pImageFile := VirtualAlloc(nil, SizeOfFile, MEM_COMMIT, PAGE_READWRITE);
   BytesRead  := _lread(hFile, pImageFile, SizeOfFile);
   CloseHandle(hFile);

   IniPE;

   Result:= BytesRead = SizeOfFile;
end;

function TPEFile.Signature: boolean;
begin
  Result:= false;
  if (DosHeader = nil) or (PESignature = nil) then exit;
  Result:= (DosHeader.e_magic = (PWORD(PAnsiCHAR('MZ')))^)
   and ((PWORD(PESignature))^ = (PWORD(PAnsiCHAR('PE')))^);
end;

// Calcula el tamaño del archivo PE
function TPEFile.GetSizeOfFile: DWORD;
var
   MaxPointerToRawData: DWORD;
   n: integer;
begin
   Result:= 0;
   if not Signature then exit;

   // Busco la última sección
   MaxPointerToRawData:= 0;
   for n:=0 to PEHeader.NumberOfSections-1 do
   begin
     if (MaxPointerToRawData = 0) or (MaxPointerToRawData <= SectionHeaders[n].PointerToRawData) then
     begin
        MaxPointerToRawData:= SectionHeaders[n].PointerToRawData;
        Result:= MaxPointerToRawData + SectionHeaders[n].SizeOfRawData;
     end;
   end;

   if Result = 0 then Result:= ToOffset(SectionHeaders);
   SizeOfFile:= Result;
end;

function  TPEFile.ToOffset(pData: Pointer): Cardinal;
begin
  Result:= DWORD(pData)-DWORD(pImageFile);
end;

function  TPEFile.ToPointer(Offset: Cardinal): Pointer;
begin
  Result:= Pointer(Offset + DWORD(pImageFile));
end;

end.

El código no funcionará compilado a 64bits.

Por otro lado, una forna de encontrar fácilmente el fin de fichero sin usar el PE, es adherirle al final una firma fija compuesta de una cadena conocida. Una búsqueda nos dará el tamaño de archivo, y a partir de esa firma podemos añadir más cosas al archivo inicial.

Código:
Stub + Firma + DatosVarios.
El mismo Stub se encargaría de localizar la firma y liberar el resto del archivo tranquilamente.


Saludos.

Última edición por escafandra fecha: 01-03-2017 a las 23:31:48.
Responder Con Cita
  #16  
Antiguo 01-03-2017
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Responder Con Cita
  #17  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Bueno, escafandra, qué puede uno decir... ¡muchas gracias! Acabo de probar con la función "GetExeSize" modificada por ti, y, en efecto, funciona como se espera sin problema alguno. Mejor aún, puesto que, probando de nuevo con el icono de 512 píxeles también funciona correctamente, es decir, la función calcula ahora el tamaño correcto del archivo ejecutable, teniendo en cuenta el cambio de icono, la firma del ejecutable, etc. Es para estar contento, pues, ciertamente, mi proyecto puede ahora trabajar sin problemas con archivos de más de 500 MB, de hecho de más de 1 GB. No ahora mismo, pero, esto me daría pie además a otros posibles proyectos similares.

¡Mejor imposible! Bueno...

Cita:
El código no funcionará compilado a 64bits.
¿Existe alguna razón para esto? ¿Podría modificarse dicho código para que funcionase en Windows de 64 bits? Podría ser interesante, si bien no imprescindible ahora mismo. Lo pregunto ya por abusar... y no quiero hacerlo.

Cita:
Por otro lado, una forna de encontrar fácilmente el fin de fichero sin usar el PE, es adherirle al final una firma fija compuesta de una cadena conocida. Una búsqueda nos dará el tamaño de archivo, y a partir de esa firma podemos añadir más cosas al archivo inicial.
¿No existirá el problema de la posible firma del ejecutable? Lo digo porque me suena haber probado algo parecido, y, todo va bien, hasta que el ejecutable se firma digitalmente. En cualquier caso este hilo, y, particularmente tu mensaje y la clase que compartes, merecerían un poco de atención más calmada...

¡Muchas gracias de nuevo a todos! ¡Y muchas gracias a escafandra en particular, que pudo entender y traducir la función de Angus Johnson!

P.D. Definitivamente he sido muy tonto dejando pasar tanto tiempo en preguntar aquí en el ClubDelphi: ¡mucho mejor me hubiera ido! Ahora toca agarrarse a aquello de que nunca es tarde, etc.
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 02-03-2017 a las 09:18:13.
Responder Con Cita
  #18  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Pues nada, que, pasaba por aquí, y, me he dicho, voy a darle de nuevo las gracias a estos muchachos, que, ciertamente, lo merecen.

¡Muchas gracias de nuevo a todos por vuestra ayuda!
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #19  
Antiguo 02-03-2017
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Cita:
Empezado por dec Ver Mensaje
¡Muchas gracias de nuevo a todos por vuestra ayuda!
A escafandra, los demás sólo somos una cara bonita

LineComment Saludos
Responder Con Cita
  #20  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Cita:
Empezado por roman Ver Mensaje
A escafandra, los demás sólo somos una cara bonita

LineComment Saludos
No; sabes de sobre que eso no es así. Por muchos motivos, pero, además aquí los compañeros propusieron una solución que, aunque no era perfecta, lo cierto es que podía hacer el apaño. Otra cosa es que escafandra se merezca el reconocimiento debido.
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
determinar el tamaño de una fichero lmpadron C++ Builder 5 15-11-2011 00:04:27
Determinar el tamaño óptimo de un datafile SMTZ Oracle 2 03-02-2007 14:04:45
Form Vuelve al Tamaño original Enan0 Varios 5 15-08-2006 19:53:14
como achicar el tamaño de mi Ejecutable Patricio Varios 2 22-11-2005 14:48:45
Como determinar el tamaño de una base de dato ? Raptor Varios 4 06-08-2004 03:17:56


La franja horaria es GMT +2. Ahora son las 15:36:07.


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
Copyright 1996-2007 Club Delphi