Este es un ejemplo, muy sencillo, de como crear un fichero autoextraible. Este pequeño programa funciona de una manera muy simple, si se le pasa como parámetro un archivo, crea una copia de si mismo donde se incrusta una copia comprimida del archivo que se le paso como parámetro. Y si por el contrario, el programa detecta que tiene un fichero incrustado lo extrae.
El ejemplo es intencionadamente simple, por ese motivo las rutas donde se guardan los ficheros las calcula internamente el programa en vez de mostrar un dialogo "Guardar como ...", que seria lo mas correcto. En caso de utilizar el código en una aplicación "real", no sería muy complicado añadir estos diálogos, aunque sería conveniente reducir al mínimo el uso de estos para no incrementar demasiado el tamaño del ejecutable, que en este caso si es importante.
La compresión se realiza usando zlib, que viene incluido con delphi, así que no necesitamos ningún componente de terceros, ni dlls, ni nada que engorde el ejecutable o haga más difícil su distribución.
El código es el siguiente (es una aplicación sin formularios):
program SimpleSFX; uses Windows, SysUtils, Classes, ZLib; const strUID = '{EE6068A1-487C-4ED0-9DCA-9DCB0E6B35CB}'; // Esta funcion comprueba si existe un recurso con el nombre strUID function CheckRes: Boolean; var hRes: HRSRC; begin Result:= FALSE; hRes:= FindResource(0, strUID, RT_RCDATA); if hRes <> 0 then Result:= TRUE; end; // Crea una copia del ejecutable con el archivo incrustado procedure CrearSFX; var Src: TFileStream; Temp: TMemoryStream; Path: String; hUpdateRes: THANDLE; begin // Abrimos el fichero de origen como "solo lectura" Src:= TFileStream.Create(ParamStr(1),fmOpenRead); try Temp:= TMemoryStream.Create; try // Comprimimos el fichero with TCompressionStream.Create(clMax,Temp) do try CopyFrom(Src,0); finally Free; end; // La ruta del nuevo fichero es la misma que el fichero de origen + .exe Path:= ParamStr(1)+'.exe'; if CopyFile(PChar(ParamStr(0)),PChar(Path),FALSE) then begin hUpdateRes:= BeginUpdateResource(PChar(Path), FALSE); if hUpdateRes <> 0 then if UpdateResource(hUpdateRes, RT_RCDATA,strUID,0,Temp.Memory,Temp.Size) then // Guardamos el fichero como un recurso if EndUpdateResource(hUpdateRes,FALSE) then Exit; DeleteFile(Path); end; finally Temp.Free; end; finally Src.Free; end; end; const BUFFERSIZE = 1024*1024; procedure Extraer; var Src: TResourceStream; Dst: TFileStream; Path: String; Buffer: PByte; i: Integer; begin // Abrimos el recurso donde esta guardado el fichero Src:= TResourceStream.Create(0,strUID,RT_RCDATA); try // El fichero se extrae en la mima ruta que el ejecutable - .exe Path:= ChangeFileExt(ParamStr(0),EmptyStr); if Path <> ParamStr(0) then begin Dst:= TFileStream.Create(Path,fmCreate); try // Descomprimimos el fichero with TDeCompressionStream.Create(Src) do try GetMem(Buffer,BUFFERSIZE); try i:= Read(Buffer^,BUFFERSIZE); while i > 0 do begin Dst.Write(Buffer^,i); i:= Read(Buffer^,BUFFERSIZE); end; finally FreeMem(Buffer); end; finally Free; end; finally Dst.Free; end; end; finally Src.Free; end; end; begin // Comprobamos si tenemos algun archivo incrustado if CheckRes then begin // Si lo tenemos lo extraemos Extraer; end else // Si no lo tenemos y nos pasan uno como parametro, lo incrustamos if ParamCount = 1 then CrearSFX; end.
El código se puede bajar de aquí
Para usarlo simplemente arrastra un fichero sobre el ejecutable y automáticamente se creara un fichero .exe al lado del fichero original. También puedes pasarle el fichero como parámetro, por ejemplo "SimpleSFX archivo.txt"
Enlaces de interés:
http://www.zlib.net/
Comentarios
Como siempre, muy interesante
Como siempre, muy interesante tu entrada.
Gracias Domingo.
Desconocía que existía de
Desconocía que existía de fábrica un componente de compresión en Delphi.
Muchas gracias, fenomenal post.
Muy bueno :-)
Muy bueno :-)
Como siempre amigo, muy
Como siempre amigo, muy interesante el código que publicas y sobre todo con mucha aplicación.
Saludos
¡Hijo de ****! ¡Desde que
¡Hijo de ****! ¡Desde que tengo memoria que quería saber cómo se hacía!
Un grande, Seoane, sos un grande.
Hola Seoane,
Hola Seoane,
me ha parecido genial, muy bueno.
Tengo una duda respecto al strUID, ¿cómo lo has calculado?
Saludos y gracias
Es un número aleatorio que
Es un número aleatorio que utilizo como "identificador único", cualquier otro nombre "único" hubiese servido también. Para generarlo simplemente pulsa en el editor de delphi Ctrl+Mayus+G y se insertara un nuevo número en el texto de forma automática.