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

  • 主题发起人 主题发起人 DreamTiger
  • 开始时间 开始时间
D

DreamTiger

Unregistered / Unconfirmed
GUEST, unregistred user!
我的主程序中有一个函数:
function GetText:PChar;stdcall;
begin
if assigned(MainForm.CurrentChild) then
begin
result := PChar(MainForm.CurrentChild.Editor.Lines.Text);
end
else
result := '';
end;

//把它exports
exports
GetText;
在dll中调用:
type
TGetString=function:pchar;stdcall;
调用:
sText := String(TGetString(GetProcAddress(AOwner,'GetText')));
//AOwner是主程序的HInstance
问题是当
MainForm.CurrentChild.Editor.Lines.Text很短的时候,传递没问题,
当MainForm.CurrentChild.Editor.Lines.Text很长的时候,传递就出错了。
我搞不懂怎么回事。
 
uses ShareMem或改为
procedure GetText(var text:PChar);stdcall;
试试。
 
看看在线帮助中关于Widestring, PChar, String的相关区别(这也是
Delphi比C麻烦的地方)
 
严格警告,在DLL调用中,尽量适用与C兼容的数据格式,这样在混合编程中特有
好处,不要用STRING,用PCHAR,DELPHI大量使各种事物成为指针,在DLL中最
容易出错误!
 
Chenlili,阿朱:
你们说的我都注意到了,用的都是PChar和stdcall。
o*o:
用procedure GetText(var text:PChar);stdcall;
的话,text是不是需要预先分配好空间?
用ShareMem的话,怎么弄呢?

 
》MainForm.CurrentChild.Editor.Lines.Text很长的时候,传递就出错
什么现象,多长?随机吗?是访问出错还是字符串变短?
 
沈前卫:
大概几千个字符就出错了。
如果只是几个字符就不会出错。
 
你要是象我一样不怕麻烦,可以象windows一样这样写程序:
主程序:
function GetText(buf:PChar;buf_size:DWORD): Boolean;stdcall;
//返回值改为bool,标志成功失败
//内存由调用者提供(buf)
begin

如果buf size够,就把字符串copy到buf去,返回true
如果不够或者不合条件,返回false
end

在dll调用时用:
var buf:array[0..1023] of char;
if call_fun(buf,sizeof(buf)) then
buf 就是 要的 字符串
 
Pipi.:
其实我已经把程序都改成这样的了,只是想看看有没有别的更好的办法。
搞不懂的是,为什么短的字符串可以直接传递过来,长了就不行?
 
是访问出错还是字符串变短?
 
你的错误是:
内存是在GetText这个函数里面申请的,退出GetText时已经清除改内存
可以这样,如果你可以保证,在一个线程里面,得到该字符串后,处理完之后
才会调用下一次,可以这样:
threadvar
stext:string;
function TForm1.func: PChar;
begin
Memo1.Lines.LoadFromFile('c:/winnt/drwtsn32.log');
stext:=Memo1.Lines.Text;
Result:=PChar(stext);
end;
 
补充:
你的错误的原因是:
内存是在GetText这个函数里面申请的,退出GetText时已经清除该内存,
该内存可能已经改做其他用途,因此原来指针指向的内容是不定的。
如果该内存块已经被删除,那么会引起存取冲突`
 
Pipi.兄
如果相你说的那样,下面的程序不对???
procedure GetText(AText:string):string;
begin
result := AText;
end;

 
你还没弄清楚delphi怎么使用string的
GetText返回值是string的情况,
我现在要调用GetText了,我先在eax放返回的string的地址(调用者申请的内存),
传输到GetText函数,GetText函数里面返回时值被拷入那个地址(调用者申请的)
(上面的程序读MainForm.CurrentChild.Editor.Lines.Text
实际是调用 MainForm.CurrentChild.Editor.Lines.GetTextStr 函数,
调用者申请内存)
返回 PChar 和返回 integer 是一回事,直接从eax返回一个32bit值,即使
该指针指向的内存已经释放也不管的
 
》eax放返回的string的地址(调用者申请的内存),
多大,1K,1M,1G....不确定嘛。
 
你举的例:
procedure GetText(AText:string):string;
begin

result := AText;
end;

你的例子不好,AText 本来就是外面来的。换成这样吧:
procedure GetText:string;
var
s:string;
begin

s:='aaaaaaaa';
result := s;
//s是本地变量
end;

为什么这样也没问题?
关键是 result指向的内存是调用 GetText 的过程在调用前申请的。
(通过eax传递进来)
调用者的基本过程:
一块string内存指针给 eax
call GetText
GetText的基本过程:
保存eax(作为result)
把返回值内容copy给result
返回

返回值是PChar的话,大不一样:返回值是32bit整数,通过eax,
没有哪里分配内存之说:
 
1.》eax放返回的string的地址(调用者申请的内存),
多大,1K,1M,1G....不确定嘛。
2.
string的地址的4为字符串大小,再前四个为应用此字符串的个数,在个数为0时
将字符内存释放。当其中某个应用改变字符串,Delphhi将为其分陪空间并将字符靠
被过去。
 
一个string,实际上是个record(struct),
只是包含一个指针,指向另外一个区域,另外一个区域是char array
-4 是长度 -8是引用数,传进去的是空string (record指针)
虽然在其他地方申请了放char array的内存,或者调整了char array的内存的大小
但是最终释放是随 string 这个结构的释放而释放的。
你可以看看 @string 是这个真正string结构的地址,
PChar(string) 是string这个结构里面的一个成员,别人可以调整成员指向的东西
string释放时它的成员data(就是PChar(string)的值)指向的内存也要释放
 
>>在dll中调用:
>>type TGetString=function:pchar;stdcall;
>>调用:
>> sText := String(TGetString(GetProcAddress(AOwner,'GetText')));
我想在调用'GetText'之前先用SetLength(sText, ...)将sText的长度设定好,
不知能否解决问题?(我瞎猜的,还没试过)
 
今天晚上我反映不过来了,等明天清醒再回答你。 {B-(
 
后退
顶部