Sign in Join
Wiki copytemporary

Multiplatform Programming Guide/es: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Esta 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.
22
33## Introducción a la programación multiplataforma
44
55### ¿Cuántas *cajas* son necesarias?
66
77   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.
88
99   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.
1010
1111   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.
1212
1313   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.
1414
1515   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.
1616
1717   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.
1818
1919## Programación multiplataforma
2020
2121### Trabajar con archivos y carpetas
2222
2323   Cuando se trabaja con archivos y carpetas, es importante usar delimitadores de ruta y de [[End of Line|fin de línea]] no específicos de una plataforma\]. Aquí está una lista de declaraciones [[Constant|constantes]] en Lazarus para usar cuando se trabaja con archivos y carpetas.
2424
2525- **PathSep**, **PathSeparator**: separador de ruta al agregar varias rutas juntas (';', ...)
2626- **PathDelim**, **DirectorySeparator**: separador de directorio para cada plataforma ('/', '\\', ...)
2727- **LineEnding**: secuencia de caracteres correcta de final de línea (#13#10 - CRLF, \#10 - [[Line feed|LF]], ...)
2828
2929   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.
3030
3131   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.
3232
3333``` pascal
3434 // AnsiToUTF8 y UTF8ToAnsi necesitan de un gestor de ''widestring'' en Linux, BSD y MacOSX
3535 // pero usualmente estos SO utilizan UTF-8 como codificación del sistema por lo que el gestor de ''widestring''
3636 // no es necesario.
3737 function NeedRTLAnsi: boolean;// cierto (true) si el sistema no es UTF-8
3838 procedure SetNeedRTLAnsi(NewValue: boolean);
3939 function UTF8ToSys(const s: string): string;// similar a UTF8ToAnsi pero más independiente delgestor de ''widestring''
4040 function SysToUTF8(const s: string): string;// similar a AnsiToUTF8 pero más independiente delgestor de ''widestring''
4141 function UTF8ToConsole(const s: string): string;// convierte cadenas UTF8 a la codificación de la consola (usado por Write, WriteLn)
4242
4343 // operaciones de archivo
4444 function FileExistsUTF8(const Filename: string): boolean;
4545 function FileAgeUTF8(const FileName: string): Longint;
4646 function DirectoryExistsUTF8(const Directory: string): Boolean;
4747 function ExpandFileNameUTF8(const FileName: string): string;
4848 function ExpandUNCFileNameUTF8(const FileName: string): string;
4949 function ExtractShortPathNameUTF8(Const FileName : String) : String;
5050 function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
5151 function FindNextUTF8(var Rslt: TSearchRec): Longint;
5252 procedure FindCloseUTF8(var F: TSearchrec);
5353 function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
5454 function FileGetAttrUTF8(const FileName: String): Longint;
5555 function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
5656 function DeleteFileUTF8(const FileName: String): Boolean;
5757 function RenameFileUTF8(const OldName, NewName: String): Boolean;
5858 function FileSearchUTF8(const Name, DirList : String): String;
5959 function FileIsReadOnlyUTF8(const FileName: String): Boolean;
6060 function GetCurrentDirUTF8: String;
6161 function SetCurrentDirUTF8(const NewDir: String): Boolean;
6262 function CreateDirUTF8(const NewDir: String): Boolean;
6363 function RemoveDirUTF8(const Dir: String): Boolean;
6464 function ForceDirectoriesUTF8(const Dir: string): Boolean;
6565
6666 // entorno
6767 function ParamStrUTF8(Param: Integer): string;
6868 function GetEnvironmentStringUTF8(Index: Integer): string;
6969 function GetEnvironmentVariableUTF8(const EnvVar: string): String;
7070 function GetAppConfigDirUTF8(Global: Boolean): string;
7171
7272 // otras
7373 function SysErrorMessageUTF8(ErrorCode: Integer): String;
7474```
7575
7676### Nombres de archivo vacios y delimitadores de ruta duplicados
7777
7878   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.
7979
8080   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:
8181
8282``` pascal
8383 FullFilename:=FilePath+PathDelim+ShortFilename; // el resultado tiene dos delimitadores de ruta y da resultados distinto en Windows y Linux
8484 FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // crea un solo delimitador de ruta
8585 FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // crea un solo delimitador de ruta y es más limpio
8686```
8787
8888   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.
8989
9090   Si quieres saber si un directorio existe utiliza **DirectoryExistsUTF8**.
9191
9292   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.
9393
9494### Codificación de texto
9595
9696   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:
9797
9898``` pascal
9999 function GuessEncoding(const s: string): string;
100100 function GetDefaultTextEncoding: string;
101101```
102102
103103   Y contiene funciones para la conversión de una codificación a otra:
104104
105105``` pascal
106106 function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;
107107
108108 function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM
109109 function ISO_8859_1ToUTF8(const s: string): string; // central europe
110110 function CP1250ToUTF8(const s: string): string; // central europe
111111 function CP1251ToUTF8(const s: string): string; // cyrillic
112112 function CP1252ToUTF8(const s: string): string; // latin 1
113113 ...
114114 function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM
115115 function UTF8ToISO_8859_1(const s: string): string; // central europe
116116 function UTF8ToCP1250(const s: string): string; // central europe
117117 function UTF8ToCP1251(const s: string): string; // cyrillic
118118 function UTF8ToCP1252(const s: string): string; // latin 1
119119 ...
120120```
121121
122122   Por ejemplo, para cargar un archivo de texto y convertirlo a UTF-8 se puede utilizar:
123123
124124``` pascal
125125 var
126126 sl: TStringList;
127127 OriginalText: String;
128128 TextAsUTF8: String;
129129 begin
130130 sl:=TStringList.Create;
131131 try
132132 sl.LoadFromFile('alguntexto.txt'); // Ojo: esta sentencia de cambia los delimitadores de final de línea a los del sistema
133133 OriginalText:=sl.Text;
134134 TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);
135135 ...
136136 finally
137137 sl.Free;
138138 end;
139139 end;
140140```
141141
142142   Y para guardar el archivo de texto con la codificación del sistema usaremos esto:
143143
144144``` pascal
145145 sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);
146146 sl.SaveToFile('sometext.txt');
147147```
148148
149149### Archivos de configuración
150150
151151   Puede utilizar la función [[doc:rtl/sysutils/getappconfigdir.html|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.
152152
153153   También existe la función [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]], que devolverá un nombre apropiado para un archivo de configuración de la aplicación. Se puede utilizar de esta manera:
154154
155155ConfigFilePath := GetAppConfigFile(False) + '.conf';
156156
157157   Estos son ejemplos de la salida de las funciones de ruta por defecto en diferentes sistemas:
158158
159159``` pascal
160160 program proyecto1;
161161
162162 {$mode objfpc}{$H+}
163163
164164 uses
165165 SysUtils;
166166
167167 begin
168168 WriteLn(GetAppConfigDir(True));
169169 WriteLn(GetAppConfigDir(False));
170170 WriteLn(GetAppConfigFile(True));
171171 WriteLn(GetAppConfigFile(False));
172172 end.
173173```
174174
175175   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:
176176
177177 /etc/project1/
178178 /home/user/.config/project1/
179179 /etc/project1.cfg
180180 /home/user/.config/project1.cfg
181181
182182   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í.
183183
184184   El resultado en Windows XP con FPC 2.2.4 + :
185185
186186 C:\Documents and Settings\All Users\Application Data\project1\
187187 C:\Documents and Settings\user\Local Settings\Application Data\project1
188188 C:\Documents and Settings\All Users\Application Data\project1\project1.cfg
189189 C:\Documents and Settings\user\Local Settings\Application Data\project1\project1.cfg
190190
191191   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.
192192
193193   El resultado en Windows 98 con FPC 2.2.0:
194194
195195 C:\Program Files\PROJECT1
196196 C:\Windows\Local Settings\Application Data\PROJECT1
197197 C:\Program Files\PROJECT1\PROJECT1.cfg
198198 C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg
199199
200200   El resultado en Mac OS X con FPC 2.2.0:
201201
202202 /etc
203203 /Users/user/.config/project1
204204 /etc/project1.cfg
205205 /Users/user/.config/project1/project1.cfg
206206
207207   Nota: El uso de UPX interfiere con el uso de las funciones *GetAppConfigDir* y *GetAppConfigFile*.
208208
209209   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.
210210
211211### Archivos de datos y recursos
212212
213213   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.
214214
215215   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.
216216
217217   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*.
218218
219219   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.
220220
221221   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.
222222
223223#### Ejemplo
224224
225225   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.
226226
227227   El archivo de configuración se localiza con la función *GetAppConfigFile* de la RTL de Free Pascal.
228228
229229   A continuación se muestra una unidad completa que puedes utilizar en tus aplicaciones.
230230
231231``` pascal
232232 unit appsettings;
233233
234234interface
235235
236236{$ifdef fpc}
237237 {$mode delphi}{$H+}
238238{$endif}
239239
240240uses
241241 Classes, SysUtils, Forms, IniFiles, constants;
242242
243243type
244244
245245 { TConfigurations }
246246
247247 TConfigurations = class(TObject)
248248 private
249249 function GetResourcesPath: string;
250250 public
251251 {otros ajustes serán campos aquí}
252252 ConfigFilePath: string;
253253 ResourcesPath: string;
254254 constructor Create;
255255 destructor Destroy; override;
256256 procedure ReadFromFile(Sender: TObject);
257257 procedure Save(Sender: TObject);
258258 end;
259259
260260var
261261 vConfigurations: TConfigurations;
262262
263263implementation
264264
265265{$IFDEF Win32}
266266uses
267267 Windows;
268268{$ENDIF}
269269{$ifdef Darwin}
270270uses
271271 MacOSAll;
272272{$endif}
273273
274274const
275275 DefaultDirectory = '/usr/share/myapp/';
276276
277277 SectionGeneral = 'General';
278278 SectionUnix = 'UNIX';
279279
280280 IdentResourcesPath = 'ResourcesPath';
281281
282282{ TConfigurations }
283283
284284constructor TConfigurations.Create;
285285begin
286286{$ifdef win32}
287287 ConfigFilePath := ExtractFilePath(Application.EXEName) + 'myapp.ini';
288288{$endif}
289289{$ifdef Unix}
290290 ConfigFilePath := GetAppConfigFile(False) + '.conf';
291291{$endif}
292292
293293 ResourcePath := GetResourcesPath();
294294
295295 ReadFromFile(nil);
296296end;
297297
298298destructor TConfigurations.Destroy;
299299begin
300300 Save(nil);
301301
302302 inherited Destroy;
303303end;
304304
305305procedure TConfigurations.Save(Sender: TObject);
306306var
307307 MyFile: TIniFile;
308308begin
309309 MyFile := TIniFile.Create(ConfigFilePath);
310310 try
311311 MyFile.WriteString(SectionUnix, IdentResourcesPath, ResourcesPath);
312312 finally
313313 MyFile.Free;
314314 end;
315315end;
316316
317317procedure TConfigurations.ReadFromFile(Sender: TObject);
318318var
319319 MyFile: TIniFile;
320320begin
321321 MyFile := TIniFile.Create(ConfigFilePath);
322322 try
323323 // Aquí puedes leer más información del archivo de configuración
324324
325325{$ifdef Win32}
326326 ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
327327ExtractFilePath(Application.EXEName));
328328{$else}
329329 {$ifndef darwin}
330330 ResourcesPath := MyFile.ReadString(SectionUnix, IdentResourcesPath,
331331DefaultDirectory);
332332 {$endif}
333333{$endif}
334334 finally
335335 MyFile.Free;
336336 end;
337337end;
338338
339339function TConfigurations.GetResourcesPath(): string;
340340begin
341341{$ifdef Darwin}
342342var
343343 pathRef: CFURLRef;
344344 pathCFStr: CFStringRef;
345345 pathStr: shortstring;
346346{$endif}
347347begin
348348{$ifdef UNIX}
349349{$ifdef Darwin}
350350 pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
351351 pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
352352 CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
353353 CFRelease(pathRef);
354354 CFRelease(pathCFStr);
355355
356356 Result := pathStr + BundleResourcesDirectory;
357357{$else}
358358 Result := DefaultDirectory;
359359{$endif}
360360{$endif}
361361
362362{$ifdef Windows}
363363 Result := ExtractFilePath(Application.EXEName);
364364{$endif}
365365end;
366366
367367initialization
368368
369369 vConfigurations := TConfigurations.Create;
370370
371371finalization
372372
373373 FreeAndNil(vTranslations);
374374
375375end.
376376```
377377
378378   y este es un ejemplo de utilización de esta unidad:
379379
380380``` pascal
381381 bmp := TBitmap.Create
382382 try
383383 bmp.LoadFromFile(vConfigurations.ResourcesPath + 'MiMapaBits.bmp');
384384 finally
385385 bmp.Free;
386386 end;
387387```
388388
389389### 32/64 bits
390390
391391#### Conversión de Punteros / Enteros
392392
393393   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.
394394
395395   Utilización en código que debe trabajar con Delphi y FPC:
396396
397397``` pascal
398398 {$IFNDEF FPC}
399399 type
400400 PtrInt = integer;
401401 PtrUInt = cardinal;
402402 {$ENDIF}
403403```
404404
405405   Sustituye todos las ocurrecias de **integer(unPuntero_o_unObjeto)** por **PtrInt(unPuntero_o_unObjeto)**.
406406
407407### Formato de multiByte
408408
409409   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.
410410
411411   Usaremos esto en código que trabaje en ambos sistemas:
412412
413413``` pascal
414414 {$IFDEF ENDIAN_BIG}
415415 ...
416416 {$ELSE}
417417 ...
418418 {$ENDIF}
419419```
420420
421421    Lo contrario es `{$IFDEF ENDIAN_LITTLE}`.
422422
423423   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).
424424
425425#### Libc y otras unidades especiales
426426
427427   Evita las unidades heredadas cómo "oldlinux" y "libc" no admitidos fuera de linux/i386.
428428
429429#### Ensamblador
430430
431431   Evita el ensamblador.
432432
433433#### Directivas del compilador
434434
435435``` pascal
436436 {$ifdef CPU32}
437437 ...escribe aquí código para procesadores de 32 bits
438438 {$ENDIF}
439439 {$ifdef CPU64}
440440 ...escribe aquí código para procesadores de 64 bits
441441 {$ENDIF}
442442```
443443
444444### Proyectos, paquetes y rutas de búsqueda
445445
446446   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.
447447
448448   Algunos consejos para lograrlo
449449
450450   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í.
451451
452452   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|Creación de un paquete para las unidades comunes]]
453453
454454   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.
455455
456456#### Unidades específicas de plataforma
457457
458458   Por ejemplo la unidad *wintricks.pas* sólo debe usarse en Windows. En la sección uses pon:
459459
460460``` pascal
461461 uses
462462 Classes, SysUtils
463463 {$IFDEF Windows}
464464 ,WinTricks
465465 {$ENDIF}
466466 ;
467467```
468468
469469   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*.
470470
471471#### Rutas de búsqueda específicas de plataforma
472472
473473   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.
474474
475475   Un ejemplo para un archivo de inclusión para cada *LCL widgetset*:
476476
477477   Crear un archivo para cada conjunto de controles para los quieras dar soporte:
478478
479479``` pascal
480480 win32/example.inc
481481 gtk/example.inc
482482 gtk2/example.inc
483483 carbon/example.inc
484484```
485485
486486   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.
487487
488488   En tu unidad utiliza la directiva: `{$I example.inc}`
489489
490490   Estas son algunas macros y valores comunes útiles:
491491
492492- LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui
493493- TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (y más)
494494- TargetCPU: i386, x86_64, arm, powerpc, sparc
495495- SrcOS: win, unix
496496
497497   Puedes utilizar la macro `$Env()` para acceder a variables de entorno.
498498
499499   Y por supuesto puedes usar combinaciones. Por ejemplo, la LCL usa:
500500
501-`$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)`
501+```text
502+ $(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)
503+```
502504
503505   Ver aquí la lista completa de macros:: [[IDE Macros in paths and filenames/es|Macros del IDE en rutas y nombres de archivo]]
504506
505507#### Rutas específicas de búsqueda de Máquina / Usuario
506508
507509   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*.
508510
509511   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).
510512
511513   Al compilar un proyecto que requiere el paquete *MaterialCompartido*, el IDE y lazbuild saben dónde está. Así que no se necesita configuración.
512514
513515   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.
514516
515517### Diferencias [locales](http://es.wikipedia.org/wiki/Locale)
516518
517519   Algunas funciones de Free Pascal, como StrToFloat se comportan de manera diferente dependiendo de la [localización](http://es.wikipedia.org/wiki/Internacionalización_y_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
518520
519521#### StrToFloat
520522
521523   Un nuevo conjunto de opciones de formato que establece un separador decimal fijo se pueden crear con el siguiente código:
522524
523525``` pascal
524526 var
525527 FPointSeparator, FCommaSeparator: TFormatSettings;
526528 begin
527529 // Ajustes del formato para convertir una cadena a coma flotante
528530 FPointSeparator := DefaultFormatSettings;
529531 FPointSeparator.DecimalSeparator := '.';
530532 FPointSeparator.ThousandSeparator := '#';// desactivar el separador de miles
531533 FCommaSeparator := DefaultFormatSettings;
532534 FCommaSeparator.DecimalSeparator := ',';
533535 FCommaSeparator.ThousandSeparator := '#';// desactivar el separador de miles
534536```
535537
536538   Por último puedes utilizar esta configuración de formato al llamar a StrToFloat, así:
537539
538540``` pascal
539541 // Esta función funciona como StrToFloat, pero trata dos posibles separadores decimales
540542 // Esto evitará una excepción cuando el formato de cadena no coincide con la configuración local
541543 function AnSemantico.StringToFloat(AStr: string): Double;
542544 begin
543545 if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
544546 else Result := StrToFloat(AStr, FCommaSeparator);
545547 end;
546548```
547549
548550## Cuestiones específicas de Windows
549551
550552### Funciones API de Windows
551553
552554   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} ).
553555
554556   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.
555557
556558   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]].
557559
558560### En Unix no hay *directorio de aplicación*
559561
560562   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.).
561563
562564   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)
563565
564566   Para evitar esto, lea las secciones sobre [[Multiplatform Programming Guide/es|Archivos de configuración]] y [[Multiplatform Programming Guide/es|Archivos de datos y recursos]].
565567
566568## Prescindir de la automatización COM de Windows
567569
568570   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ón COM para interaccionar con OpenOffice y Microsoft Office]].
569571
570572   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.
571573
572574   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):
573575
574-` tell application "NeoOffice"`\
575-`   launch`\
576-` end tell`
576+``` pascal
577+ tell application "NeoOffice"
578+ launch
579+ end tell
580+```
577581
578582   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:
579583
580-` Shell('myscript.applescript');`
584+```text
585+ Shell('myscript.applescript');
586+```
581587
582588   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:
583589
584-` Shell('osascript -e '#39'tell application "NeoOffice"'#39 +`\
585-`       ' -e '#39'launch'#39' -e '#39'end tell'#39);`\
586-`       {Note use of #39 to single-quote the parameters}`
590+``` pascal
591+ Shell('osascript -e '#39'tell application "NeoOffice"'#39 +
592+ ' -e '#39'launch'#39' -e '#39'end tell'#39);
593+ {Note use of #39 to single-quote the parameters}
594+```
587595
588596   Sin embargo, estos ejemplos son sólo el equivalente del comando Open:
589597
590-` Shell('open -a NeoOffice');`
598+```text
599+ Shell('open -a NeoOffice');
600+```
591601
592602   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:
593603
594-` fpsystem('open -a safari "`[`http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html`](http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html)`"');`
604+```text
605+ fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
606+```
595607
596608   y
597609
598-` fpsystem('open -a mail "`[`mailto:ss4200@invalid.org`](mailto:ss4200@invalid.org)`"');`
610+```text
611+ fpsystem('open -a mail "mailto:ss4200@invalid.org"');
612+```
599613
600614   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:
601615
602-` fpsystem('open "`[`http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html`](http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html)`"');`
616+```text
617+ fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
618+```
603619
604620   y
605621
606-` fpsystem('open "`[`mailto:ss4200@invalid.org`](mailto:ss4200@invalid.org)`"');`
622+```text
623+ fpsystem('open "mailto:ss4200@invalid.org"');
624+```
607625
608626   No olvides incluir la unidad de Unix en la cláusula uses si utilizas `fpsystem` o `shell` (indistintamente).
609627
610628   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.
611629
612630   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).
613631
614632   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.
615633
616634## See Also
617635
618636- ~~<http://www.midnightbeach.com/jon/pubs/Kylix.html>~~ <http://www.midnightbeach.com/KylixForDelphiProgrammers.html> A guide for Windows programmers starting with Kylix. Many of concepts / code snippets apply to Lazarus.
619637- <http://www.stack.nl/~marcov/porting.pdf> A guide for writing portable source code, mainly between different compilers.
620638
621639---
622640*From the Free Pascal wiki, page "Multiplatform Programming Guide/es", as it stood on 2020-02-20 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
623641Category: Castellano
624642Category: Español

Back to history