PDA

View Full Version : استفاده ي از Dll ها به صروت يكم حرفه اي !!



ali_mohamadi8928
جمعه 20 اردیبهشت 1387, 17:23 عصر
سلام
يك مشكل برام پيش اومده كه هر چي فكر ميكنم راه حلي ازش به ذهنم نميرسه ...
فرض كنيد چندين Dll در اختيار داريم كه همگي يك تابع مشترك دارن كه همه چيز اون تابع از جمله پارامترهاي ورودي و مقدار بازگشي اون مثل هم هستن ... حالا من ميخوام توي برنامم يه جوري كد نويسي كنم كه بعد از باز شدن كادر انتخاب فايل يكي از اون DLL ها رو انتخاب كنم و تابع مورد نظر رو كه توي همه ي DLL هاي مد نظرم هست رو از داخل اون فراخوناي كنم .... من اون تابع رو اينجوري تعريف كردم توي برنامم .


Function Moshtarak(Vorodi:String):String; external 'Test.DLL';

فقط الان مشكل اينه كه راهي به ذهنم نميرسه كه نام DLL رو در زمان اجرا تغيير بدم تا برنامه درست كار كنه ..

دوستان اگه راهي هست لطفا راهنمايي كنيد

مهران موسوی
جمعه 20 اردیبهشت 1387, 17:39 عصر
با سلام ... دوست عزيز شما به يك نكته ي حساس و جالب اشاره كردين ... در اين مواقع بايد فراخواني ايستا رو فراموش كنيد و يكم حرفه اي تر كار كنيد و برين سراغ فراخواني DLL ها به صورت پويا ... در اين شرايط ميتونيد توابع كاملا همشكل رو از DLL هاي متفاوت صدا بزنيد ... ( بهترين راه و بهينه ترين راه همينه .... )

ali_mohamadi8928
جمعه 20 اردیبهشت 1387, 18:56 عصر
يك مشكل عجيب غريب داره دلفي ...

اقا من يك متغير سراسري براي هندل DLL تعريف كردم به اين صورت


var
Form1: TForm1;
handd:HWND;

حال كدها ي زير رو در يك دكمه نوشتم براي فراخواني پويا



procedure TForm1.Button1Click(Sender: TObject);
type
TMessd= Function (valr:string):string;
var
dor:TMessd;
d:string;
begin
handd:=LoadLibrary('Dll_p.dll');
if handd = 0 then begin
ShowMessage('Error');
end;
@dor:=GetProcAddress(handd,'Messd');
if @dor <> nil then begin
d:=dor('salam ');
ShowMessage(d);
end;
end;


در رويداد بستن فورم هم كد زير رو براي ازاد كردن حافظه ي DLL نوشتم


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FreeLibrary(handd);
end;

حالا وقتي برنامه رو اجرا ميكنم و بر روي دكمه كليك ميكنم نتيجه درست هست ولي بعد از Ok كردن پيغام ارور زير از طرف دلفي صادر ميشه . علتش چيه اخه ؟؟؟؟


---------------------------
Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EInvalidPointer with message 'Invalid pointer operation'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------

مهران موسوی
جمعه 20 اردیبهشت 1387, 19:44 عصر
با سلام ...

اگه كدت رو اينجوري بنويسي جالبتر و بهينه تر ميشه ...


var
LibHandle: THandle;
fMessd: Function (valr:string):string;
begin
LibHandle := LoadLibrary(PChar('Dll_p.dll'));
if LibHandle = 0 then
raise Exception.Create('Not loaded')
else
begin
try @fMessd := GetProcAddress(LibHandle, 'Messd');
if @fMessd <> nil then
ShowMessage(fMessd('Hello '));
except
on E: Exception do
ShowMessage('Exception error: ' + E.Message);
end;
end;
FreeLibrary(LibHandle);


ولي موضوع اون ارور كه توسط دلفي صادر ميشه نميدونم چيه :متعجب:

اگه دوستان ديگه لطف كنن نظر بدن بهتر ميشه ... :چشمک:

يا حق ...

مهران موسوی
جمعه 20 اردیبهشت 1387, 21:08 عصر
كم كم دارم شك ميكنم به دلفي ... من همين عمليات رو در VC++ انجام ميدم ولي با هيچ پيغام خطايي مواجح نميشم ... نميدونم چرا توي دلفي اينجوري ميشه !!! عجيب اينه كه درست كار ميكنه و وقتي نتيجه رو برگردوند پيغام Invalid pointer operation رو ميده ...

اين پيغام زماني بايد رخ بده كه برنامه بخواد يك اشاره گر رو كه به خارج از heap اشاره مكنه حذف كنه ... ولي نميدونم چرا توي اين كد اين پيغام رو ميده ... لطفا بقيه ي دوستان هم چك كنن ....

مهران موسوی
جمعه 20 اردیبهشت 1387, 23:13 عصر
ضميمه : سورس DLL كه من تستش ميكنم ولي بعد از نمايش نتيجه با پيغام دلفي مواجح ميشم رو ميزارم تا دوستان هم يكم روي اين مسئله تفكر كنن ... وقتي داشتم به دوستمون كمك ميكردم هيچ وقت فكرش رو نميكردم كه فراخواني پويا با همچين مشكلي رو به رو ميشه ...



library Dll_p;

{ Test Dll }

uses
SysUtils,
Classes;

{$R *.res}



Function Messd(valr:string):string; cdecl;
var
a:string;
begin
a:= valr+'(Test - Dll)';
Result:=a;
end;

exports
Messd;


end.

vcldeveloper
شنبه 21 اردیبهشت 1387, 01:28 صبح
1- لزومی به استفاده از cdecl به عنوان Calling Convention نبود. این Convention زمانی استفاده میشه که DLL شما با C یا ++C نوشته شده باشه.
2- هر وقت که پروژه DLL جدیدی در دلفی ایجاد می کنید، دلفی از طریق یک Comment در سورس کد بهتون هشدار میده که برای تبادل داده بین DLL و برنامه فراخوان آن، از نوع داده String یا از Dynamic Array استفاده نکنید. اگر خواستید از این نوع داده ها برای تبادل داده استفاده کنید، یونیت ShareMem را به عنوان اولین یوینت در بخش uses هر دو پروژه تعریف کنید.
3- وقتی از رشته string بصورت Literal استفاده می کنید، نیازی نیست که آن را به PChar تایپ کست کنید:


LoadLibrary(PChar('Dll_p.dll'));
بجاش:


LoadLibrary('Dll_p.dll');
4- از بلوک try-finally استفاده کنید تا مطمئن بشید DLL لود شده تحت هر شرایطی آزاد میشه.

با توجه به نکات بالا، کد شما به شکل زیر تغییر میکنه:
کد DLL:


library Dll_p;

{ Test Dll }

uses
SysUtils,
Windows;

{$R *.res}

function Messd(valr: ShortString): ShortString; stdcall;
begin
try
Result := valr + '(Test - Dll)';
except
on E: Exception do
MessageBox(0,PAnsiChar(E.Message),'Error',MB_OK + MB_ICONERROR + MB_APPLMODAL);
end;
end;

exports
Messd;

end.
کد برنامه فراخوان:


procedure TForm1.Button1Click(Sender: TObject);
var
LibHandle: THandle;
fMessd: Function (valr: ShortString): ShortString; stdcall;
begin
LibHandle := LoadLibrary('Dll_p.dll');
try
if LibHandle > 0 then
begin
@fMessd := GetProcAddress(LibHandle, 'Messd');
if @fMessd <> nil then
ShowMessage(fMessd('Hello '))
else
raise Exception.Create('Function not found');
end
else
raise Exception.Create('Library not loaded');
finally
FreeLibrary(LibHandle);
end;
end;



كم كم دارم شك ميكنم به دلفي ... من رو یاد این تاپیک (http://barnamenevis.org/forum/showthread.php?t=104665) انداختید!

مهران موسوی
شنبه 21 اردیبهشت 1387, 06:44 صبح
سلام اقاي كشاورز ...

والا توجه نكرده بودم كه نميشه از String براي پاس دادن اطلاعات استفاده كرد ...

در رابطه با قسمتهاي زير هم بايد بگم چون شكل DLL كه نوشته بودم اينجوري نبود و ميخواستم همه ي حالت ها رو چك كنم اشتباهي همونجوري Copy / Paste كردم اينجا ...


Function Messd(valr:string):string; cdecl;


LoadLibrary(PChar('Dll_p.dll'));

فقط نكته ي انحرافيش اينجا بودكه نميشه مستقيما String رو پاس داد ... البته به گفته اقا علي با استفاده ازShareMem ميشه اين كار رو كرد :گیج:

در ضمن من فكر كنم اگه نوع پاس دادن داده ها رو از نوع Cdecl ( راست به چپ ) انتخاب نكنيم خودش Register ( چپ به راست ) انتخاب ميكنه .. اون نكته اي هم كه گفتين Cdecl براي C++ استفاده ميشه قبول دارم .... :چشمک:


يا حق ...

ali_mohamadi8928
جمعه 04 بهمن 1387, 00:33 صبح
2- هر وقت که پروژه DLL جدیدی در دلفی ایجاد می کنید، دلفی از طریق یک Comment در سورس کد بهتون هشدار میده که برای تبادل داده بین DLL و برنامه فراخوان آن، از نوع داده String یا از Dynamic Array استفاده نکنید. اگر خواستید از این نوع داده ها برای تبادل داده استفاده کنید، یونیت ShareMem را به عنوان اولین یوینت در بخش uses هر دو پروژه تعریف کنید.



ممنونم اقاي كشاورز با اضافه كردن ShareMem به اول Uses مشكل String حل شد ولي يك مشكل خيلي بدتر با اضافه كردن اين Unit به برنامم به وجود اومده !!! :ناراحت:

بعد از اضافه شدن ShareMem به برنامه وقتي برنامه ميخواد بسته بشه دو تا Error وحشتناك ميده .. اين امر حتي زماني كه توي برنامه هيچ كدي هم نباشه و فقط ShareMem تو Uses باشه باز رخ ميده .
پيغامها عبارتند از :


Project Project1.exe raised exception class EInvalidPointer with message 'Invalid pointer operation'. Process stopped. Use Step or Run to continue.


Runtime error 217 at 00413708

سورس كد زير رو امتحان كنيد . ميبينيد كه فقط ShareMem اضافه شده ولي با اين وجود بعد از اجرا وقتي قصد بستن برنامه رو داريم پيغام ها ظاهر ميشه !!

چرا ؟


unit Unit1;
interface
uses
ShareMem,Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
end.

vcldeveloper
جمعه 04 بهمن 1387, 02:34 صبح
بعد از اضافه شدن ShareMem به برنامه وقتي برنامه ميخواد بسته بشه دو تا Error وحشتناك ميده .. جای ShareMem اونجا نیست! ShareMem را باید به عنوان اولین یونیت در لیست uses از فایل پروژه (فایل dpr) بنویسید، نه در سورس مربوط به فرم.
در ضمن، اگر از دلفی 2006 به بالا استفاده می کنید، بهتر هست بجای ShareMem از SimpleShareMem استفاده کنید که کارایی بیشتری از ShareMem داره، و نیازی هم به فایل DLL مربوطه (BorlandMM.dll) نداره.