在Delphi中实现一个C语言动态链接库

3

我正在努力实现一个来自C动态链接库的函数。它的声明如下:

int DetectTransactionCode(wchar_t* wi_type, wchar_t* wi_id);

如果我在Delphi代码中声明并调用了以下内容:
function DetectTransactionCode (var wi_type, wi_id: PWideChar): Integer;
     cdecl; external 'WiGroupDetect.dll';

procedure TForm20.Button2Click(Sender: TObject);
var witype,wi_id : widestring;
res : integer;
begin
 res := DetectTransactionCode(PWideChar(witype),PWideChar(wi_id));
 showmessage(res.tostring);
 ShowMessage(witype +' & ' +wi_id);
end;

我收到了结果,但是我的witype和wi_id导致了访问冲突。

我也尝试过:

Function  DetectTransactionCode (var witype,wi_id :widestring ) : Integer cdecl;
                  external 'WiGroupDetect.dll';

procedure TForm20.Button2Click(Sender: TObject);
var witype,wi_id : widestring;
 res : integer;
begin
 res := DetectTransactionCode(witype,wi_id);
 showmessage(res.tostring);
 ShowMessage(witype +' & ' +wi_id);
end;

我假设这两个参数都是输出参数。第三方提供了以下内容:

成功返回1,取消/失败返回0

注意: 阻塞调用仅在以下情况下返回: 1- 检测到wiCode, 2- 调用KillDetectionProcess(), 3- 发生某些错误或失败, 或者 4- 最终超时(30分钟)。

参数: wi_type 在成功时返回检索到的标记类型(例如“WIQR”表示QR); 在取消/失败时返回“NONE”。 wi_id 成功时返回检测到的wiCode; 取消时返回“CANCELLED”; 失败时返回附加的错误信息(例如“ERROR: …”)。

我尝试将参数更改为ansistring、unicodestring,但仍然遇到相同的问题。我怀疑这与var参数有关,但不确定如何克服。如果您能提供帮助,将不胜感激。

他们已经给了我一个C示例实现。

[DllImport("WiGroupDetect.dll", EntryPoint = "DetectTransactionCode", CallingConvention =             CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern int DetectTransactionCode([MarshalAsAttribute(UnmanagedType.LPWStr)]  StringBuilder strWITYPE, [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder strUID);

不确定这是否会改变我的 Delphi 实现。


3
在C/C++中默认的调用约定是cdecl - LU RD
2个回答

2
两个参数的类型为wchar_t*,它是指向16位字符的指针。通常情况下,这意味着指向以宽UTF-16字符结尾的空字符数组的指针。因此,代码的正确翻译如下:
function DetectTransactionCode (wi_type, wi_id: PWideChar): Integer;
    cdecl; external 'WiGroupDetect.dll';

你使用了类型为WideStringvar参数,这将匹配指向BSTR的指针参数,两者差别很大。
我假设使用的是cdecl调用约定,这是我遇到的每个C编译器的默认值。如果你有一些额外的信息表明函数是stdcall,那就这样吧。但是根据问题描述,它是cdecl
数据流动不太清楚。这两个字符串参数是输入还是输出?一个是输入,另一个是输出吗?如果其中任何一个是输出参数,则需要某种方式知道要分配多大的缓冲区。函数不允许你传递缓冲区长度的事实表明数据是输入的。即使如此,在C代码中,参数也应该是const wchar_t*。这里存在很多不确定性,请注意函数原型并不能完全定义函数的语义。

感谢您的快速回复。我已经从第三方添加了一些额外的细节。根据他们的信息,我假设这两个参数都是输出参数,但我不确定这会如何改变Delphi函数声明。 - Oliver
1
参数语义不会改变声明,而是改变了你调用函数的方式。如果数据流出,则需要分配字符数组,然后由函数填充。这些数组必须有多长?我们无法回答。 - David Heffernan

0

感谢David的回答,它们非常有帮助。我使用了这段代码成功解决了问题。我知道如果传回的字符串超过了我提供的长度,那么就会出错。

function DetectTransactionCode (wi_type, wi_id: pwidechar): Integer;
stdcall; external 'WiGroupDetect.dll';

procedure TForm20.Button2Click(Sender: TObject);
var witype,wi_id :  widestring;
 res : integer;
begin
 setlength(witype,10);
 setlength(wi_id,100);
 res := DetectTransactionCode(pwidechar(witype),pwidechar(wi_id));
 showmessage(res.tostring);
 setlength(witype,pos(#0,witype)-1);
 setlength(wi_id,pos(#0,wi_id)-1);
 ShowMessage(trim(witype) +' & ' +trim(wi_id));
end;

2
你应该停止使用 WideString。那是为了COM互操作而设计的。使用 string,它是 UnicodeString 的别名。我个人会使用固定长度数组来进行函数调用,然后将它们赋值给字符串变量。这样可以避免调用 SetLengthPos - David Heffernan
2
实际上,var witype, wi_id: array[0..适当的长度 - 1] of WideChar; 是更好的选择。如果需要,它们可以直接赋值给一个字符串。而且,Oliver 可以使用 Setlength(witype, StrLen(witype)) 或者更好的 s := witype; 来代替 SetLength( witype, Pos(#0, witype) - 1)。如果 witype 是一个 array[...] of Char,那么甚至可以直接在 Trim() 中使用它。所需的转换将会被隐式地完成。 - Rudy Velthuis
谢谢,我已经将函数更改为使用WideChar的数组[0..100],并使用字符串代替widestring。非常感谢您的帮助。 - Oliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接