Difference between revisions of "Multiplatform Programming Guide/es"

From Lazarus wiki
Jump to navigationJump to search
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 múltiplataforma, 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 múltiplataforma 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 o VMware.
  
 
== Cross-platform Programming ==
 
== Cross-platform Programming ==

Revision as of 22:17, 12 June 2010

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 va 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.

   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.

   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 o VMware.

Cross-platform Programming

Working with files and folders

When working with files and folders, this is important to use non-platform specific path delimiters and line ending sequences. Here is a list of declared constants in Lazarus to be used when working with files and folders.

  • PathSep, PathSeparator: path separator when adding many paths together (';', ...)
  • PathDelim, DirectorySeparator: directory separator for each platform ('/', '\', ...)
  • LineEnding: proper line ending character sequence (#13#10 - CRLF, #10 - LF, ...)

Another important thing to be noted is the case sensitiveness of the file system. On Windows filenames are never case sensitive, while they usually are on Linux and BSD platforms. Mac OS X use case insensitive filenames by default. This can be the cause of annoying bugs, so any portable application should use consistently filenames.

The RTL file functions use the system encoding for file names. Under Windows this is one of the windows code pages, while Linux, BSD and Mac OS X usually use UTF-8. The unit FileUtil of the LCL provides file functions which takes UTF-8 strings like the rest of the LCL.

<Delphi> // AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX // but normally these OS use UTF-8 as system encoding so the widestringmanager // is not needed. function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8 procedure SetNeedRTLAnsi(NewValue: boolean); function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager function UTF8ToConsole(const s: string): string;// converts UTF8 string to console encoding (used by Write, WriteLn)

// file operations 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;

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

// other function SysErrorMessageUTF8(ErrorCode: Integer): String; </Delphi>


Text encoding

Text files are often encoded in the current system encoding. Under Windows this is usually one of the windows code pages, while Linux, BSD and Mac OS X usually use UTF-8. There is no 100% rule to find out which encoding a text file uses. The LCL unit lconvencoding has a function to guess the encoding:

<Delphi> function GuessEncoding(const s: string): string; function GetDefaultTextEncoding: string; </Delphi>

And it contains functions to convert from one encoding to another:

<Delphi> 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 ... </Delphi>

For example to load a text file and convert it to UTF-8 you can use:

<Delphi> var

 sl: TStringList;
 OriginalText: String;
 TextAsUTF8: String;

begin

 sl:=TStringList.Create;
 try
   sl.LoadFromFile('sometext.txt'); // beware: this changes line endings to system line endings
   OriginalText:=sl.Text;
   TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);
   ...
 finally
   sl.Free;
 end;

end; </Delphi>

And to save a text file in the system encoding you can use: <Delphi>

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

</Delphi>

Configuration files

You can use the GetAppConfigDir function from SysUtils unit to get a suitable place to store configuration files on different system. The function has one parameter, called Global. If it is True then the directory returned is a global directory, i.e. valid for all users on the system. If the parameter Global is false, then the directory is specific for the user who is executing the program. On systems that do not support multi-user environments, these two directories may be the same.

There is also the GetAppConfigFile which will return an appropriate name for an application configuration file. You can use it like this:

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

Bellow are examples of the output of default path functions on different systems:

<delphi> program project1;

{$mode objfpc}{$H+}

uses

 SysUtils;

begin

 WriteLn(GetAppConfigDir(True));
 WriteLn(GetAppConfigDir(False));
 WriteLn(GetAppConfigFile(True));
 WriteLn(GetAppConfigFile(False));

end. </delphi>

The output on a GNU/Linux system with FPC 2.2.2. Note that using True is buggy, already fixed in 2.2.3:

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

You can notice that glocal configuration files are stored on the /etc directory and local configurations are stored on a hidden folder on the user's home directory. Directories whose name begin with a dot (.) are hidden on Linux. You can create a directory on the location returned by GetAppConfigDir and then store configuration files there.

The output on Windows XP with 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

Notice that before FPC 2.2.4 the function was using the directory where the application was to store global configurations on Windows.

The output on Windows 98 with 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

The output on Mac OS X with FPC 2.2.0:

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

Note: The use of UPX interferes with the use of the GetAppConfigDir and GetAppConfigFile functions.

Note: In most cases config files are preference files, which should be XML files with the ending ".plist" and be stored in /Library/Preferences or ~/Library/Preferences with Names taken from the field "Bundle identifier" in the Info.plist of the application bundle. Using the Carbon calls CFPreference... is probably the easiest way to achieve this. .config files in the User directory are a violation of the programming guide lines.

Data and resource files

A very common question is where to store data files an application might need, such as Images, Music, XML files, database files, help files, etc. Unfortunately there is no cross-platform function to get the best location to look for data files. The solution is to implement differently on each platform using IFDEFs.

On Windows you can simply assume that the files are at the same directory as the executable, or at a position relative to it.

On most unixes (like Linux, BSDs, solaris, etc), data files are located on a fixed location, that can be something like: /usr/share/app_name or /opt/app_name

Most programs will be executed without root privileges and thus a cross-platform software should store data files on a place where it will always have read access to, but not necessarely write access, and configuration files on a place where it has read and write access.

Mac OS X is an exception among UNIXes. 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.

Example

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.

The configuration file path is located with the GetAppConfigFile function from the Free Pascal Runtime Library.

Below is a full unit which you can use at your applications.

<delphi> unit appsettings;

interface

{$ifdef fpc}

 {$mode delphi}{$H+}

{$endif}

uses {$IFDEF Win32}

 Windows,

{$ENDIF}

 Classes, SysUtils, Forms, IniFiles, constants;

type

{ TConfigurations }
TConfigurations = class(TObject)
private
  ConfigFilePath: string;
public
  {other settings as fields here}
  MyDirectory: string;
  constructor Create;
  destructor Destroy; override;
  procedure ReadFromFile(Sender: TObject);
  procedure Save(Sender: TObject);
end;


var

vConfigurations: TConfigurations;

implementation

const

 DefaultDirectory = '/usr/share/myapp/';
 SectionGeneral = 'General';
 SectionUnix = 'UNIX';
 IdentMyDirectory = 'MyDirectory';

{ TConfigurations }

constructor TConfigurations.Create; begin {$ifdef win32}

ConfigFilePath := ExtractFilePath(Application.EXEName) + 'myapp.ini';

{$endif} {$ifdef Unix}

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

{$endif}

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, IdentMyDirectory, MyDirectory);
finally
  MyFile.Free;
end;

end;

procedure TConfigurations.ReadFromFile(Sender: TObject); var

MyFile: TIniFile;

begin

MyFile := TIniFile.Create(ConfigFilePath);
try
 // Here you can read other information from the config file

{$ifdef Win32}

  MyDirectory := MyFile.ReadString(SectionUnix, IdentMyDirectory,

ExtractFilePath(Application.EXEName)); {$else}

  MyDirectory := MyFile.ReadString(SectionUnix, IdentMyDirectory,

DefaultDirectory); {$endif}

finally
  MyFile.Free;
end;

end;

initialization

vConfigurations := TConfigurations.Create;

finalization

FreeAndNil(vTranslations);

end. </delphi>

and here example code of how to use that unit:

<delphi>

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

</delphi>

32/64 bit

Pointer / Integer Typecasts

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.

Use for code that should work with Delphi and FPC:

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

Replace all integer(SomePointerOrObject) with PtrInt(SomePointerOrObject).

Endianess

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. 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:

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

The opposite is ENDIAN_LITTLE.

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).


Libc and other special units

Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.

Assembler

Avoid assembler.

Compiler defines

<DELPHI> {$ifdef CPU32} ...write here code for 32 bit processors {$ENDIF} {$ifdef CPU64} ...write here code for 64 bit processors {$ENDIF} </DELPHI>

Projects, packages and search paths

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.

Some advice to achieve this

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.

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: Creating a package for your common units

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.

Platform specific units

For example the unit wintricks.pas should only be used under Windows. In the uses section use:

<Delphi> uses

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

</Delphi>

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

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. An example for one include file for each LCL widget set:

Create one file for each widget set you want to support:

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

You do not need to add the files to the package or project. Add the include search path $(LCLWidgetType) to the compiler options of your package or project.

In your unit use the directive: {$I example.inc}

Here are some useful macros and common values:

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

You can use the $Env() macro to use environment variables.

And of course you can use combinations. For example the LCL uses:

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

See here the complete list of macros: IDE Macros in paths and filenames

Machine / User specific search paths

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. Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml). When compiling a project that requires the package SharedStuff, the IDE and lazbuild knows where it is. So no configuration is needed.

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.

Windows specific issues

Windows API Functions

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} ).

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.

On Unix there is no "application directory"

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).

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).

To avoid this read the sections about configuration files and data files.

Making do without Windows COM Automation

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, see 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.

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):

 tell application "NeoOffice"
   launch
 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:

 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:

 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}

However, these examples are just the equivalent of the following Open command:

 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:

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

and

 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:

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

and

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

Do not forget to include the Unix unit in your uses clause if you use fpsystem or shell (interchangeable).

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.

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).

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.

See Also