关于Delphi中调用C语言动态链接库的问题

dandycheung 2000-08-23 10:39:00
用 C 编写了一个动态链接库,其中输出了一个要用 char * 作为参数的函数,在 delphi 中调用时采用如下形式:

fxn(PChar('Some chars'));

当 PChar 中强制转换的字符串仅包含一个字符时(如:'a')调用失败,而只要多于一个字符则正常,敢问是什么原因?

题外话:我觉得 delphi 本是个不错的东西,但 Borland 把它做得太糙了,在细微的地方经常出毛病,这一点不如 VC++。
...全文
724 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
JGTM2000 2000-09-05
  • 打赏
  • 举报
回复
而且这句话:“相反的,我不动我的DLL,把delphi中对我DLL的输出函数原型中的参数类型由PChar改为Integer,调用的时候用Integer(@StrBuffer)的形式传入到DLL中,执行结果完全正常”。稍微明白一点Delphi的同志都应该能看出来前后是没有任何差别的,计算机执行程序是不知道什么PChar还是Integer的,总之这是一个32bit DWORD,类型无非编译器的概念。所以为什么结果忽对忽错,我虽然还是不能确认,但总不宜就说是开发环境的错误吧。
JGTM2000 2000-09-04
  • 打赏
  • 举报
回复
请注意前例中'Test'在需要接受PChar类型参数处的直接使用。
JGTM2000 2000-09-04
  • 打赏
  • 举报
回复
我C是不好,不过汇编不错。我不明白的是你说的这句话:"delphi中传入的参数原封不动的传入到MessageBox函数里,结果照样出错。您知道,MessageBox函数是必然会将该值作为字符缓冲区的首址"。怎么我编同样的程序就没有任何错误,我想只可能是您有粗心的错误。另外,fxn(PChar(String('a constant string')))和直接写fxn('a constant string')是没有区别的,如果有一个常量const s = 'a constant string',或者const s:PChar = 'a constant string',或者const s: array [0..255] of char = 'a constant string',则fxn(s)都是正确的。只有当s的类型为显式的string({$H+}时等同于AnsiString),需要用PChar(s)的形式强制转换(注:改转换的目的仅是为了骗过编译器,因为s作为指针时其目标地址的布局和PChar是完全兼容的)。

为了进一步说明问题不在Delphi,我们找一个C写成的Win32 API来测试一下:

implementation

const
sConst = 'a';
pcConst : PChar = 'a';
caConst0: array [0..0] of char = 'a'; // !!
caConst1: array [0..1] of char = 'a';
strConst: string = 'a';
cConst: char = 'a';
nullChar: char = #0;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
// asm int 3; end; // comment/uncomment this line to trigger breakpoint here
Windows.MessageBox(Handle,sConst,'Test',MB_OK);
Windows.MessageBox(Handle,pcConst,'Test',MB_OK);
Windows.MessageBox(Handle,caConst0,'Test',MB_OK); // cause incorrect tail
Windows.MessageBox(Handle,caConst1,'Test',MB_OK);
Windows.MessageBox(Handle,PChar(strConst),'Test',MB_OK);
Windows.MessageBox(Handle,@cConst,'Test',MB_OK); // cause incorrect tail
Windows.MessageBox(Handle,@nullChar,'Test',MB_OK);
Windows.MessageBox(Handle,nil,'Test',MB_OK);
end;

什么情况编译和结果都正确,为什么那两句结果不正确,希望大家好好体会一下(最好在CPU窗口中研究一下内存布局,相信体会更深)。BTW, 评论一件产品(或一个人)的时候,最好先确保自己完全理解他,否则坏了人家的名声多冤呀。哈哈

dandycheung 2000-08-28
  • 打赏
  • 举报
回复
各位好,请原谅我的粗心,kxy 是对的,应该以fxn(PChar(String('Some chars')));这样的形式来调用,我原来试的时候把代码改了一下没有编译就执行了(并且还不在集成环境中)。昨天又试才发现。JGTM2000 朋友非常热心,但可能由于对 C 语言了解不够,所以未能找到问题要害。
顺便再问一下,为什么在 Delphi 的集成环境中不能调试对 Oracle 数据有操作的程序?无论是使用 ADO 还是其他控件我都能遇见这个问题。
JGTM2000 2000-08-26
  • 打赏
  • 举报
回复
From: <dandycheung>
To: <JGTM2000>

> 您好,
> 首先为您能关注我的问题致谢。
> 您的考虑有一定的正确之处,但好像和我的问题不能吻合。
> 我为了验证这一问题,曾经专门另外做了一个DLL,输出的函数里面只有一行代码,就是将delphi中传入的参数原封不动的传入到MessageBox函数里,结果照样出错。您知道,MessageBox函数是必然会将该值作为字符缓冲区的首址。
> 相反的,我不动我的DLL,把delphi中对我DLL的输出函数原型中的参数类型由PChar改为Integer,调用的时候用Integer(@StrBuffer)的形式传入到DLL中,执行结果完全正常。
> 由此推断,错误必然发生在delphi上,而不是我的DLL。

能否将您所说的相关代码贴来?根据我的判断,PChar和Integer不是重要的,因为那是对编译器而言的概念,只要在内存中的地址和布局一致,结果一定是一样的,无论用谁来写。我想搞清楚一个问题,就是您如何能够判断传入的指针的目标内存区到底是一个字符还是一个字串?从Delphi的角度讲,无论多长的字串内存布局都是一样的,即字串本身内容+ASCIIZ(对于array [0..x] of char, PChar(AnsiString), string const)。


JGTM2000 2000-08-26
  • 打赏
  • 举报
回复
我希望能通过类比相似的例子解释这个问题的出处,您想,在Delphi中调用Win32 API不也是调用C的DLL函数吗?Win32 API中有很多的函数同样接收LPCZSTR即char*类型的参数,无论你用Delphi传入多长的字串它也不会出现错误,为什么?因为函数把char*总视为指向ASCIIZ字符串的指针,这种串占用内存空间的大小为实际字串长度+1(结尾的NULL),因此如果你传入的缓冲区大小为1,它只能代表长度为0的字串,而且这仅有的一个字节也必须为NULL。然而,如果函数把char*理解为指向一个字符的指针,则当同样大小为1的内存区域就被认为是字符,也就可以不是0。这明显的带来了歧义:长度为1的目标内存区域到底是什么内容?!

因此,这个问题的来源不一定是Delphi,而是C函数的内部实现没有处理这种明显的二异性。可以肯定的说,你用C程序以同样的内存布局去调用它,同样会出问题。所以解决方法很简单,明确定义char*的实际含义,目标内存区域是字符还是字串,如果是字串,一个字符的字串,同样按两个字节的内存占用处理,就不会出现任何问题。这也是为什么API定义中引入LPCZSTR宏的原因之一。
dandycheung 2000-08-25
  • 打赏
  • 举报
回复
再没有人回答了吗?
ckbmail 2000-08-25
  • 打赏
  • 举报
回复
我觉得delphi处理指针的能力不是太强。不知道各位意下如何?
please mailto :ckbmail@elong.com和我讨论
Sayhigh 2000-08-25
  • 打赏
  • 举报
回复
var
Buffer: PChar;
begin
GetMem(Buffer, 1024);//你的字串长度
StrCopy(Buffer, 'some chars');
fxn(Buffer);
Freemem(Buffer);
end;
应该可以的,如果不行传指针过去试一下.
dandycheung 2000-08-23
  • 打赏
  • 举报
回复
首先感谢yinfudan的精彩讲解。我是一个VC++的程序员,但也经常用到delphi。
问题是我在delphi 中调用时,该参数既可能是一个字符的字符串,也可能是多个字符的字符串。所以当前我的代码是这个样子的:

var
s: string;
...
// assigns value to s here, 'a' or 'abc'
...
// then:
fxn(PChar(s));

如何能最简单的解决这个问题?
mytulip 2000-08-23
  • 打赏
  • 举报
回复
试一下,function StrPCopy(Dest: PChar; const Source: string): PChar;吧,将string转成PChar
yinfudan 2000-08-23
  • 打赏
  • 举报
回复
同意dandycheung的题外话

问题解决方案:
PChar(...)可以把String型转成PChar型,具体实现方案是
建立一个Buffer,和这个String一样长,然后返回这个Buffer的首地址,首地址为32比特
例如PChar('abcde')的值可能等于$00450F03
PChar(...)可以把Integer或Char型转成PChar型,具体实现方案是
直接把这个整数或字符转成32比特的指针型
例如PChar(65535)=$0000FFFF,PChar('2')=$00000032

所以,传入参数PChar('a')当然不行了。应该改为
var
c:char;
begin
c:='a';
CFunc(@c);
end;
一定可以
dandycheung 2000-08-23
  • 打赏
  • 举报
回复
to guanxuegong:
肯定不是这种问题,LPTSTR 在非 Unicode 的版本中就等于 char *,那只不过是一个宏而已。
guanxuegong 2000-08-23
  • 打赏
  • 举报
回复
试试把c参数设为LPTSTR。
dandycheung 2000-08-23
  • 打赏
  • 举报
回复
to kxy: 我的第二次将问题提出,已经证明我曾经试过fxn(PChar(String('Some chars')));这样的形式或其下面的形式。一样行不通。如果你有兴趣的话,可以自己试一下。顺便说一下,我用的是Delphi 5 + UP1。也多谢kxy。
dandycheung 2000-08-23
  • 打赏
  • 举报
回复
错误是读写非法内存。各位,我已经相信yinfudan的讲解了,也请大家看清楚他的讲解,如果对于一个字符PChar就直接把它的十六进制值转换为指针类型的话,出现读写非法内存几乎是肯定的。问题是如何让delphi把一个字符时的串能够和多个字符时的串同等对待起来。多谢各位捧场。
kxy 2000-08-23
  • 打赏
  • 举报
回复
pascal中'a',可以是char类型,也可能是string
你可以这样
fxn(PChar(String('Some chars')));
如果你先申明一个string的变量,
s:string;
s:='Some chars';
fxn(PChar(s));也可以。

我不同意题外话:)
用C,C++,你更要注意不要让编译器产生歧义
kxy 2000-08-23
  • 打赏
  • 举报
回复
调用失败?,错误信息是什么?
dandycheung 2000-08-23
  • 打赏
  • 举报
回复
to goodman1999:
当然加了,要不然怎么能其他的长度就正常。
~~~
goodman1999 2000-08-23
  • 打赏
  • 举报
回复
在Delphi的函数声明中,最后加stdcall;了没有。

5,392

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧