Wprowadzenie Tym razem zajmiemy się bardziej zaawansowanym narzędziem do programowania, czyli ILINK32. Jest to jak sama nazwa wskazuje linker (bardziej po polsku: konsolidator). Linker, czyli program, który łączy wszystkie "półprodukty" utworzone z plików źródłowych - pliki *.obj z bibliotekami, zasobami programu (o ile je uprzednio stworzymy) itp. Tak naprawdę bez konsolidatora nie byłoby programu takiego, jaki sobie wyobrażamy, ponieważ to on łączy wszystkie elementy na które składa się program w jeden plik EXE (lub w bibliotekę). Mniej doświadczonych programistów już teraz ostrzegam, że bawienie się w samodzielne wywoływanie ILINK32 będzie od was wymagało dodatkowej roboty, którą wcześniej "odwalał" za was kompilator (BCC32), ale ILINK32 w zamian daje jeszcze większy wpływ na to, jak wasz ostateczny program będzie wyglądał. Wywołanie W przypadku ILINK32 wywołanie jest znacznie bardziej skomplikowane niż w BCC32:
ILINK32 [] , [], [], , [], []
tak jak w przypadku BCC32, tak samo konsolidacji możemy nadać wiele opcji, z których ważniejsze opisuje w następnym podrozdziale. spotkałem się kiedyś z takim określeniem i skoro sam nie potrafię tego jakoś sensownie nazwać będę używał tego terminu; to specjalny plik *.obj, który musisz dodać do Twojego kodu w zależności jaką aplikację chcesz uzyskać: c0w32.obj jeśli ma to być zwykła aplikacja Win32 GUI (Graphic User Interface) c0x32.obj jeśli chcesz utworzyć program uruchamiany w konsoli c0d32.obj jeśli masz zamiar wygenerować bibliotekę Win32 DLL W przypadku kompilacji przez BCC32 nie musisz o tym pamiętać, kompilator automatycznie dodaje ten plik po wpisaniu odpowiedniej :opcji, jeśli sam wywołujesz ILINK32 - Ty musisz zrobić. plik(i) *.obj generowane przez kompilator wpisujesz tutaj oddzielając każdy od siebie spacją. Jeśli ktoś nie wie, jak zmusić kompilator do kompilacji pliku tylko do *.obj (bez samodzielnego wywoływania ILINK32), to przypominam, że wystarczy dodać do linii poleceń BCC32 opcję -c możesz tu sam zdefiniować nazwę swojego pliku wynikowego; może to być zarówno program (*.exe) jak i biblioteka statyczna (*.lib) lub dynamiczna (*.dll) w tym miejscu możesz wpisać nazwę pliku MAP, który wygeneruje ILINK32 (jeżeli tego nie zrobisz linker nazwie go tak jak twój wynik (obcinając rozszerzenie). Plik MAP przechowuje adresy poszczególnych segmentów aplikacji i adresy funkcji Twojego programu, jest tworzony domyślnie. po prostu biblioteka/i które masz zamiar użyć w programie; możesz dodać każdą o ile znajduje się w lokalizacji podanej przy opcji -L w parametrach wywołania ILINK32 lub w pliku ilink32.cfg. Dokumentacja FCLT określa prawidłową kolejność w jakich powinno się te biblioteki podawać: bilioteki pomocne w analizowaniu i debugowaniu kodu (tzw. code guards) inne (własne) biblioteki biblioteka import32.lib - jeśli ma to być program dla Windows biblioteki operacji matematycznych tzw. runtime libaries (taka rozbiegówka, ale tym razem z rozszerzeniem lib) w przypadku programów dla Windows będzie to cw32.lib w praktyce aby skompilować zwykły program dla Windows należy podać tu przynajmniej: import32.lib cw32.lib tutaj określasz plik DEF; w nim możesz określić wiele parametrów Twojego programu, które dokładnie opisuje poniżej. zasoby, ale w wersji skompilowanej (*.res) - wcześniej musisz użyć kompilatora zasobów
Bardzo ważną rzeczą w wywoływaniu ILINK32 są przecinki, nawet jeśli nie chcesz wpisywać wszystkich parametrów masz obowiązek zaznaczyć wszystkie przecinki! Oto przykładowe wywołanie ILINK32 generujące aplikację Win32 GUI:
ILINK32 /aa /Tpe /Gk /t /M c0w32.obj Helo.obj klasa.obj,\ Hello.exe,,import32.lib cw32.lib,,zasoby.RES
Opcje
Źródła opcjaopis @ tu możesz zdefiniować nazwę response file dla ILINK32 /j (dodatkowa) lokalizacja plików *.obj /L dodatkowa lokalizacja bibliotek
Wynik opcjaopis /aa aplikacja Windows GUI /ap aplikacja Windows Console /ad sterownik urządzenia /Tpe program *.exe /Tpd biblioteka dynamiczna *.dll /Gl biblioteka statyczna (*.lib) /Gn ILINK32 przy każdej konsolidacji generuje 4 pliki (*.ilc, *.ilf, *.ild, *.ils), dzięki którym szybciej konsoliduje Twój program, jeżeli nie chcesz aby zaśmiecał Ci tym katalogu użyj tej opcji /Gk kiedy pojawią się jakieś błędy podczas konsolidacji linker automatycznie usuwa plik wynikowy(!) co czasami może być niepożądaną operacją, aby temu zapobiec możesz użyć tej opcji Plik MAP ILINK32 pozwala na określenie stopnia zaawansowania pliku MAP: opcjaopis /x nie generuj pliku MAP /M generuj mapę najmniej zaawansowaną /m generuj mapę publiczną, przydatną podczas debugowania programu /s generuj szczegółową mapę
Ostrzeżenia opcjaopis -q pomijaj nagłówek ILINK32 /t pokaż czas spędzony na linkowanie -w-dup nie pokazuj ostrzeżenia "duplicate symbol"
Plik DEF Plik DEF określa wiele aspektów programu, które możesz zdefiniować w zwykłym pliku ASCII, w Notatniku (pamiętając, żeby plik ten miał rozszerzenie *.def). Aspekty te określamy wpisując odpowiednie selektory i w tej samej linii dopisując do nich wartości. Standardowo jeśli nie określimy przy wywoływaniu ILINK32 pliku DEF, będzie on wyglądał tak:
CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE ; (for applications) PRELOAD MOVEABLE SINGLE ; (for DLLs) HEAPSIZE 4096 STACKSIZE 1048576
Już w tym kodzie widzisz 4 selektory i określone dla nich wartości. Są to podstawowe oznaczenia pliku DEF (CODE, DATA, HEAPSIZE i STACKSIZE). Od razu można się domyślić co one oznaczają, ale aby zbytnio nie mącić przejdę teraz do omówienia tych i innych parametrów pliku DEF.
CODE Jak wiemy plik EXE jest podzielony na dwa podstawowe segmenty:
kod (ciąg instrukcji np. w C++ które sprawiają, że program "żyje") zasoby (ikony, bitmapy, menu itp.)
Selektor CODE pliku DEF określa specyficzne opcje dla tego pierwszego segmentu (kod): wartość selektorastandardopis PRELOAD nie kod jest wczytywany, gdy wywoływany program jest wczytywany LOADONCALL tak kod jest wczytywany, kiedy jest wywoływany przez program EXECUTEONLY nie kod może być tylko wykonywany EXECUTEREAD tak kod może być czytany i wykonywany FIXED tak segment pozostaje w pamięci MOVEABLE/ nie segment może być przeniesiony DISCARDABLE nie segment może być odrzucony kiedy nie jest potrzebny (zakłada opcję MOVEABLE). NONDISCARDABLE tak segment nie może być odrzucony
DATA Parametr DATA określa zachowanie się segmentu zasobów: wartosćstandardopis NONEnie nie ma segmentu DATA SINGLE tak dla DLL jeden segment zasobów jest tworzony i współdzielony dla wszystkich procesów MULTIPLEtak dla EXE każdy segment zasobów jest tworzony dla jednego procesu READONLYnie segemnt może być tylko czytany READWRITEtak segment może być i czytany i nadpisywany PRELOADnie zasoby są wczytywane kiedy moduł, który ich używa jest wczytywany LOADONCALLtak segment zasobów jest wczytywany kiedy jest po raz pierwszy przetwarzany (ta opcja jest ignorowana przez aplikacje 32-bitowe) SHAREDnie jedna kopia jest współdzielona między wszystkie procesy NONSHAREDtak dla DLL kopia segmentu jest wczytywana dla każdego procesu potrzebującego zasoby
HEAPSIZE Jak sama nazwa wskazuje wpisujesz tutaj wielkość sterty (standardowo: 4096). Jeżeli w Twoim programie jest wiele obiektów zadeklarowanych na stercie możesz zwiększyć tę liczbę.
STACKSIZE Tym razem określamy wielkość stosu (tu znajdują się wszystkie zmienne lokalne i parametry funkcji), dlatego wielkość tego parametru powinna być nieporównywalnie większa (standardowo: 1048576).
NAME Nazwa pliku *.exe, który jest wynikiem konsolidacji, dodatkowo możesz tu umieścić po spacji jeden z dwóch znaczników: WINDOWAPI jeżeli Twoja aplikacja będzie korzystać z WinAPI (to samo, co opcja linkera /aa) WINDOWCOMPATjeżeli Twój program będzie uruchamiany w konsoli (to samo, co opcja linkera /ap)
LIBRARY Nazwa biblioteki DLL, która jest wynikiem konsolidacji i tutaj masz do wyboru 2 znaczniki: INITGLOBAL biblioteka jest wczytywana do pamięci tylko raz INITINSTANCE biblioteka jest wczytywana za każdym razem kiedy jest potrzebna
EXPORTS Tego selektora mogą używać tylko biblioteki Jego wartością są nazwy wszystkich funkcji udostępniane przez bibliotekę. Więcej o tym selektorze przeczytasz w artykule: Biblioteki DLL w FCLT IMPORTS Wartością tego selektora są funkcje importowane z bibliotek zadeklarowane w kodzie źródłowym. I o tym selektorze przeczytasz w artykule o bibliotekach DLL. DESCRIPTION Ujęty w pojedynczy cudzysłów:'' opis programu (mogą tu się znajdować również np. prawa autorskie do programu). Oto jak może wyglądać plik
DEF w praktyce:
NAME Hello WINDOWAPI DESCRIPTION '(c) 2003 by Karol Ossowski' CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 4096 STACKSIZE 1048576
Zasoby Jak już pisałem aby sprowadzić skrypt zasobów z rozszerzenia *.rc do skompilowanego pliku *.RES należy użyć brcc32 z katalogu BIN. Wywołanie brcc32 będzie wyglądać przykładowo tak: brcc32 .rc Nie będę się zagłębiał tutaj w opcje, jakich może użyć ten kompilator (jeśli Cię one interesują możesz sprawdzić je w dokumentacji FCLT), ważne że prze konwertuje on nasz plik .rc na .RES. Dopiero teraz możesz go dołączyć do wywołania ILINK32.