Delphi ile Dinamik DLL Çağırma: Plug-in

Delphi ile Dinamik DLL Çağırma: Plug-in

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 := Nil;
    Obj.Free;

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.Nameile 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;

  1. Burada formumuzun uses yani tanımı bulunmakta. Böylelikle DLL sınıfından erişim sağlayabiliyoruz.
  2. createModule ilgili formun create edilmesini sağlamaktadır.
  3. showModule ilgili formu gösterime sunar. Yani son kullanıcıya gösterir.
  4. moduleInfo en önemli prosedür. Burada eklenti adını belirtiyoruz. Buraya istediğiniz bilgiyi ekleyip ana uygulamada işleyebilirsiniz.
  5. 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

LoadLibraryile 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!


Share Tweet Send
0 Comments
Loading...