大悟还俗

邮箱 key_ok@qq.com 我的收集 http://pan.baidu.com/share/home?uk=1177427271
posts - 236, comments - 8, trackbacks - 0, articles - 0
  新随笔  :: 联系 :: 订阅 订阅  :: 管理

编写dll时的内存分配策略

Posted on 2013-10-09 12:26 大悟还俗 阅读(...) 评论(...) 编辑 收藏

 

前一篇文章介绍了为何要共用内存管理器,有人要问可不可以在编写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;
View Code