Sayfa: [1]   Aşağı git
  Yazdır  
Gönderen Konu: Delphi ile UDF Yazma  (Okunma Sayısı 1367 defa)
0 Üye ve 1 Ziyaretçi konuyu incelemekte.
JoKeR
Rock On Roal
Administrator
*

Karma: 112
Offline Offline

Cinsiyet: Bay
Mesaj Sayısı: 1291



Üyelik Bilgileri
« : 06 Haziran 2008, 16:20:52 »

Firebird’ün güzel yanlarından biri de, Firebird’e kendi yazdığınız fonksiyonları ekleyip, veritabanında her yerde kullanabilmeniz. UDF’leri Delphi ile de yapabilirsiniz. Aşağıda basitçe Delphi ile UDF yazmayı anlatmaya çalıştım. Ekleyeceğiniz veya düzelteceğiniz birşey varsa mutlaka bize ulaştırın.

UDF oluşturmak için bir .dll hazırlayıp, Firebird’ün UDF klasörüne koymak gerekiyor.

Delphi’yi açıp, File -> New -> Other... komutunu verin. Açılan pencereden DLL Wizard seçip, OK butonuna basın. Delphi size boş bir dll kodu oluşturacaktır. Direk bu kısma fonksiyonlarımızı ekleyebiliriz, ama ilerde fonksiyonlarımız arttığında takibin kolay olması açısından gruplara ayırmak ve grupların her biri için yeni bir unit ekleyip, oraya yazmamız daha iyi olur. File -> New -> Unit komutunu vererek yeni bir unit ekleyelim.

Şu an için unit’imizde verilen bir yıl ve aydaki gün sayısını veren bir fonksiyon olacak. Fonksiyonumuz zaten Delphi’de DateUtils unitinde olduğu için uses kısmına DateUtils ekliyoruz. Fonksiyonu ekledikten sonra unitimizin kodu :

Kod:
[FONT=Courier New]unit untTarih;

interface

uses DateUtils;

function BirAydakiGunSayisi(var Yil, Ay:Integer):Integer;stdcall;

implementation

function BirAydakiGunSayisi(var Yil, Ay:Integer):Integer;
begin
  Result := 0;
  if (Ay <= 0) or (Ay > 12) then
    Result := 0
  else
    Result := DaysInAMonth(Yil, Ay);
end;

end.[/FONT]

Bu fonksiyonu dll kısmında export etmemiz lazım. Proje dosyamızın son hali :
 
Kod:
[FONT=Courier New]library visualbasicturk.net;

uses
  SysUtils,
  Classes,
  untTarih in 'untTarih.pas';

{$R *.res}

exports
  BirAydakiGunSayisi;

begin
end.[/FONT]
 

Daha sonra Project -> Build All komutunu verip, dll dosyasını oluşturalım. Oluşan .dll dosyasını ismi “visualbasicturk.dll” olsun. Bu dll dosyasını Program Files\Firebird içerisindeki UDF klasörüne kopyalayın. Daha sonra IB Expert ile herhangi bir veritabanına bağlanıp, UDF’imizi aşağıdaki kodla tanımlayabiliriz.

Kod:
[FONT=Courier New]DECLARE EXTERNAL FUNCTION BirAydakiGunSayisi
    integer, integer
    RETURNS integer BY VALUE
    ENTRY_POINT 'BirAydakiGunSayisi' MODULE_NAME 'visualbasicturk.net';[/FONT]
 

Burda fonksiyonumuzun adını, aldığı iki parametrenin tiplerini, döneceği değerin tipini ve hangi dll’de, hangi isimle export ettiğimizi tanımladık. Bu tanımları yaptıktan sonra veritabanı bağlantısını kesip, Denetim masasından Firebird servisini kapatıp açın. Artık fonksiyonumuzu kullanabiliriz.

Kod:
[FONT=Courier New]select BirAydakiGunSayisi(2008, 2)
from rdb$database[/FONT]
 

Hepsi bu kadar. Şimdi kod yazarken nelere dikkat etmek gerekiyor, onları belirtelim :

* Fonksiyon parametrelerini mutlaka var kelimesi ile geçin. Örneğimizde Yil ve Ay parametrelerini bu şekilde geçtik. Parametre pchar ise var kullanmamanız lazım.

* Firebird C’de yazıldığı için, C notasyonu istiyor. Fonksiyonumuzu dll’den çağırırken sonuna stdcall direktifini eklememiz gerekiyor.

* Fonksiyonu çağırırken near, far ve export direktiflerinin bir etkisi yoktur, bunları kullanmayın.

* Eğer fonksiyonu denerken bir hata alırsanız veya bağlantınız koparsa:
o Kodunuzda hata vardır, kodunuzu kontrol edin.
o .dpr kısmında fonksiyonu export ettiniz mi, bir bakın. Eğer etmediyseniz genelde “invalid request BLR at offset xxx” gibi bir hata alacaksınız.
o Derledikten sonra oluşan .dll dosyasını UDF klasörüne kopyaladınız mı, kontrol edin. Firebird 2.1 için normal kurulumda bu klasörün tam yolu : “C:\Program Files\Firebird\Firebird_2_1\UDF” tir.
o Firebird servisini denetim masası kısmından kapatıp açın.

* Eğer dll’de değişiklik yaparsanız, parametre değişikliği yoksa .dll’i UDF klasörüne kopyalamanız yeterlidir. Eğer açık bağlantı varsa, dll kullanılacağı için Windows .dll’in üstüne yazmanıza izin vermez. Açık veritabanı bağlantılarını kesip, .dll’i öyle kopyalayın.

* Eğer .dll’deki fonksiyonlarda parametre değişikliği yaptıysanız, UDF tanımlarını silin (drop), .dll’i kopyalayıp tanımları değişen parametreleri de dikkate alarak tekrar yapın.

* Pchar bir parametre kullanırsanız, yapacağınız işleme göre Trim kullanıp boşlukları atmak iyi olur.

* Firebird’teki tiplerin Delphi karşılığı :
o integer : integer
o char veya varchar : pchar
o double precision : double

UDF’imizi biraz daha geliştirelim :

String İşlemleri

String işlemleri için Pchar parametre kullanmak gerekiyor. Pchar C tipi string’tir ve sonunda #0 karakteri bulunur. Bu #0 karakteri string’in sonunu gösterir.

Verilen string’in uzunluğunu veren bir UDF yazalım, ismi “Uzunluk” olsun.

Kod:
[FONT=Courier New]function Uzunluk(Str:Pchar):integer;stdcall;
...
function Uzunluk(Str:Pchar):integer;
var
  i : integer;
begin
  Result := -1;
  i := 0;
  while (Str[i] <> #0) do
    Inc(i);
  Result := i;
end;[/FONT]

 

Bu fonksiyon #0 karakterine kadar, her karakter için i değişkenini bir artırarak string’in uzunluğunu buluyor ve döndürüyor.

Firebird tarafında UDF’i tanımlarken, Cstring kullanılır :
 
Kod:
[FONT=Courier New]DECLARE EXTERNAL FUNCTION Uzunluk
CSTRING(255)
RETURNS integer by value
ENTRY_POINT 'Uzunluk' MODULE_NAME 'visualbasicturk.net';[/FONT]

 

Tarih İşlemleri

Tarih işlemleri için, Firebird’ten gelen tarih değerini api fonksiyonları kullanarak, Delphi’nin anlayacağı şekle çevirmek gerekiyor.

İki tane fonksiyon yazacağız, biri Delphi’deki FormatDateTime fonksiyonunun yaptığı işi Firebird tarafında yapacak, diğerinde de iki tarih arasındaki ay sayısını bulacağız. Kodlar :

Kod:
[FONT=Courier New]const
  IBASE_DLL = 'gds32.dll'; [/FONT]

 

Kullanacağımız Firebird api’leri gds32.dll içerisinde.
 
Kod:
[FONT=Courier New]type
  Long   = LongInt; // 32 bit signed
  ULong = DWord;   // 32 bit unsigned[/FONT]

 

Date ve Time tiplerini tutan değişkenlerimizin tipleri. Date için Long, Time için Ulong kullanıyoruz.
 
Kod:
[FONT=Courier New]type
  TM = record
    tm_sec   : integer;  // Saniye
    tm_min   : integer;  // Dakika
    tm_hour  : integer;  // Saat (0-23)
    tm_mday  : integer;  // aydaki gün (1-31)
    tm_mon   : integer;  // Ay (0-11)
    tm_year  : integer;  // Yil (takvim yılından 1900 eksiği)
    tm_wday  : integer;  // Haftanın günü (0-6) Pazar = 0)
    tm_yday  : integer;  // Yılın günü (0-365)
    tm_isdst : integer;  // gün ışığından faydalanıyorsa 0)
  end;
  PTM    = ^TM;

  ISC_TIMESTAMP = record
    timestamp_date : Long;
    timestamp_time : ULong;
  end;
  PISC_TIMESTAMP = ^ISC_TIMESTAMP;[/FONT]

 

Tarih ve saati tutacağımız record’umuz.
 
Kod:
[FONT=Courier New]procedure isc_decode_sql_date   (var ib_date: Long; tm_date: PTM); stdcall; external IBASE_DLL;[/FONT]

 

Tarihi alacağımız Firebird API’si

Kod:
[FONT=Courier New]function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;stdcall;
...
function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;
var
   tm_date : TM;
   TempTarih : TDateTime;
begin
  if FormatStr = nil then
    Result := nil
  else
  begin
    isc_decode_sql_date(ib_date, @tm_date);
    TempTarih := EncodeDate(tm_date.tm_year + 1900, tm_date.tm_mon + 1, tm_date.tm_mday);
    Result := PChar(FormatDateTime(string (FormatStr), TempTarih));
  end;
end;[/FONT]

 

isc_decode_sql_date api’si ile gelen tarih değerini, tm_date record’una aldık ve bunu EncodeDate kullanarak Delphi TDateTime değerine çevirdik. Burda dikkat edeceğiniz iki nokta var. Yıl değeri o anki yıldan, 1900 eksik gelir. Dolayısıyla, tarihteki yılı bulmak için 1900 ile topladık. Ayrıca ay değerleri 0 ile 11 arasındadır. Delphi’de aylar, 1 ile 12 arası olduğu için ay değerine de 1 ekledik.

String ile pchar değerini stringe, pchar ile de stringi pchar’a çevirebilirsiniz.

İkinci tarih UDF’imiz :

 
Kod:
[FONT=Courier New]function KacAyVar(var IlkTarih, IkinciTarih : Long):integer;stdcall;
...
function KacAyVar(var IlkTarih, IkinciTarih : Long):integer;
var
   tm_ilk, tm_ikinci : TM;
   TempIlkTarih, TempIkinciTarih : TDateTime;
begin
  Result := -1;

  isc_decode_sql_date(IlkTarih, @tm_ilk);
  isc_decode_sql_date(IkinciTarih, @tm_ikinci);

  TempIlkTarih := EncodeDate(tm_ilk.tm_year + 1900, tm_ilk.tm_mon+1, tm_ilk.tm_mday);
  TempIkinciTarih := EncodeDate(tm_ikinci.tm_year + 1900, tm_ikinci.tm_mon+1, tm_ikinci.tm_mday);

  Result := MonthsBetween(TempIlkTarih, TempIkinciTarih);
end;[/FONT]

 

Bunları Firebird tarafında tanımlamak için :
 
Kod:
[FONT=Courier New]DECLARE EXTERNAL FUNCTION DUZENLITARIH
    CSTRING(64),
    DATE
RETURNS CSTRING(128) FREE_IT
ENTRY_POINT 'DuzenliTarih' MODULE_NAME 'visualbasicturk.net';

DECLARE EXTERNAL FUNCTION KACAYVAR
    DATE,
    DATE
RETURNS INTEGER BY VALUE
ENTRY_POINT 'KacAyVar' MODULE_NAME 'visualbasicturk.net';[/FONT]
 

Hata Ayıklama (Debug)

Bazen bir işlem yaparken, fonksiyon istediğiniz sonuçları döndürmeyebilir veya bağlantı kopabilir. Bu durumda büyük ihtimalle kodunuzda bir yanlış yapmışsınız demektir. Nerede yanlış yaptığınızı görmek için fonksiyon içindeki bazı değerleri görmek isteyebilirsiniz. Bu durumda en kolay yöntem, bir text dosya açıp istediğiniz değerleri oraya yazdırmanızdır.

Mesela DuzenliTarih fonksiyonumuz düzgün çalışmıyor ve format string’imizin düzgün gelip gelmediğini görmek istiyoruz. Aşağıdaki gibi bir dosya açıp, fonksiyonu Firebird’te denedikten sonra fonksiyon c:\debug.txt içerisine gelen FormatStr parametresinin değerini yazar.
 
Kod:
[FONT=Courier New]function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;
var
   tm_date : TM;
   TempTarih : TDateTime;
   DebugFile : TextFile;
begin
  try
    AssignFile(DebugFile, ‘c:\debug.txt’);
    ReWrite(DebugFile);
    WriteLn(DebugFile, string(FormatStr));
  finally
    CloseFile(DebugFile);
  end;
  ...[/FONT]

 

 
Hadi Kolay Gelsin....
Logged

10 Konusu Olmayan Soru Sormasın Soruları Cevaplanmayacak...

Linklerin Görülmesine Izin Verilmiyor
Linki Görebilmek Için Üye Ol veya Giris Yap
Sayfa: [1]   Yukarı git
  Yazdır  
GoogleTagged

 
Gitmek istediğiniz yer: