下午在公司写程序,关于DLL中传递对象作为参数的问题。考虑了许多方法,也参考了一些资料,得出下面的方法。
DLL中:
interface TMsg = class public procedure Show; virtual; end; function CreateMsg: TMsg; stdcall; implementation procedure TMsg.Show; begin MessageBox(0, 'ok', 'ok', 0); end; function CreateMsg: TMsg; begin Result := TMsg.Create; end;
导出函数中加入exports CreateMsg;
EXE中是需要对之前声明的类重新定义的,但不需要实现,所以类中的方法都标识成为abstract抽象方法:
interface type TMsg = class public procedure Show; virtual; abstract; end; function CreateUDPMsg: TMsg; stdcall; external 'IM.dll'; implementation procedure TForm1.Button1Click(Sender: TObject); var Msg: TMsg; begin Msg := CreateUDPMsg; Msg.Show; Msg.Free; end;
不过这种传递参数的方式要求DLL中的类声明和EXE中的类声明必须一致,否则就会出错。一旦修改了DLL中的类声明,所有调用了此DLL的EXE程序必须也修改为同样的类声明,并将方法变更为abstract。为了方便省事,参考了一些资料,可以使用interface接口来作为传递的类型。
首先定义接口,文件为msginterface.pas:
interface type IMsg = interface procedure Show; end;
DLL:
interface uses msginterface; type TMsg = class(TInterfacedObject, IMsg) public procedure Show; end; function CreateMsg: IMsg; stdcall; //返回值改为IMsg implementation //TMsg的实现部分同上一种方式完全相同 procedure TMsg.Show; begin MessageBox(0, 'ok', 'ok', 0); end; function CreateMsg: IMsg; begin Result := TMsg.Create; end;
EXE:
interface uses msginterface; function CreateMsg: IMsg; stdcall; external 'IM.dll'; implementation procedure TForm1.Button1Click(Sender: TObject); var Msg: IMsg; //Msg声明的类型变为IMsg接口 begin Msg := CreateUDPMsg; Msg.Show; Msg := nil; //接口的释放直接设为nil即可 end;
这样,不论是在DLL中,还是在EXE中,只需要调用同一个msginterface文件中定义的接口,省了要两边同时修改类声明的麻烦。甚至还可以将CreateMsg函数放入msginterface.pas中,以条件编译来控制开关。