怎么在dll中调用主程序的函数?(200分)

  • 主题发起人 主题发起人 DreamTiger
  • 开始时间 开始时间
再说清楚一点:
就是说:
string就是一个结构:
type string=record
Data:PChar;
end
总共固定4个字节, data指向的就是一般人理解的所谓string
只要字符串长度变了,data的值大家都可以改,(赋新的data前,旧data值的引用值
减1,如果减到了0就删它)
但是,string这个结构它如果是本地变量,或者临时变量(象上面的例子,没有出现string的定义)
是在堆栈中的,当过程退出时,它要被删除,作为总的根,data指向的内存引用值
也减1,为0的话删除。
 
来看一个例子说明:
function func:PChar;
var
s,s1,s2:string;
begin
s:='123';
s:=s+'456';
//为什么不直接 s:='123456', 原因就是: 假如这样,'123456'是常数string,该内存块引用数设置为FFFFFFFF,
//s也引用该内存块,但是不会改变引用数了,即使s释放,该内存块也是不会释放的。所以不能说明问题
Result:=PChar(s);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
pc:PChar;
ss:string;
begin
pc:=func();
ss:='aaaaa';
showmessage(ss);
//为什么要弄个ss来搞一下?这也是说明一下,如果删了上面2行,
//下面显示的好象就会正确,其实,如果删了这2行,还没有机会
//破坏那些被回收的内存,有了这2行,可以表现pc这个指针指向的
//内存其实已经被回收,不再收到保护了,会分配给别人使用
showmessage(pc);
end;
 
Pipi.:
function GetText(buf:PChar;buf_size:DWORD): Boolean;stdcall;
//返回值改为bool,标志成功失败
//内存由调用者提供(buf)
begin

如果buf size够,就把字符串copy到buf去,返回true
如果不够或者不合条件,返回false
end
在这里,是不是要求buf的实际长度必须比buf_size大1,以便最后一位放#0?
也就是说,如果我实际需要拷贝的字符串为Text,你指的buf_size够,是指
buf_size>Length(Text)还是buf_size>=Length(Text)?
是否需要用strlcopy(buf,PCHar(Text),Length(Text))后,
再设定buf[Length(Text)] = #0?
我是用GetMem来给buf分配内存的。
 
Pipi.兄:
我今天利用上班时间跟踪过与string的付值的过程,确实如你说言。
你对Delphi的了解之深刻,真令人佩服!!!
作晚我还没理解:
》上面的程序读MainForm.CurrentChild.Editor.Lines.Text
》实际是调用 MainForm.CurrentChild.Editor.Lines.GetTextStr 函数,
》调用者申请内存

我还以为PChar(MainForm.CurrentChild.Editor.Lines.Text)是直接取
MainForm.CurrentChild.Editor.Lines.Text的地址,原来是其一个拷贝的
地址。错了,呵呵.....
 
to DreamTiger:
buf_size 应该比 text 的长度 大 1 (用来存储最后一个 0 字符)
strlcopy会在最后一个字节加0,就不用自己加0了
 
其实没有这么麻烦的,千万不要用C++的观念来对待delphi.
pascal是不支持函数返回pchar结果的,当然要是给函数结果分配足够的内存
程序还是不会出错的,改用变参(var AResult: pchar)就不会错了
 
再问一个问题:
我在主程序中给变参分配空间,为什么在DLL中释放的时候会出错?
主程序:
//在函数内分配空间
function GetIniFile2(var s:PChar):boolean;stdcall;
var
sIni:string;
begin
sIni := GetDefaultIniName;
s := StrNew(PChar(sIni));
result := true;
end;

//在DLL中分配空间
function GetTitle(var s:PChar;iLength:integer):boolean;stdcall;
var
sTitle:string;
begin
if assigned(MainForm.CurrentChild) then
begin
sTitle := MainForm.CurrentChild.Caption;
if iLength < Length(sTitle) then
begin
result := false;
exit;
end;

strlcopy(s,PChar(sTitle),Length(sTitle));
result := true;
end
else
result := false;
end;

//在函数内分配空间
function GetText2(var s:PChar;iLength:integer):boolean;stdcall;
begin
if assigned(MainForm.CurrentChild) then
begin
s := strnew(PChar(MainForm.CurrentChild.Editor.Lines.Text));
result := true;
end
else
result := false;
end;

DLL中的调用:
var
iLength:integer;
sText:PChar;
sTitle:PChar;
sIni:PChar;
bResult :boolean;
begin
bResult := TGetString2(GetProcAddress(AOwner,'GetIniFile2'))(sIni);
if not bResult then
begin
StrDispose(sIni);
exit;
end;

GetMem(sTitle,256);
bResult := TGetString(GetProcAddress(AOwner,'GetTitle'))(sTitle,255);
if not bResult then
begin
StrDispose(sIni);
FreeMem(sTitle);
exit;
end;

bResult := TGetString2(GetProcAddress(AOwner,'GetText2'))(sText);
if not bResult then
begin
StrDispose(sIni);
FreeMem(sTitle);
StrDispose(sText);
exit;
end;

//sIni,sText,sTitle的值都是正确的。
StrDispose(sIni);//出错,invalid pointer operation
StrDispose(sText);
FreeMem(sTitle);
end;

但是如果把
bResult := TGetString2(GetProcAddress(AOwner,'GetText2'))(sText);
if not bResult then
begin
StrDispose(sIni);
FreeMem(sTitle);
StrDispose(sText);
exit;
end;
去掉,也就是说不给sText分配内存,StrDispose(sIni)就正常,难道两次分配
内存有相关性?
 
在主程序中申请内存不要使用StrNew,使用GetMem,因为StrNew申请时还要在数据
的地址前还有8个字节。可能是这个引起的。
 
你搞错了,不是StrDispose(sIni)出错,而是GetText2返回时出错
GetText2实际有2个参数:
GetText2(var s:PChar;iLength:integer)
调用时才一个参数:
TGetString2(GetProcAddress(AOwner,'GetText2'))(sText)
GetText2返回时堆栈被破坏(栈指针多加了),返回地址错误
 
Pipi.:
我改了如下:
主程序:
function GetIniFile2(var s:PChar):boolean;stdcall;
function GetText2(var s:PChar):boolean;stdcall;
Dll:
bResult := TGetString2(GetProcAddress(AOwner,'GetIniFile2'))(sIni);
bResult := bResult and TGetString2(GetProcAddress(AOwner,'GetText2'))(sText);
StrDispose(sIni);
StrDispose(sText);
还是出错。
沈前卫:
我用GetMem、FreeMem也不行,真奇怪。
难道在主程序中不能给Dll的变量赋值?
 
你的代码我试验了,没有问题(不过你的代码也是不好,太麻烦过头了)
GetProcAddress返回的函数指针是否有效你判断了吗?是否为nil?
TGetString2的类型你定义正确吗(你是怎么定义的?)
 
》TGetString2定义正确
没定义正确编译通得过???
 
Pipi.:
>>代码我试验了,没有问题(不过你的代码也是不好,太麻烦过头了)
我也想简单一点,所以希望能在主程序内分配空间,省得调用的时候先要分配。
不知你有什么高招?
>>GetProcAddress返回的函数指针是否有效你判断了吗?是否为nil?
呵呵,因为知道它肯定有效,所以没有判断。不过,调用没问题。
>>TGetString2的类型你定义正确吗(你是怎么定义的?)
TGetString2=function(var s:PChar):boolean;stdcall;
bResult := TGetString2(GetProcAddress(AOwner,'GetText2'))(sText);
在调用时候都没有问题,sText也得到了正确的值,
但是就是在释放的时候出了错。
StrDispose(sText);//出错
而且很怪,如果sText只有几十个字符,正常,一旦传过来的sText包含几千个字符,
我的例子中是7000多个字符,就出错。
是不是在主程序中调用的GetMem或者StrNew,必须在主程序中释放?Dll的堆栈和
主程序的堆栈是分开的?参数怎么传递,我不是很清楚,还望指点。



 
要是想简单,还是用你最初的,加2、3行:
threadvar
sText:string;
//这里加一行
function GetText:PChar;stdcall;
begin
if assigned(MainForm.CurrentChild) then
begin
sText:=MainForm.CurrentChild.Editor.Lines.Text;
//这里转换一下
result := PChar(sText);
end
else
result := '';
end;

在dll中你一接到 pchar 的字符串就copy到自己的字符串中就没事了。
dll中:
var s:string;
s:=GetText;
就可以了,因为每调用一次GetText都写了sText,所以GetText一返回
就保存到了本地的string s 里面,不怕下次调用破坏了内容
 
多人接受答案了。
 
后退
顶部