Delphi, cz. 16
To jeszcze nie jest koniec kursu. Od następnego rozdziału trochę rzeczy "dla maniaków" :)))) Nie, no żartuje, zapraszam, ale będą tam omawiane po prostu rzeczy trudniejsze niż to co było do tej pory.
Co to jest biblioteka DLL?
Biblioteka DLL to plik zawierający kod procedur i funkcji, które mogą współpracować z Twoją aplikacją. Zapytasz: "Po co stosować te DLL-e?". Dobre pytanie. Wyobraź sobie, że masz program. Jak każdy program i Twój zawiera błędy. Cześć procedur Twojego programu zawarte są w DLL-u. Teraz jeżeli wykryjesz jakiś błąd w programie to wystarczy, że roześlesz e-mailem unowocześnioną bibliotekę - nie musisz aktualizować całego programu!
Biblioteki DLL możesz wykorzystywać w innych językach programowania. Tak więc pisząc bibliotekę w C++ możesz ją wykorzystać w Delphi.
Tak też jest z Delphi. Niektóre procedury ( ot choćby polecenie "DrawText" ) są procedurami Windows! Tak! Polecenie "DrawText" jest poleceniem Windows, które jest zawarte w bibliotece wchodzącej w skład Microsoft Windows. Delphi nie robi nic innego jak wywołuje tę procedurę z biblioteki.
Aby pracować nad biblioteką z menu "File" wybierasz "New", a następnie bibliotekę DLL. Ujrzysz kod z dość dużym komentarzem. Nim się na razie nie przejmuj. Tak, w bibliotece DLL nie ma formularzy - wszystko piszesz ręcznie. Na samym początku w DLL-u zamieścisz kod, który odpowiada za ustawienie tapety pulpitu:
(*************************************************************)
(* Test Dynamic Link Library *)
(* Copyright (c) 2001 by Adam Boduch *)
(*************************************************************)
library TestDLL;
uses
Windows;
procedure SetWallper(BitMap : PCHar); stdcall;
begin
{
Jeżeli nie znaleziono bitmapy zasygnalizuj blad wyswietlajac odpowiedni
komunikat!
}
if not FileExists(BitMap) then
MessageBox(0, 'Błąd. Nie znaleziono pliku!', 'Błąd', MB_OK + MB_ICONERROR);
{
Jezeli wszystko pojdzie dobrze to zmien tapete pulpitu ns plik zapisany
pod zmienna "Bitmap".
}
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, BitMap,
SPIF_UPDATEINIFILE or SPIF_SENDWININICHANGE);
end;
exports
SetWallper name 'SetWallper'; // eksportuj procedure...
begin
end.
Do listy uses dodany został tylko jeden moduł - Windows. Gwarantuje to jak najmniejsze rozmiary DLL-a. Następnie procedura, która jako parametr posiada ziemną typu "PChar" oznaczającą. Jeżeli nie istnieje plik o podanej nazwie to bląd sygnalizowany jest wyświetleniem komunikatu. Zapewne się już domyśliłeś, że polecenie "FileExist" sprawdza, czy plik istnieje, czy też nie. Jeżeli wszystko jest OK to bitmapa jest ustawiana jako tapeta. Biblioteka ta zawiera tylko jedną procedurę. Na samym końcu po słowie "exports" wypisuje się procedury, które mają być eksportowane. Za nazwą procedury wpisuje się słowo kluczowe "name", po którym to wpisuje się nazwę eksportowanej procedury. Zauważ dyrektywę "stdcall" przy nazwie procedury. Gwarantuje to, że procedura będzie wpełni kompatybilna z wykorzystującym ją programem. Możesz np. napisać bibliotekę w Delphi5, która będzie zawierać procedury których nie można wykorzystać w niższych wersjach Delphi - np. 2.0. Teraz możesz taką bibliotekę zaimportować do programu napisanego w Delphi 2.
Dobra teraz skompiluj bibliotekę ( Ctrl + F9 ) i utwórz nowy projekt. Zapisz nowy projekt gdzieś na dysku i do katalogu, w którym się znajduje skopiuj utworzoną przez Ciebie bibliotekę.
Teraz w nowym projekcie w sekcji "Interface" umieśc taki nagłówek:
procedure WallperChage(BitMap: PCHar); stdcall; external 'TestDLL.dll' name 'SetWallper';
Taki nagłówek importuje procedurę z DLL-a. Najpierw nazwa procedury, która nie musi być taka sama jak nazwa procedury znajdującej się w bibliotece. Później dyrektywa "stdcall". Następnie słowo kluczowe "external" rozkazujące załadowanie biblioteki, później nazwa biblioteki, a na samym końcu ( po słowie "name" ) nazwa procedury, która ma być zaimportowana. Teraz gdzieś w programie możesz napisać:
WallperChage('C:\Moje dokumenty\obrazek.bmp');
To spowoduje utworzenie tapety z podanego pliku . Takie wykorzystanie procedur nazywa się ładowaniem statycznym.
Delphi umożliwia także ładowanie dynamiczne tzn., że programista ma całkowitą kontrolę nad ładowaniem i zwalnianiem biblioteki. Wiąże się to z przydziałem pamięci. Procedura wywołująca określoną procedurę z biblioteki przydziela pamięć dla jej wykonania, a na końcu zwalnia pamięć. Oto przykład:
procedure TForm1.Button1Click(Sender: TObject);
var
DLL : THandle; // uchwyt do biblioteki
WallperChange : procedure(Bitmap : PCHar); stdcall;
begin
DLL := LoadLibrary('TestDLL.dll'); // zaladuj biblioteke
WallperChange := GetProcAddress(DLL, 'SetWallper'); // odnajdz procedure
WallperChange('C:\Moje dokumenty\obrazek.bmp'); // wywolaj procedure
FreeLibrary(DLL); // zwolnij pamiec
end;
Trochę dziwna ta procedura, nie? :) Na samym początku jako zmienne deklarowane są: uchwyt do biblioteki, a następnie jako zmienna deklarowana jest procedura ( ! ), która ma być wywołana. Następnie do zmiennej "DLL" przypisany zostanie uchwyt z biblioteki. DLL został załadowany ( polecenie "LoadLibrary" ). Następnie do zmiennej "WallperChange" zostanie przypisana procedura, która jest szukana w DLL-u ( polecenie "GetProcAddress" ). Następnie zostaje załadowana procedura, a po jej zakończeniu zwalniana zostaje pamięć.
W bibliotekach możesz także przechowywać formularze! Stwórz nową bibliotekę i z menu "File" wybierz "New Form". Umieść na formie jakieś komponenty, a następnie zapisz formularz pod nazwą "MainFrm". Zmień nazwę formularza na "MainForm". Przejdź do DLL-a i z menu "Project" wybierz "Add project". W oknie, które się pojawi wybierz plik z formularzem ( "MainFrm.pas" ) - ten plik zostanie dołączony do biblioteki DLL. Innymi słowy do DLL-a zostanie dołączony formularz. Doprowadź bibliotekę do takiej postaci:
(*************************************************************)
(* Test Dynamic Link Library *)
(* Copyright (c) 2001 by Adam Boduch *)
(*************************************************************)
library TestDLL;
uses
Windows, SysUtils, Forms,
MainFrm in 'MainFrm.pas' {MainForm};
procedure ShowForm; stdcall;
var
Form : TMainForm;
begin
Form := TMainForm.Create(Application); // stworz formularz
Form.ShowModal; // wyswietl formularz
Form.Free; // zwolnij pamiec
end;
exports
ShowForm name 'MainForm'; // eksportuj procedure...
begin
end.
Procedura ma za zadanie utworzenie formularza. Tworzenie odbywa się w sposób dynamiczny. W zmiennej deklarowana zostaje zmienna, która wskazuje na nazwę formularza. Następnie formularz został stworzony i wyświetlony. Procedura została wyeksportowana. Teraz możesz skompilować bibliotekę.
Pozostało jeszcze wywołanie procedury z poziomu programu:
procedure TForm1.Button1Click(Sender: TObject);
var
DLL : THandle; // uchwyt do biblioteki
Forma : procedure; stdcall;
begin
DLL := LoadLibrary('TestDLL.dll'); // zaladuj biblioteke
Forma := GetProcAddress(DLL, 'MainForm'); // odnajdz procedure
Forma; // wywolaj procedure
FreeLibrary(DLL); // zwolnij pamiec
end;
Ta procedura jest podobna do poprzedniej wywołującej procedurę z biblioteki.
Sposoby eksportowania:
Istnieją dwa sposoby eksportowanie procedur z biblioteki DLL. Pierwszy to eksport poprzez nazwę, czyli to co robiliśmy do tej pory. Drugi sposób to eksportowanie poprzez indeks. Eksportowanie poprzez indeks jest mało popularne gdyż trudniej zapamiętać cyfrę niż wyraz.
exports
Cos index 99; // ekxport poprzez indeks
Teraz ladowanie:
external 'testdll.dll' index 99;
Kiedy stosować biblioteki DLL
Podstawową wadą tworzonych przy użyciu Delphi bibliotek jest ich rozmiar. Tak więc ich stosowanie opłaca się tylko podczas większych projektów. Z drugiej strony można łatwo wymieniać kod w DLL-ach umieszczony, aby poprawiać lub naprawiać błędy programu.
Kodowanie programu...
Kodowanie programu to nic innego jak sposób jego pisania. Uwierzcie mi, że jest to bardzo ważna rzecz w programowaniu. Nie wystarczy wiedzieć co pisać, ale trzeba jeszcze wiedzieć jak pisać! Po co to robić? Otóż Twój kod zyska wiele czytelności. Jeżeli dasz kod źródłowy innym osobom to MUSI on być czytelny. Także Ty nie będziesz miał problemów z odczytaniem swojego kodu. Ja także na początku byłem tak pochłonięty nauką programowania, że nie zważałem na to jak piszę, ale z czasem weszło mi w nawyk pisanie czytelne i teraz nie wyobrażam sobie pisania w inny sposób. Oto kilka uniwersalnych zasad:
1. Nazewnictwo zmiennych.
To taże jest ważne. Przede wszystkim Ty będziesz mógł się zorientować o co chodzi. Oto przykład:
var
i,z,k,l:integer;
s,ss,sz:string;
Tak niepowinieneś pisać! Oto inny sposób:
var
Licznik, Wykonanie : Integer;
Imie, Nazwisko : String;
Jaki styl wolisz?? Oczywiście nie w każdym wypadku. Zazwyczaj przyjęło się, że przy pętli "for" stosuje się zmienną o nazwie "i":
var
I : Integer;
begin
for I := 0 to 100 do
{ ... }
2. Nazewnictwo formularzy
To także może ułatwić oriętacje. Bo gdy widzisz w katalogu plik: Unit1.pas, Unit1.dcu, Unit2.pas, Unit2.dcu to trudno się zorientować co dany formularz robi. Zazwyczaj pzyjeło się nazywanie formularzy z końcówką "Frm" - np: MainFrm - forma główna; AboutFrm - forma "O programie".
A zapisując formularz na dysk dodajesz końcówkę "U" - np: MainFrmU.pas, AboutFrmU.pas
3. Styl pisania
Kilka podstawowych zasad: ograniczenie jednej linii do 70 znaków, zachowanie odstępów - np:
var
Count : Integer; // spacje pomiedzi znakiem ':'
Styl więlbłądzi to styl pisania komend. Np. tworząc nową procedurę nie piszesz tak:
procedure mojanowaproceduraktorarobicos;
Piszesz w ten sposob co powoduje efekt garbu ( stad styl więlbłądzi ):
procedure MojaNowaProceduraRobiacaCos;
Nie pisz wszystkiego małymi literami. Możesz zaczynać wyraz pierwsza literą.
Stosuj wcięcia - to bardzo ważna rzecz! Zapisując kolejną linię stosuj wcięcie w wielkości dwóch spacji ( lub trzech ).
var
I : Integer;
label Etykieta;
begin
I := 100;
Etykieta:
repeat
Dec(I);
if I = 50 then
I := Random(25)
else
goto Etykieta;
until I = 0;
end;
Mniejsza o użyteczność tego kodu ( nawet nie wiem, czy zadziala :P ) ale zwróć uwagę na wcięcia i zawijanie. Ooops. Zdaje się że nie omówiłem jeszcze polecenia "goto" i etykiet. No to wpadka :| Sorki. Otóż w dowolnym miejscu procedury możesz zadeklarować tzw. etykietę od której będzie się zaczynała procedura. Etykiete deklaruje się podobnie jak zmienną tyle, że przy pomocy słowa "label" po którym następuje nazwa etykiety. Teraz gdzieś w programie piszesz nazwę etykiety zakończoną dwukropkiem - od tego miejsca zaczyna się etykieta. Teraz jeżeli chcesz się do niej odwołać to piszesz słowo "goto" i dalej nazwę etykiety:
var
I : Integer;
label
Etykieta;
begin
Etykieta:
Randomize;
I := Random(20);
if I <> 1 then
goto Etykieta;
end;
Jeżeli wylosowana liczba jest różna od 1 to powróć do etykiety "Etykieta".
4 Nazewnictwo komponentów
Tak samo ma się sprawa z komponentami. Nie nazywaj ich: Button1, Button2, itd, tylko stosuj nazwy opisowe - np: btnOpen, btnSave itp.
5. Stosuj komentarze!
Tak, tak komentarze mogą Ci pomóc w rozszyfrowaniu kodu kiedy na niego patrzysz po kilku miesiącach.
Możesz także na górze każdego programu stosować krótki opis o autorze, prawa autorskie, pochodzenie programu - np:
(*********************************************************)
(* *)
(* Test Animation for Borland Delphi *)
(* Copyright (c) 2001 by Adam Boduch *)
(* Build: 03.03.2001 r. *)
(* HTTP://WWW.PROGRAMOWANIE.OF.PL *)
(* E - mail: boduch@poland.com *)
(* *)
(*********************************************************)
No i to by było na tyle. Aha! Nazwy rekordów także rozpoczynaj na literę "T".
No i musisz się wciąż uczyć i poznawać nowe techniki. Wejdź na stronę: www.programowanie.of.pl i poczytaj parę artykułów, pościągaj kody źródłowe....
Pozdrawiam! |