【问题标题】:Delphi communication with C++ dll (parameters)Delphi 与 C++ dll 通信(参数)
【发布时间】:2016-09-13 13:55:45
【问题描述】:

您好,我在 Delphi 2007 应用程序中集成 DLL 时遇到了很多问题。

我怀疑我的调用参数有问题。 目前我有 2 个问题,但我认为它们是相互关联的。

1) 第一次调用 DLL: 来自 .h 文件:

extern "C" {

__declspec(dllexport) HRESULT Startup(char* version);

}

这个调用应该初始化 DLL 并返回 DLL 的版本。 HRESULT 应为 0,版本指针应包含版本。

我的 Delphi 代码:

function Startup(var version: Pchar): HRESULT; cdecl; stdcall; external 'myDLL.dll';

以及实际调用:

var
  res : HRESULT;
  Name1 : PChar;
  test : AnsiString;
  buf2: array [0..20] of AnsiChar;
begin
  FillChar(buf2,20,0);
  Name1:= @buf2[0];
  res := RdmStartup(Name1);
//Here res = 0, but the Name1 stays empty, and the buf2 still contains 0.
end;

但结果为 0 调用成功。

然后我的第二个问题:我需要在 DLL 中调用一个函数来打开一个 COM 端口。

.h:

extern "C" {
__declspec(dllexport) HRESULT Open(HWND hWnd, int Port, DWORD BaudRate, DWORD Interval); 
}

我的 Delphi 声明:

function Open(hWnd: HWND;Port : integer;BaudRate:LongInt;Interval:LongInt): HRESULT; cdecl; stdcall; external 'myDLL.dll';

我称之为:

res:= Open(self.Handle,5,115200,500);

在这里,我从 res 变量中的 DLL 中返回了失败。 我也有 DLL 的来源,我得到的失败来自 DLL 检查参数是否有效的部分,如果它们有效,它将继续,否则返回我当前得到的错误。

它正在检查的东西:

if(hWnd == NULL)
{
    return false;
}
if(BaudRate != 2400 && BaudRate != 9600 && BaudRate != 38400 && BaudRate != 115200)
{
    return false;
} 
if(IntervalTimer < 300)
{
    return false;
}
std::string strPortName = lexical_cast<std::string>( format("COM%d") % Port);
std::string strPortName(lpPortName.c_str());
std::string::size_type loci = strPortName.find("COM");
if( loci == std::string::npos )
{
    return false;
}
return true;

上述其中之一在我的调用中返回 false,因为如果此函数的结果为 false,则 DLL 会给出我当前在结果中遇到的错误。 有谁知道我做错了什么?

我尝试了多种类型的组合,最后我坚持使用我在以下位置找到的转换:http://www.drbob42.com/delphi/headconv.htm 我也尝试过不同的方式来读取 char 指针,但都失败了.....

所以在这个阶段,我知道我成功地与 DLL 通信,因为我为 2 次调用返回了不同的 HRESULT,但我怀疑我的参数没有正常工作。

我使用的是 Delphi 2007,而 C++ DLL 是用 VS2010 构建的。

【问题讨论】:

  • 你很幸运你把stdcall放在cdecl之后,因为大多数返回HRESULT的函数都是stdcall,在这种情况下,它会否决cdecl。您可以删除cdecl,因为它只是一个与stdcall 不兼容的不同调用约定。
  • @RudyVelthuis 我一直认为如果 DLL 是使用 embarcadero 以外的其他编译器构建的,那么您必须使用 cdecl 的参数不止一个。我想我当时错了
  • 哦,太好了,我也尝试了所有的组合;)如果我没有定义它们中的任何一个,一切都会像现在一样工作。但我会删除 cdecl!
  • cdeclstdcall,对冲你的赌注?

标签: c++ delphi dll parameters


【解决方案1】:

Startup 的声明很可疑:

__declspec(dllexport) HRESULT Startup(char* version);

这翻译成:

function Startup(version: PAnsiChar): HResult; stdcall; external 'myDLL.dll';

所以那里应该没有var

我从您的 cmets 得知 cdecl 调用约定适用于您的某些代码。在这种情况下删除 stdcall,因为它会覆盖前面的cdecl

Open() 的声明似乎还不错(我会使用 DWORD 作为类型,而不是 Longint,尤其是因为 DWORD 现在是 Longword - 但在 Win32 中它们的大小相同,所以这对你来说不会有什么大的不同)。而且您似乎也传递了正确的参数。

你没有写出你得到的HRESULT 值是什么。但我假设端口COM5 根本无法使用这些设置打开。

你能做什么?

您应该从Startup() 中删除var

所以你可以试试:

  • 使用cdecl 代替stdcall(您的声明中的stdcall 推翻了cdecl
  • 用不同的参数打开不同的COM端口
  • 解码返回的HRESULT

如果没有相同的硬件和软件,就无法从远处进行更好的诊断,抱歉。

您可以阅读我的article on conversion。这也有几段解释如何调试代码以找出正确的调用约定。它可能也可以帮助您解决更多转换标题的问题。

【讨论】:

  • 感谢您的信息!我放入 var 是因为我在某处读到需要 var 或 out 才能获得返回值(在我庞大的历史列表中再也找不到它了。)对于 DWORD,我也尝试了这些,相同的结果:( 我要返回的 HRESULT 是来自 DLL 的自定义。它仅在我的 OP 中的语句为假时返回。(因此句柄为空,端口格式​​不正确,波特率不正确或间隔低于300。它甚至还没有尝试打开端口!但我会检查你的文章!
  • 不,头文件没有指定调用约定,所以使用默认值,cdecl
  • 哇 1 个问题刚刚得到解决!版本现已回归!我删除了var,只放入了cdecl!不知道我怎么还没有想出那个组合...... PChar 和 PAnsiChar 在 Delphi2007 中是相同的,所以应该没有区别。
  • 您可以将 var 或 out 与普通变量一起使用,但不能与指向缓冲区的指针一起使用。您应该始终严格转换 C 代码告诉您的内容。 Var 改变了间接的顺序,所以在转换 C 指针时不要使用它,除非你真的很清楚自己在做什么。有疑问,使用指针而不是 var。
  • PChar 和 PAnsiChar 在 D2007 中是相同的,但在任何更高版本中都不相同,因此您应该习惯于指定正确的类型。我假设您的 DLL 没有每个例程的不同 Ansi 和 Wide(后缀 A 和 W)版本?
猜你喜欢
  • 1970-01-01
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 2013-06-22
相关资源
最近更新 更多