En el caso de trabajar con datasnap podriamos publicar nuestra aplicacion en una pequeña aplicacion web y podria ser ejecutada desde cualquier lugar siempre y cuando el servidor datasnap este publico.
Hasta aca todo parece ir sobre ruedas pero aparecen algunas cuestiones, que casi todo pueda embeberse en el ejecutable no significa que absolutamente todo este incluido, pueden quedarnos cosas afuera.
Podemos poner como ejemplo alguna DLL imprescindible con algún driver que no puede faltar, incluso algún archivo ini para guardar configuraciones básicas, previas a todo, IP/Puerto etc y no puedo exigirle al usuario que descargue ademas de un ejecutable un archivo ini, un driver de base de datos etc, esto tiene que funcionar con un click
Solución
Arrancamos con el ejemplo mas sencillo, supongamos un archivo ini, donde vamos a guardar dirección ip de mi servidor datasnap y alguna que otra cosa que necesite.
Cuando alguien descargue nuestra aplicación puede ponerla en alguna carpeta o ejecutarla directamente
Si decide ejecutar la aplicación correrá en algún directorio que el sistema operativo determine, si se selecciona guardar se pedirá donde colocar el ejecutable. Vamos a tomar como general el caso mas problemático que es cuando seleccionan directamente "Ejecutar". La solución pensada es colocar un ini inicial en alguna carpeta propietaria del usuario o de la aplicación (Para evitar problemas de permisos entre otras cosas), pero hacerlo si y solo si este archivo aun no existe. Entonces primer problema preguntarle al sistema operativo por la carpeta del usuario, aquí el código con algunos comentarios (Solo coloco la implementación, son métodos de clase) :
....uses SHFolder, windows, Classes, SysUtils;
{Opciones posibles
CSIDL_DESKTOP
CSIDL_INTERNET
CSIDL_PROGRAMS
CSIDL_CONTROLS
CSIDL_PRINTERS
CSIDL_PERSONAL
CSIDL_FAVORITES
CSIDL_STARTUP
CSIDL_RECENT
CSIDL_SENDTO
CSIDL_BITBUCKET
CSIDL_STARTMENU
CSIDL_DESKTOPDIRECTORY
CSIDL_DRIVES
CSIDL_NETWORK
CSIDL_NETHOOD
CSIDL_FONTS
CSIDL_TEMPLATES
CSIDL_COMMON_STARTMENU
CSIDL_COMMON_PROGRAMS
CSIDL_COMMON_STARTUP
CSIDL_COMMON_DESKTOPDIRECTORY
CSIDL_APPDATA
CSIDL_PRINTHOOD
CSIDL_ALTSTARTUP
CSIDL_COMMON_ALTSTARTUP
CSIDL_COMMON_FAVORITES
CSIDL_INTERNET_CACHE
CSIDL_COOKIES
CSIDL_HISTORY
CSIDL_SYSTEM
}
class function TGenericsUtils.GetUserIniFile(ApplicationExeName:String):String;
begin
result := ChangeFileExt(IncludeTrailingBackslash( TGenericsUtils.GetSpecialFolderPath(CSIDL_APPDATA))+ExtractFileName(ApplicationExeName), '.ini');
end;
class function TGenericsUtils.GetSpecialFolderPath(folder : integer) : string;
const
SHGFP_TYPE_CURRENT = 0;
var
path: array [0..MAX_PATH] of char;
begin
//[Current User]\My Documents
//CSIDL_PERSONAL;
//All Users\Application Data
//CSIDL_COMMON_APPDATA;
//[User Specific]\Application Data
//CSIDL_LOCAL_APPDATA;
//Program Files
//CSIDL_PROGRAM_FILES;
//All Users\Documents
//CSIDL_COMMON_DOCUMENTS;
if SUCCEEDED(SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,@path[0])) then
Result := path
else
Result := '';
end;
Aquí el método GetUserIniFile me retornara el nombre del ini de mi aplicación, ademas del nombre del archivo esta la ruta completa a el, ruta solicitada al sistema operativo por medio del método GetSpecialFolderPath.
fn := TGenericsUtils.GetUserIniFile(Application.ExeName);
if not FileExists(fn) then begin
//Si no existe genero un ini basico.....
TGenericsUtils.ExtractResource('ARCHIVOINI', fn);
end;
El código anterior pregunta por la existencia del ini, en caso de no existir llama a una método "ExtractResource" que extrae un recurso del ejecutable, un recurso llamada "ARCHIVOINI" porque yo previamente embebí un ini de arranque en el ejecutable, tema que veremos mas adelante, para no tener que tomarme el trabajo de crear todas las secciones y claves que en un archivo ini grande puede exigir varias lineas de código.
Aquí el código de "ExtractResource":
class procedure TGenericsUtils.ExtractResource(ResName: String; Filename: String);
var
ResStream: TResourceStream;
begin
ResStream:= TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
ResStream.Position:= 0;
ResStream.SaveToFile(Filename);
finally
ResStream.Free;
end;
end;
Ademas esto me sirve para cuando tenga que embeber una dll y otro elemento que no puedo generar por código.
¿Como meto recursos en el ejecutable?
El delphi XE es muy sencillo, seleccione el item del menu como indica la figura
Luego añada los recursos colocando le tipo correspondiente y un nombre que luego sera usado para pedirle al método ExtractResource que realice la extracción deseada
En la próxima entrada extendemos la solución a modulos, dll, bpls etc