Multiplatform Programming Guide/pl

From Free Pascal wiki
Jump to navigationJump to search

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

Przewodnik programowania wieloplatformowego

To jest samouczek dotyczący pisania aplikacji wieloplatformowych z Lazarusem i Free Pascalem. Obejmuje niezbędne środki ostrożności, aby pomóc w stworzeniu wieloplatformowego programu gotowego do wdrożenia.

Większość aplikacji LCL działa na wielu platformach bez dodatkowej pracy. Ta zasada nazywa się „napisz raz, kompiluj wszędzie”.

Wprowadzenie do programowania wieloplatformowego (międzyplatformowego)

Ile platform potrzebujesz?

Aby odpowiedzieć na to pytanie, powinieneś najpierw określić, kim są Twoi potencjalni użytkownicy i jak Twój program będzie używany. To pytanie zależy od tego, gdzie wdrażasz swoją aplikację.

Jeśli tworzysz ogólne oprogramowanie komputerowe w 2014 roku, Microsoft Windows może być najważniejszą platformą. Pamiętaj, że macOS i/lub Linux zyskują na popularności i mogą być ważnym celem dla Twojej aplikacji.

Popularność różnych systemów operacyjnych na komputery stacjonarne różni się w zależności od kraju, rodzaju używanego oprogramowania i grupy docelowej; nie ma ogólnej zasady. Na przykład macOS jest dość popularny w Ameryce Północnej i Europie Zachodniej, podczas gdy w Ameryce Południowej macOS ogranicza się głównie do pracy z wideo i dźwiękiem.

W wielu projektach kontraktowych odpowiednia jest tylko jedna platforma. Free Pascal i Lazarus są w stanie pisać oprogramowanie ukierunkowane na konkretną platformę. Możesz na przykład uzyskać dostęp do pełnego interfejsu API Windows, aby napisać dobrze zintegrowany program Windows.

Jeśli tworzysz oprogramowanie, które będzie działać na serwerze sieciowym, powszechnie używana jest platforma uniksowa w jednym z jej różnych wariantów. Być może w tym przypadku jako platformy docelowe sens mają tylko Linux, Solaris, *BSD i inne Uniksy, chociaż możesz chcieć dodać do kompletu obsługę Windows.

Po rozwiązaniu wszelkich problemów międzyplatformowych w swoim projekcie, możesz w dużej mierze zignorować inne platformy, podobnie jak w przypadku programowania dla jednej platformy. Jednak w pewnym momencie będziesz musiał przetestować wdrażanie i uruchamianie programu na innych platformach. W tym celu pomocny będzie nieograniczony dostęp do maszyn z docelowymi systemami operacyjnymi. Jeśli nie chcesz mieć wielu fizycznych komputerów, sprawdź rozwiązania z podwójnym rozruchem lub maszyną wirtualną (VM), taką jak:

  • VMware Fusion for Mac - Systemie hosta, na którym musi być uruchomiony system Intel macOS (komercyjny/bezpłatny do użytku niekomercyjnego).
  • VMware Workstation Player - System hosta z 64-bitowym systemem Windows lub 64-bitowym systemem Linux (komercyjny/bezpłatny do użytku osobistego).
  • VMware Workstation Pro - System hosta z 64-bitowym systemem Windows lub 64-bitowym systemem Linux (komercyjna/bezpłatna wersja próbna).
  • Parallels Desktop for Mac - Na systemie hosta musi być uruchomiony system Intel macOS (komercyjna/bezpłatna wersja próbna). Uwaga: istnieje Podgląd Techniczny, który będzie działał na procesorach Apple M1 ARM64, i który będzie obsługiwał wersje ARM systemu Linux i Windows 10 Insider Preview.
  • VirtualBox - Na systemie hosta może działać Intel macOS, Windows, Linux lub Solaris (Open Source).

Niektóre z nich, np. Parallels na Macu, sprawiają, że instalacja stacjonarnego systemu operacyjnego Linux za pomocą jednego kliknięcia jest banalna.

Zobacz także: Małe maszyny wirtualne i Qemu i inne emulatory.

Programowanie międzyplatformowe

Praca z plikami i folderami

Podczas pracy z plikami i folderami ważne jest, aby używać niespecyficznych dla platformy ograniczników ścieżki i sekwencji końca wiersza. Oto lista zadeklarowanych stałych w Lazarusie, które powinny być używane podczas pracy z plikami i folderami.

  • PathSep, PathSeparator: separator ścieżek używany podczas dodawania wielu ścieżek razem (';', ...)
  • PathDelim, DirectorySeparator: separator katalogów dla każdej platformy ('/', '\', ...)
  • LineEnding: poprawna sekwencja znaków końca wiersza (#13#10 - CRLF, #10 - LF, ...)

Inną ważną rzeczą, na którą należy zwrócić uwagę, jest rozróżnianie wielkości liter w systemie plików. W systemie Windows w nazwach plików zwykle nie jest rozróżniana wielkość liter, podczas gdy na platformach uniksowych (np. Linux, FreeBSD) zwykle rozróżniana jest wielkość liter, ale nie w macOS, pomimo jego dziedzictwa uniksowego. Należy jednak pamiętać, że jeśli system plików EXT2, EXT3 itp. jest zamontowany w systemie Windows, rozróżniana jest wielkość liter. Podobnie system plików FAT zamontowany w systemie Linux nie powinien rozróżniać wielkości liter.

Szczególną uwagę należy zwrócić na NTFS, który nie rozróżnia wielkości liter, gdy jest używany w systemie Windows, ale rozróżnia wielkość liter, gdy jest montowany w systemach POSIX. Może to powodować różne problemy, w tym utratę plików, jeśli pliki o tych samych nazwach, lecz różnych wariantach, istnieją na partycji NTFS zamontowanej w systemie Windows. Programiści powinni rozważyć użycie niestandardowych funkcji do sprawdzania i zapobiegania tworzenia kilku plików o tych samych nazwach w systemie NTFS.

macOS domyślnie używa nazw plików bez uwzględniania wielkości liter. Może to być przyczyną irytujących błędów, więc każda przenośna aplikacja powinna konsekwentnie używać takich nazw plików.

Funkcje plikowe biblioteki RTL używają kodowania systemowego dla nazw plików. W starszych systemach Windows (95/98/Me) jest to jedna ze stron kodowych systemu Windows, w nowszych UTF-16, podczas gdy Linux, BSD i macOS zwykle używają UTF-8. Moduł FileUtil biblioteki LCL zawiera funkcje plikowe, które przyjmują łańcuchy UTF-8, podobnie jak reszta LCL.

// Funkcje AnsiToUTF8 i UTF8ToAnsi wymagają menedżera widestring pod Linuksem, BSD, macOS,
// ale zwykle te systemy operacyjne używają UTF-8 jako kodowania systemowego, więc widestringmanager
// nie jest potrzebny.
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
procedure SetNeedRTLAnsi(NewValue: boolean);
function UTF8ToSys(const s: string): string;     // podobna do UTF8ToAnsi, ale nie jest już zależna od widestringmanager
function SysToUTF8(const s: string): string;     // podobna do AnsiToUTF8, ale nie jest już zależna od widestringmanager
function UTF8ToConsole(const s: string): string; // konwertuje ciąg znaków UTF8 na kodowanie konsoli (używane przez Write, WriteLn)

// operacje na plikach
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;

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

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

Puste nazwy plików i podwójne separatory ścieżki

Istnieją pewne różnice w obsłudze nazw plików/katalogów w systemie Windows w porównaniu z systemami Linux, Unix i Unix.

  • Windows zezwala na puste nazwy plików. Dlatego FileExistsUTF8('..\') sprawdza w systemie Windows w katalogu nadrzędnym, czy istnieje plik bez nazwy.
  • W systemach Linux/Unix/Uniksopodobnych do katalogu mapowany jest pusty plik, a katalogi są traktowane jako pliki. Oznacza to, że FileExistsUTF8('../') pod Uniksem sprawdza istnienie katalogu nadrzędnego, co zwykle daje prawdę.

Podwójne separatory ścieżki w nazwach plików są również traktowane inaczej:

  • Windows: 'C:\' to nie to samo co 'C:\\'
  • Uniksy: ścieżka '/usr//' jest taka sama jak '/usr/'. Jeśli '/usr' jest katalogiem, to nawet wszystkie trzy zapisy są takie same.

Jest to ważne przy łączeniu nazw plików. Na przykład:

FullFilename:=FilePath+PathDelim+ShortFilename; // może skutkować dwoma znakami PathDelims, które dają różne wyniki pod Windows i Linux
FullFilename:=AppendPathDelim(FilePath) + ShortFilename; // tworzy tylko jeden znak PathDelim
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // tworzy tylko jeden znak PathDelim i czyści ścieżkę

Funkcja TrimFilename zastępuje podwójne separatory ścieżek pojedynczymi jedynkami i skraca ścieżki typu '..'. Na przykład /usr//lib/../src jest przekształcony do postaci /usr/src.

Jeśli chcesz wiedzieć, czy katalog istnieje, użyj DirectoryExistsUTF8.

Innym częstym zadaniem jest sprawdzenie, czy istnieje ścieżka w nazwie pliku. Ścieżkę można uzyskać za pomocą ExtractFilePath, ale będzie ona zawierać separator ścieżki.

  • W systemie uniksowym możesz po prostu użyć FileExistsUTF8 na ścieżce. Na przykład FileExistsUTF8('/home/user/') zwróci true, jeśli katalog /home/user istnieje.
  • W systemie Windows musisz użyć funkcji DirectoryExistsUTF8, ale wcześniej musisz usunąć separator ścieżki, na przykład za pomocą funkcji ChompPathDelim.

W systemach uniksowych katalogiem głównym jest „/”, a użycie funkcji ChompPathDelim utworzy pusty ciąg. Funkcja DirPathExists działa jak funkcja DirectoryExistsUTF8, ale przycina podaną ścieżkę.

Zauważ, że Unix/Linux używa symbolu „~” (tylda) oznaczającego katalog domowy, np. „/home/jan/” dla użytkownika zwanego Jan. Tak więc „~/myapp/myfile” i „/home/jan/myapp/myfile” są identyczne w wierszu poleceń i skryptach. Jednak tylda nie jest automatycznie rozwijana przez Lazarusa. Aby uzyskać pełną ścieżkę, konieczne jest użycie ExpandFileNameUTF8('~/myapp/myfile').

Kodowanie tekstu

Pliki tekstowe są często kodowane w bieżącym kodowaniu systemowym. W starszych systemach Windows jest to zwykle jedna ze stron kodowych systemu Windows, a w nowszych UTF-16, podczas gdy Linux, BSD i macOS zwykle używają UTF-8. Nie ma zasady, która na 100% da odpowiedź, jakiego kodowania używa plik tekstowy. Moduł LCL lconvencoding ma funkcję do odgadywania kodowania:

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

oraz zawiera funkcje do konwersji z jednego kodowania na inne:

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

function UTF8BOMToUTF8(const s: string): string; // UTF8 z BOM
function ISO_8859_1ToUTF8(const s: string): string; // Europa Środkowa
function CP1250ToUTF8(const s: string): string; // Europa Środkowa
function CP1251ToUTF8(const s: string): string; // cyrylica
function CP1252ToUTF8(const s: string): string; // Latin-1 (Europa Zachodnia)
...
function UTF8ToUTF8BOM(const s: string): string; // UTF8 z BOM
function UTF8ToISO_8859_1(const s: string): string; // Europa Środkowa
function UTF8ToCP1250(const s: string): string; // Europa Środkowa
function UTF8ToCP1251(const s: string): string; // cyrylica
function UTF8ToCP1252(const s: string): string; // Latin-1 (Europa Zachodnia)
...

Na przykład, aby załadować plik tekstowy i przekonwertować go na UTF-8, możesz użyć:

var
  sl: TStringList;
  OriginalText: String;
  TextAsUTF8: String;
begin
  sl:=TStringList.Create;
  try
    sl.LoadFromFile('jakis_tekst.txt'); // uwaga: zmienia to zakończenia linii na systemowe zakończenia linii
    OriginalText:=sl.Text;
    TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);
    ...
  finally
    sl.Free;
  end;
end;

Aby zapisać plik tekstowy w kodowaniu systemowym, możesz użyć:

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

Pliki konfiguracyjne

Możesz użyć funkcji GetAppConfigDir z modułu SysUtils, aby uzyskać odpowiednie miejsce do przechowywania plików konfiguracyjnych w innym systemie. Funkcja ma jeden parametr o nazwie Global. Jeśli ma wartość True, zwracany katalog jest katalogiem globalnym, tj. ważnym dla wszystkich użytkowników w systemie. Jeśli parametr Global ma wartość false, katalog jest specyficzny dla użytkownika, który wykonuje program. W systemach, które nie obsługują środowisk wielu użytkowników, te dwa katalogi mogą być takie same.

Istnieje również funkcja GetAppConfigFile, która zwróci odpowiednią nazwę dla pliku konfiguracyjnego aplikacji. Możesz jej użyć w ten sposób:

ConfigFilePath := GetAppConfigFile(False);

Poniżej znajdują się przykłady funkcji wypisujące domyślne ścieżki w różnych systemach:

program project1;

{$mode objfpc}{$H+}

uses
  SysUtils;

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

Możesz zauważyć, że w systemie Linux globalne pliki konfiguracyjne przechowywane są w katalogu /etc, a lokalne w ukrytym katalogu w katalogu domowym użytkownika. Katalogi, których nazwa zaczyna się od kropki (.) są ukryte w systemach operacyjnych UNIX i podobnych do UNIX. Możesz utworzyć katalog w lokalizacji zwróconej przez GetAppConfigDir, a następnie przechowywać tam pliki konfiguracyjne.

Note-icon.png

Uwaga: Zwykli użytkownicy nie mogą pisać do katalogu /etc. Mogą to zrobić tylko użytkownicy z uprawnieniami administratora.

Zauważ, że przed FPC 2.2.4 funkcja korzystała z katalogu, w którym aplikacja przechowywała globalne konfiguracje systemu Windows.

Dane wyjściowe w systemie Windows 98 z 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

Dane wyjściowe w systemie Windows XP z FPC 3.0.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

Dane wyjściowe w systemach Windows 7 i Windows 10 z FPC 3.0.4:

C:\ProgramData\project1\
C:\Users\user\AppData\Local\project1\
C:\ProgramData\project1\project1.cfg
C:\Users\user\AppData\Local\project1\project1.cfg

Dane wyjściowe w systemie macOS 10.14.5 z FPC 3.0.4 (naruszają Wytyczne Apple — zobacz poniżej, aby poznać prawidłowe lokalizacje plików w systemie macOS):

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

Dane wyjściowe na FreeBSD 12.1 z FPC 3.0.4:

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

Obserwator może zauważyć różnicę między danymi wyjściowymi systemów operacyjnych Windows i innych niż Windows — dane wyjściowe systemu Windows dla WriteLn(GetAppConfigFile(True)); globalne ścieżki konfiguracji zawierają podkatalog projektu, ale inne systemy operacyjne nie. Aby uzyskać te same wyniki dla systemów operacyjnych innych niż Windows, należy dołączyć dodatkowy parametr logiczny: WriteLn(GetAppConfigFile(True,True));.

Note-icon.png

Uwaga: Użycie UPX zakłóca korzystanie z funkcji GetAppConfigDir i GetAppConfigFile.

macOS

W większości przypadków pliki konfiguracyjne to pliki preferencji, które w systemie macOS powinny być plikami XML z rozszerzeniem „.plist” i znajdować się w katalogu /Library/Preferences lub ~/Library/Preferences z nazwami pobranymi z pola „Identyfikator pakietu” w pliku Info.plist pakietu aplikacji. Używanie plików .config w katalogu użytkownika jest naruszeniem wytycznych programistycznych Apple. Zapoznaj się z artykułem Lokalizowanie obsługi aplikacji macOS, foldery preferencji, aby zapoznać się z kodem, który obsługuje to w sposób zgodny z Apple.

Pliki danych i zasobów

Bardzo częstym pytaniem jest, gdzie przechowywać pliki danych, których może potrzebować aplikacja, takie jak obrazy, muzyka, pliki XML, pliki bazy danych, pliki pomocy itp. Niestety nie ma funkcji wieloplatformowej, aby uzyskać najlepszą lokalizację do wyszukiwania plików danych. Rozwiązaniem jest implementacja w inny sposób na każdej platformie przy użyciu IFDEF.

Windows

W systemie Windows dane aplikacji, które modyfikuje program, nie powinny być umieszczane w katalogu aplikacji (np. C:\Program Files\), ale w określonej lokalizacji. System Windows Vista i nowsze aktywnie to wymuszają (użytkownicy mają dostęp tylko do zapisu w tych katalogach podczas korzystania z funkcji podnoszenia uprawnień lub wyłączania kontroli konta użytkownika), ale używają mechanizmu przekierowywania folderów, aby uwzględnić starsze, błędnie zaprogramowane aplikacje. Samo odczytywanie, a nie pisanie, danych z katalogów aplikacji nadal działa, ale nie jest to zalecane.

W skrócie: użyj takiego folderu:

    OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';

Zobacz: Wskazówki dotyczące programowania w systemie Windows — uzyskiwanie specjalnych folderów

Unix/Linux

W większości systemów Unix (takich jak Linux, FreeBSD, OpenBSD, Solaris, ale nie macOS) pliki danych aplikacji znajdują się w stałej lokalizacji, która może wyglądać tak: /usr/local/share/nazwa_aplikacji lub /opt/nazwa_aplikacji.

Dane aplikacji, do których aplikacja musi zapisać, często są przechowywane w miejscach takich jak /usr/local/var/nazwa_aplikacji, /usr/local/share/nazwa_aplikacji lub /usr/local/nazwa_aplikacji, z odpowiednimi uprawnieniami.

Pliki pomocy (inaczej strony podręcznika) powinny być przechowywane w /usr/local/man/man[numer odpowiedniej sekcji podręcznika]/nazwa_aplikacji>, z odpowiednimi uprawnieniami. Zauważ, że niektóre sekcje podręcznika FreeBSD i Linuksa różnią się lub nie istnieją.

Pliki konfiguracji/danych do odczytu/zapisu specyficzne dla użytkownika będą zwykle przechowywane gdzieś w katalogu domowym użytkownika (np. w ~/.config/<nazwa programu>). Zapoznaj się z powyższymi plikami konfiguracyjnymi.

macOS

macOS jest wyjątkiem wśród systemów operacyjnych UNIX. Aplikacja jest publikowana w pakiecie (katalog z rozszerzeniem „.app”), który jest traktowany przez menedżera plików Finder jako plik (w Terminalu można użyć „cd path/myapp.app” lub w Finderze prawym przyciskiem myszy na aplikacji i wybierz „Show Package Contents” („Pokaż zawartość pakietu”). Twoje pliki zasobów powinny znajdować się wewnątrz pakietu. Jeśli pakiet to „path/MojaAplikacja.app”, to:

  • plik wykonywalny to "path/MojaAplikacja.app/Contents/MacOS/myapp"
  • katalog zasobów to "path/MojaAplikacja.app/Contents/Resources"

Dane i pliki zasobów dostarczane przez aplikację powinny być zazwyczaj przechowywane w katalogu Resources pakietu aplikacji. Aby zapoznać się z kodem, który obsługuje to w sposób zgodny z Apple, zobacz artykuł Lokalizowanie katalogu zasobów aplikacji systemu macOS.

Pliki danych aplikacji wygenerowane przez użytkownika powinny być przechowywane w katalogu użytkownika „~/Library/Application Support”. Aby poznać kod, który obsługuje to w sposób zgodny z Apple, zapoznaj się z artykułem Lokalizowanie obsługi aplikacji macOS, foldery preferencji.

Warning-icon.png

Ostrzeżenie: Nigdy nie używaj paramStr(0) ani żadnej funkcji, która go używa, na żadnej platformie UNIX do określenia lokalizacji pliku wykonywalnego, ponieważ jest to konwencja DOS-Windows-OS/2 i ma kilka problemów koncepcyjnych, których nie można rozwiązać za pomocą emulacji na innych platformach. Jedyną rzeczą, którą paramStr(0) gwarantuje na platformach UNIX jest nazwa, pod którą program został uruchomiony. Nie ma gwarancji, że katalog, w którym się znajduje, oraz nazwa rzeczywistego pliku binarnego (w przypadku, gdy został uruchomiony przy użyciu dowiązania symbolicznego) będą dostępne za pośrednictwem paramStr(0). W przypadku systemu macOS można niezawodnie zlokalizować katalog pakietu aplikacji przy użyciu kodu natywnego opisanego w tym artykule.

32/64 bit

Wykrywanie wersji bitowej systemu w czasie wykonywania

Chociaż możesz kontrolować, czy kompilujesz dla systemu 32 lub 64-bit za pomocą definicji kompilatora, czasami chcesz wiedzieć, jaka jest bitowość systemu operacyjnego. Na przykład, jeśli używasz 32-bitowego programu Lazarus w 64-bitowym systemie Windows, możesz chcieć uruchomić zewnętrzny program w katalogu 32-bitowych plików programu lub możesz chcieć przekazać użytkownikom inne informacje: Potrzebuję tego w moim instalatorze Lazarusa LazUpdater oferujący użytkownikowi wybór 32- i 64-bitowych kompilatorów. Przykład kodu: Wykryj Windows x32-x64.

Wykrywanie wersji bitowej zewnętrznej biblioteki przed jej załadowaniem

Kiedy chcesz załadować funkcje z biblioteki dynamicznej do twojego programu, musi ona mieć taką samą wersję bitową jak twoja aplikacja. W 64-bitowym systemie Windows aplikacja może być 32-bitowa lub 64-bitowa, a w systemie mogą znajdować się biblioteki 32-bitowe i 64-bitowe. Więc możesz chcieć sprawdzić, czy wersja bitowa biblioteki dll jest taka sama jak twojej aplikacji przed dynamicznym załadowaniem dll. Oto funkcja testująca wersję bitową dll (opublikowana na forum przez GetMem) (komentarze zostały przetłumaczone):

uses {..., } JwaWindows;

function GetPEType(const APath: WideString): Byte;
const
  PE_UNKNOWN = 0; //jeśli plik nie jest poprawną biblioteką dll, zwracane jest 0
 // PE_16BIT   = 1; // nieobsługiwane przez tę funkcję
  PE_32BIT   = 2;
  PE_64BIT   = 3;
var
  hFile, hFileMap: THandle;
  PMapView: Pointer;
  PIDH: PImageDosHeader;
  PINTH: PImageNtHeaders;
  Base: Pointer;
begin
  Result := PE_UNKNOWN;
 
  hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if hFile = INVALID_HANDLE_VALUE then
  begin
    CloseHandle(hFile);
    Exit;
  end;
 
  hFileMap  := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  if hFileMap = 0 then
  begin
    CloseHandle(hFile);
    CloseHandle(hFileMap);
    Exit;
  end;
 
  PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
  if PMapView = nil then
  begin
    CloseHandle(hFile);
    CloseHandle(hFileMap);
    Exit;
  end;
 
  PIDH := PImageDosHeader(PMapView);
  if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then
  begin
    CloseHandle(hFile);
    CloseHandle(hFileMap);
    UnmapViewOfFile(PMapView);
    Exit;
  end;
 
  Base := PIDH;
  PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));
  if PINTH^.Signature = IMAGE_NT_SIGNATURE then
  begin
    case PINTH^.OptionalHeader.Magic of
      $10b: Result := PE_32BIT;
      $20b: Result := PE_64BIT
    end;
  end;
 
  CloseHandle(hFile);
  CloseHandle(hFileMap);
  UnmapViewOfFile(PMapView);
end;

//Teraz, jeśli skompilujesz swoją aplikację dla 32-bitowych i 64-bitowych wersji Windows,
//możesz sprawdzić, czy wersja bitowa dll jest taka sama jak w Twojej aplikacji:
function IsCorrectBitness(const APath: WideString): Boolean;
begin  
  {$ifdef CPU32}
    Result := GetPEType(APath) = 2; //aplikacja jest kompilowana jako 32-bitowa, pytamy czy GetPeType zwraca 2
  {$endif}
  {$ifdef CPU64}
    Result := GetPEType(APath) = 3; //aplikacja jest kompilowana jako 64-bitowa, pytamy czy GetPeType zwraca 3
  {$endif}
end;

Typy wskaźników / liczb całkowitych

Wskaźniki pod architekturą 64-bitową zajmują 8 bajtów zamiast 4 na 32-bitowej. Typ 'Integer' pozostaje 32-bitowy na wszystkich platformach, aby zapewnić zgodność. Oznacza to, że nie możesz rzutować wskaźników na typ Integer i z powrotem.

FPC definiuje w tym celu dwa typy: PtrInt i PtrUInt. PtrInt to 32-bitowa liczba całkowita ze znakiem na platformach 32-bitowych i 64-bitowa liczba całkowita ze znakiem na platformach 64-bitowych. To samo dla PtrUInt, ale dla liczb całkowitych bez znaku.

Użyj dla kodu, który powinien działać z Delphi i FPC:

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

Zamień wszystkie rzutowania wskaźnika Integer(SomePointerOrObject) na PtrInt(SomePointerOrObject).

Kolejność bajtów

Platformy Intela są little endian, co oznacza, że ​​najmniej znaczący bajt jest na pierwszym miejscu. Na przykład dwa bajty słowa $1234 są przechowywane kolejno jako $34 $12 w systemach little endian. W systemach typu big endian, takich jak powerpc, dwa bajty słowa $1234 są przechowywane kolejno jako $12 $34. Różnica jest istotna przy odczytywaniu plików utworzonych na innych systemach.

Użyj dla kodu, który powinien działać na obu systemach:

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

Przeciwieństwo dla ENDIAN_BIG to ENDIAN_LITTLE.

Moduł system zapewnia wiele funkcji konwertujących endian, takich jak SwapEndian, BEtoN (big endian do bieżącego endian), LEtoN (little endian do bieżącego endian), NtoBE (bieżący endian do big endian) i NtoLE (bieżący endian do little endian).

Libc i inne jednostki specjalne

Unikaj starszych modułów, takich jak „oldlinux” i „libc”, które nie są obsługiwane poza linux/i386.

Asembler

Unikaj używania asemblera.

Dyrektywy kompilatora

{$ifdef CPU32}
...tutaj napisz kod dla procesorów 32-bitowych
{$ENDIF}
{$ifdef CPU64}
...tutaj napisz kod dla procesorów 64-bitowych
{$ENDIF}

Projekty, pakiety i ścieżki wyszukiwania

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. Projekty i pakiety Lazarusa są przeznaczone dla wielu platform. Zwykle możesz po prostu skopiować projekt i wymagane pakiety na inny komputer i tam je skompilować. Nie musisz tworzyć osobnego projektu dla każdej platformy.

Kilka rad, jak to osiągnąć.

Kompilator tworzy dla każdego modułu plik ppu o tej samej nazwie. Ten plik ppu może być używany przez inne projekty i pakiety. Pliki źródłowe modułu (np. unit1.pas) nie powinny być udostępniane. Po prostu daj kompilatorowi katalog wyjściowy modułu, w którym ma tworzyć pliki ppu. IDE robi to domyślnie, więc nie ma tu nic do roboty.

Każdy plik modułu musi być częścią jednego projektu lub pakietu. Jeśli plik modułu jest używany tylko w jednym projekcie, dodaj go do tego projektu. W przeciwnym razie dodaj go do pakietu. Jeśli jeszcze nie utworzyłeś pakietu dla swoich modułów współdzielonych, zobacz tutaj: Tworzenie pakietu dla wspólnych modułów.

Każdy projekt i każdy pakiet powinien mieć oddzielne katalogi - nie powinny one współdzielić katalogów. W przeciwnym razie musisz być ekspertem w dziedzinie ścieżek wyszukiwania kompilatora. Jeśli nie jesteś ekspertem lub jeśli inne osoby, które mogą korzystać z Twojego projektu/pakietu, nie są ekspertami: nie twórz wspólnego katalogu dla projektów i pakietów.

Moduły zależne od platformy

Na przykład moduł wintricks.pas powinien być używany tylko w systemie Windows. W sekcji uses użyj:

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

Jeśli moduł jest częścią pakietu, należy również wybrać ten moduł w edytorze pakietów i odznaczyć pole wyboru Use unit.

Zobacz także Moduły zależne od platformy

Ścieżki wyszukiwania specyficzne dla platformy

Kiedy wybierasz kilka platform i masz bezpośredni dostęp do systemu operacyjnego, szybko zmęczysz się niekończącymi się konstrukcjami IFDEF. Jednym z rozwiązań często stosowanych w źródłach FPC i Lazarus jest użycie plików dołączanych (include files). Utwórz jeden podkatalog na każdą docelową platformę. Na przykład win32, linux, bsd, darwin. Umieść w każdym katalogu plik dołączany o tej samej nazwie. Następnie użyj makra w ścieżce include. Moduł może używać normalnej dyrektywy include.

Przykład jednego pliku include dla każdego zestawu widżetów LCL:

Utwórz jeden plik dla każdego zestawu widżetów, który chcesz obsługiwać:

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

Nie musisz dodawać plików do pakietu lub projektu. Dodaj ścieżkę wyszukiwania include $(LCLWidgetType) do opcji kompilatora pakietu lub projektu.

W swoim module użyj dyrektywy: {$I example.inc}

Oto kilka przydatnych makr i typowych wartości:

  • 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

Do używania zmiennych środowiskowych można użyć makra $Env().

I oczywiście możesz używać ich kombinacji. Na przykład LCL wykorzystuje:

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

Zobacz tutaj pełną listę makr: Makra IDE w ścieżkach i nazwach plików.

Ścieżki wyszukiwania specyficzne dla maszyny/użytkownika

Na przykład masz dwa komputery z systemem Windows, stan i oliver. W komputerze 'stan twoje moduły znajdują się w C:\units, a na oliver w D:\path. Moduły należą do pakietu SharedStuff, który jest w pliku C:\units\sharedstuff.lpk na stan i i w pliku D:\path\sharedstuff.lpk na oliver. Po otwarciu pliku lpk w IDE lub przez lazbuild, ścieżka jest automatycznie zapisywana w jego plikach konfiguracyjnych (packagefiles.xml). Podczas kompilowania projektu, który wymaga pakietu SharedStuff, IDE i lazbuild wiedzą, gdzie on jest. Więc nie jest potrzebna konfiguracja.

Jeśli chcesz wdrożyć pakiet na wielu maszynach lub dla wszystkich użytkowników maszyny (np. basen dla studentów), możesz dodać plik lpl w katalogu źródłowym lazarusa. Zobacz katalog Lazarusa packager/globallinks, aby zapoznać się z przykładami.

Różnice regionalne

Niektóre funkcje z Free Pascal, takie jak StrToFloat, zachowują się różnie w zależności od ustawień bieżącego regionu. Na przykład w USA separatorem dziesiętnym jest zwykle „.”, ale w wielu krajach Europy (w tym w Polsce) i Ameryki Południowej jest to „,”. Może to stanowić problem, ponieważ czasami pożądane jest, aby te funkcje zachowywały się w ustalony sposób, niezależnie od ustawień regionalnych. Przykładem jest format pliku z kropkami dziesiętnymi, które zawsze należy interpretować w ten sam sposób.

Następne sekcje wyjaśniają, jak to zrobić.

macOS

Zapoznaj się z artykułem Ustawienia regionalne dla systemu macOS, aby uzyskać szczegółowe informacje na temat ustawiania języka w systemie macOS.

StrToFloat

Nowy zestaw ustawień formatu, który ustawia stały separator dziesiętny, można utworzyć za pomocą następującego kodu:

// w pliku projektu .lpr
uses
...
{$IFDEF UNIX}
clocale 
{ wymagane w systemie Linux/Unix do obsługi ustawień formatów. Powinien być jednym z pierwszych (prawdopodobnie po cthreads?}
{$ENDIF}

and:

// w twoim kodzie:
var
  FPointSeparator, FCommaSeparator: TFormatSettings;
begin
  // Format settings to convert a string to a float
  // Ustawienia formatu, aby przekonwertować ciąg string na liczbę zmiennoprzecinkowąq
  FPointSeparator := DefaultFormatSettings;
  FPointSeparator.DecimalSeparator := '.';
  FPointSeparator.ThousandSeparator := '#';// wyłącz separator tysięcy
  FCommaSeparator := DefaultFormatSettings;
  FCommaSeparator.DecimalSeparator := ',';
  FCommaSeparator.ThousandSeparator := '#';// wyłącz separator tysięcy

Później możesz użyć tych ustawień formatu podczas wywoływania StrToFloat, na przykład:

// Ta funkcja działa jak StrToFloat, ale po prostu próbuje dwóch możliwych separatorów dziesiętnych
// Pozwoli to uniknąć wyjątku, gdy format ciągu string nie pasuje do ustawień regionalnych
function AnSemantico.StringToFloat(AStr: string): Double;
begin
  if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
  else Result := StrToFloat(AStr, FCommaSeparator);
end;

Gtk2 i maskowanie wyjątków FPU

Biblioteka Gtk2 zmienia domyślną wartość maski wyjątków FPU (koprocesor - jednostka zmiennoprzecinkowa). Konsekwencją tego jest to, że niektóre wyjątki zmiennoprzecinkowe nie są zgłaszane, jeśli aplikacja korzysta z biblioteki Gtk2. Oznacza to, że jeśli na przykład tworzysz aplikację LCL w systemie Windows z zestawem widżetów win32/64 (który jest domyślnym systemem Windows) i planujesz kompilację dla systemu Linux (gdzie Gtk2 jest domyślnym zestawem widżetów), powinieneś pamiętać o tych niezgodnościach.

Po tym zgłoszeniu na forum i odpowiedziach na ten raport o błędzie stało się jasne, że nic nie można z tym zrobić, więc musimy wiedzieć, czym właściwie są te różnice.

Dlatego zróbmy test:

uses
  ..., math,...

{...}

var
  FPUException: TFPUException;
  FPUExceptionMask: TFPUExceptionMask;
begin
  FPUExceptionMask := GetExceptionMask;
  for FPUException := Low(TFPUException) to High(TFPUException) do begin
    write(FPUException, ' - ');
    if not (FPUException in FPUExceptionMask) then
      write('not ');

    writeln('masked!');
  end;
  readln;
end.

Nasz prosty program otrzyma domyślne wartości FPC:

 exInvalidOp - not masked!
 exDenormalized - masked!
 exZeroDivide - not masked!
 exOverflow - not masked!
 exUnderflow - masked!
 exPrecision - masked!

Jednak w przypadku Gtk2 tylko exOverflow nie jest maskowany.

Konsekwencją jest to, że wyjątki EInvalidOp i EZeroDivide nie są zgłaszane, jeśli aplikacja łączy się z biblioteką Gtk2! Zwykle dzielenie wartości niezerowej przez zero powoduje wyjątek EZeroDivide, a dzielenie zera przez zero powoduje zwiększenie EInvalidOp. Na przykład taki kod:

var
  X, A, B: Double;
// ...

try
  X := A / B;
  // blok kodu 1
except   
  // blok kodu 2
end;
// ...

przyjmie inny kierunek po skompilowaniu w aplikacji z zestawem widżetów Gtk2. W zestawie widżetów win, gdy B jest równe zero, zostanie zgłoszony wyjątek (EZeroDivide lub EInvalidOp, w zależności od tego, czy A jest zerem) i zostanie wykonany „blok kodu 2”. Na Gtk2 X staje się Infinity, NegInfinity lub NaN i „blok kodu 1” zostanie wykonany.

Możemy wymyślić różne sposoby przezwyciężenia tej niespójności. W większości przypadków możesz po prostu sprawdzić, czy B jest równe zero i nie próbować dzielić w takim przypadku. Czasami jednak będziesz potrzebować innego podejścia. Spójrz więc na następujące przykłady:

uses
  ..., math,...

//...
var
  X, A, B: Double;
  Ind: Boolean;
// ...
try
  X := A / B;
  Ind := IsInfinite(X) or IsNan(X); // z gtk2 wpadamy tutaj
except   
  Ind := True; // w Windows wpadamy tutaj, gdy B równa się zero
end;
if Ind then begin
  // blok kodu 2
end else begin
  // blok kodu 1
end;
// ...

Lub:

uses
  ..., math,...

//...
var
  X, A, B: Double;
  FPUExceptionMask: TFPUExceptionMask;
// ...

try
  FPUExceptionMask := GetExceptionMask;
  SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // niemaskowane
  try
    X := A / B;
  finally
    SetExceptionMask(FPUExceptionMask); // natychmiast zwróć poprzednie maskowanie, nie możemy pozwolić, aby wewnętrzne elementy Gtk2 były wywoływane bez maski
  end;
  // blok kodu 1
except   
  // blok kodu 2
end;
// ...

Bądź ostrożny, nie rób czegoś takiego (wywołanie LCL z wciąż zdjętą maską):

try
  FPUExceptionMask := GetExceptionMask;
  SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);
  try
    Edit1.Text := FloatToStr(A / B); // NIE! Ustawienie tekstu Edit sprowadza się do wewnętrznych widżetów, a API Gtk2 nie może być wywoływane bez maski!
  finally
    SetExceptionMask(FPUExceptionMask);
  end;
  // blok kodu 1
except   
  // blok kodu 2
end;
// ...

Ale użyj zmiennej pomocniczej:

try
  FPUExceptionMask := GetExceptionMask;
  SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);
  try
    X := A / B; // Najpierw ustawiamy zmienną pomocniczą X
  finally
    SetExceptionMask(FPUExceptionMask);
  end;
  Edit1.Text := FloatToStr(X); // Teraz możemy ustawić tekst w Edit.
  // blok kodu 1
except   
  // blok kodu 2
end;
// ...

We wszystkich sytuacjach podczas tworzenia aplikacji LCL najważniejsze jest, aby o tym wiedzieć i pamiętać, że niektóre operacje zmiennoprzecinkowe mogą przebiegać w inny sposób z różnymi zestawami widżetów. Następnie możesz wymyślić odpowiedni sposób na obejście tego, ale nie powinno to pozostać niezauważone.

Problemy przy przejściu z systemu Windows do *nix itp.

W tym miejscu opisano problemy specyficzne dla systemów Linux, macOS, Android i innych Uniksów. Nie wszystkie tematy mogą dotyczyć wszystkich platform.

W Uniksie nie ma „katalogu aplikacji”

Wielu programistów jest przyzwyczajonych do wywoływania ExtractFilePath(ParamStr(0)) lub Application.ExeName w celu uzyskania lokalizacji pliku wykonywalnego, a następnie wyszukiwania plików niezbędnych do wykonania programu (obrazy, pliki XML, pliki bazy danych itp.) na podstawie lokalizacji pliku wykonywalnego. To jest złe na unixach. Ciąg w ParamStr(0) może zawierać katalog inny niż plik wykonywalny, a także różni się między różnymi programami powłoki (sh, bash, itp.).

Nawet jeśli Application.ExeName może w rzeczywistości znać katalog, w którym znajduje się plik wykonywalny, plik ten może być dowiązaniem symbolicznym, więc zamiast tego można uzyskać katalog dowiązania (w zależności od wersji jądra Linux można uzyskać katalog dowiązania lub samego programu binarnego).

Aby tego uniknąć, przeczytaj sekcje dotyczące plików konfiguracyjnych i plików danych.

Obycie się bez Windows COM Automation

W systemie Windows Automatyzacja COM jest potężnym sposobem nie tylko na zdalne manipulowanie innymi programami, ale także na umożliwienie innym programom manipulowania Twoim programem. Dzięki Delphi możesz uczynić swój program zarówno klientem COM Automation, jak i serwerem COM Automation, co oznacza, że ​​może on zarówno manipulować innymi programami, jak i być manipulowany przez inne programy. Aby zapoznać się z przykładami, zobacz Używanie automatyzacji COM do interakcji z pakietami OpenOffice i Microsoft Office.

alternatywa dla macOS

Niestety, COM Automation nie jest dostępna w systemach macOS i Linux. Można jednak symulować niektóre funkcje COM Automation w systemie macOS za pomocą AppleScript.

AppleScript jest pod pewnymi względami podobny do COM Automation. Na przykład możesz pisać skrypty, które manipulują innymi programami. Oto bardzo prosty przykład AppleScript, który uruchamia NeoOffice (wersja OpenOffice.org na Maca):

  tell application "NeoOffice"
    launch
  end tell

Aplikacja zaprojektowana do manipulowania przez AppleScript udostępnia „słownik” klas i poleceń, których można używać z aplikacją, podobnie jak klasy serwera Windows Automation. Jednak nawet aplikacje, takie jak NeoOffice, które nie udostępniają słownika, nadal będą odpowiadać na polecenia „launch” (uruchom), „activate” (aktywuj) i „quit” (wyjście). AppleScript można uruchomić z edytora skryptów macOS lub Findera, a nawet przekonwertować na aplikację, którą można upuścić do stacji dokującej, tak jak każdą aplikację. Możesz także uruchomić AppleScript ze swojego programu, jak w tym przykładzie:

  fpsystem('myscript.applescript');

Zakłada się, że skrypt znajduje się we wskazanym pliku. Możesz także uruchamiać skrypty w locie ze swojej aplikacji za pomocą polecenia macOS OsaScript:

  fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +
        ' -e '#39'launch'#39' -e '#39'end tell'#39);
        {Zwróć uwagę na użycie #39 jako pojedynczego cudzysłowu do parametrów}

Jednak te przykłady są tylko odpowiednikiem następującego polecenia Open (otwórz):

  fpsystem('open -a NeoOffice');

Podobnie w systemie macOS można emulować polecenia powłoki systemu Windows, aby uruchomić przeglądarkę internetową i uruchomić klienta poczty e-mail za pomocą:

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

i

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

co zakłada, dość bezpiecznie, że system macOS będzie miał zainstalowane aplikacje Safari i Mail. Oczywiście nigdy nie należy przyjmować takich założeń, a w przypadku dwóch poprzednich przykładów można po prostu polegać na systemie macOS, aby postępować właściwie i wybrać domyślną przeglądarkę internetową i klienta poczty e-mail użytkownika, jeśli zamiast tego użyjesz tych odmian:

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

i

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

Nie zapomnij dołączyć modułu Unix do klauzuli uses, jeśli używasz fpsystem lub shell (wymiennie).

Prawdziwą mocą AppleScript jest zdalne manipulowanie programami w celu tworzenia i otwierania dokumentów oraz automatyzacji innych czynności. To, ile możesz zrobić z programem, zależy od tego, jak obszerny jest jego słownik AppleScript (jeśli taki posiada). Na przykład programy Microsoft Office X nie są zbyt użyteczne z AppleScript, podczas gdy nowsze programy Office 2004 mają całkowicie przepisane słowniki AppleScript, które pod wieloma względami porównywalne są z tym, co jest dostępne za pośrednictwem serwerów Windows Office Automation.

Alternatywy dla Linuksa

Chociaż powłoki Linuksa obsługują zaawansowane skrypty wiersza poleceń, typ skryptów jest ograniczony do tego, co można przekazać do programu w wierszu poleceń. Nie ma jednego, zunifikowanego sposobu, aby uzyskać dostęp do wewnętrznych klas i poleceń programu w systemie Linux w taki sposób, w jaki są one dostępne, za pośrednictwem Windows COM Automation i macOS AppleScript. Jednak indywidualne środowiska graficzne (GNOME/KDE) i frameworki aplikacji często zapewniają takie metody komunikacji międzyprocesowej. W GNOME zobacz Komponenty Bonobo. KDE posiada framework KParts, DCOP. OpenOffice ma neutralne dla platformy API do zdalnego sterowania biurem (Google OpenOffice SDK) – chociaż prawdopodobnie, aby go używać, będziesz musiał napisać kod łączący w innym języku, który ma powiązania (takie jak Python). Ponadto niektóre aplikacje mają „tryby serwera” aktywowane przez specjalne opcje wiersza polecenia, które umożliwiają sterowanie nimi z innego procesu. Możliwe jest również (Borland zrobił to za pomocą przeglądarki dokumentów Kylix) „osadzenie” jednego okna aplikacji X najwyższego poziomu w innym za pomocą XReparentWindow (chyba).

Podobnie jak w przypadku systemu Windows, wiele programów dla systemów MacOS i Linux składa się z wielu plików bibliotek (rozszerzenia .dylib i .so). Czasami te biblioteki są zaprojektowane tak, abyś mógł ich używać również w programach, które piszesz. Chociaż może to być sposób na dodanie niektórych funkcji zewnętrznego programu do twojego programu, nie jest to tak naprawdę to samo, co uruchamianie i manipulowanie samym zewnętrznym programem. Zamiast tego twój program po prostu łączy się i używa biblioteki programu zewnętrznego, podobnie jak używałby dowolnej biblioteki programistycznej.

Alternatywy dla funkcji Windows API

Wiele programów systemu Windows intensywnie korzysta z interfejsu API systemu Windows. W aplikacjach wieloplatformowych funkcje Win API w module Windows nie powinny być używane lub powinny być zawarte w kompilacji warunkowej (np. {$IFDEF MSWINDOWS} ).

Na szczęście wiele z powszechnie używanych funkcji Windows API jest zaimplementowanych wieloplatformowo w module lclintf. Może to być rozwiązanie dla programów, które w dużym stopniu opierają się na API Windows, chociaż najlepszym rozwiązaniem jest zastąpienie tych wywołań prawdziwymi wieloplatformowymi komponentami z LCL. Na przykład można zastąpić wywołania funkcji malowania GDI za pomocą wywołania metod obiektu TCanvas.

Kody klawiszy

Na szczęście wykrywanie kodów klawiszy (np. w zdarzeniach KeyUp) jest przenośne: patrz Obsługa klawiszy LCL.

Instalowanie swojej aplikacji

Zobacz Wdrażanie swojej aplikacji.

Zobacz także

Linki zewnętrzne