menu

darmowe eBooki

C++ dla początkujących

okładka

Jak szybko i przyjemnie nauczyć się podstaw C++?

Przekonaj się sam czytając recenzję eBooka C++ dla początkujących.

Google Adsense

migawka z forum

Dowcipy o programistach/informatykach

Witam, grunt to potrafić śmiać sie z samego siebie  ;DPrzychodzi informatyk do domu po pra...

Rysiek z "Klanu"

W związku z ogromnym poruszeniem we wszystkich mediach w naszym kraju, dotyczącym tak kultowej postaci jak Rysiek z &quo...

Propozycje zmian w serwisie Guide C++

Witam, ze względu na to że nie znalazłem podobnego tematu na forum, a mam kilka sugestii   ;) postanowiłem je ...

buttony

SDJ
vortal programistów
Vademecum sieci komputerowych
Soldiers'04 - fan klub Legii Warszawa
www.katalog.bajery.pl
katalog stron
katalog najlepszych stron
jestem w katalogu
Wszystko o C++
[zamknij]

Korzystamy z plików cookies i umożliwiamy zamieszczanie ich osobom trzecim. Pliki cookie pozwalają na poznanie twoich preferencji na podstawie zachowań w serwisie.
Uznajemy, że jeżeli kontynuujesz korzystanie z serwisu, wyrażasz na to zgodę.

jesteś w: Kurs WinAPI / Biblioteki dynamiczne
Szkoła Hakerów - Kurs Hackingu Bez Cenzury

Biblioteki dynamiczne

Teraz powiemy sobie o czymś bardzo przydatnym i pożytecznym i wbrew pozorom bardzo prostym :) Zacznijmy od wyjaśnienia, czym tak naprawdę są te, jakże popularne dzisiaj pliki o rozszerzeniu dll, chociaż wydaje mi się, że wszyscy lub przynajmniej większość czytelników doskonale wie w czym rzecz. Mówiąc najprościej biblioteki dynamiczne to, praktycznie nic innego, jak zwyczajny program. Powiedziałem praktycznie, bo tak naprawdę sama biblioteka nie jest programem, a jedynie modułem programu, czyli jakimś fragmentem kodu składającego się na gotowy program. Najważniejsze jest, że takie moduły NIE są ładowane bezpośrednio do pamięci razem z programem, a jedynie w momencie, gdy naprawdę są potrzebne. Tylko po co komu taki triki? No właśnie. Okazuje się, że takie podejście do sprawy jest jak najbardziej uzasadnione. Spójrz, napisałeś program graficzny obsługujący kilka najbardziej popularnych formatów graficznych. Nagle wpadasz na pomysł rozbudowy programu od nowy format pliku. Co robisz? Dopisujesz kilka funkcji odpowiedzialnych za obsługę nowego formatu i po sprawie. Jednak nie jest to dobre rozwiązanie. Po pierwsze konieczna była ingerencja w kod źródłowy, a po drugie funkcje zapisu i odczytu są ładowane do pamięci od razu po uruchomieniu programu. A przecież zapis i odczyt wykonuje się rzadziej, więc po co zaśmiecać pamięć czymś zbędnym [w danej chwili] ? Ale co tam, przecież to tylko 2 funkcje. Niby tak, ale powiedzmy, że po roku program się rozrośnie i będzie obsługiwał 30 różnych formatów plików. Wtedy podczas jego uruchamiania do pamięci zostanie załadowanych 60 funkcji stosowanych dość sporadycznie. Czy nie dało by się zrobić tego inaczej, tak aby nie zapychać pamięci zbędnym kodem? Okazuje się, że da się to zrobić. W tym celu wymyślono właśnie biblioteki dołączane dynamicznie.
Dobra, wiemy już, czym to ustrojstwo jest, nie wiemy tylko, jak je utworzyć. Looknij zatem sobie na kod najprostszej biblioteki dynamicznej:

  1. extern "C"
  2. {
  3. __declspec(dllexport)void Funkcja ()
  4. {
  5. //...
  6. }
  7. }

Wygląda to nieco kosmicznie, ale można to zrozumieć. Jedyne o czym trzeba pamiętać to deklarowanie funkcji jako extern "C" oraz umieszczenie przed deklaracją słów: __declspec(dllexport). Jedna uwaga co do składni. Wprawdzie kompilator sobie z tym radzi, ale jest to mało czytelne. Znacznie lepiej jest rozdzielić deklaracje i definicje w taki sposób:

  1. extern "C" __declspec(dllexport)void Funkcja ();
  2.  
  3. void Funkcja ()
  4. {
  5. //...
  6. }

Deklaracja nadal jest trochę dziwaczna, ale za to definicja jest bardziej ludzka. Pozostaje jeszcze wyjaśnić, po co są te dwie klamry opatrzone extern "C". Jest to znak dla kompilatora, aby zachowywał się, w tym przypadku jak kompilator czystego 'C', czyli nie dodawał żadnych znaczków do nazw funkcji. Jest to niezbędne, gdyż podczas wywoływania funkcji z naszego DLL'a musimy podać jej nazwę. Skoro kompilator dodałby do nazwy jakieś śmieci, to program nie odnalazłby później naszej funkcji i na 97% [gdyż nie zawsze] bezczelnie wywaliłby na ekran jakże irytujące okienko typu: 'Program wykonał nieprawidłową operację... blablabla'. Druga sprawa to ten dziwny zapis: __declspec(dllexport). Ten dziwny zapis jest informacją dla linkera, aby dodał on daną funkcję do tablicy exportu pliku dll. Jest jeszcze jedna, czasem przydatna rzecz. Każdy DLL może [choć nie musi] posiadać własną funkcję główną. Wygląda ona następująco:

  1. bool APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
  2. {
  3. switch (reason)
  4. {
  5. case DLL_PROCESS_ATTACH:
  6. // podłączenie do procesu
  7.  
  8. case DLL_THREAD_ATTACH:
  9. // podłączenie do watku
  10.  
  11. case DLL_PROCESS_DETACH:
  12. // odłączenie od procesu
  13.  
  14. case DLL_THREAD_DETACH:
  15. // odłączenie od watku
  16. }
  17. return true;
  18. }

Funkcji oczywiście musi nazywać się DllMain. Wartość zwracana to typ bool. Jeśli chodzi o argumenty to pierwszy jest uchwytem instancji, z której został załadowany moduł DLL, drugi określa, czy nasz DLL został załadowany czy też zwolniony z pamięci. To trzecie badziewie jest zarezerwowane i lepiej go nie tykać ;)
To tak pokrótce wygląda tworzenie biblioteki dynamicznej. Teraz pokażę, jak można wywoływać funkcje z takiej biblioteki.

  1. #include <windows.h>
  2.  
  3. typedef void (*fun)(void);
  4.  
  5. int main (void)
  6. {
  7. HMODULE hModule = LoadLibrary ("modul.dll");
  8. if (hModule == NULL) return 0; //sprawdzenie, czy moduł faktycznie został załadowany
  9. fun hFun = (fun)GetProcAddress (hModule, "Funkcja");
  10. if (hFun == NULL) return 0; //tak dla pewności sprawdźmy czy nasza funkcja także została odnaleziona
  11. hFun ("blablabla");
  12. FreeLibrary (hModule);
  13. }

Raptem 10 linijek kodu. Najpierw ładujemy naszego DLL'a do pamięci funkcją LoadLibrary. Pobiera ona tylko jeden argument, a jest nim ścieżka wraz z nazwą ładowanego modułu. Jeżeli wszystko się powiedzie zwróci uchwyt do nowego modułu. I tutaj mała przestroga. Po załadowaniu biblioteki należy, a nawet TRZEBA sprawdzić, czy rzeczywiście moduł faktycznie został odczytany. Może akurat szukany plik nie istnieje. Co wtedy? Program się wysypie. Następnie mamy definicję wskaźnika do funkcji. Dla wygody uprościłem zapis korzystając ze słówka typedef. Korzystając z funkcji GetProcAddress pobieramy adres konkretnej funkcji i przypisujemy go wcześniej zdefiniowanemu wskaźnikowi. Później kontrolnie sprawdzamy, czy taka funkcja rzeczywiście istnieje i wywołujemy ją za pomocą wskaźnika. Na koniec zwalniamy bibliotekę z pamięci, aby nie robić bajzlu. I to wszystko. Wystarczy zapamiętać sposób deklarowania funkcji oraz opanować zaledwie 3 funkcje.