Delphide yazdığım programa ait fonksiyonlar dll içinde toplamak istiyorum. (Program bitti dll işi sonradan çıktı.)
Ancak yazdığım fonskiyonlar, işleyecekleri değerlerin bir kısmını parametre olarak bazıları da doğrudan global alandan alıyorlardı.
DLL yapısına geçince global değişkenler sorun olacak. Her dll fonksiyon çağrılışında bu parametreleri tekrar tekrar yollamak zoruma gidiyor en azından fonskiyonun kullanıldığı satırda uzun ve kafa karıştırıcı görüntü olışacak.
Bu global değişkenleri bir kereye mahsus bir fonksiyonın parametreleri olarak dll içine atsam ve ordaki global alana yerleşse, daha sonrada fonksiyonlar ihtiyaç duyduğunda dll içindeki bu global alandan alıp alıp kullansınlar dedim ve basit bir deneme yaptım. Program çalıştırıldığında hata mesajı alıyorum. (Exception Processing Message...)
Yapmayı düşündüğüm bu yapıda hata görüyormusunuz? Alternatif öneriniz varmı? Neden hata mesajı alıyorum?
Bu arada yazdığım fonksiyonlar bir başka dll fonskiyonlarını da çağırıyordu. Şimdi kendi fonskiyonlarımı dll içine atınca bunlar diğer dll içinden fonskiyonları kolayca çağırabileceklermi?
Delphi7 de varmı bilmiyorum ama .nette bu iş için property kullanılabilir;
class InstanceClass
{
private int _Instance; // Sınıf içinde kullanılan değişken
public int Instance; // Property, dışardan _Instance değişkeninin değerini atamak veya okumak için
{
get
{
return _Instance; //Değişkenin değerini döndürür
}
set
{
_Instance = value; //Değişkene yeni değeri atar
}
}
}
Eski yeni tüm fonksiyonları tek bir dll içine toplamanıza engel nedir ?
Dlllerden birisini çip üreticisi veriyor. (Kod hali yok)
Diğeri de kendi yazdığım fonksiyonları içerecek.
Bir diğer problem de dll içine atmak istediğim bazı fonskiyonlar form üzerinde bazı label yada editbox ların text değerlerini değiştiriyordu. Bu fonskiyonları bu halleriyle dll içine atabilecekmiyim?
DLL in uses kısmına Unit1 i dahil edip
Fonksiyonlardan birisinin içine form1.caption:='AAA' gibi bir satır ekleyip dll oluşturdum.
Bir uygulama programından da bu dll in caption değiştiren fonksiyonunu çağıdırdım.
Kod hata vermeden çalıştı ancak form captionda hiç bir değişiklik yapmadı.
Sorun nedir?
Winows'ta her pencerein ir handle numarası vardır. tüm uygulamalar bu numara ile işlem yapar. Sizin DLL'inize form1 var ama , çalıştırdığınız uygulamadaki form1 , o form1 değil. Ancak programınızda DLL içerisindekiş formun create, init vs... gibi fonksiyonlarını çalıştırııp o DLL içerisinde tarif edilen formu oluşturursanız ancak o zaman formun bir HWND numarası olur.
Eğer DLL'e yerleştireceğiniz fonksiyonlar form üzerindeki bazı omponentler üzerinde işlem yapıyorsa , doğrudan o komponente referans vermek zaten yanlış bir yaklaşım olur.
DLL dosyaları dinamik kütüphanelerdir. Her birinin fonksiyonlarını herhangi bir kod içine import edip kullanabilirsiniz. Diğer yöntem ise loadlibrary apisi ile kütüphaneyi belleğe yükleyip getprocadress apisi ile bellek adresinden ilgili fonksiyonu çalıştırmaktır.
Ancak kütüphaneler içindeki kodlar form üzerindeki nesnelerle direk etkileşime giremez yani form üzerindeki bir nesneye müdahale edemez. Bunu yapmak için caption değiştiren fonksiyonunuz bir event yaratacak, event meydana geldiğinde çalışacak kod form class içinde olacak.
Bülent hocam epeydir delphi kullanmıyorum, makinemdede kurulu değil bunun için yine .netten örnek verdim. Kütüphane kod örneği;
Public Class InstanceClass
Public Shared Event CaptionEvent(ByVal Caption As String)
Public Sub ChangeCaption()
'İŞLEMLER
RaiseEvent CaptionEvent("New Caption")
End Sub
End Class
Form kod örneği;
Private Class FormX
Private WithEvents CaptionProcesses As New InstanceClass
Private Sub CaptionEvent(ByVal NewCaption As String)
Me.Caption = NewCaption
End Sub
End Class
Bu sayede dinamik kütüphane içinde olay tetiklendiğinde kod akışı form içindeki rutine yönelip captionda değişiklik yapabilir.
Bu işin bence doğru yolu
üzerinde işlem yapılacak nesneyi , DLL içerisindeki fonksiyona parametre olarak geçmektir.
Örneğin
DLL örneği
library DllTest;
uses
SysUtils,
Classes,stdctrls;
{$R *.res}
procedure Label_DLL_Test(TargetLabel:Tlabel ; captiondata:string);
Begin
targetlabel.Caption:=captiondata;
End;
exports
Label_DLL_Test;
begin
end.
Bu da DLL içerisindeki fonksiyonu çağırmak ve formumuz üzerindeki Label nesnesinin başlığını değiştirmek için kullanacağımız yöntem
unit unt_DllTestApp;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,stdctrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Label_DLL_Test(TargetLabel:Tlabel ; captiondata:string); external 'dlltest.dll';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Label_DLL_Test(label1,'DLL den label caption degistirme');
end;
end.
Alıntı yapılan: Klein - 10 Nisan 2010, 11:28:14
Bu işin bence doğru yolu
üzerinde işlem yapılacak nesneyi , DLL içerisindeki fonksiyona parametre olarak geçmektir.
%100 haklısın, eğer kod akışı içinde caption değiştirilmek istenirse caption değeri için dll fonksiyonunun dönüşü kullanılabilir.
Benim verdiğim event örneği, Bülent hocanın epeydir uğraştığı ve baya büyüdüğünü düşündüğüm proje içerisinde kullanışlı olabileceği içindi. Sanırım hocam formdaki değerleri belli olaylar sonucunda değiştirerek arka planda yürüyen işler hakkında kullanıcıya bilgi veriyor. Eventlerde bu işlemleri otomatiğe bağlar diye düşünmüştüm.
Bir göz atın
http://delphi.about.com/od/objectpascalide/a/dlldelphi.htm (http://delphi.about.com/od/objectpascalide/a/dlldelphi.htm)
Alıntı yapılan: hasangurlek - 10 Nisan 2010, 11:45:49
%100 haklısın, eğer kod akışı içinde caption değiştirilmek istenirse caption değeri için dll fonksiyonunun dönüşü kullanılabilir.
DLL'in dönüşüne bile gerek yok. doğrudan form da parametre olarak geçilebilir.
Ama illa ki form içerisine nesneleri doğrudan yerleştirmek gerekirse , RESOURCE DLL'leri var. delphi buna da izin veriyor. Ama ben hiç bu tip bir DLL ile çalışmadım.
Bu sefer rutinimiz a ile b yi toplayıp , sonucu hedef nesnenin başlığına yazıyor.
library DllTest;
uses
SysUtils,
Classes,stdctrls,forms;
{$R *.res}
procedure Label_DLL_Test(TargetLabel:Tlabel ; captiondata:string);
Begin
targetlabel.Caption:=captiondata;
End;
procedure Object_DLL_Test(TargetObject:TObject ; a:integer ; b:integer);
var x:integer;
Begin
x:= a+b;
tform(targetobject).Caption:=inttostr(x);
End;
exports
Object_DLL_Test,
Label_DLL_Test;
begin
end.
unit unt_DllTestApp;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,stdctrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Label_DLL_Test(TargetLabel:Tlabel ; captiondata:string); external 'dlltest.dll';
procedure Object_DLL_Test(TargetObject:TObject ; a:integer ; b:integer); external 'dlltest.dll';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Label_DLL_Test(label1,'DLL den label caption degistirme');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
object_DLL_Test(form1,60,30);
end;
end.
Form1 fonskiyonlarinin bir kismini DLL yaptim ve soyle bir sorunla daha karsilastim.
Form kisminda kalan bazi fonskiyonlarin da dll icindeki fonksiyonlarca cagrilmasi gerekiyor.
Ornegin hata mesajlarinin gosteren fonskiyonum dll disinda kaldi. Dll deki bir fonskiyon bu fonskiyonu cagirabilirmi?
En basta dll kullanaracagimi ongorseydim isim kolay olacakti. Bitmis programin fonskiyonlarini dll yapisina cevirmeye kalktigimda
bir suru takla atmam gerekiyor.
Aslinda en iyisi biraz zaman ayririp yeni bastan yazmak.
DLL'i kullanan başak programlarınız var mı ? DLL yerine bi tane unit tanımlayıp ortak kullanılan fonksiyon ve prosedürleri oraya yazabilirsiniz. DLL kullanarak delphinin tek exe ile çalışma mantığını da kırmış oluyorsunuz.
DLL fonsksiyonlarin urettigi degerleri structure uzerinden ana programa donduruyorum.
Structure in elemanlarindan birisi de string tanimli Hata mesaji degiskeni.
Ancak bu degisken sorun yaratiyor.
Soyleki;
Eger structure dan string tanimli degiskeni kaldirirsam (hem ana programdan hemde DLL den) fonskiyonlar sorunsuz calisiyor.
Eger string tanimli degiskeni devreye sokarsam exeption hatalari cikiyor.
Sorun nedir?
String degiskenleri dll uzerinden yollamanin bir sakincasi varmi?
DLL fonksiyon geri dönüşlerinde string kullanılırsa delphi bir exception üretiyor. Ama buna karşın görevi yerine getiriyor. Geri dönüş olarak string kullanmayı tercih etmediğim için sebebi üzerine araştırma yapmadım.
Ancak çok gerekli olursa try...except bloğu kullanarak hatayı kendim yöneterek , yani hatayı görmezden gelerek sorunu aşardım.
Ancak hata kodu yerine , doğrudan hataya ilişkin metni geri döndürmenin de efektif bir yöntem olduğu kanaatinde değilim.
Hocam sanırım sorun Delphinin string formatı ile Windows'un string formatı arasındaki uyumsuzluktan kaynaklanıyor. Delphide metnin ilk baytı metin uzunluğunu veriyor , windows'ta ise bildiğimiz sıfır sonlu katar sistemi kullanılıyor.
Sorunu çözmek için "string" yerine , windows ile uyumlu tip olan "pchar" tipini kullanmalısınız.
Ama hatanın metnini değil de kodunu gönderip bunu program içerisinde metne çevirme yöntemini neden kullanmadığınızı da merak ettim doğrusu.
function returnstring_DLL_Test(a:integer ; b:integer ):pchar;
var x:integer;
Begin
x:= a+b;
result:=pchar(inttostr(x));
End;
Kodlama yontemini, program kodlarina bakarken bu ne hatasiymis diye dusunmek gerektirdigi icin
sevmiyorum. Ancak isin icinden cikamadim ve mecburen dediginiz gibi her hataya bir kod verip karsi tarafta buna karsilik
aciklama yapacagim.
Global alanda degiskenleri tanimlarken
var
Hata: Array [0..30] of string;
Seklinde tanimlama yaptigimizda
Hata[0]:='Ok'
Hata[1]:='xxxx'
Hata[2]:='yyyy'
Hata[3]:='zzzz'
....
....
Seklinde deger atamasini kod isletmeden yapabilirmiyim? (a:integer=1; da oldugu gibi)
Bu islemi degisken degilde const nasil yaparim?
const errmsg:array[0..2] of string =('ahmet','mehmet','hasan');
const
Days : array[0..6] of string = ( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ) ;
const renkler : array[0..2] of string =('kırmızı','mavi','yeşil');
şeklinde de olabilir.
veya
const aylar : array[0..2] of string =( 'ocak' , 'şubat' , 'mart' );
şeklinde farklı bir düzenleme de getirilebilir.
Delphi'de (en azından CodeGear 2007'de) Yeni bir dll projesi açtığınızda proje kodunun tepesinde aşağıdaki comment yer alır.
Özetle WideString kullanmak istiyorsanız dll'inizi BORLNDMM.DLL ile birlikte dağıtmanız ve buna uygun kodlama yapmanız lazım.
Bundan kaçınmak için iki yol var ya parametreleriniz ShortString yapmalısınız yada PChar kullanmalısınız.
Elinizdeki her hangi bir string'i pchar'a çevirmek de kolay PChar(s) ile (Compiler Option'larında "huge strings" işaretliyse) direk çevirebilirsiniz.
İşaretli değilse çevrimi yapmanız gereken yerde
{$H+}
PChar(s)
{$H-}
yaparsınız olur biter.
Kolay Gelsin.
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }