前一篇文章介绍了为何要共用内存管理器,有人要问可不可以在编写dll时更通用一些,可以兼顾其它编译器(如果是其它编译器的话,Delphi写的dll不能与其它语言共用内存管理器),采用一定的策略来避免在dll内创建RTL自动管理的数据类型做参数或返回值,这样其它非Delphi的开发语言也可以用了!
完全可以!
一般的策略如下:
1.在主调函数中申请一块空间
2.把这个空间的地址传送给dll
3.dll内部进行逻辑运算,得到结果后把值添到主调函数传递过来的空间上
4.主调函数获取值,根据业务规则对空间进行处理(释放等操作)
在整个工作周期中,申请的内存一直掌握在主调函数的手中,没有给dll任何管理对象的机会!
看一下下面的主调函数申请内存的模板!
在这里需要强调几点: //1.不用共用内存管理器的话,数据类型需要使用Windows标准类型,如PChar!另外在dll中返回结构体时,最好结构体内的成员为基本数据类型,不要成员又是指针,一不小心就会出错! //2.PChar是一个指针,一般在主调函数中申请空间时先申请255,并把地址传入dll,dll进行逻辑处理,处理完毕后进行返回,返回时,长度可能远远小于255,所以在返回时通过一个变参告诉主调,实际只用了N个长度,这时主调在主动清理实际使用的长度! //3.一般在程序中string与PChar都是结合使用,即返回PChar后直接给一个string,这样更方便!但string类型的特殊性(与Java的string类似),直接赋值会出现问题,故需要用Move或CopyMemory进行内存复制工作,对于为什么出现问题,我想单独写一个文章来解释! type TGetStr=function(Src: PChar;srcLen: Integer;Buffer: PChar;var Size: Integer):boolean;stdcall; function GetDllStr: string; var DllHnd: THandle; GetStr: TGetStr; Str,Buf: PChar; size:integer; begin size:=255; DllHnd := LoadLibrary(PChar('project1.dll')); try if (DllHnd <> 0) then begin @GetStr :=GetProcAddress(DllHnd, 'TestFunc'); if (@GetStr<>nil) then begin GetMem(Str, size);{分配} StrPCopy(Str,'asdf'); GetMem(Buf,Size); try GetStr(Str,size,buf,size); result := StrPas(buf);{返回} finally FreeMem(Str);{释放} FreeMem(Buf,Size); end; end else begin application.MessageBox(PChar('DLL加载出错,DLL可能不存在!'), PChar('错误'), MB_ICONWARNING or MB_OK); end; end; finally FreeLibrary(DllHnd); end; end; //dll实现 function AddStr(Src:PChar):string; begin result:=Src+'/Liangpei hello!'; end; function TestFunc(Src: PChar;srcLen: Integer;Buffer: PChar;var Size: Integer):boolean;stdcall;//函数调用协议 var LocalString:string; begin result:=false; LocalString:=AddStr(Src);//一般字符串传入dll后,均要根据某些业务做一些加工或处理,AddStr就为处理函数 if (Buffer = Nil) or (Size < srcLen) then begin SetLastError(Byte(Buffer <> Nil) * 234 (*ERROR_MORE_DATA, need more buffer size*) ); Size := srcLen; Exit; end; CopyMemory(Buffer,PChar(LocalString),Min(Size,Length(LocalString))); Size := srcLen; Result := true; end; exports TestFunc;