Merhabalar, değerli okuyucular. Bugün sizlerle birlikte bir konuyu işleyeceğiz. Bu konu biraz uzun olabilir. Uzun olması önemli değil. Normalde şu anda sınav dönemindeyim fakat Matematik sınavını verdiğimde kendime söz vermiştim bir makale yayınlayacağım diye. Belki ne alaka diyebilirsiniz bunun nedeni; makale yazarken, soruları yanıtlarken bir iyilik yapmış gibi hissediyorum. Bu yüzden böyle bir şey yapmak istedim.
Başlık biraz alakasız gelmiş olabilir. Elimden geldikçe kısa tutmaya çalıştım. Konumuz şu; örneğin bir uygulama geliştiriyorsunuz. Uygulamanızın bazı ücretli eklentilerinin olmasını istiyorsunuz. Buna örnek verilmesi gerekirsek; bir muhasebe uygulamasındaki kasa modülü olarak düşünebilirsiniz. Peki biz bu tarz durumda hangi yollardan gidebiliriz ona bakmamız gerek.
Başlıyoruz
Bilindiği üzere Delphi ile statik bir tanımlama ile kolayca DLL'leri kullanabiliyoruz. Burada yapmamız gereken dinamik olarak DLL'i yükleyip uygulamanın ilgili yerlerine; ilgili özellikleri eklemek. Peki bu nasıl olacak? Algoritma basit. 1 adet modules (modüller) adına klasör oluşturalım (bu durum size kalmış). Sonraki adım ise bu klasör içinde bulunan .dll uzantılı dosyaları almak olacak. Bu listelediğimiz dosyaları tek tek load (yüklemek) edip; uygulamamız üzerinden kullanmayı hedefliyoruz. Öncellikle bize gerekli olacak bazı fonksiyonlardan bahsedelim. Bunlardan ilki FindFirst fonksiyonu. Bu fonksiyon ile birlikte Modules klasöründe bulunan .dll uzantılı dosyaları tek tek listeleyeceğiz.
modulesList.Clear;
if FindFirst(modulesFolder + '*.dll', faArchive, SR) = 0 then
begin
repeat
modulesList.Add(Path + SR.Name);
until FindNext(SR) <> 0;
FindClose(SR);
end;
modulesList.Text := Trim(modulesList.Text);
Burada modulesList bir global StringList. modulesFolder ise global olarak tanımlanmış bir modules klasörü yolunu verir. Biraz daha açıklayıcı olmamız gerekirse;
private
modulesList: TStringList;
modulesFolder: string;
private alanında tanımlamış olduğumuz ilgili değişkenlerimiz. Bir diğer nokta ise TStringList nesnesini create etmek ve modulesFolder değişkenine ilgili yolu set etmek.
procedure TfrmMain.FormCreate(Sender: TObject);
begin
modulesList := TStringList.Create;
modulesFolder := ExtractFilePath(Application.ExeName) + '\Modules\';
end;
ExtractFilePath, Application.ExeName'den dönen exe yolunun klasörünü bize verir. Bu yüzden bu şekilde bir kullanım tercih ettim. Sonrasında ise exe yani uygulamanın bulunduğu klasörde Modules klasörü olarak eklemesini yaptım. Şimdi FindFirst ile yaptığımız işlemi bir procedure (prosedür)'a dönüştürelim.
Unutmadan önce söylemekte fayda var. OnCreate ile oluşturmuş olduğumuz nesneyi, OnDestroy veya işleminizin bitiminde FreeAndNil ile yok etmenizi şiddetle öneririm. Eğer yapmadığınız zaman form her create edildiğinde bellekte yer ayıracaktır. Bu da MemoryLeak yani bellek sızıntısına sebep olacaktır. Neden FreeAndNil derseniz; bunun manuel olarak aslında;
Obj.Free;
Obj := nil;
bu kod yerine direkt olarak FreeAndNil kullanıyoruz.
procedure TfrmMain.setModules;
var
SR: TSearchRec;
begin
modulesList.Clear;
if FindFirst(modulesFolder + '*.dll', faArchive, SR) = 0 then
begin
repeat
modulesList.Add(modulesFolder + SR.Name);
until FindNext(SR) <> 0;
FindClose(SR);
end;
modulesList.Text := Trim(modulesList.Text);
end;
Bu prosedür ile birlikte SR
olarak tanımlamış olduğum TSearchRec
tipli değişkene ilgili klasörde bulunan dosyaları set ediyoruz. Sonrasında ise SR.Name
ile dosya isimlerini alıyoruz. modulesFolder + SR.Name
ile ilgili dosyanın tam adresini elde etmiş oluyoruz. Bu prosedürümüz cepte. Bundan sonrası ise artık asıl meseleye geleceğiz. Adım adım gidelim. Önce ana uygulama formumuzu tasarlayalım.
Tasarım bu şekilde; işaretlemiş olduğum buton ile birlikte klasörde olan tüm DLL dosyalarını toplu olarak yükleyeceğiz. Şimdi bu ana uygulamayı bir kenara bırakıp DLL oluşturmaya dönelim. 2 adet DLL oluşturacağım. Bu DLL dosyalarında 2 farklı form olacak. Her DLL için bir buton oluşturacağım. Bu butonlar sayesinde ilgili eklentiye ulaşım sağlayacağız. Şimdi DLL tarafına geçelim. Delphi'ye yeni başlayanlar veya DLL ile hiç haşır neşir olmayanlar için nasıl DLL oluşturacağınızı da kısaca anlatmak isterim.
Delphi ile DLL Oluşturma
Yukarıda gösterildiği üzere File->New->Dynamic Library - Delphi seçeneği ile birlikte DLL dosyamız için ilgili dosyalar oluşturulmuştur. Sonrasında DLL kuralları ile işlemlerimizi yapıyoruz. İşimize yarayacak bir takım kütüphaneleri tanımını yapıyoruz.
DLL'imizi Tasarlayalım
File->New->VCL Form - Delphi seçeneği ile DLL dosyamıza 1 adet form ekliyoruz. Formu isteğimize göre tasarlayabiliriz ve kodlayabilirsiniz. Normal bir uygulama geliştirir gibi form üzerinde geliştirme yapabilirsiniz.
Bu şekilde mesaj gösteren, toplama işlemi yapan bir form tasarladım ve aşağıya formun kodlarını bırakıyorum.
unit BirincEklentiForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TfrmBirinciEklentiFormu = class(TForm)
btnMesajGoster: TButton;
edtBirinciSayi: TEdit;
Label1: TLabel;
edtIkinciSayi: TEdit;
Label2: TLabel;
btnTopla: TButton;
procedure btnMesajGosterClick(Sender: TObject);
procedure btnToplaClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmBirinciEklentiFormu: TfrmBirinciEklentiFormu;
implementation
{$R *.dfm}
procedure TfrmBirinciEklentiFormu.btnMesajGosterClick(Sender: TObject);
begin
ShowMessage('Hello world!');
end;
procedure TfrmBirinciEklentiFormu.btnToplaClick(Sender: TObject);
begin
ShowMessage('İşlem sonucunuz: ' + IntToStr(StrToInt(edtBirinciSayi.Text) + StrToInt(edtIkinciSayi.Text)));
end;
end.
Şimdi ise DLL tarafında bu işlemler için neler yapacağı ona bakalım. Bu kodları formun sahip olduğu .pas dosyasına yazdığımı hatırlatayım.
DLL'i Hazır Hale Getirmek
Burada yazılan fonksiyonları ve tanımlamalarını maddelere göre aşağıda belirtiyorum;
- Burada formumuzun uses yani tanımı bulunmakta. Böylelikle DLL sınıfından erişim sağlayabiliyoruz.
createModule
ilgili formun create edilmesini sağlamaktadır.
showModule
ilgili formu gösterime sunar. Yani son kullanıcıya gösterir.
moduleInfo
en önemli prosedür. Burada eklenti adını belirtiyoruz. Buraya istediğiniz bilgiyi ekleyip ana uygulamada işleyebilirsiniz.
Exports
DLL'de dışarıdan erişilebilecek prosedür ve fonksiyonların listesini kapsar. Bu şekilde tüm prosedür ve fonksiyonlara erişim izni verdik.
Sonrasında derleyerek DLL'imizin çıktısını alıp modules klasörüne atalım.
Görüldüğü üzere modules klasöründe ilgili DLL yerini aldı. Kolaylık olması açısından ilgili DLL kodlarını aşağıya bırakıyorum.
library BirinciEklenti;
{ Create: Halil Han}
uses
System.SysUtils,
System.Classes,
ShareMem,
Windows,
Forms,
Controls,
BirincEklentiForm in 'BirincEklentiForm.pas' {frmBirinciEklentiFormu};
{$R *.res}
procedure createModule;
begin
frmBirinciEklentiFormu := TfrmBirinciEklentiFormu.Create(nil);
end;
procedure showModule;
begin
frmBirinciEklentiFormu.Show;
end;
function moduleInfo: String;
begin
Result := 'Birinci Eklenti';
end;
Exports
createModule, showModule, moduleInfo;
begin
end.
Diğer DLL'i oluşturup hızlıca ana işleme geçmek istiyorum fazla uzatmayalım...
Ikinci eklentiyi de oluşturup ilgili klasöre ekledim.
LoadLibrary
LoadLibrary
ile ilgili DLL dosyasını uygulamamıza load edeceğiz. Sonrasında ise GetProcAddress
ile birlikte DLL'de ayrılan bellek adresini get edeceğiz. Bu bilgiyi pointer'e kaydedip sonrasında bunu tanımlamış olduğumuz fonksiyona veya prosedüre atamasını gerçekleştireceğiz. Bunun için bir prosedür hazırladım.
procedure TfrmMain.loadModules(modulesFolder: String);
type
TModuleInfo = function: string; ///eklentinin bilgilerini alan fonksiyon. Hatırlarsanız shareMem kullanım için anlatım ///yapmıştım.
TBtnProc = procedure(Sender: TObject) of object; ///burada ise butonun OnClick eventi için oluşturmuş olduğumuz ///tanımlama
TCreateModule = procedure; ///Burada ise create edilen prosedür.
var
getModuleInfo: TModuleInfo; //Yukarıdaki tanımlamanın değişkeni.
btnProc: TBtnProc; //Yukarıdaki tanımlamanın değişkeni.
createModule: TCreateModule; //Yukarıdaki tanımlamanın değişkeni.
Ptr: TFarProc; //TFarProc ile bir Pointer tanımlıyoruz.
LoadDll: Cardinal; //DLL'i saklayan değişken.
I: Integer; ///for döngüsünde gerekli olacaktır. Her DLL için döngü mevcuttur.
moduleBtn: TButton; ///Butonu runtime ile create ettiğimiz için değişken olara tanımladık.
begin
setModules; ///modules klasöründe olan dosyalar TStringList'e set ediliyor.
for I := 0 to modulesList.Count - 1 do //her dosya için döngü yapıyoruz.
begin
LoadDll := LoadLibrary(pChar(modulesList[I])); ///ilk dll dosyası load ediliyor.
if LoadDll <> 0 then //0 ise load edilmemiş veya bir sorun var demektir.
begin
Ptr := GetProcAddress(LoadDll, 'moduleInfo'); /// ilgili pointerı ataması yapılıyor. GetProcAddress ile DLL ///içerisindeki moduleInfo çekiliyor.
@getModuleInfo := Ptr; ///atama yapılıyor.
Ptr := GetProcAddress(LoadDll, 'createModule'); ///yukarıdaki işlemler diğer prosedür/fonksiyonlar için yapılıyor.
@createModule := Ptr;
createModule; //form create ediliyor.
moduleBtn := TButton.Create(frmMain); //buton oluşturuluyor.
moduleBtn.Parent := frmMain;
moduleBtn.Name := 'modulbtn' + I.ToString;
moduleBtn.Width := 100;
moduleBtn.Height := 50;
moduleBtn.Caption := getModuleInfo;
moduleBtn.Top := 10;
moduleBtn.Left := (I + 1) * 150; // burada ilgili left değeri set ediliyor.
Ptr := GetProcAddress(LoadDll, 'showModule');
@btnProc := Ptr;
moduleBtn.OnClick := btnProc; // oluşturulan butonun onClick eventi set ediliyor.
end;
end;
end;
Şimdi test edelim;
butonlar sağlıklı bir şekilde geldi şimdi çalışıyor mu diye bakalım.
Bu kısımda ise eklenti başarılı bir şekilde çalışıyor.
Burada ise ikinci eklentiyi test ediyoruz ve başarılı.
Son
Biraz anlatımı zor oldu. Ortalama 1.30 saatimi aldı ama en azından kendime vermiş olduğum sözü tuttum. Aklınıza takılan bir soru mevcut ise yorum yazmaktan çekinmeyin. Kendinize iyi bakın, sağlıkla kalın!