Difference between revisions of "Multiplatform Programming Guide/es"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(23 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Multiplatform Programming Guide}}
+
{{Multiplatform Programming Guide}}[[Category:Castellano]][[Category:Español]]
  
 
   Esta página es el comienzo de una tutoría sobre la creación de aplicaciones multiplataforma con Lazarus. Comprenderá tanto las precauciones necesarias para garantizar que un programa pueda ser fácilmente migrado y el proceso de migración de un programa ya existente. Todos están invitados a ayudar a mejorar el artículo.
 
   Esta página es el comienzo de una tutoría sobre la creación de aplicaciones multiplataforma con Lazarus. Comprenderá tanto las precauciones necesarias para garantizar que un programa pueda ser fácilmente migrado y el proceso de migración de un programa ya existente. Todos están invitados a ayudar a mejorar el artículo.
Line 9: Line 9:
 
=== ¿Cuántas ''cajas'' son necesarias? ===
 
=== ¿Cuántas ''cajas'' son necesarias? ===
  
   Para responder a esta pregunta, primero debes determinar quiénes serán los usuarios potenciales y la forma en que tu programa se utilizará. Esta cuestión depende de dónde va a implementar la aplicación.
+
   Para responder a esta pregunta, primero debes determinar quiénes serán los usuarios potenciales y la forma en que tu programa se utilizará. Esta cuestión depende de dónde vas a implementar la aplicación.
  
   Si está desarrollando una aplicación de escritorio genérica, Windows es, obviamente, la plataforma más importante, pero también incluye Mac OS X y/o versiones de Linux a veces puede ser la diferencia que haga que tu aplicación sea elegido en lugar de una aplicación no multiplataforma.
+
   Si estás desarrollando una aplicación de escritorio genérica, Windows es, obviamente, la plataforma más importante, pero también incluye Mac OS X y/o versiones de Linux a veces puede ser la diferencia que haga que tu aplicación sea elegida en lugar de una aplicación no multiplataforma.
  
   La popularidad de los distintos sistemas operativos de escritorio difiere según el país, por el tipo de aplicaciones utilizado, y con el público al que se dirige, así que no hay regla general. Por ejemplo, Mac OS X es muy popular en América del Norte y Europa occidental, mientras que en América del Sur los Macs se circunscriben principalmente a trabajos de vídeo y sonido.
+
   La popularidad de los distintos sistemas operativos de escritorio difiere según el país, por el tipo de aplicaciones utilizado, y por el público al que se dirige, así que no hay regla general. Por ejemplo, Mac OS X es muy popular en América del Norte y Europa occidental, mientras que en América del Sur los Macs se circunscriben principalmente a trabajos de vídeo y sonido.
  
 
   En muchos proyectos por contrato una sola plataforma es relevante, y eso no es un problema. Free Pascal y Lazarus son capaces de escribir código destinado a una plataforma específica. Puede, por ejemplo, acceder a la API completa de Windows para escribir un programa de Windows, bien integrado.
 
   En muchos proyectos por contrato una sola plataforma es relevante, y eso no es un problema. Free Pascal y Lazarus son capaces de escribir código destinado a una plataforma específica. Puede, por ejemplo, acceder a la API completa de Windows para escribir un programa de Windows, bien integrado.
Line 19: Line 19:
 
   Si estás desarrollando una aplicación que se ejecuta en un servidor Web, una plataforma Unix en uno de sus varios sabores es usada comúnmente. En este caso, tal vez sólo Linux, Solaris, *BSD y otros Unix tiene sentido como plataformas de destino, aunque es posible que desee agregar el soporte para Windows para que sea más completa.
 
   Si estás desarrollando una aplicación que se ejecuta en un servidor Web, una plataforma Unix en uno de sus varios sabores es usada comúnmente. En este caso, tal vez sólo Linux, Solaris, *BSD y otros Unix tiene sentido como plataformas de destino, aunque es posible que desee agregar el soporte para Windows para que sea más completa.
  
   Una vez que domines el desarrollo multiplataforma, por lo general sólo debes centrarte en el problema para el cual la aplicación se ha diseñado para resolver y hacer la mayor parte de su desarrollo en cualquier plataforma que dispongas o te sientas más cómodo. Es decir, una vez que has abordado todas las cuestiones multiplataforma en su diseño, se puede pasar por alto en gran medida las otras plataformas, tanto como lo harías en el desarrollo para una plataforma única. Sin embargo, en algún momento tendrás que poner a prueba implementar y ejecutar tu programa para las otras plataformas y será útil tener acceso sin restricciones a equipos que ejecuten los sistemas de destino con toda la información operacional. Si no deseas varias máquinas reales, usted puedes mirar en la configuración de una máquina de arranque dual Windows-Linux o con Windows y Linux en tu Mac a través de ''Bootstrap'' o bajo un emulador como Parallels o VMware.
+
   Una vez que domines el desarrollo multiplataforma, por lo general sólo debes centrarte en el problema para el cual la aplicación se ha diseñado para resolver y hacer la mayor parte de su desarrollo en cualquier plataforma que dispongas o te sientas más cómodo. Es decir, una vez que has abordado todas las cuestiones multiplataforma en su diseño, se puede pasar por alto en gran medida las otras plataformas, tanto como lo harías en el desarrollo para una plataforma única. Sin embargo, en algún momento tendrás que poner a prueba implementar y ejecutar tu programa para las otras plataformas y será útil tener acceso sin restricciones a equipos que ejecuten los sistemas de destino con toda la información operacional. Si no deseas varias máquinas reales, usted puedes mirar en la configuración de una máquina de arranque dual Windows-Linux o con Windows y Linux en tu Mac a través de ''Bootstrap'' o bajo un emulador como Parallels, Virtual Box o VMware.
  
 
== Programación multiplataforma ==
 
== Programación multiplataforma ==
Line 35: Line 35:
 
   Las funciones de archivo de la RTL utilizan la de codificación del sistema para nombres de archivo. En Windows esta es una de las páginas de códigos de Windows, mientras que Linux, BSD y Mac OS X suelen utilizar UTF-8. La  unidad '''FileUtil''' de la LCL ofrece funciones de archivo que utilizan caracteres UTF-8 como el resto de la LCL.
 
   Las funciones de archivo de la RTL utilizan la de codificación del sistema para nombres de archivo. En Windows esta es una de las páginas de códigos de Windows, mientras que Linux, BSD y Mac OS X suelen utilizar UTF-8. La  unidad '''FileUtil''' de la LCL ofrece funciones de archivo que utilizan caracteres UTF-8 como el resto de la LCL.
  
<delphi> // AnsiToUTF8 y UTF8ToAnsi necesitan de un gestor de ''widestring'' en Linux, BSD y MacOSX
+
<syntaxhighlight lang=pascal> // AnsiToUTF8 y UTF8ToAnsi necesitan de un gestor de ''widestring'' en Linux, BSD y MacOSX
 
  // pero usualmente estos SO utilizan UTF-8 como codificación del sistema por lo que el gestor de ''widestring''
 
  // pero usualmente estos SO utilizan UTF-8 como codificación del sistema por lo que el gestor de ''widestring''
 
  // no es necesario.
 
  // no es necesario.
Line 74: Line 74:
  
 
  // otras
 
  // otras
  function SysErrorMessageUTF8(ErrorCode: Integer): String;</delphi>
+
  function SysErrorMessageUTF8(ErrorCode: Integer): String;</syntaxhighlight>
 +
 
 +
=== Nombres de archivo vacios y delimitadores de ruta duplicados ===
 +
&nbsp;&nbsp;&nbsp;Windows permite nombres de archivo vacíos, Linux, BSD y Mac OS X por contra no.
 +
Es por eso que ''FileExistsUTF8('..\')'' comprueba en Windows un archivo sin nombre en el directorio padre. En Unix, como los sistemas Linux, BSD y OS X, un archivo vacío se asigna al directorio y los directorios se tratan como archivos. Esto significa que ''FileExistsUTF8 ('../')'' en Unix comprueba la existencia del directorio padre, que normalmente será verdadero.
 +
 
 +
&nbsp;&nbsp;&nbsp;Por la misma razón los delimitadores de ruta duplicados en un nombre de archivo tienen tratamiento distinto. En Windows 'C:\' no es lo mismo que 'C:\\', mientras que en los *nix la ruta '/usr//' es equivalente a '/usr/', y si '/usr' es un directorio, incluso estas  tres son iguales. Esto es importante cuando se concatena nombres de archivo. Por ejemplo:
 +
<syntaxhighlight lang=pascal> FullFilename:=FilePath+PathDelim+ShortFilename; // el resultado tiene dos delimitadores de ruta y da resultados distinto en Windows y Linux
 +
FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // crea un solo delimitador de ruta
 +
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // crea un solo delimitador de ruta y es más limpio</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;La función TrimFilename reemplaza delimitadores de ruta dobles por un solo y recorta rutas '..'. Por ejemplo /usr//lib/../src es recortado a /usr/src.
 +
 
 +
&nbsp;&nbsp;&nbsp;Si quieres saber si un directorio existe utiliza '''DirectoryExistsUTF8'''.
 +
 
 +
&nbsp;&nbsp;&nbsp;Otra tarea común es comprobar si existe la parte de la ruta de un nombre de archivo. Puedes obtener la ruta con '''ExtractFilePath''', pero este contendrá el delimitador de ruta. En sistemas Unix simplemente usa '''FileExistsUTF8''' para la ruta. Por ejemplo FileExistsUTF ('/home/usuario/') será cierto si el directorio /home/usuario existe. En Windows debes utilizar la función '''DirectoryExistsUTF8''', pero antes se debe eliminar el delimitador de ruta, por ejemplo con la función '''ChompPathDelim '''. En Unix el directorio raíz es '/' la función ChompPathDelim creará una cadena vacía. La función '''DirPathExists'''  trabaja como la función '''DirectoryExistsUTF8''', pero recorta la ruta dada.
  
 
=== Codificación de texto ===
 
=== Codificación de texto ===
Line 81: Line 96:
 
No existe una regla del 100% para saber qué codificación utiliza un archivo de texto. La unidad de la LCL '''lconvencoding''' tiene la función de adivinar la codificación:
 
No existe una regla del 100% para saber qué codificación utiliza un archivo de texto. La unidad de la LCL '''lconvencoding''' tiene la función de adivinar la codificación:
  
<delphi> function GuessEncoding(const s: string): string;
+
<syntaxhighlight lang=pascal> function GuessEncoding(const s: string): string;
  function GetDefaultTextEncoding: string;</delphi>
+
  function GetDefaultTextEncoding: string;</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;Y contiene funciones para la conversión de una codificación a otra:
 
&nbsp;&nbsp;&nbsp;Y contiene funciones para la conversión de una codificación a otra:
  
<delphi> function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;
+
<syntaxhighlight lang=pascal> function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;
  
 
  function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM
 
  function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM
Line 99: Line 114:
 
  function UTF8ToCP1251(const s: string): string; // cyrillic
 
  function UTF8ToCP1251(const s: string): string; // cyrillic
 
  function UTF8ToCP1252(const s: string): string; // latin 1
 
  function UTF8ToCP1252(const s: string): string; // latin 1
  ...</delphi>
+
  ...</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;Por ejemplo, para cargar un archivo de texto y convertirlo a UTF-8 se puede utilizar:
 
&nbsp;&nbsp;&nbsp;Por ejemplo, para cargar un archivo de texto y convertirlo a UTF-8 se puede utilizar:
  
<delphi> var
+
<syntaxhighlight lang=pascal> var
 
   sl: TStringList;
 
   sl: TStringList;
 
   OriginalText: String;
 
   OriginalText: String;
Line 117: Line 132:
 
     sl.Free;
 
     sl.Free;
 
   end;
 
   end;
  end;</delphi>
+
  end;</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;Y para guardar el archivo de texto con la codificación del sistema usaremos esto:
 
&nbsp;&nbsp;&nbsp;Y para guardar el archivo de texto con la codificación del sistema usaremos esto:
<delphi> sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);
+
<syntaxhighlight lang=pascal> sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);
  sl.SaveToFile('sometext.txt');</delphi>
+
  sl.SaveToFile('sometext.txt');</syntaxhighlight>
  
 
=== Archivos de configuración ===
 
=== Archivos de configuración ===
Line 133: Line 148:
 
&nbsp;&nbsp;&nbsp;Estos son ejemplos de la salida de las funciones de ruta por defecto en diferentes sistemas:
 
&nbsp;&nbsp;&nbsp;Estos son ejemplos de la salida de las funciones de ruta por defecto en diferentes sistemas:
  
<delphi> program proyecto1;
+
<syntaxhighlight lang=pascal> program proyecto1;
  
 
  {$mode objfpc}{$H+}
 
  {$mode objfpc}{$H+}
Line 145: Line 160:
 
   WriteLn(GetAppConfigFile(True));
 
   WriteLn(GetAppConfigFile(True));
 
   WriteLn(GetAppConfigFile(False));
 
   WriteLn(GetAppConfigFile(False));
  end.</delphi>
+
  end.</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;La salida en un sistema GNU/Linux con FPC 2.2.2. Tenga en cuenta que el uso de ''true'' está libre de errores, ya están fijadas en 2.2.3:
 
&nbsp;&nbsp;&nbsp;La salida en un sistema GNU/Linux con FPC 2.2.2. Tenga en cuenta que el uso de ''true'' está libre de errores, ya están fijadas en 2.2.3:
Line 185: Line 200:
 
=== Archivos de datos y recursos ===
 
=== Archivos de datos y recursos ===
  
Una pregunta muy común es donde almacenar los archivos de datos que una aplicación puede necesitar, como Imágenes, Música, archivos XML, archivos de bases de datos, archivos de ayuda, etc. Desafortunadamente no hay funciones multi-plataforma para obtener la mejor localización para buscar archivos de datos. La solución es implementar de forma diferente en cada plataforma usando IFDEFs.
+
&nbsp;&nbsp;&nbsp;Una pregunta muy común es donde almacenar los archivos de datos que una aplicación puede necesitar, como imágenes, música, archivos XML, archivos de bases de datos, archivos de ayuda, etc. Desafortunadamente no hay funciones multi-plataforma para obtener la mejor localización para buscar archivos de datos. La solución es implementar de forma diferente en cada plataforma usando IFDEFs.
  
En Windows usted,simplemente puede asumir que los archivos están en el mismo directorio que el ejecutable, o en una posición relacionada con él.
+
&nbsp;&nbsp;&nbsp;En Windows, simplemente puedes asumir que los archivos están en el mismo directorio que el ejecutable, o en una posición relativa al mismo.
  
En la mayoría de los sistemas operativos tipo Unix(como Linux, BSDs, solaris, etc), los archivos de datos están situados en posiciones fijas, que pueden ser algo como: /usr/share/app_name or /opt/app_name
+
&nbsp;&nbsp;&nbsp;En la mayoría de los sistemas operativos tipo Unix(como Linux, BSDs, solaris, etc), los archivos de datos están situados en posiciones fijas, que pueden ser algo como: ''/usr/share/app_name'' o ''/opt/app_name''.
  
La mayoría de programas se ejecutarán sin privilegios de administrador y por lo tanto un software multi-plataforma deberá almacenar los archivos de datos en un lugar donde siempre tenga permiso para leerlos, pero no necesariamente para modificarlos,y archivos de configuración en lugares donde tenga permiso para escribir en ellos.  
+
&nbsp;&nbsp;&nbsp;La mayoría de programas se ejecutarán sin privilegios de administrador y por lo tanto un programa multi-plataforma deberá almacenar los archivos de datos en un lugar donde siempre tenga permiso para leerlos, pero no necesariamente para modificarlos, y los archivos de configuración en lugares donde tenga además permiso para escribir en ellos.  
  
Mac OS X es una excepción entre los sistemas operativos tipo UNIX. There the best way to deploy applications is using an application bundle, which includes all files your software will need. Then your resource files should be located inside the bundle, so it can be moved and continue working normally. You need to use CoreFoundation API calls to find the location of the bundle.
+
&nbsp;&nbsp;&nbsp;Mac OS X es una excepción entre los sistemas operativos tipo UNIX. Aquí, la mejor manera de implementar aplicaciones es utilizar un paquete de aplicaciones, que incluye todos los archivos que la aplicación va a necesitar. Así, los archivos de recursos deben estar dentro del paquete, por lo que se puede mover y seguir trabajando normalmente. Tienes que usar llamadas a la API CoreFoundation para localizar el paquete.
  
==== Example ====
+
==== Ejemplo ====
  
This section presents a particular solution where under Windows the data files are stored on the same directory as the executable (or any other directory based on it, like MyDirectory + 'data' + PathDelim + 'myfile.dat'), and on Unixes it will be on a directory read from a configuration file. If no configuration file exists or it contains no info, then a constant ('/usr/share/myapp/') is utilized as the default directory.
+
&nbsp;&nbsp;&nbsp;Esta sección presenta una solución particular para el caso en que en Windows los archivos de datos están en el mismo directorio que el ejecutable (o en un directorio relativo a este, tal que MiDirectorio + 'datos' + SeparadorRuta + 'miArchivo.datos'), y en *nix están en un directorio que se lee de un archivo de configuración. Si no existe el archivo de configuración o este no contiene información, entonces la constante ('/usr/share/myapp/') se utiliza como el directorio predeterminado.
  
The configuration file path is located with the GetAppConfigFile function from the Free Pascal Runtime Library.
+
&nbsp;&nbsp;&nbsp;El archivo de configuración se localiza con la función ''GetAppConfigFile'' de la RTL de Free Pascal.
  
Below is a full unit which you can use at your applications.
+
&nbsp;&nbsp;&nbsp;A continuación se muestra una unidad completa que puedes utilizar en tus aplicaciones.
  
<delphi>
+
<syntaxhighlight lang=pascal> unit appsettings;
unit appsettings;
 
  
 
interface
 
interface
Line 213: Line 227:
  
 
uses
 
uses
{$IFDEF Win32}
 
  Windows,
 
{$ENDIF}
 
 
   Classes, SysUtils, Forms, IniFiles, constants;
 
   Classes, SysUtils, Forms, IniFiles, constants;
  
Line 224: Line 235:
 
  TConfigurations = class(TObject)
 
  TConfigurations = class(TObject)
 
  private
 
  private
 +
  function GetResourcesPath: string;
 +
public
 +
  {otros ajustes serán campos aquí}
 
   ConfigFilePath: string;
 
   ConfigFilePath: string;
public
+
   ResourcesPath: string;
   {other settings as fields here}
 
  MyDirectory: string;
 
 
   constructor Create;
 
   constructor Create;
 
   destructor Destroy; override;
 
   destructor Destroy; override;
Line 233: Line 245:
 
   procedure Save(Sender: TObject);
 
   procedure Save(Sender: TObject);
 
  end;
 
  end;
 
  
 
var
 
var
Line 239: Line 250:
  
 
implementation
 
implementation
 +
 +
{$IFDEF Win32}
 +
uses
 +
  Windows;
 +
{$ENDIF}
 +
{$ifdef Darwin}
 +
uses
 +
  MacOSAll;
 +
{$endif}
  
 
const
 
const
Line 246: Line 266:
 
   SectionUnix = 'UNIX';
 
   SectionUnix = 'UNIX';
  
   IdentMyDirectory = 'MyDirectory';
+
   IdentResourcesPath = 'ResourcesPath';
  
 
{ TConfigurations }
 
{ TConfigurations }
Line 258: Line 278:
 
  ConfigFilePath := GetAppConfigFile(False) + '.conf';
 
  ConfigFilePath := GetAppConfigFile(False) + '.conf';
 
{$endif}
 
{$endif}
 +
 +
  ResourcePath := GetResourcesPath();
  
 
  ReadFromFile(nil);
 
  ReadFromFile(nil);
Line 275: Line 297:
 
  MyFile := TIniFile.Create(ConfigFilePath);
 
  MyFile := TIniFile.Create(ConfigFilePath);
 
  try
 
  try
   MyFile.WriteString(SectionUnix, IdentMyDirectory, MyDirectory);
+
   MyFile.WriteString(SectionUnix, IdentResourcesPath, ResourcesPath);
 
  finally
 
  finally
 
   MyFile.Free;
 
   MyFile.Free;
Line 287: Line 309:
 
  MyFile := TIniFile.Create(ConfigFilePath);
 
  MyFile := TIniFile.Create(ConfigFilePath);
 
  try
 
  try
   // Here you can read other information from the config file
+
   // Aquí puedes leer más información del archivo de configuración
  
 
{$ifdef Win32}
 
{$ifdef Win32}
   MyDirectory := MyFile.ReadString(SectionUnix, IdentMyDirectory,
+
   ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
 
ExtractFilePath(Application.EXEName));
 
ExtractFilePath(Application.EXEName));
 
{$else}
 
{$else}
   MyDirectory := MyFile.ReadString(SectionUnix, IdentMyDirectory,
+
  {$ifndef darwin}
 +
   ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
 
DefaultDirectory);
 
DefaultDirectory);
 +
  {$endif}
 
{$endif}
 
{$endif}
 
  finally
 
  finally
 
   MyFile.Free;
 
   MyFile.Free;
 
  end;
 
  end;
 +
end;
 +
 +
function TConfigurations.GetResourcesPath(): string;
 +
begin
 +
{$ifdef Darwin}
 +
var
 +
  pathRef: CFURLRef;
 +
  pathCFStr: CFStringRef;
 +
  pathStr: shortstring;
 +
{$endif}
 +
begin
 +
{$ifdef UNIX}
 +
{$ifdef Darwin}
 +
  pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
 +
  pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
 +
  CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
 +
  CFRelease(pathRef);
 +
  CFRelease(pathCFStr);
 +
 +
  Result := pathStr + BundleResourcesDirectory;
 +
{$else}
 +
  Result := DefaultDirectory;
 +
{$endif}
 +
{$endif}
 +
 +
{$ifdef Windows}
 +
  Result := ExtractFilePath(Application.EXEName);
 +
{$endif}
 
end;
 
end;
  
Line 309: Line 361:
 
  FreeAndNil(vTranslations);
 
  FreeAndNil(vTranslations);
  
end.
+
end.</syntaxhighlight>
</delphi>
+
 
 +
&nbsp;&nbsp;&nbsp;y este es un ejemplo de utilización de esta unidad:
  
and here example code of how to use that unit:
+
<syntaxhighlight lang=pascal> bmp := TBitmap.Create
 +
try
 +
  bmp.LoadFromFile(vConfigurations.ResourcesPath + 'MiMapaBits.bmp');
 +
finally
 +
  bmp.Free;
 +
end;</syntaxhighlight>
  
<delphi>
+
=== 32/64 bits ===
  bmp := TBitmap.Create
 
  try
 
    bmp.LoadFromFile(vConfigurations.MyDirectory + 'MyBitmap.bmp');
 
  finally
 
    bmp.Free;
 
  end;
 
</delphi>
 
  
=== 32/64 bit ===
+
==== Conversión de Punteros / Enteros ====
  
==== Pointer / Integer Typecasts ====
+
&nbsp;&nbsp;&nbsp;Un Puntero en 64 bits necesita 8 bytes en vez de los 4 sobre 32 bits. El tipo 'Integer' sigue siendo por compatibilidad de 32 bits en todas las plataformas. Esto significa que no se puede convertir a los punteros en números enteros y a la inversa (no son compatibles). FPC define dos tipos para realizar esto: PtrInt y PtrUInt. PtrInt es un entero con signo de 32 bits en plataformas de 32 bits y de 64 bits en las plataformas de 64 bits. Lo mismo para el número entero PtrUInt, pero sin signo en este caso.
  
Pointers under 64bit need 8 bytes instead of 4 on 32bit. The 'Integer' type remains for compatibility 32bit on all platforms. This means you can not typecast pointers into integers and back. FPC defines two types for this: PtrInt and PtrUInt. PtrInt is a 32bit signed integer on 32 bit platforms and a 64bit signed integer on 64bit platforms. The same for PtrUInt, but ''unsigned'' integer instead.
+
&nbsp;&nbsp;&nbsp;Utilización en código que debe trabajar con Delphi y FPC:
  
Use for code that should work with Delphi and FPC:
+
<syntaxhighlight lang=pascal> {$IFNDEF FPC}
{$IFNDEF FPC}
 
 
  type
 
  type
 
   PtrInt = integer;
 
   PtrInt = integer;
 
   PtrUInt = cardinal;
 
   PtrUInt = cardinal;
  {$ENDIF}
+
  {$ENDIF}</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Sustituye todos las ocurrecias de '''integer(unPuntero_o_unObjeto)''' por '''PtrInt(unPuntero_o_unObjeto)'''.
  
Replace all '''integer(SomePointerOrObject)''' with '''PtrInt(SomePointerOrObject)'''.
+
=== Formato de multiByte ===
  
=== Endianess ===
+
&nbsp;&nbsp;&nbsp;Las plataformas de Intel son ''little endian'', eso significa que el byte menos significativo va primero. Por ejemplo, los dos bytes del ''word'' $1234 se almacenan como $34 $12 en estos sistemas.
 +
En los sistemas ''big endian'' como el IBM powerpc los dos bytes de $1234 se almacenan como $12 $34. La diferencia es importante al leer los archivos creados en otros sistemas.
  
Intel platforms are little endian, that means the least significant byte comes first. For example the two bytes of a word $1234 is stored as $34 $12 on little endian systems.
+
&nbsp;&nbsp;&nbsp;Usaremos esto en código que trabaje en ambos sistemas:
On big endian systems like the powerpc the two bytes of a word $1234 are stored as $12 $34. The difference is important when reading files created on other systems.
 
  
Use for code that should work on both:
+
<syntaxhighlight lang=pascal> {$IFDEF ENDIAN_BIG}
{$IFDEF ENDIAN_BIG}
 
 
  ...
 
  ...
 
  {$ELSE}
 
  {$ELSE}
 
  ...
 
  ...
  {$ENDIF}
+
  {$ENDIF}</syntaxhighlight>
  
The opposite is ENDIAN_LITTLE.
+
&nbsp;&nbsp;&nbsp; Lo contrario es <code> {$IFDEF  ENDIAN_LITTLE}</code>.
  
The system unit provides plenty of endian converting functions, like SwapEndian, BEtoN (big endian to current endian), LEtoN (little endian to current endian), NtoBE (current endian to big endian) and NtoLE (current endian to little endian).
+
&nbsp;&nbsp;&nbsp;La unidad system proporciona muchas funciones de conversión, como  de SwapEndian, BEtoN (big endian a endian actual),LEtoN (little endian a endian actual), NtoBE (endian actual a big endian) y NtoLE (endian actual a little endian).
  
  
==== Libc and other special units ====
+
==== Libc y otras unidades especiales ====
  
Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.
+
&nbsp;&nbsp;&nbsp;Evita las unidades heredadas cómo "oldlinux" y "libc" no admitidos fuera de linux/i386.
  
==== Assembler ====
+
==== Ensamblador ====
  
Avoid assembler.
+
&nbsp;&nbsp;&nbsp;Evita el ensamblador.
  
==== Compiler defines ====
+
==== Directivas del compilador ====
  
<DELPHI>
+
<syntaxhighlight lang=pascal>
{$ifdef CPU32}
+
{$ifdef CPU32}
...write here code for 32 bit processors
+
...escribe aquí código para procesadores de 32 bits
{$ENDIF}
+
{$ENDIF}
{$ifdef CPU64}
+
{$ifdef CPU64}
...write here code for 64 bit processors
+
...escribe aquí código para procesadores de 64 bits
{$ENDIF}
+
{$ENDIF}
</DELPHI>
+
</syntaxhighlight>
  
=== Projects, packages and search paths ===
+
=== Proyectos, paquetes y rutas de búsqueda ===
  
Lazarus projects and packages are designed for multi platforms. Normally you can simply copy the project and the required packages to another machine and compile them there. You don't need to create one project per platform.
+
&nbsp;&nbsp;&nbsp;Los proyectos y paquetes Lazarus se diseñan para múltiples plataformas. Normalmente copiamos el proyecto y los paquetes necesarios en otra máquina y los compilamos. No es necesario crear un proyecto para cada plataforma.
  
Some advice to achieve this
+
&nbsp;&nbsp;&nbsp;Algunos consejos para lograrlo
  
The compiler creates for every unit a ppu with the same name. This ppu can be used by other projects and packages. The unit source files (e.g. unit1.pas) should not be shared. Simply give the compiler a unit output directory where to create the ppu files. The IDE does that by default, so nothing to do for you here.
+
&nbsp;&nbsp;&nbsp;El compilador crea por cada unidad un archivo .ppu con el mismo nombre. Este .ppu puede ser utilizado por otros proyectos y paquetes. El fuente de la unidad (por ejemplo, Unidad1.pas) no debe ser compartida. Simplemente dale al compilador un directorio de salida para la unidad donde crear los archivos .ppu. El IDE realiza esto de forma predeterminada, así que nada que hacer por aquí.
  
Every unit file must be part of '''one''' project or package. If a unit file is only used by a single project, add it to this project. Otherwise add it to a package. If you have not yet created a package for your shared units, see here: [[Lazarus_Packages#Creating a package for your common units|Creating a package for your common units]]
+
&nbsp;&nbsp;&nbsp;Cada archivo de unidad debe ser parte de  '''un''' proyecto o paquete. Si un archivo de unidad es para uso exclusivo de un único proyecto, agrégalo a ese proyecto. En caso contrario, agrégalo a un paquete. Si aún no has creado un paquete para tus unidades para compartir, mira aquí: [[Lazarus_Packages/es#Crear_un_paquete_para_nuestras_unidades_comunes | Creación de un paquete para las unidades comunes]]
  
Every project and every package should have '''disjunct directories''' - they should not share directories. Otherwise you must be an expert in the art of compiler search paths. If you are not an expert or if others who may use your project/package are not experts: do not share directories between projects/packages.
+
&nbsp;&nbsp;&nbsp;Cada proyecto y paquete debe tener  '''directorios disjuntos''', no deben compartir directorios. De lo contrario deberás ser un experto en el arte de rutas de búsqueda del compilador. Si no eres un experto o si otras personas que puedan utilizar tu proyecto o paquete no son expertos: no compartir directorios entre los proyectos / paquetes.
  
==== Platform specific units ====
+
==== Unidades específicas de plataforma ====
For example the unit wintricks.pas should only be used under Windows. In the uses section use:
+
&nbsp;&nbsp;&nbsp;Por ejemplo la unidad ''wintricks.pas'' sólo debe usarse en Windows. En la sección uses pon:  
  
<Delphi>
+
<syntaxhighlight lang=pascal> uses
uses
 
 
   Classes, SysUtils
 
   Classes, SysUtils
 
   {$IFDEF Windows}
 
   {$IFDEF Windows}
 
   ,WinTricks
 
   ,WinTricks
 
   {$ENDIF}
 
   {$ENDIF}
   ;
+
   ;</syntaxhighlight>
</Delphi>
+
 
 +
&nbsp;&nbsp;&nbsp;Si la unidad es parte de un paquete, también debes seleccionar la unidad en el editor de paquetes y desactivar casilla de verificación de ''Usar unidad''.
 +
 
 +
==== Rutas de búsqueda específicas de plataforma ====
  
If the unit is part of a package, you must also select the unit in the package editor of the package and disable the ''Use unit'' checkbox.
 
  
==== Platform specific search paths ====
+
&nbsp;&nbsp;&nbsp;Al desarrollar para varias plataformas y acceder al sistema operativo directamente, entonces rápidamente te cansarás de las interminables construcciones IFDEF. Una solución que se utiliza a menudo en FPC y los fuentes de Lazarus es utilizar los archivos de inclusión. Crear un directorio por cada plataforma. Por ejemplo win32, Linux, BSD, Darwin. Poner en cada directorio un archivo de inclusión con el mismo nombre. A continuación, utilice una macro en la ruta de inclusión. La unidad puede utilizar una directiva include normal.
  
When you target several platforms and access the operating system directly, then you will quickly get tired of endless IFDEF constructions. One solution that is used often in the FPC and Lazarus sources is to use include files. Create one sub directory per target. For example win32, linux, bsd, darwin. Put into each directory an include file with the same name. Then use a macro in the include path. The unit can use a normal include directive.
+
&nbsp;&nbsp;&nbsp;Un ejemplo para un archivo de inclusión para cada ''LCL widgetset'':
An example for one include file for each LCL widget set:
 
  
Create one file for each widget set you want to support:
+
&nbsp;&nbsp;&nbsp;Crear un archivo para cada conjunto de controles para los quieras dar soporte:
win32/example.inc
+
 
 +
<syntaxhighlight lang=pascal> win32/example.inc
 
  gtk/example.inc
 
  gtk/example.inc
 
  gtk2/example.inc
 
  gtk2/example.inc
  carbon/example.inc
+
  carbon/example.inc</syntaxhighlight>
  
You do not need to add the files to the package or project.
+
&nbsp;&nbsp;&nbsp;No es necesario añadir los archivos al paquete o proyecto.
Add the include search path ''$(LCLWidgetType)'' to the compiler options of your package or project.
+
&nbsp;&nbsp;&nbsp;Agrega la ruta de búsqueda <code>''$(LCLWidgetType)''</code> a las opciones del compilador de su paquete o proyecto.
  
In your unit use the directive:
+
&nbsp;&nbsp;&nbsp;En tu unidad utiliza la directiva:
{$I example.inc}
+
<code> {$I example.inc}</code>
  
Here are some useful macros and common values:
+
&nbsp;&nbsp;&nbsp;Estas son algunas macros y valores comunes útiles:
 
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui
 
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)
+
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (y más)
 
*TargetCPU: i386, x86_64, arm, powerpc, sparc
 
*TargetCPU: i386, x86_64, arm, powerpc, sparc
 
*SrcOS: win, unix
 
*SrcOS: win, unix
  
You can use the $Env() macro to use environment variables.
+
&nbsp;&nbsp;&nbsp;Puedes utilizar la macro <code>$Env()</code> para acceder a variables de entorno.
  
And of course you can use combinations. For example the LCL uses:  
+
&nbsp;&nbsp;&nbsp;Y por supuesto puedes usar combinaciones. Por ejemplo, la LCL usa:
  
  $(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)
+
  <code> $(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)</code>
  
See here the complete list of macros: [[IDE Macros in paths and filenames]]
+
&nbsp;&nbsp;&nbsp;Ver aquí la lista completa de macros:: [[IDE Macros in paths and filenames/es | Macros del IDE en rutas y nombres de archivo]]
 +
 
 +
==== Rutas específicas de búsqueda de Máquina / Usuario ====
 +
 
 +
&nbsp;&nbsp;&nbsp;Por ejemplo tienes dos máquinas windows ''stan'' y ''oliver''. En ''stan'' tus unidades están en ''C:\unidades'' y en  ''oliver'' tus unidades están in ''D:\ruta''. Las unidades del paquete ''MaterialCompartido'' están en ''C:\unidades\MaterialCompartido.lpk'' en ''stan'' y en ''D:\ruta\MaterialCompartido'' en ''oliver''.
 +
 
 +
&nbsp;&nbsp;&nbsp;Una vez que abras el archivo .lpk en el IDE o con lazbuild, la ruta se almacena automáticamente en los archivos de configuración (packagefiles.xml).
 +
 
 +
&nbsp;&nbsp;&nbsp;Al compilar un proyecto que requiere el paquete ''MaterialCompartido'', el IDE y lazbuild saben dónde está. Así que no se necesita configuración.
 +
 
 +
&nbsp;&nbsp;&nbsp;Si quieres desplegar un paquete sobre varias máquina o para todos los usuarios de una máquina (por ejemplo, las compartidas para estudiantes), entonces puedes agregar un archivo .lpl en el directorio de fuentes de Lazarus. Ver empaquetado / vínculos globales para los ejemplos.
 +
 
 +
=== Diferencias [http://es.wikipedia.org/wiki/Locale locales] ===
 +
&nbsp;&nbsp;&nbsp;Algunas funciones de Free Pascal, como StrToFloat se comportan de manera diferente dependiendo de la [http://es.wikipedia.org/wiki/Internacionalización_y_localización localización] actual. Por ejemplo, en EE.UU. el separador decimal es por lo general ".", Pero en Europa y muchos países de América del Sur es ",". Este puede ser un problema ya que a veces se desea tener esta funciones se comporten de una manera fija, independientemente de la localización. En las secciones siguientes se explica cómo hacerlo
 +
 
 +
====StrToFloat====
 +
&nbsp;&nbsp;&nbsp;Un nuevo conjunto de opciones de formato que establece un separador decimal fijo se pueden crear con el siguiente código:
 +
 
 +
<syntaxhighlight lang=pascal> var
 +
  FPointSeparator, FCommaSeparator: TFormatSettings;
 +
begin
 +
  // Ajustes del formato para convertir una cadena a coma flotante
 +
  FPointSeparator := DefaultFormatSettings;
 +
  FPointSeparator.DecimalSeparator := '.';
 +
  FPointSeparator.ThousandSeparator := '#';// desactivar el separador de miles
 +
  FCommaSeparator := DefaultFormatSettings;
 +
  FCommaSeparator.DecimalSeparator := ',';
 +
  FCommaSeparator.ThousandSeparator := '#';// desactivar el separador de miles</syntaxhighlight>
  
==== Machine / User specific search paths ====
+
&nbsp;&nbsp;&nbsp;Por último puedes utilizar esta configuración de formato al llamar a StrToFloat, así:
  
For example you have two windows machines stan and oliver. On stan your units are in ''C:\units'' and on oliver your units are in ''D:\path''. The units belong to the package ''SharedStuff'' which is ''C:\units\sharedstuff.lpk'' on stan and ''D:\path\sharedstuff.lpk'' on oliver.
+
<syntaxhighlight lang=pascal> // Esta función funciona como StrToFloat, pero trata dos posibles separadores decimales
Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml).
+
// Esto evitará una excepción cuando el formato de cadena no coincide con la configuración local
When compiling a project that requires the package ''SharedStuff'', the IDE and lazbuild knows where it is. So no configuration is needed.
+
function AnSemantico.StringToFloat(AStr: string): Double;
 +
  begin
 +
  if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
 +
  else Result := StrToFloat(AStr, FCommaSeparator);
 +
  end;</syntaxhighlight>
  
If you have want to deploy a package over many machine or for all users of a machine (e.g. a pool for students), then you can add a lpl file in the lazarus source directory. See packager/globallinks for examples.
+
==Cuestiones específicas de Windows==
  
==Windows specific issues==
+
=== Funciones API de Windows ===
  
=== Windows API Functions ===
+
&nbsp;&nbsp;&nbsp;Muchos programas de Windows utilizan la API de Windows ampliamente. En las aplicaciones multiplataforma las funciones de la Win API en la unidad windows no deben utilizarse, o deberían estar encerradas por una compilación condicional (v.g. {$IFDEF MSWINDOWS} ).
  
Many Windows programs use the Windows API extensively. In crossplatform applications Win API functions in the Windows unit should not be used, or should be enclosed by a conditional compile (e.g. {$IFDEF MSWINDOWS} ).
+
&nbsp;&nbsp;&nbsp;Por suerte muchas funciones comunes del API de Windows están incorporadas de manera multiplataforma en la unidad LCLIntf. Esto puede ser una solución para los programas que dependen fuertemente de la API de Windows, aunque la mejor solución es reemplazar estas llamadas con la verdadera componentes multiplataforma de la LCL. Por ejemplo, reemplazar las llamadas a funciones de dibujo GDI con llamadas a los métodos de un objeto TCanvas.
  
Fortunately many of the commonly used Windows API functions are implemented in a multiplatform way in the unit LCLIntf. This can be a solution for programs which rely heavily on the Windows API, although the best solution is to replace these calls with true crossplatform components from the LCL. You can replace calls to GDI painting functions with calls to a TCanvas object's methods, for example.
+
&nbsp;&nbsp;&nbsp;La detección de códgigos de (v.g. en eventos KeyUp) es portable: ver [[LCL Key Handling/es|Gestión de teclas de la LCL]].
  
=== On Unix there is no "application directory" ===
+
=== En Unix no hay ''directorio de aplicación'' ===
  
Many programmers are used to call ExtractFilePath(ParamStr(0)) or Application.ExeName to get the location of the executable, and then search for the necessary files for the program execution (Images, XML files, database files, etc) based on the location of the executable. This is wrong on unixes. The string on ParamStr(0) may contain a directory other than the one of the executable, and it also varies between different shell programs (sh, bash, etc).
+
&nbsp;&nbsp;&nbsp;Muchos programadores utilizan ExtractFilePath(ParamStr(0)) o Application.ExeName para obtener la ubicación del ejecutable, y luego buscar los archivos necesarios para la ejecución del programa (imágenes, archivos XML, archivos de base de datos, etc.) basandose en la ubicación del ejecutable. Esto en Unix está equivocado. La cadena ParamStr(0) puede contener un directorio que no sea el del ejecutable, y que también variar según con  interpretes de comandos diferentes (sh, bash, etc.).
  
Even if Application.ExeName could in fact know the directory where the executable is, that file could be a symbolic link, so you could get the directory of the link instead (depending on the Linux kernel version, you either get the directory of the link or of the program binary itself).
+
&nbsp;&nbsp;&nbsp;Incluso si Application.ExeName puede conocer el directorio donde el ejecutable está, el archivo puede ser un enlace simbólico, por lo que se obtendrá el directorio del enlace en su lugar (dependiendo de la versión del núcleo Linux, se obtendrá el directorio del enlace o del programa binario mismo)
  
To avoid this read the sections about [[Multiplatform_Programming_Guide#Configuration_files|configuration files]] and [[Multiplatform_Programming_Guide#Data_and_resource_files|data files]].
+
&nbsp;&nbsp;&nbsp;Para evitar esto, lea las secciones sobre [[Multiplatform_Programming_Guide/es#Archivos_de_configuraci.C3.B3n|Archivos de configuración]] y [[Multiplatform_Programming_Guide/es#Archivos_de_datos_y_recursos|Archivos de datos y recursos]].
  
== Making do without Windows COM Automation ==
+
== Prescindir de la automatización COM de Windows ==
  
With Windows, Automation is a powerful way not only of manipulating other programs remotely but also for allowing other programs to manipulate your program. With Delphi you can make your program both an Automation client and an Automation server, meaning it can both manipulate other programs and in turn be manipulated by other programs. For examples,
+
&nbsp;&nbsp;&nbsp;En Windows la automatización COM es una potente forma no sólo para utilizar otros programa remotamente, sino también para permitir que otros programas utilizar tu programa. Con Delphi puedes hacer un programa, tanto cliente como servidor de automatización, lo que significa que puede manipular otros programas y, a su vez ser manipulado por otros programas. Para ejemplos ver [[Office_Automation/es#Usar_automatizaci.C3.B3n_COM_para_interactuar_con_OpenOffice_y_Microsoft_Office|Usar automatización COM para interaccionar con OpenOffice y Microsoft Office]].
see [http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office  Using COM Automation to interact with OpenOffice and Microsoft Office].
 
  
Unfortunately, Automation isn't available on OS X and Linux. However, you can simulate some of the functionality of Automation on OS X using AppleScript.
+
&nbsp;&nbsp;&nbsp;Por desgracia, la automatización COM no está disponible en OS X y Linux. Sin embargo, puedes simular algunas de las funcionalidades de automatización en OS X usando AppleScript.
  
AppleScript is similar to Automation in some ways. For example, you can write scripts that manipulate other programs. Here's a very simple example of AppleScript that starts NeoOffice (the Mac version of OpenOffice.org):
+
&nbsp;&nbsp;&nbsp;AppleScript es similar a la automatización en cierto modo. Por ejemplo, puedes escribir secuencias de comandos que manipulan otros programas. He aquí un ejemplo muy simple de AppleScript que comienza NeoOffice (la versión para Mac de OpenOffice.org):
  
 
   tell application "NeoOffice"
 
   tell application "NeoOffice"
Line 468: Line 550:
 
   end tell
 
   end tell
  
An app that is designed to be manipulated by AppleScript provides a "dictionary" of classes and commands that can be used with the app, similar to the classes of a Windows Automation server. However, even apps like NeoOffice that don't provide a dictionary will still respond to the commands "launch", "activate" and "quit". AppleScript can be run from the OS X Script Editor or Finder or even converted to an app that you can drop on the dock just like any app. You can also run AppleScript from your program, as in this example:
+
&nbsp;&nbsp;&nbsp;Una aplicación que está diseñada para ser manipulada por AppleScript ofrece un "diccionario" de las clases y comandos que se pueden utilizar con la aplicación, similar a las clases de un servidor de Automatización Windows. Sin embargo, incluso aplicaciones como NeoOffice que no proporcionan un diccionario responderá a los comandos "launch", "activate" y "quit". AppleScript puede ejecutarse desde el Finder de OS X o desde el Editor de secuencias o incluso convertirse en una aplicación que puedes colocar en el panel al igual que cualquier aplicación. También puede ejecutar AppleScript desde su programa, como en este ejemplo:
  
 
   Shell('myscript.applescript');
 
   Shell('myscript.applescript');
  
This assumes the script is in the indicated file. You can also run scripts on the fly from your app using the OS X OsaScript command:
+
&nbsp;&nbsp;&nbsp;Se supone que la secuencia de comandos se encuentra en el archivo indicado. También puedes ejecutar secuencias de comandos al vuelo a partir de tu aplicación utilizando el comando OsaScript de OS X:
  
 
   Shell('osascript -e '#39'tell application "NeoOffice"'#39 +
 
   Shell('osascript -e '#39'tell application "NeoOffice"'#39 +
Line 478: Line 560:
 
         {Note use of #39 to single-quote the parameters}
 
         {Note use of #39 to single-quote the parameters}
  
However, these examples are just the equivalent of the following Open command:
+
&nbsp;&nbsp;&nbsp;Sin embargo, estos ejemplos son sólo el equivalente del comando Open:
  
 
   Shell('open -a NeoOffice');
 
   Shell('open -a NeoOffice');
  
Similarly, in OS X you can emulate the Windows shell commands to launch a web browser and launch an email client with:
+
&nbsp;&nbsp;&nbsp;Del mismo modo, en OS X se puede emular la línea de comandos de Windows para iniciar un navegador web e iniciar un cliente de correo electrónico con:
  
 
   fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
 
   fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
  
and
+
&nbsp;&nbsp;&nbsp;y
  
 
   fpsystem('open -a mail "mailto:ss4200@invalid.org"');
 
   fpsystem('open -a mail "mailto:ss4200@invalid.org"');
  
which assumes, fairly safely, that an OS X system will have the Safari and Mail applications installed. Of course, you should never make assumptions like this, and for the two previous examples, you can in fact just rely on OS X to do the right thing and pick the user's default web browser and email client if you instead use these variations:
+
&nbsp;&nbsp;&nbsp;que asume, con bastante certeza, que un sistema OS X tendrá Safari y la aplicación de correo instalados. Por supuesto, no se deben hacer presunciones como esta, y para los dos ejemplos anteriores, puedes, de hecho sólo basarte en OS X para hacer lo correcto y elegir el navegador y el cliente de correo electrónico predeterminados del usuario si en su lugar utilizas estas variaciones:
  
 
   fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
 
   fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
  
and
+
&nbsp;&nbsp;&nbsp;y
  
 
   fpsystem('open "mailto:ss4200@invalid.org"');
 
   fpsystem('open "mailto:ss4200@invalid.org"');
  
Do not forget to include the Unix unit in your uses clause if you use <code>fpsystem</code> or <code>shell</code> (interchangeable).
+
&nbsp;&nbsp;&nbsp;No olvides incluir la unidad de Unix en la cláusula uses si utilizas <code>fpsystem</code> o <code>shell</code> (indistintamente).
  
The real power of AppleScript is to manipulate programs remotely to create and open documents and automate other activities. How much you can do with a program depends on how extensive its AppleScript dictionary is (if it has one). For example, Microsoft's Office X programs are not very usable with AppleScript, whereas the newer Office 2004 programs have completely rewritten AppleScript dictionaries that compare in many ways with what's available via the Windows Office Automation servers.
+
&nbsp;&nbsp;&nbsp;La potencia real de AppleScript es manejar de forma remota los programas para crear o abrir documentos y automatizar otras actividades. Cuánto se puede hacer con un programa depende de qué tan extenso es su diccionario AppleScript (si lo tiene). Por ejemplo, los programas de Office de Microsoft X no son muy útiles con AppleScript, mientras que los nuevos programas de Office 2004 han reescrito por completo diccionarios para AppleScript que son comparables en muchos aspectos con lo que está disponible a través de los servidores de automatización del Office en Windows.
  
While Linux shells support sophisticated command line scripting, the type of scripting is limited to what can be passed to a program on the command line. There is no single, unified way to access a program's internal classes and commands  with Linux the way they are via Windows Automation and OS X AppleScript. However, individual desktop environments (GNOME/KDE) and application frameworks often provide such methods of interprocess communication. On GNOME see Bonobo Components. KDE has the KParts framework, DCOP. OpenOffice has a platform neutral API for controlling the office remotely (google OpenOffice SDK) - though you would probably have to write glue code in another language that has bindings (such as Python) to use it. In addition, some applications have "server modes" activated by special command-line options that allow them to be controlled from another process. It is also possible (Borland did it with Kylix document browser) to "embed" one top-level X application window into another using XReparentWindow (I think).
+
&nbsp;&nbsp;&nbsp;Mientras que las secuencias de comandos Linux permiten sofisticadas líneas de ordenes, el tipo de secuencias de comandos se limita a lo que se puede pasar a un programa en la línea de parámetros. No hay una forma única y unificada para acceder a las clases y comandos internos de un programa Linux en la forma en que se hace a través de la automatización de Windows y AppleScript de OS X. Sin embargo, cada entorno de escritorio (GNOME / KDE) y los entornos de aplicaciones a menudo proporcionan estos métodos de comunicación entre procesos. En GNOME hay que ver los componentes Bonobo. KDE tiene el marco KParts y DCOP. OpenOffice tiene una plataforma neutral de la API para el control remoto de las aplicaciones (SDK OpenOffice); aunque probablemente tendrás que escribir código de unión en otro lenguaje que tenga enlaces (como Python) para utilizarlo. Además, algunas aplicaciones tienen "modos de servidor" activados por las opciones especiales de línea de comandos que les permiten ser controladas desde otro proceso. También es posible (Borland lo hizo con el visor de documentos de Kylix) para "integrar" una ventana de aplicación X de nivel superior en otra utilizando XReparentWindow (creo).
  
As with Windows, many OS X and Linux programs are made up of multiple library files (.dylib and .so extensions). Sometimes these libraries are designed so you can also use them in programs you write. While this can be a way of adding some of the functionality of an external program to your program, it's not really the same as running and manipulating the external program itself. Instead, your program is just linking to and using the external program's library similar to the way it would use any programming library.
+
&nbsp;&nbsp;&nbsp;Al igual que con Windows, muchos programas de OS X y Linux se componen de múltiples archivos de librería (extensiones .Dylib y .so). A veces, estas librerías están diseñadas para que también podamos utilizarlas en los programas que escribamos. Si bien esto es una manera de añadir algunas funcionalidades de un programa externo en nuestro programa, en realidad no es la mismo que ejecutar y manipular el propio programa externo. En cambio, tu programa se enlaza a la librería del programa externo y la usa de manera similar a cualquier otra librería de programación.
  
 
== See Also ==
 
== See Also ==

Latest revision as of 00:25, 21 February 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) русский (ru) 中文(中国大陆)‎ (zh_CN)

   Esta página es el comienzo de una tutoría sobre la creación de aplicaciones multiplataforma con Lazarus. Comprenderá tanto las precauciones necesarias para garantizar que un programa pueda ser fácilmente migrado y el proceso de migración de un programa ya existente. Todos están invitados a ayudar a mejorar el artículo.

Introducción a la programación multiplataforma

¿Cuántas cajas son necesarias?

   Para responder a esta pregunta, primero debes determinar quiénes serán los usuarios potenciales y la forma en que tu programa se utilizará. Esta cuestión depende de dónde vas a implementar la aplicación.

   Si estás desarrollando una aplicación de escritorio genérica, Windows es, obviamente, la plataforma más importante, pero también incluye Mac OS X y/o versiones de Linux a veces puede ser la diferencia que haga que tu aplicación sea elegida en lugar de una aplicación no multiplataforma.

   La popularidad de los distintos sistemas operativos de escritorio difiere según el país, por el tipo de aplicaciones utilizado, y por el público al que se dirige, así que no hay regla general. Por ejemplo, Mac OS X es muy popular en América del Norte y Europa occidental, mientras que en América del Sur los Macs se circunscriben principalmente a trabajos de vídeo y sonido.

   En muchos proyectos por contrato una sola plataforma es relevante, y eso no es un problema. Free Pascal y Lazarus son capaces de escribir código destinado a una plataforma específica. Puede, por ejemplo, acceder a la API completa de Windows para escribir un programa de Windows, bien integrado.

   Si estás desarrollando una aplicación que se ejecuta en un servidor Web, una plataforma Unix en uno de sus varios sabores es usada comúnmente. En este caso, tal vez sólo Linux, Solaris, *BSD y otros Unix tiene sentido como plataformas de destino, aunque es posible que desee agregar el soporte para Windows para que sea más completa.

   Una vez que domines el desarrollo multiplataforma, por lo general sólo debes centrarte en el problema para el cual la aplicación se ha diseñado para resolver y hacer la mayor parte de su desarrollo en cualquier plataforma que dispongas o te sientas más cómodo. Es decir, una vez que has abordado todas las cuestiones multiplataforma en su diseño, se puede pasar por alto en gran medida las otras plataformas, tanto como lo harías en el desarrollo para una plataforma única. Sin embargo, en algún momento tendrás que poner a prueba implementar y ejecutar tu programa para las otras plataformas y será útil tener acceso sin restricciones a equipos que ejecuten los sistemas de destino con toda la información operacional. Si no deseas varias máquinas reales, usted puedes mirar en la configuración de una máquina de arranque dual Windows-Linux o con Windows y Linux en tu Mac a través de Bootstrap o bajo un emulador como Parallels, Virtual Box o VMware.

Programación multiplataforma

Trabajar con archivos y carpetas

   Cuando se trabaja con archivos y carpetas, es importante usar delimitadores de ruta y de fin de línea no específicos de una plataforma]. Aquí está una lista de declaraciones constantes en Lazarus para usar cuando se trabaja con archivos y carpetas.

  • PathSep, PathSeparator: separador de ruta al agregar varias rutas juntas (';', ...)
  • PathDelim, DirectorySeparator: separador de directorio para cada plataforma ('/', '\', ...)
  • LineEnding: secuencia de caracteres correcta de final de línea (#13#10 - CRLF, #10 - LF, ...)

   Otra cosa importante a señalar es el caso de la sensibilidad (a mayúsculas/minúsculas) del sistema de archivos. El nombre de archivo de Windows no distinguen entre mayúsculas y minúsculas, mientras que en las plataformas Linux y BSD es lo usual. Mac OS X usa nombres de archivo insensibles por defecto. Esto puede ser la causa de molestos errores, por lo que cualquier aplicación portable debe utilizar nombres de archivo de forma consistente.

   Las funciones de archivo de la RTL utilizan la de codificación del sistema para nombres de archivo. En Windows esta es una de las páginas de códigos de Windows, mientras que Linux, BSD y Mac OS X suelen utilizar UTF-8. La unidad FileUtil de la LCL ofrece funciones de archivo que utilizan caracteres UTF-8 como el resto de la LCL.

 // AnsiToUTF8 y UTF8ToAnsi necesitan de un gestor de ''widestring'' en Linux, BSD y MacOSX
 // pero usualmente estos SO utilizan UTF-8 como codificación del sistema por lo que el gestor de ''widestring''
 // no es necesario.
 function NeedRTLAnsi: boolean;// cierto (true) si el sistema no es  UTF-8
 procedure SetNeedRTLAnsi(NewValue: boolean);
 function UTF8ToSys(const s: string): string;// similar a UTF8ToAnsi pero más independiente delgestor de ''widestring''
 function SysToUTF8(const s: string): string;// similar a AnsiToUTF8 pero más independiente delgestor de ''widestring''
 function UTF8ToConsole(const s: string): string;// convierte cadenas UTF8 a la codificación de la consola (usado por Write, WriteLn)

 // operaciones de archivo
 function FileExistsUTF8(const Filename: string): boolean;
 function FileAgeUTF8(const FileName: string): Longint;
 function DirectoryExistsUTF8(const Directory: string): Boolean;
 function ExpandFileNameUTF8(const FileName: string): string;
 function ExpandUNCFileNameUTF8(const FileName: string): string;
 function ExtractShortPathNameUTF8(Const FileName : String) : String;
 function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
 function FindNextUTF8(var Rslt: TSearchRec): Longint;
 procedure FindCloseUTF8(var F: TSearchrec);
 function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
 function FileGetAttrUTF8(const FileName: String): Longint;
 function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
 function DeleteFileUTF8(const FileName: String): Boolean;
 function RenameFileUTF8(const OldName, NewName: String): Boolean;
 function FileSearchUTF8(const Name, DirList : String): String;
 function FileIsReadOnlyUTF8(const FileName: String): Boolean;
 function GetCurrentDirUTF8: String;
 function SetCurrentDirUTF8(const NewDir: String): Boolean;
 function CreateDirUTF8(const NewDir: String): Boolean;
 function RemoveDirUTF8(const Dir: String): Boolean;
 function ForceDirectoriesUTF8(const Dir: string): Boolean;

 // entorno
 function ParamStrUTF8(Param: Integer): string;
 function GetEnvironmentStringUTF8(Index: Integer): string;
 function GetEnvironmentVariableUTF8(const EnvVar: string): String;
 function GetAppConfigDirUTF8(Global: Boolean): string;

 // otras
 function SysErrorMessageUTF8(ErrorCode: Integer): String;

Nombres de archivo vacios y delimitadores de ruta duplicados

   Windows permite nombres de archivo vacíos, Linux, BSD y Mac OS X por contra no. Es por eso que FileExistsUTF8('..\') comprueba en Windows un archivo sin nombre en el directorio padre. En Unix, como los sistemas Linux, BSD y OS X, un archivo vacío se asigna al directorio y los directorios se tratan como archivos. Esto significa que FileExistsUTF8 ('../') en Unix comprueba la existencia del directorio padre, que normalmente será verdadero.

   Por la misma razón los delimitadores de ruta duplicados en un nombre de archivo tienen tratamiento distinto. En Windows 'C:\' no es lo mismo que 'C:\\', mientras que en los *nix la ruta '/usr//' es equivalente a '/usr/', y si '/usr' es un directorio, incluso estas tres son iguales. Esto es importante cuando se concatena nombres de archivo. Por ejemplo:

 FullFilename:=FilePath+PathDelim+ShortFilename; // el resultado tiene dos delimitadores de ruta y da resultados distinto en Windows y Linux
 FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // crea un solo delimitador de ruta
 FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // crea un solo delimitador de ruta y es más limpio

   La función TrimFilename reemplaza delimitadores de ruta dobles por un solo y recorta rutas '..'. Por ejemplo /usr//lib/../src es recortado a /usr/src.

   Si quieres saber si un directorio existe utiliza DirectoryExistsUTF8.

   Otra tarea común es comprobar si existe la parte de la ruta de un nombre de archivo. Puedes obtener la ruta con ExtractFilePath, pero este contendrá el delimitador de ruta. En sistemas Unix simplemente usa FileExistsUTF8 para la ruta. Por ejemplo FileExistsUTF ('/home/usuario/') será cierto si el directorio /home/usuario existe. En Windows debes utilizar la función DirectoryExistsUTF8, pero antes se debe eliminar el delimitador de ruta, por ejemplo con la función ChompPathDelim . En Unix el directorio raíz es '/' la función ChompPathDelim creará una cadena vacía. La función DirPathExists trabaja como la función DirectoryExistsUTF8, pero recorta la ruta dada.

Codificación de texto

   Los archivos de texto son a menudo codificados en el sistema de codificación actual. En Windows esto suele ser una de las páginas de código de Windows, mientras que Linux, BSD y Mac OS X suelen utilizar UTF-8. No existe una regla del 100% para saber qué codificación utiliza un archivo de texto. La unidad de la LCL lconvencoding tiene la función de adivinar la codificación:

 function GuessEncoding(const s: string): string;
 function GetDefaultTextEncoding: string;

   Y contiene funciones para la conversión de una codificación a otra:

 function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;

 function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM
 function ISO_8859_1ToUTF8(const s: string): string; // central europe
 function CP1250ToUTF8(const s: string): string; // central europe
 function CP1251ToUTF8(const s: string): string; // cyrillic
 function CP1252ToUTF8(const s: string): string; // latin 1
 ...
 function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM
 function UTF8ToISO_8859_1(const s: string): string; // central europe
 function UTF8ToCP1250(const s: string): string; // central europe
 function UTF8ToCP1251(const s: string): string; // cyrillic
 function UTF8ToCP1252(const s: string): string; // latin 1
 ...

   Por ejemplo, para cargar un archivo de texto y convertirlo a UTF-8 se puede utilizar:

 var
  sl: TStringList;
  OriginalText: String;
  TextAsUTF8: String;
 begin
  sl:=TStringList.Create;
  try
    sl.LoadFromFile('alguntexto.txt'); // Ojo: esta sentencia de cambia los delimitadores de final de línea a los del sistema
    OriginalText:=sl.Text;
    TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);
    ...
  finally
    sl.Free;
  end;
 end;

   Y para guardar el archivo de texto con la codificación del sistema usaremos esto:

 sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);
 sl.SaveToFile('sometext.txt');

Archivos de configuración

   Puede utilizar la función GetAppConfigDir de la unidad SysUtils para conseguir un lugar adecuado para almacenar los archivos de configuración en diferentes sistemas. La función tiene un parámetro, llamado Global. Si es true entonces el directorio devuelto es un directorio global, es decir, válida para todos los usuarios del sistema. Si el parámetro global es falso, entonces el directorio es específico para el usuario que está ejecutando el programa. En los sistemas que no son compatibles con entornos multiusuario, estos dos directorios pueden ser el mismo.

   También existe la función GetAppConfigFile, que devolverá un nombre apropiado para un archivo de configuración de la aplicación. Se puede utilizar de esta manera:

ConfigFilePath := GetAppConfigFile(False) + '.conf';

   Estos son ejemplos de la salida de las funciones de ruta por defecto en diferentes sistemas:

 program proyecto1;

 {$mode objfpc}{$H+}

 uses
  SysUtils;

 begin
  WriteLn(GetAppConfigDir(True));
  WriteLn(GetAppConfigDir(False));
  WriteLn(GetAppConfigFile(True));
  WriteLn(GetAppConfigFile(False));
 end.

   La salida en un sistema GNU/Linux con FPC 2.2.2. Tenga en cuenta que el uso de true está libre de errores, ya están fijadas en 2.2.3:

 /etc/project1/
 /home/user/.config/project1/
 /etc/project1.cfg
 /home/user/.config/project1.cfg

   Se puede notar que los archivos de configuración global se almacenan en el directorio /etc y las configuraciones locales se almacenan en una carpeta oculta en el directorio inicial (home) del usuario. Los directorios cuyo nombre comienzan con un punto (.) están ocultos en Linux. Puedes crear un directorio en la ubicación que devuelve GetAppConfigDir y luego almacenar los archivos de configuración allí.

   El resultado en Windows XP con FPC 2.2.4 + :

 C:\Documents and Settings\All Users\Application Data\project1\
 C:\Documents and Settings\user\Local Settings\Application Data\project1
 C:\Documents and Settings\All Users\Application Data\project1\project1.cfg
 C:\Documents and Settings\user\Local Settings\Application Data\project1\project1.cfg

   Tenga en cuenta que antes de FPC 2.2.4 la función usaba el directorio del ejecutable para almacenar las configuraciones globales en Windows.

   El resultado en Windows 98 con FPC 2.2.0:

 C:\Program Files\PROJECT1
 C:\Windows\Local Settings\Application Data\PROJECT1
 C:\Program Files\PROJECT1\PROJECT1.cfg
 C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg

   El resultado en Mac OS X con FPC 2.2.0:

 /etc
 /Users/user/.config/project1
 /etc/project1.cfg
 /Users/user/.config/project1/project1.cfg

   Nota: El uso de UPX interfiere con el uso de las funciones GetAppConfigDir y GetAppConfigFile.

   Nota: En la mayoría de los casos los archivos de configuración son archivos de preferencias, que deben ser archivos XML con la terminación .plist y se almacenará en /Library/Preferences o en ~/Library/Preferences con nombres tomados del campo "identificador de paquete" en el Info.plist del paquete de aplicaciones. Utilizando las llamadas CFPreference de Carbon... es probablemente la forma más fácil de crear estos archivos de configuración .config en el directorio del Usuario son una violación de las líneas guía de programación.

Archivos de datos y recursos

   Una pregunta muy común es donde almacenar los archivos de datos que una aplicación puede necesitar, como imágenes, música, archivos XML, archivos de bases de datos, archivos de ayuda, etc. Desafortunadamente no hay funciones multi-plataforma para obtener la mejor localización para buscar archivos de datos. La solución es implementar de forma diferente en cada plataforma usando IFDEFs.

   En Windows, simplemente puedes asumir que los archivos están en el mismo directorio que el ejecutable, o en una posición relativa al mismo.

   En la mayoría de los sistemas operativos tipo Unix(como Linux, BSDs, solaris, etc), los archivos de datos están situados en posiciones fijas, que pueden ser algo como: /usr/share/app_name o /opt/app_name.

   La mayoría de programas se ejecutarán sin privilegios de administrador y por lo tanto un programa multi-plataforma deberá almacenar los archivos de datos en un lugar donde siempre tenga permiso para leerlos, pero no necesariamente para modificarlos, y los archivos de configuración en lugares donde tenga además permiso para escribir en ellos.

   Mac OS X es una excepción entre los sistemas operativos tipo UNIX. Aquí, la mejor manera de implementar aplicaciones es utilizar un paquete de aplicaciones, que incluye todos los archivos que la aplicación va a necesitar. Así, los archivos de recursos deben estar dentro del paquete, por lo que se puede mover y seguir trabajando normalmente. Tienes que usar llamadas a la API CoreFoundation para localizar el paquete.

Ejemplo

   Esta sección presenta una solución particular para el caso en que en Windows los archivos de datos están en el mismo directorio que el ejecutable (o en un directorio relativo a este, tal que MiDirectorio + 'datos' + SeparadorRuta + 'miArchivo.datos'), y en *nix están en un directorio que se lee de un archivo de configuración. Si no existe el archivo de configuración o este no contiene información, entonces la constante ('/usr/share/myapp/') se utiliza como el directorio predeterminado.

   El archivo de configuración se localiza con la función GetAppConfigFile de la RTL de Free Pascal.

   A continuación se muestra una unidad completa que puedes utilizar en tus aplicaciones.

 unit appsettings;

interface

{$ifdef fpc}
  {$mode delphi}{$H+}
{$endif}

uses
  Classes, SysUtils, Forms, IniFiles, constants;

type

 { TConfigurations }

 TConfigurations = class(TObject)
 private
   function GetResourcesPath: string;
 public
   {otros ajustes serán campos aquí}
   ConfigFilePath: string;
   ResourcesPath: string;
   constructor Create;
   destructor Destroy; override;
   procedure ReadFromFile(Sender: TObject);
   procedure Save(Sender: TObject);
 end;

var
 vConfigurations: TConfigurations;

implementation

{$IFDEF Win32}
uses
  Windows;
{$ENDIF}
{$ifdef Darwin}
uses
  MacOSAll;
{$endif}

const
  DefaultDirectory = '/usr/share/myapp/';

  SectionGeneral = 'General';
  SectionUnix = 'UNIX';

  IdentResourcesPath = 'ResourcesPath';

{ TConfigurations }

constructor TConfigurations.Create;
begin
{$ifdef win32}
 ConfigFilePath := ExtractFilePath(Application.EXEName) + 'myapp.ini';
{$endif}
{$ifdef Unix}
 ConfigFilePath := GetAppConfigFile(False) + '.conf';
{$endif}

  ResourcePath := GetResourcesPath();

 ReadFromFile(nil);
end;

destructor TConfigurations.Destroy;
begin
 Save(nil);

 inherited Destroy;
end;

procedure TConfigurations.Save(Sender: TObject);
var
 MyFile: TIniFile;
begin
 MyFile := TIniFile.Create(ConfigFilePath);
 try
   MyFile.WriteString(SectionUnix, IdentResourcesPath, ResourcesPath);
 finally
   MyFile.Free;
 end;
end;

procedure TConfigurations.ReadFromFile(Sender: TObject);
var
 MyFile: TIniFile;
begin
 MyFile := TIniFile.Create(ConfigFilePath);
 try
  // Aquí puedes leer más información del archivo de configuración

{$ifdef Win32}
   ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
ExtractFilePath(Application.EXEName));
{$else}
  {$ifndef darwin}
   ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
DefaultDirectory);
  {$endif}
{$endif}
 finally
   MyFile.Free;
 end;
end;

function TConfigurations.GetResourcesPath(): string;
begin
{$ifdef Darwin}
var
  pathRef: CFURLRef;
  pathCFStr: CFStringRef;
  pathStr: shortstring;
{$endif}
begin
{$ifdef UNIX}
{$ifdef Darwin}
  pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
  pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
  CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
  CFRelease(pathRef);
  CFRelease(pathCFStr);
	
  Result := pathStr + BundleResourcesDirectory;
{$else}
  Result := DefaultDirectory;
{$endif}
{$endif}

{$ifdef Windows}
  Result := ExtractFilePath(Application.EXEName);
{$endif}
end;

initialization

 vConfigurations := TConfigurations.Create;

finalization

 FreeAndNil(vTranslations);

end.

   y este es un ejemplo de utilización de esta unidad:

 bmp := TBitmap.Create
 try
   bmp.LoadFromFile(vConfigurations.ResourcesPath + 'MiMapaBits.bmp');
 finally
   bmp.Free;
 end;

32/64 bits

Conversión de Punteros / Enteros

   Un Puntero en 64 bits necesita 8 bytes en vez de los 4 sobre 32 bits. El tipo 'Integer' sigue siendo por compatibilidad de 32 bits en todas las plataformas. Esto significa que no se puede convertir a los punteros en números enteros y a la inversa (no son compatibles). FPC define dos tipos para realizar esto: PtrInt y PtrUInt. PtrInt es un entero con signo de 32 bits en plataformas de 32 bits y de 64 bits en las plataformas de 64 bits. Lo mismo para el número entero PtrUInt, pero sin signo en este caso.

   Utilización en código que debe trabajar con Delphi y FPC:

 {$IFNDEF FPC}
 type
   PtrInt = integer;
   PtrUInt = cardinal;
 {$ENDIF}

   Sustituye todos las ocurrecias de integer(unPuntero_o_unObjeto) por PtrInt(unPuntero_o_unObjeto).

Formato de multiByte

   Las plataformas de Intel son little endian, eso significa que el byte menos significativo va primero. Por ejemplo, los dos bytes del word $1234 se almacenan como $34 $12 en estos sistemas. En los sistemas big endian como el IBM powerpc los dos bytes de $1234 se almacenan como $12 $34. La diferencia es importante al leer los archivos creados en otros sistemas.

   Usaremos esto en código que trabaje en ambos sistemas:

 {$IFDEF ENDIAN_BIG}
 ...
 {$ELSE}
 ...
 {$ENDIF}

    Lo contrario es {$IFDEF ENDIAN_LITTLE}.

   La unidad system proporciona muchas funciones de conversión, como de SwapEndian, BEtoN (big endian a endian actual),LEtoN (little endian a endian actual), NtoBE (endian actual a big endian) y NtoLE (endian actual a little endian).


Libc y otras unidades especiales

   Evita las unidades heredadas cómo "oldlinux" y "libc" no admitidos fuera de linux/i386.

Ensamblador

   Evita el ensamblador.

Directivas del compilador

 {$ifdef CPU32}
 ...escribe aquí código para procesadores de 32 bits
 {$ENDIF}
 {$ifdef CPU64}
 ...escribe aquí código para procesadores de 64 bits
 {$ENDIF}

Proyectos, paquetes y rutas de búsqueda

   Los proyectos y paquetes Lazarus se diseñan para múltiples plataformas. Normalmente copiamos el proyecto y los paquetes necesarios en otra máquina y los compilamos. No es necesario crear un proyecto para cada plataforma.

   Algunos consejos para lograrlo

   El compilador crea por cada unidad un archivo .ppu con el mismo nombre. Este .ppu puede ser utilizado por otros proyectos y paquetes. El fuente de la unidad (por ejemplo, Unidad1.pas) no debe ser compartida. Simplemente dale al compilador un directorio de salida para la unidad donde crear los archivos .ppu. El IDE realiza esto de forma predeterminada, así que nada que hacer por aquí.

   Cada archivo de unidad debe ser parte de un proyecto o paquete. Si un archivo de unidad es para uso exclusivo de un único proyecto, agrégalo a ese proyecto. En caso contrario, agrégalo a un paquete. Si aún no has creado un paquete para tus unidades para compartir, mira aquí: Creación de un paquete para las unidades comunes

   Cada proyecto y paquete debe tener directorios disjuntos, no deben compartir directorios. De lo contrario deberás ser un experto en el arte de rutas de búsqueda del compilador. Si no eres un experto o si otras personas que puedan utilizar tu proyecto o paquete no son expertos: no compartir directorios entre los proyectos / paquetes.

Unidades específicas de plataforma

   Por ejemplo la unidad wintricks.pas sólo debe usarse en Windows. En la sección uses pon:

 uses
  Classes, SysUtils
  {$IFDEF Windows}
  ,WinTricks
  {$ENDIF}
  ;

   Si la unidad es parte de un paquete, también debes seleccionar la unidad en el editor de paquetes y desactivar casilla de verificación de Usar unidad.

Rutas de búsqueda específicas de plataforma

   Al desarrollar para varias plataformas y acceder al sistema operativo directamente, entonces rápidamente te cansarás de las interminables construcciones IFDEF. Una solución que se utiliza a menudo en FPC y los fuentes de Lazarus es utilizar los archivos de inclusión. Crear un directorio por cada plataforma. Por ejemplo win32, Linux, BSD, Darwin. Poner en cada directorio un archivo de inclusión con el mismo nombre. A continuación, utilice una macro en la ruta de inclusión. La unidad puede utilizar una directiva include normal.

   Un ejemplo para un archivo de inclusión para cada LCL widgetset:

   Crear un archivo para cada conjunto de controles para los quieras dar soporte:

 win32/example.inc
 gtk/example.inc
 gtk2/example.inc
 carbon/example.inc

   No es necesario añadir los archivos al paquete o proyecto.    Agrega la ruta de búsqueda $(LCLWidgetType) a las opciones del compilador de su paquete o proyecto.

   En tu unidad utiliza la directiva: {$I example.inc}

   Estas son algunas macros y valores comunes útiles:

  • LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui
  • TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (y más)
  • TargetCPU: i386, x86_64, arm, powerpc, sparc
  • SrcOS: win, unix

   Puedes utilizar la macro $Env() para acceder a variables de entorno.

   Y por supuesto puedes usar combinaciones. Por ejemplo, la LCL usa:

 $(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)

   Ver aquí la lista completa de macros:: Macros del IDE en rutas y nombres de archivo

Rutas específicas de búsqueda de Máquina / Usuario

   Por ejemplo tienes dos máquinas windows stan y oliver. En stan tus unidades están en C:\unidades y en oliver tus unidades están in D:\ruta. Las unidades del paquete MaterialCompartido están en C:\unidades\MaterialCompartido.lpk en stan y en D:\ruta\MaterialCompartido en oliver.

   Una vez que abras el archivo .lpk en el IDE o con lazbuild, la ruta se almacena automáticamente en los archivos de configuración (packagefiles.xml).

   Al compilar un proyecto que requiere el paquete MaterialCompartido, el IDE y lazbuild saben dónde está. Así que no se necesita configuración.

   Si quieres desplegar un paquete sobre varias máquina o para todos los usuarios de una máquina (por ejemplo, las compartidas para estudiantes), entonces puedes agregar un archivo .lpl en el directorio de fuentes de Lazarus. Ver empaquetado / vínculos globales para los ejemplos.

Diferencias locales

   Algunas funciones de Free Pascal, como StrToFloat se comportan de manera diferente dependiendo de la localización actual. Por ejemplo, en EE.UU. el separador decimal es por lo general ".", Pero en Europa y muchos países de América del Sur es ",". Este puede ser un problema ya que a veces se desea tener esta funciones se comporten de una manera fija, independientemente de la localización. En las secciones siguientes se explica cómo hacerlo

StrToFloat

   Un nuevo conjunto de opciones de formato que establece un separador decimal fijo se pueden crear con el siguiente código:

 var
  FPointSeparator, FCommaSeparator: TFormatSettings;
 begin
  // Ajustes del formato para convertir una cadena a coma flotante
  FPointSeparator := DefaultFormatSettings;
  FPointSeparator.DecimalSeparator := '.';
  FPointSeparator.ThousandSeparator := '#';// desactivar el separador de miles
  FCommaSeparator := DefaultFormatSettings;
  FCommaSeparator.DecimalSeparator := ',';
  FCommaSeparator.ThousandSeparator := '#';// desactivar el separador de miles

   Por último puedes utilizar esta configuración de formato al llamar a StrToFloat, así:

 // Esta función funciona como StrToFloat, pero trata dos posibles separadores decimales
 // Esto evitará una excepción cuando el formato de cadena no coincide con la configuración local
 function AnSemantico.StringToFloat(AStr: string): Double;
  begin
   if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
   else Result := StrToFloat(AStr, FCommaSeparator);
  end;

Cuestiones específicas de Windows

Funciones API de Windows

   Muchos programas de Windows utilizan la API de Windows ampliamente. En las aplicaciones multiplataforma las funciones de la Win API en la unidad windows no deben utilizarse, o deberían estar encerradas por una compilación condicional (v.g. {$IFDEF MSWINDOWS} ).

   Por suerte muchas funciones comunes del API de Windows están incorporadas de manera multiplataforma en la unidad LCLIntf. Esto puede ser una solución para los programas que dependen fuertemente de la API de Windows, aunque la mejor solución es reemplazar estas llamadas con la verdadera componentes multiplataforma de la LCL. Por ejemplo, reemplazar las llamadas a funciones de dibujo GDI con llamadas a los métodos de un objeto TCanvas.

   La detección de códgigos de (v.g. en eventos KeyUp) es portable: ver Gestión de teclas de la LCL.

En Unix no hay directorio de aplicación

   Muchos programadores utilizan ExtractFilePath(ParamStr(0)) o Application.ExeName para obtener la ubicación del ejecutable, y luego buscar los archivos necesarios para la ejecución del programa (imágenes, archivos XML, archivos de base de datos, etc.) basandose en la ubicación del ejecutable. Esto en Unix está equivocado. La cadena ParamStr(0) puede contener un directorio que no sea el del ejecutable, y que también variar según con interpretes de comandos diferentes (sh, bash, etc.).

   Incluso si Application.ExeName puede conocer el directorio donde el ejecutable está, el archivo puede ser un enlace simbólico, por lo que se obtendrá el directorio del enlace en su lugar (dependiendo de la versión del núcleo Linux, se obtendrá el directorio del enlace o del programa binario mismo)

   Para evitar esto, lea las secciones sobre Archivos de configuración y Archivos de datos y recursos.

Prescindir de la automatización COM de Windows

   En Windows la automatización COM es una potente forma no sólo para utilizar otros programa remotamente, sino también para permitir que otros programas utilizar tu programa. Con Delphi puedes hacer un programa, tanto cliente como servidor de automatización, lo que significa que puede manipular otros programas y, a su vez ser manipulado por otros programas. Para ejemplos ver Usar automatización COM para interaccionar con OpenOffice y Microsoft Office.

   Por desgracia, la automatización COM no está disponible en OS X y Linux. Sin embargo, puedes simular algunas de las funcionalidades de automatización en OS X usando AppleScript.

   AppleScript es similar a la automatización en cierto modo. Por ejemplo, puedes escribir secuencias de comandos que manipulan otros programas. He aquí un ejemplo muy simple de AppleScript que comienza NeoOffice (la versión para Mac de OpenOffice.org):

 tell application "NeoOffice"
   launch
 end tell

   Una aplicación que está diseñada para ser manipulada por AppleScript ofrece un "diccionario" de las clases y comandos que se pueden utilizar con la aplicación, similar a las clases de un servidor de Automatización Windows. Sin embargo, incluso aplicaciones como NeoOffice que no proporcionan un diccionario responderá a los comandos "launch", "activate" y "quit". AppleScript puede ejecutarse desde el Finder de OS X o desde el Editor de secuencias o incluso convertirse en una aplicación que puedes colocar en el panel al igual que cualquier aplicación. También puede ejecutar AppleScript desde su programa, como en este ejemplo:

 Shell('myscript.applescript');

   Se supone que la secuencia de comandos se encuentra en el archivo indicado. También puedes ejecutar secuencias de comandos al vuelo a partir de tu aplicación utilizando el comando OsaScript de OS X:

 Shell('osascript -e '#39'tell application "NeoOffice"'#39 +
       ' -e '#39'launch'#39' -e '#39'end tell'#39);
       {Note use of #39 to single-quote the parameters}

   Sin embargo, estos ejemplos son sólo el equivalente del comando Open:

 Shell('open -a NeoOffice');

   Del mismo modo, en OS X se puede emular la línea de comandos de Windows para iniciar un navegador web e iniciar un cliente de correo electrónico con:

 fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');

   y

 fpsystem('open -a mail "mailto:ss4200@invalid.org"');

   que asume, con bastante certeza, que un sistema OS X tendrá Safari y la aplicación de correo instalados. Por supuesto, no se deben hacer presunciones como esta, y para los dos ejemplos anteriores, puedes, de hecho sólo basarte en OS X para hacer lo correcto y elegir el navegador y el cliente de correo electrónico predeterminados del usuario si en su lugar utilizas estas variaciones:

 fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');

   y

 fpsystem('open "mailto:ss4200@invalid.org"');

   No olvides incluir la unidad de Unix en la cláusula uses si utilizas fpsystem o shell (indistintamente).

   La potencia real de AppleScript es manejar de forma remota los programas para crear o abrir documentos y automatizar otras actividades. Cuánto se puede hacer con un programa depende de qué tan extenso es su diccionario AppleScript (si lo tiene). Por ejemplo, los programas de Office de Microsoft X no son muy útiles con AppleScript, mientras que los nuevos programas de Office 2004 han reescrito por completo diccionarios para AppleScript que son comparables en muchos aspectos con lo que está disponible a través de los servidores de automatización del Office en Windows.

   Mientras que las secuencias de comandos Linux permiten sofisticadas líneas de ordenes, el tipo de secuencias de comandos se limita a lo que se puede pasar a un programa en la línea de parámetros. No hay una forma única y unificada para acceder a las clases y comandos internos de un programa Linux en la forma en que se hace a través de la automatización de Windows y AppleScript de OS X. Sin embargo, cada entorno de escritorio (GNOME / KDE) y los entornos de aplicaciones a menudo proporcionan estos métodos de comunicación entre procesos. En GNOME hay que ver los componentes Bonobo. KDE tiene el marco KParts y DCOP. OpenOffice tiene una plataforma neutral de la API para el control remoto de las aplicaciones (SDK OpenOffice); aunque probablemente tendrás que escribir código de unión en otro lenguaje que tenga enlaces (como Python) para utilizarlo. Además, algunas aplicaciones tienen "modos de servidor" activados por las opciones especiales de línea de comandos que les permiten ser controladas desde otro proceso. También es posible (Borland lo hizo con el visor de documentos de Kylix) para "integrar" una ventana de aplicación X de nivel superior en otra utilizando XReparentWindow (creo).

   Al igual que con Windows, muchos programas de OS X y Linux se componen de múltiples archivos de librería (extensiones .Dylib y .so). A veces, estas librerías están diseñadas para que también podamos utilizarlas en los programas que escribamos. Si bien esto es una manera de añadir algunas funcionalidades de un programa externo en nuestro programa, en realidad no es la mismo que ejecutar y manipular el propio programa externo. En cambio, tu programa se enlaza a la librería del programa externo y la usa de manera similar a cualquier otra librería de programación.

See Also