Sign in Join
Wiki copytemporary

Multiplatform Programming Guide/pl: what changed

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

OldNewLine
11Przewodnik programowania wieloplatformowego
22
33To 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 [[Deploying Your Application|wdrożenia]].
44
55Większość aplikacji LCL działa na wielu platformach bez dodatkowej pracy. Ta zasada nazywa się [[Write once compile anywhere/pl|„napisz raz, kompiluj wszędzie”]].
66
77## Wprowadzenie do programowania wieloplatformowego (międzyplatformowego)
88
99### Ile platform potrzebujesz?
1010
1111Aby 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ę.
1212
1313Jeś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.
1414
1515Popularność 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.
1616
1717W 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.
1818
1919Jeś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.
2020
2121Po 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:
2222
2323- [VMware Fusion for Mac](https://www.vmware.com/products/fusion.html) - Systemie hosta, na którym musi być uruchomiony system Intel macOS (komercyjny/bezpłatny do użytku niekomercyjnego).
2424- [VMware Workstation Player](https://www.vmware.com/products/workstation-player.html) - System hosta z 64-bitowym systemem Windows lub 64-bitowym systemem Linux (komercyjny/bezpłatny do użytku osobistego).
2525- [VMware Workstation Pro](https://www.vmware.com/products/workstation-pro.html) - System hosta z 64-bitowym systemem Windows lub 64-bitowym systemem Linux (komercyjna/bezpłatna wersja próbna).
2626- [Parallels Desktop for Mac](https://www.parallels.com/) - Na systemie hosta musi być uruchomiony system Intel macOS (komercyjna/bezpłatna wersja próbna). Uwaga: istnieje [Podgląd Techniczny](https://b2b.parallels.com/Apple-Silicon), 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](https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewARM64).
2727- [VirtualBox](https://www.virtualbox.org/) - Na systemie hosta może działać Intel macOS, Windows, Linux lub Solaris (Open Source).
2828
2929Niektóre z nich, np. Parallels na Macu, sprawiają, że instalacja stacjonarnego systemu operacyjnego Linux za pomocą jednego kliknięcia jest banalna.
3030
3131**Zobacz także:** [[Small Virtual Machines|Małe maszyny wirtualne]] i [[Qemu and other emulators|Qemu i inne emulatory]].
3232
3333## Programowanie międzyplatformowe
3434
3535### Praca z plikami i folderami
3636
3737Podczas pracy z plikami i folderami ważne jest, aby używać niespecyficznych dla platformy ograniczników ścieżki i sekwencji [[End of Line|końca wiersza]]. Oto lista zadeklarowanych [[Constant|stałych]] w Lazarusie, które powinny być używane podczas pracy z plikami i folderami.
3838
3939- **PathSep**, **PathSeparator**: separator ścieżek używany podczas dodawania wielu ścieżek razem (';', ...)
4040- **PathDelim**, **DirectorySeparator**: separator katalogów dla każdej platformy ('/', '\\', ...)
4141- **LineEnding**: poprawna sekwencja znaków końca wiersza (#13#10 - CRLF, \#10 - [[Line feed|LF]], ...)
4242
4343Inną 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.
4444
4545Szczegó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.
4646
4747macOS 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.
4848
4949Funkcje 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.
5050
5151``` pascal
5252// Funkcje AnsiToUTF8 i UTF8ToAnsi wymagają menedżera widestring pod Linuksem, BSD, macOS,
5353// ale zwykle te systemy operacyjne używają UTF-8 jako kodowania systemowego, więc widestringmanager
5454// nie jest potrzebny.
5555function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
5656procedure SetNeedRTLAnsi(NewValue: boolean);
5757function UTF8ToSys(const s: string): string; // podobna do UTF8ToAnsi, ale nie jest już zależna od widestringmanager
5858function SysToUTF8(const s: string): string; // podobna do AnsiToUTF8, ale nie jest już zależna od widestringmanager
5959function UTF8ToConsole(const s: string): string; // konwertuje ciąg znaków UTF8 na kodowanie konsoli (używane przez Write, WriteLn)
6060
6161// operacje na plikach
6262function FileExistsUTF8(const Filename: string): boolean;
6363function FileAgeUTF8(const FileName: string): Longint;
6464function DirectoryExistsUTF8(const Directory: string): Boolean;
6565function ExpandFileNameUTF8(const FileName: string): string;
6666function ExpandUNCFileNameUTF8(const FileName: string): string;
6767function ExtractShortPathNameUTF8(Const FileName : String) : String;
6868function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
6969function FindNextUTF8(var Rslt: TSearchRec): Longint;
7070procedure FindCloseUTF8(var F: TSearchrec);
7171function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
7272function FileGetAttrUTF8(const FileName: String): Longint;
7373function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
7474function DeleteFileUTF8(const FileName: String): Boolean;
7575function RenameFileUTF8(const OldName, NewName: String): Boolean;
7676function FileSearchUTF8(const Name, DirList : String): String;
7777function FileIsReadOnlyUTF8(const FileName: String): Boolean;
7878function GetCurrentDirUTF8: String;
7979function SetCurrentDirUTF8(const NewDir: String): Boolean;
8080function CreateDirUTF8(const NewDir: String): Boolean;
8181function RemoveDirUTF8(const Dir: String): Boolean;
8282function ForceDirectoriesUTF8(const Dir: string): Boolean;
8383
8484// środowisko
8585function ParamStrUTF8(Param: Integer): string;
8686function GetEnvironmentStringUTF8(Index: Integer): string;
8787function GetEnvironmentVariableUTF8(const EnvVar: string): String;
8888function GetAppConfigDirUTF8(Global: Boolean): string;
8989
9090// inne
9191function SysErrorMessageUTF8(ErrorCode: Integer): String;
9292```
9393
9494### Puste nazwy plików i podwójne separatory ścieżki
9595
9696Istnieją pewne różnice w obsłudze nazw plików/katalogów w systemie Windows w porównaniu z systemami Linux, Unix i Unix.
9797
9898- Windows zezwala na puste nazwy plików. Dlatego FileExistsUTF8('..\\') sprawdza w systemie Windows w katalogu nadrzędnym, czy istnieje plik bez nazwy.
9999- 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ę.
100100
101101Podwójne separatory ścieżki w nazwach plików są również traktowane inaczej:
102102
103103- Windows: 'C:\\' to nie to samo co 'C:\\\\'
104104- Uniksy: ścieżka '/usr//' jest taka sama jak '/usr/'. Jeśli '/usr' jest katalogiem, to nawet wszystkie trzy zapisy są takie same.
105105
106106Jest to ważne przy łączeniu nazw plików. Na przykład:
107107
108108``` pascal
109109FullFilename:=FilePath+PathDelim+ShortFilename; // może skutkować dwoma znakami PathDelims, które dają różne wyniki pod Windows i Linux
110110FullFilename:=AppendPathDelim(FilePath) + ShortFilename; // tworzy tylko jeden znak PathDelim
111111FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // tworzy tylko jeden znak PathDelim i czyści ścieżkę
112112```
113113
114114Funkcja 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.
115115
116116Jeśli chcesz wiedzieć, czy katalog istnieje, użyj **DirectoryExistsUTF8**.
117117
118118Innym 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.
119119
120120- 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.
121121- W systemie Windows musisz użyć funkcji DirectoryExistsUTF8, ale wcześniej musisz usunąć separator ścieżki, na przykład za pomocą funkcji ChompPathDelim.
122122
123123W 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ę.
124124
125125Zauważ, ż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').
126126
127127### Kodowanie tekstu
128128
129129Pliki 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:
130130
131131``` pascal
132132function GuessEncoding(const s: string): string;
133133function GetDefaultTextEncoding: string;
134134```
135135
136136oraz zawiera funkcje do konwersji z jednego kodowania na inne:
137137
138138``` pascal
139139function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;
140140
141141function UTF8BOMToUTF8(const s: string): string; // UTF8 z BOM
142142function ISO_8859_1ToUTF8(const s: string): string; // Europa Środkowa
143143function CP1250ToUTF8(const s: string): string; // Europa Środkowa
144144function CP1251ToUTF8(const s: string): string; // cyrylica
145145function CP1252ToUTF8(const s: string): string; // Latin-1 (Europa Zachodnia)
146146...
147147function UTF8ToUTF8BOM(const s: string): string; // UTF8 z BOM
148148function UTF8ToISO_8859_1(const s: string): string; // Europa Środkowa
149149function UTF8ToCP1250(const s: string): string; // Europa Środkowa
150150function UTF8ToCP1251(const s: string): string; // cyrylica
151151function UTF8ToCP1252(const s: string): string; // Latin-1 (Europa Zachodnia)
152152...
153153```
154154
155155Na przykład, aby załadować plik tekstowy i przekonwertować go na UTF-8, możesz użyć:
156156
157157``` pascal
158158var
159159 sl: TStringList;
160160 OriginalText: String;
161161 TextAsUTF8: String;
162162begin
163163 sl:=TStringList.Create;
164164 try
165165 sl.LoadFromFile('jakis_tekst.txt'); // uwaga: zmienia to zakończenia linii na systemowe zakończenia linii
166166 OriginalText:=sl.Text;
167167 TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);
168168 ...
169169 finally
170170 sl.Free;
171171 end;
172172end;
173173```
174174
175175Aby zapisać plik tekstowy w kodowaniu systemowym, możesz użyć:
176176
177177``` pascal
178178sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);
179179sl.SaveToFile('jakis_tekst.txt');
180180```
181181
182182### Pliki konfiguracyjne
183183
184184Możesz użyć funkcji [[doc:rtl/sysutils/getappconfigdir.html|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.
185185
186186Istnieje również funkcja [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]], która zwróci odpowiednią nazwę dla pliku konfiguracyjnego aplikacji. Możesz jej użyć w ten sposób:
187187
188188``` pascal
189189ConfigFilePath := GetAppConfigFile(False);
190190```
191191
192192Poniżej znajdują się przykłady funkcji wypisujące domyślne ścieżki w różnych systemach:
193193
194194``` pascal
195195program project1;
196196
197197{$mode objfpc}{$H+}
198198
199199uses
200200 SysUtils;
201201
202202begin
203203 WriteLn(GetAppConfigDir(True));
204204 WriteLn(GetAppConfigDir(False));
205205 WriteLn(GetAppConfigFile(True));
206206 WriteLn(GetAppConfigFile(False));
207207end.
208208```
209209
210210Moż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.
211211
212212> **Note:** Zwykli użytkownicy nie mogą pisać do katalogu /etc. Mogą to zrobić tylko użytkownicy z uprawnieniami administratora.
213213
214214Zauważ, że przed FPC 2.2.4 funkcja korzystała z katalogu, w którym aplikacja przechowywała globalne konfiguracje systemu Windows.
215215
216216Dane wyjściowe w systemie Windows 98 z FPC 2.2.0:
217217
218218 C:\Program Files\PROJECT1
219219 C:\Windows\Local Settings\Application Data\PROJECT1
220220 C:\Program Files\PROJECT1\PROJECT1.cfg
221221 C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg
222222
223223Dane wyjściowe w systemie Windows XP z FPC 3.0.4:
224224
225225 C:\Documents and Settings\All Users\Application Data\project1\
226226 C:\Documents and Settings\user\Local Settings\Application Data\project1\
227227 C:\Documents and Settings\All Users\Application Data\project1\project1.cfg
228228 C:\Documents and Settings\user\Local Settings\Application Data\project1\project1.cfg
229229
230230Dane wyjściowe w systemach Windows 7 i Windows 10 z FPC 3.0.4:
231231
232232 C:\ProgramData\project1\
233233 C:\Users\user\AppData\Local\project1\
234234 C:\ProgramData\project1\project1.cfg
235235 C:\Users\user\AppData\Local\project1\project1.cfg
236236
237237Dane 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):
238238
239239 /etc/project1/
240240 /Users/user/.config/project1/
241241 /etc/project1.cfg
242242 /Users/user/.config/project1.cfg
243243
244244Dane wyjściowe na FreeBSD 12.1 z FPC 3.0.4:
245245
246246 /etc/project1/
247247 /home/user/.config/project1/
248248 /etc/project1.cfg
249249 /home/user/.config/project1.cfg
250250
251251Obserwator 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 [[Boolean|logiczny]]: `WriteLn(GetAppConfigFile(True,True));`.
252252
253253> **Note:** Użycie UPX zakłóca korzystanie z funkcji GetAppConfigDir i GetAppConfigFile.
254254
255255#### macOS
256256
257257W 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 [[macOS property list files|Info.plist]] [[Application Bundle|pakietu aplikacji]]. Używanie plików .config w katalogu użytkownika jest naruszeniem wytycznych programistycznych Apple. Zapoznaj się z artykułem [[Locating macOS app support, preferences folders|Lokalizowanie obsługi aplikacji macOS, foldery preferencji]], aby zapoznać się z kodem, który obsługuje to w sposób zgodny z Apple.
258258
259259### Pliki danych i zasobów
260260
261261Bardzo 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.
262262
263263#### Windows
264264
265265W 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.
266266
267267W skrócie: użyj takiego folderu:
268268
269269``` pascal
270270 OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';
271271```
272272
273273Zobacz: [[Windows Programming Tips|Wskazówki dotyczące programowania w systemie Windows — uzyskiwanie specjalnych folderów]]
274274
275275#### Unix/Linux
276276
277277W 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.
278278
279279Dane 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.
280280
281281Pliki 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ą.
282282
283283Pliki 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 [[Multiplatform Programming Guide|plikami konfiguracyjnymi]].
284284
285285#### macOS
286286
287287macOS 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:
288288
289289- plik wykonywalny to "path/MojaAplikacja.app/Contents/MacOS/myapp"
290290- katalog zasobów to "path/MojaAplikacja.app/Contents/Resources"
291291
292292Dane 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ł [[Locating the macOS application resources directory|Lokalizowanie katalogu zasobów aplikacji systemu macOS]].
293293
294294Pliki 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 [[Locating macOS app support, preferences folders|Lokalizowanie obsługi aplikacji macOS, foldery preferencji]].
295295
296296> **Warning:** **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 [[Locating the macOS application resources directory|tym artykule]].
297297
298298### 32/64 bit
299299
300300#### Wykrywanie wersji bitowej systemu w czasie wykonywania
301301
302302Chociaż 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: [[Detect Windows x32-x64 example|Wykryj Windows x32-x64]].
303303
304304#### Wykrywanie wersji bitowej zewnętrznej biblioteki przed jej załadowaniem
305305
306306Kiedy 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](http://forum.lazarus.freepascal.org/index.php/topic,36834.msg245859.html#msg245859)) (komentarze zostały przetłumaczone):
307307
308308``` pascal
309309
310310uses {..., } JwaWindows;
311311
312312function GetPEType(const APath: WideString): Byte;
313313const
314314 PE_UNKNOWN = 0; //jeśli plik nie jest poprawną biblioteką dll, zwracane jest 0
315315 // PE_16BIT = 1; // nieobsługiwane przez tę funkcję
316316 PE_32BIT = 2;
317317 PE_64BIT = 3;
318318var
319319 hFile, hFileMap: THandle;
320320 PMapView: Pointer;
321321 PIDH: PImageDosHeader;
322322 PINTH: PImageNtHeaders;
323323 Base: Pointer;
324324begin
325325 Result := PE_UNKNOWN;
326326
327327 hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
328328 if hFile = INVALID_HANDLE_VALUE then
329329 begin
330330 CloseHandle(hFile);
331331 Exit;
332332 end;
333333
334334 hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
335335 if hFileMap = 0 then
336336 begin
337337 CloseHandle(hFile);
338338 CloseHandle(hFileMap);
339339 Exit;
340340 end;
341341
342342 PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
343343 if PMapView = nil then
344344 begin
345345 CloseHandle(hFile);
346346 CloseHandle(hFileMap);
347347 Exit;
348348 end;
349349
350350 PIDH := PImageDosHeader(PMapView);
351351 if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then
352352 begin
353353 CloseHandle(hFile);
354354 CloseHandle(hFileMap);
355355 UnmapViewOfFile(PMapView);
356356 Exit;
357357 end;
358358
359359 Base := PIDH;
360360 PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));
361361 if PINTH^.Signature = IMAGE_NT_SIGNATURE then
362362 begin
363363 case PINTH^.OptionalHeader.Magic of
364364 $10b: Result := PE_32BIT;
365365 $20b: Result := PE_64BIT
366366 end;
367367 end;
368368
369369 CloseHandle(hFile);
370370 CloseHandle(hFileMap);
371371 UnmapViewOfFile(PMapView);
372372end;
373373
374374//Teraz, jeśli skompilujesz swoją aplikację dla 32-bitowych i 64-bitowych wersji Windows,
375375//możesz sprawdzić, czy wersja bitowa dll jest taka sama jak w Twojej aplikacji:
376376function IsCorrectBitness(const APath: WideString): Boolean;
377377begin
378378 {$ifdef CPU32}
379379 Result := GetPEType(APath) = 2; //aplikacja jest kompilowana jako 32-bitowa, pytamy czy GetPeType zwraca 2
380380 {$endif}
381381 {$ifdef CPU64}
382382 Result := GetPEType(APath) = 3; //aplikacja jest kompilowana jako 64-bitowa, pytamy czy GetPeType zwraca 3
383383 {$endif}
384384end;
385385```
386386
387387#### Typy wskaźników / liczb całkowitych
388388
389389Wskaź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.
390390
391391FPC 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.
392392
393393Użyj dla kodu, który powinien działać z Delphi i FPC:
394394
395395``` pascal
396396 {$IFNDEF FPC}
397397 type
398398 PtrInt = integer;
399399 PtrUInt = cardinal;
400400 {$ENDIF}
401401```
402402
403403Zamień wszystkie rzutowania wskaźnika **Integer(SomePointerOrObject)** na **PtrInt(SomePointerOrObject)**.
404404
405405### Kolejność bajtów
406406
407407Platformy 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.
408408
409409Użyj dla kodu, który powinien działać na obu systemach:
410410
411411``` pascal
412412{$IFDEF ENDIAN_BIG}
413413...
414414{$ELSE}
415415...
416416{$ENDIF}
417417```
418418
419419Przeciwieństwo dla ENDIAN_BIG to ENDIAN_LITTLE.
420420
421421Moduł **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*).
422422
423423#### Libc i inne jednostki specjalne
424424
425425Unikaj starszych modułów, takich jak „oldlinux” i „libc”, które nie są obsługiwane poza linux/i386.
426426
427427#### Asembler
428428
429429Unikaj używania [[Assembly language|asemblera]].
430430
431431#### Dyrektywy kompilatora
432432
433433``` pascal
434434{$ifdef CPU32}
435435...tutaj napisz kod dla procesorów 32-bitowych
436436{$ENDIF}
437437{$ifdef CPU64}
438438...tutaj napisz kod dla procesorów 64-bitowych
439439{$ENDIF}
440440```
441441
442442### Projekty, pakiety i ścieżki wyszukiwania
443443
444444Lazarus 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.
445445
446446Kilka rad, jak to osiągnąć.
447447
448448Kompilator 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.
449449
450450Każ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: [[Lazarus Packages|Tworzenie pakietu dla wspólnych modułów]].
451451
452452Każ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.
453453
454454#### Moduły zależne od platformy
455455
456456Na przykład moduł wintricks.pas powinien być używany tylko w systemie Windows. W sekcji **uses** użyj:
457457
458458``` pascal
459459uses
460460 Classes, SysUtils
461461 {$IFDEF Windows}
462462 ,WinTricks
463463 {$ENDIF}
464464 ;
465465```
466466
467467Jeśli moduł jest częścią pakietu, należy również wybrać ten moduł w edytorze pakietów i odznaczyć pole wyboru *Use unit*.
468468
469469Zobacz także [[Lazarus Packages|Moduły zależne od platformy]]
470470
471471#### Ścieżki wyszukiwania specyficzne dla platformy
472472
473473Kiedy 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.
474474
475475Przykład jednego pliku include dla każdego zestawu widżetów LCL:
476476
477477Utwórz jeden plik dla każdego zestawu widżetów, który chcesz obsługiwać:
478478
479-`win32/example.inc`\
480-`gtk/example.inc`\
481-`gtk2/example.inc`\
482-`carbon/example.inc`
479+```text
480+win32/example.inc
481+gtk/example.inc
482+gtk2/example.inc
483+carbon/example.inc
484+```
483485
484486Nie musisz dodawać plików do pakietu lub projektu. Dodaj ścieżkę wyszukiwania include *\$(LCLWidgetType)* do opcji kompilatora pakietu lub projektu.
485487
486488W swoim module użyj dyrektywy: {\$I example.inc}
487489
488490Oto kilka przydatnych makr i typowych wartości:
489491
490492- LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui
491493- TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)
492494- TargetCPU: i386, x86_64, arm, powerpc, sparc
493495- SrcOS: win, unix
494496
495497Do używania zmiennych środowiskowych można użyć makra \$Env().
496498
497499I oczywiście możesz używać ich kombinacji. Na przykład LCL wykorzystuje:
498500
499-`$(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+```
500504
501505Zobacz tutaj pełną listę makr: [[IDE Macros in paths and filenames|Makra IDE w ścieżkach i nazwach plików]].
502506
503507#### Ścieżki wyszukiwania specyficzne dla maszyny/użytkownika
504508
505509Na 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.
506510
507511Jeś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.
508512
509513### Różnice regionalne
510514
511515Niektóre funkcje z Free Pascal, takie jak StrToFloat, zachowują się różnie w zależności od ustawień bieżącego [[locale|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.
512516
513517Następne sekcje wyjaśniają, jak to zrobić.
514518
515519#### macOS
516520
517521Zapoznaj się z artykułem [[Locale settings for macOS|Ustawienia regionalne dla systemu macOS]], aby uzyskać szczegółowe informacje na temat ustawiania języka w systemie macOS.
518522
519523#### StrToFloat
520524
521525Nowy zestaw ustawień formatu, który ustawia stały separator dziesiętny, można utworzyć za pomocą następującego kodu:
522526
523527``` pascal
524528// w pliku projektu .lpr
525529uses
526530...
527531{$IFDEF UNIX}
528532clocale
529533{ wymagane w systemie Linux/Unix do obsługi ustawień formatów. Powinien być jednym z pierwszych (prawdopodobnie po cthreads?}
530534{$ENDIF}
531535```
532536
533537and:
534538
535539``` pascal
536540// w twoim kodzie:
537541var
538542 FPointSeparator, FCommaSeparator: TFormatSettings;
539543begin
540544 // Format settings to convert a string to a float
541545 // Ustawienia formatu, aby przekonwertować ciąg string na liczbę zmiennoprzecinkowąq
542546 FPointSeparator := DefaultFormatSettings;
543547 FPointSeparator.DecimalSeparator := '.';
544548 FPointSeparator.ThousandSeparator := '#';// wyłącz separator tysięcy
545549 FCommaSeparator := DefaultFormatSettings;
546550 FCommaSeparator.DecimalSeparator := ',';
547551 FCommaSeparator.ThousandSeparator := '#';// wyłącz separator tysięcy
548552```
549553
550554Później możesz użyć tych ustawień formatu podczas wywoływania StrToFloat, na przykład:
551555
552556``` pascal
553557// Ta funkcja działa jak StrToFloat, ale po prostu próbuje dwóch możliwych separatorów dziesiętnych
554558// Pozwoli to uniknąć wyjątku, gdy format ciągu string nie pasuje do ustawień regionalnych
555559function AnSemantico.StringToFloat(AStr: string): Double;
556560begin
557561 if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)
558562 else Result := StrToFloat(AStr, FCommaSeparator);
559563end;
560564```
561565
562566### Gtk2 i maskowanie wyjątków FPU
563567
564568Biblioteka 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.
565569
566570Po [tym zgłoszeniu na forum](http://www.lazarus.freepascal.org/index.php/topic,13460.0.html) i odpowiedziach na [ten raport o błędzie](http://bugs.freepascal.org/view.php?id=19674) stało się jasne, że nic nie można z tym zrobić, więc musimy wiedzieć, czym właściwie są te różnice.
567571
568572Dlatego zróbmy test:
569573
570574``` pascal
571575uses
572576 ..., math,...
573577
574578{...}
575579
576580var
577581 FPUException: TFPUException;
578582 FPUExceptionMask: TFPUExceptionMask;
579583begin
580584 FPUExceptionMask := GetExceptionMask;
581585 for FPUException := Low(TFPUException) to High(TFPUException) do begin
582586 write(FPUException, ' - ');
583587 if not (FPUException in FPUExceptionMask) then
584588 write('not ');
585589
586590 writeln('masked!');
587591 end;
588592 readln;
589593end.
590594```
591595
592596Nasz prosty program otrzyma domyślne wartości FPC:
593597
594598 exInvalidOp - not masked!
595599 exDenormalized - masked!
596600 exZeroDivide - not masked!
597601 exOverflow - not masked!
598602 exUnderflow - masked!
599603 exPrecision - masked!
600604
601605Jednak w przypadku Gtk2 tylko exOverflow nie jest maskowany.
602606
603607Konsekwencją 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:
604608
605609``` pascal
606610var
607611 X, A, B: Double;
608612// ...
609613
610614try
611615 X := A / B;
612616 // blok kodu 1
613617except
614618 // blok kodu 2
615619end;
616620// ...
617621```
618622
619623przyjmie 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](http://www.freepascal.org/docs-html/rtl/math/infinity.html), [NegInfinity](http://www.freepascal.org/docs-html/rtl/math/neginfinity.html) lub [NaN](http://www.freepascal.org/docs-html/rtl/math/nan.html) i „blok kodu 1” zostanie wykonany.
620624
621625Moż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:
622626
623627``` pascal
624628uses
625629 ..., math,...
626630
627631//...
628632var
629633 X, A, B: Double;
630634 Ind: Boolean;
631635// ...
632636try
633637 X := A / B;
634638 Ind := IsInfinite(X) or IsNan(X); // z gtk2 wpadamy tutaj
635639except
636640 Ind := True; // w Windows wpadamy tutaj, gdy B równa się zero
637641end;
638642if Ind then begin
639643 // blok kodu 2
640644end else begin
641645 // blok kodu 1
642646end;
643647// ...
644648```
645649
646650Lub:
647651
648652``` pascal
649653uses
650654 ..., math,...
651655
652656//...
653657var
654658 X, A, B: Double;
655659 FPUExceptionMask: TFPUExceptionMask;
656660// ...
657661
658662try
659663 FPUExceptionMask := GetExceptionMask;
660664 SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // niemaskowane
661665 try
662666 X := A / B;
663667 finally
664668 SetExceptionMask(FPUExceptionMask); // natychmiast zwróć poprzednie maskowanie, nie możemy pozwolić, aby wewnętrzne elementy Gtk2 były wywoływane bez maski
665669 end;
666670 // blok kodu 1
667671except
668672 // blok kodu 2
669673end;
670674// ...
671675```
672676
673677Bądź ostrożny, nie rób czegoś takiego (wywołanie LCL z wciąż zdjętą maską):
674678
675679``` pascal
676680try
677681 FPUExceptionMask := GetExceptionMask;
678682 SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);
679683 try
680684 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!
681685 finally
682686 SetExceptionMask(FPUExceptionMask);
683687 end;
684688 // blok kodu 1
685689except
686690 // blok kodu 2
687691end;
688692// ...
689693```
690694
691695Ale użyj zmiennej pomocniczej:
692696
693697``` pascal
694698try
695699 FPUExceptionMask := GetExceptionMask;
696700 SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);
697701 try
698702 X := A / B; // Najpierw ustawiamy zmienną pomocniczą X
699703 finally
700704 SetExceptionMask(FPUExceptionMask);
701705 end;
702706 Edit1.Text := FloatToStr(X); // Teraz możemy ustawić tekst w Edit.
703707 // blok kodu 1
704708except
705709 // blok kodu 2
706710end;
707711// ...
708712```
709713
710714We 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.
711715
712716## Problemy przy przejściu z systemu Windows do *nix itp.
713717
714718W tym miejscu opisano problemy specyficzne dla systemów Linux, macOS, Android i innych Uniksów. Nie wszystkie tematy mogą dotyczyć wszystkich platform.
715719
716720### W Uniksie nie ma „katalogu aplikacji”
717721
718722Wielu 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.).
719723
720724Nawet 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).
721725
722726Aby tego uniknąć, przeczytaj sekcje dotyczące [[Multiplatform Programming Guide/pl|plików konfiguracyjnych]] i [[Multiplatform Programming Guide/pl|plików danych]].
723727
724728### Obycie się bez Windows COM Automation
725729
726730W 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](http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office).
727731
728732#### alternatywa dla macOS
729733
730734Niestety, 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.
731735
732736AppleScript 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):
733737
734738```applescript
735739 tell application "NeoOffice"
736740 launch
737741 end tell
738742```
739743
740744Aplikacja 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:
741745
742746``` pascal
743747 fpsystem('myscript.applescript');
744748```
745749
746750Zakł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:
747751
748752``` pascal
749753 fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +
750754 ' -e '#39'launch'#39' -e '#39'end tell'#39);
751755 {Zwróć uwagę na użycie #39 jako pojedynczego cudzysłowu do parametrów}
752756```
753757
754758Jednak te przykłady są tylko odpowiednikiem następującego polecenia Open (otwórz):
755759
756760``` pascal
757761 fpsystem('open -a NeoOffice');
758762```
759763
760764Podobnie w systemie macOS można emulować polecenia powłoki systemu Windows, aby uruchomić przeglądarkę internetową i uruchomić klienta poczty e-mail za pomocą:
761765
762766``` pascal
763767 fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
764768```
765769
766770i
767771
768772``` pascal
769773 fpsystem('open -a mail "mailto:ss4200@invalid.org"');
770774```
771775
772776co 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:
773777
774778``` pascal
775779 fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');
776780```
777781
778782i
779783
780784``` pascal
781785 fpsystem('open "mailto:ss4200@invalid.org"');
782786```
783787
784788Nie zapomnij dołączyć modułu Unix do klauzuli uses, jeśli używasz `fpsystem` lub `shell` (wymiennie).
785789
786790Prawdziwą 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.
787791
788792#### Alternatywy dla Linuksa
789793
790794Chociaż 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).
791795
792796Podobnie 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.
793797
794798### Alternatywy dla funkcji Windows API
795799
796800Wiele 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} ).
797801
798802Na 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.
799803
800804### Kody klawiszy
801805
802806Na szczęście wykrywanie kodów klawiszy (np. w zdarzeniach KeyUp) jest przenośne: patrz [[LCL Key Handling|Obsługa klawiszy LCL]].
803807
804808### Instalowanie swojej aplikacji
805809
806810Zobacz [[Deploying Your Application|Wdrażanie swojej aplikacji]].
807811
808812## Zobacz także
809813
810814- [[Writing portable code regarding the processor architecture|Pisanie przenośnego kodu dotyczącego architektury procesora]]
811815- [[Introduction to platform-sensitive development|Wprowadzenie do rozwoju zależnego od platformy]]
812816- [[Apple-specific UI elements|Elementy interfejsu użytkownika specyficzne dla Apple]]
813817
814818## Linki zewnętrzne
815819
816820- <http://www.midnightbeach.com/KylixForDelphiProgrammers.html> Przewodnik dla programistów Windows zaczynających pracę z Kylix. Wiele pojęć / fragmentów kodu dotyczy Lazarusa.
817821
818822- <http://www.stack.nl/~marcov/porting.pdf> Przewodnik po pisaniu przenośnego kodu źródłowego, głównie pomiędzy różnymi kompilatorami.
819823
820824---
821825*From the Free Pascal wiki, page "Multiplatform Programming Guide/pl", as it stood on 2021-11-01 after a change by Slawek, 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).*

Back to history