最近很少上论坛,这几次发现好几个问题都是围绕如何使用DLL中的接口而展开的。
问题描述:
具体问题就是在隐式使用接口变量后,在FreeLibrary执行后,就会出现一个非法访址的错误。
这个错误的原因就是在FreeLibrary后,DLL以的代码均为不可用状态,而在代码执行完整个过程后,VCL要对RTL类型的数据进行清理。而在清理过程中肯定要对接口进行减1并进行释放相关对象。而对象代码已从进程空间卸载,故报非法访址错误!
解决方法:
所以要想解决该问题,就应该把DLL调用过程放到一个单独的过程中,其目的就是让调用完毕后,让VCL来清理接口。清理完毕后返回后,再调用FreeLibrary来从进程空间中卸载DLL。
错误调用代码为:
- var
- libHandle: THandle;
- GetDllObject: TGetDllObject;
- ADllObj: ICustomDLL;
- begin
- libHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + edtDLLFileName.Text));
- try
- if (libHandle = 0) then raise Exception.Create('载入DLL失败!');
- @GetDllObject := GetProcAddress(libHandle, 'GetDllObject');
- if (@GetDllObject <> nil) then
- begin
- ADllObj := GetDllObject() AS ICustomDLL; //GetDllObject()
- ADllObj.OwnerAppHandle := Application.Handle;
- edtDLLName.Text := ADllObj.DLLName;
- ADllObj.Execute;
- end
- else RaiseLastOSError();
- finally
- FreeLibrary(libHandle); //***前面正常,到这里就报错***
- end;
- end;
正确的全过程为:
- //DLL部分
- 1.接口定义
- unit DLLInf;
- interface
- type
- ITest = interface
- ['{623008B1-5E8C-463C-9048-821C14FB20C1}']
- function ShowMSG(ParamStr:Widestring):Widestring;
- end;
- implementation
- end.
- 2.接口实现
- unit DLLImpl;
- interface
- uses
- DLLInf ;
- type
- TTest=class(TinterfacedObject,ITest)
- public
- function ShowMSG(ParamStr:Widestring):Widestring;
- end;
- implementation
- function TTest.ShowMSG(ParamStr: Widestring): Widestring;
- begin
- result:=result+ ParamStr;
- end;
- end.
- 3.导出类单元
- function TestObj:ITest;stdcall;
- begin
- result := TTest.create;
- end;
- exports
- TestObj;
- //前端调用
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls,DLLInf;
- type
- TTestObj=function:ITest;stdcall;
- TForm1 = class(TForm)
- btn1: TButton;
- edt1: TEdit;
- procedure btn1Click(Sender: TObject);
- private
- { Private declarations }
- TestObj: TTestObj;
- myDLLHandle: THandle;
- procedure getDLLObject;
- public
- { Public declarations }
- end;
- var
- Form1: TForm1;
- implementation
- {$R *.dfm}
- procedure TForm1.getDLLObject;
- var
- testStr:Widestring;
- begin
- testStr:='Test String';
- @TestObj:= GetProcAddress(myDLLHandle, 'TestObj');
- if @TestObj<>nil then
- TestObj.ShowMSG(testStr) //调用DLL中的对象并执行相关方法
- else
- Application.MessageBox('在Dll动态链接库中加载方法失败!','提示',mb_ok);
- end;
- procedure TForm1.btn1Click(Sender: TObject);
- begin
- myDLLHandle:=loadlibrary('DLLDemo.dll');
- try
- if myDLLHandle>0 then
- getDLLObject;
- finally
- FreeLibrary(myDLLHandle);
- End ;
- end;
- end.