Club Delphi  
    Paypal   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

Coloboración Paypal con ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 01-03-2017
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.211
Poder: 22
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
  #2  
Antiguo 01-03-2017
Avatar de Casimiro Noteví
Casimiro Noteví Casimiro Noteví is offline
Merodeador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.714
Poder: 10
Casimiro Noteví Tiene un aura espectacularCasimiro Noteví Tiene un aura espectacular
Responder Con Cita
  #3  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.145
Poder: 36
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
  #4  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.145
Poder: 36
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
  #5  
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
  #6  
Antiguo 02-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.145
Poder: 36
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
  #7  
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
Estoy de broma. Además, sabía desde el principio que éste es un tema de la especialidad de él

Pero sí soy una cara bonita

LineComment Saludos
Responder Con Cita
  #8  
Antiguo 03-03-2017
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.211
Poder: 22
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por dec Ver Mensaje
¿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.
EL PE de ejecutables 64bits tiene algunas diferencias con el de 32bits. Sin ir muy lejos, hay dos versiones de la estructura IMAGE_NT_HEADERS que dependen a su vez de dos versiones de IMAGE_OPTIONAL_HEADER, una para 32 y otra para 64bits.

Esta modificación del código original lo capacita para funcionar compilado a 64bits:
Código Delphi [-]
function GetExeSize: cardinal;
var
  p: PBYTE;
  i, NumSections: integer;
begin
  Result := 0; // if error then result = 0
  p := pointer(GetModuleHandle(nil));      // DosHeader  'MZ'
  inc(p, PImageDosHeader(p)^._lfanew + 4); // ImageFileHeader
  NumSections := PImageFileHeader(p)^.NumberOfSections;
  inc(p, sizeof(TImageFileHeader) + PImageFileHeader(p)^.SizeOfOptionalHeader); // IMAGE_SECTION_HEADER (SectionHeaders)
  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;

PD/ Una cosa más, la información de depuración puede alterar el tamaño del ejecutable con lo que es conveniente hacer las añadiduras al archivo una vez depurado el ejecutable y eliminada esta información.

Saludos.

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

Te estás pasando ya escafandra, muchísimas gracias. No he probado con ejecutables de 64 bits pero lo haré en cuantito pueda. Respecto de la información de depuración, es cierto, que, tal vez desde otro punto de vista, ya me había topado con dicho problema, de modo que "fuerzo" a que el ejecutable "final" se compile en "modo release". Esto lo hago de una forma tal vez "bruta" pero efectiva, añadiendo estas líneas al comenzar el "begin" del archivo de proyecto del ejecutable en cuestión:

Código Delphi [-]
  {$IFDEF DEBUG}
   Remember that...
  {$ENDIF}

De este modo, si estamos en "modo debug", el "Remember that...", en este caso, causará un error de compilación que nos hará saber de no podemos seguir por dicho camino.

Muchas gracias de nuevo escafandra, voy a intentar hacer las pruebas con 64 bits lo antes posible, y tal vez hasta pueda preparar algo más o menos genérico que compartir por aquí.
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #10  
Antiguo 03-03-2017
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.145
Poder: 36
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola a todos,

Adjunto a este mensaje tenéis "la prueba" de que todo lo que hemos tratado en este hilo funciona, es decir, se trata de un programa "escritor" que adjunta un "archivo zip" a un archivo ejecutable "plantilla". Dicho programa plantilla puede ser compilado para 32 ó 64 bits y funcionará igualmente. Echad un vistazo y si tenéis alguna duda no dejéis de comentarlo aquí mismo. El proyecto está hecho con Delphi XE2: no sé hasta qué punto funcionará en versiones más "antiguas" de Delphi.

P.D. Cabe añadir que el archivo que se adjunta al ejecutable "plantilla" es un archivo zip no por gusto. Como he comentado arriba, si se llega a firmar el ejecutable plantilla, pareciera que la función "GetExeSize" no se percata de dicha información añadida al ejecutable, sin embargo, no hay problema en descomprimir el "archivo zip" incrustado en el ejecutable, por ejemplo, con los componentes "Abbrevia" (pero también con programas como IZArc), porque, la información que queda "detrás" del "archivo zip" parece ignorarse sin más. No sé si estoy equivocado, pero, me parece que esto implica que el archivo tenga que ser un "archivo zip", puesto que de otro modo igual entraríamos en problemas "corrompiendo" el archivo original, ya digo, todo esto, si se llega a firmar el ejecutable "plantilla", que, por otro lado, será lo más normal.
Archivos Adjuntos
Tipo de Archivo: zip ExeUtils.zip (213,4 KB, 7 visitas)
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #11  
Antiguo 03-03-2017
Avatar de jhonny
jhonny jhonny is offline
Jhonny Suárez
 
Registrado: may 2003
Ubicación: Colombia
Posts: 7.070
Poder: 32
jhonny Va camino a la famajhonny Va camino a la fama
Muy interesante todo lo que leo acá, no he sido quien abrió el hilo pero muchas gracias a todos.
__________________
Lecciones de mi Madre. Tema: modificación del comportamiento, "Pará de actuar como tu padre!"

http://www.purodelphi.com/
http://www.nosolodelphi.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 18:56:41.


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