如何在Pascal / Delphi中调用以下C函数?

时间:2013-02-20 09:28:31

标签: c delphi pointers pascal

我遇到以下情况:

我想从我的Pascal程序中调用C函数。 C函数应该用值填充传递的指针。

这是C函数:

DLLEXPORT int dpstate_callPluginFunction(const char* plugin, const char* function, bool synchronous, const char* p0, const char* p1, const char* p2, const char* p3, const char* p4, const char* p5, const char* p6, char** o0, char** o1, char** o2, char** o3, char** o4, char** o5, char** o6)

“p”参数是输入参数,“o”参数是输出参数。我试图在我的Pascal程序中调用函数,如下所示:

C Functioncall声明:

var dpstate_callPluginFunction: function(plugin, method: PAnsiChar; synchronous: boolean; p0, p1, p2, p3, p4, p5, p6: PAnsiChar; o0, o1, o2, o3, o4, o5, o7: PPAnsiChar): integer; cdecl;

C功能调用加载:

@dpstate_callPluginFunction:= GetProcAddress(mConnectorLibrary, 'dpstate_callPluginFunction');                

功能调用声明:

function callPluginFunction(plugin, method: PAnsiChar; synchronous :boolean; param, returnParam:array of PAnsiChar): integer;

应该调用函数的函数:

procedure TForm1.btn_pluginFunctionClick(Sender: TObject);
var param, returnParam: array of PAnsiChar;
begin
SetLength(param, 7);
SetLength(returnParam, 7);
param[0]:= 'Param1';
param[1]:= 'Param2';
connector.callPluginFunction('dpserverplugin', 'showconfigdialog', true, param, returnParam);

output.Append(returnParam[0]);
output.Append(returnParam[1]);
end; 

功能:

function PConnect.callPluginFunction(plugin, method: PAnsiChar; synchronous :boolean; param, returnParam:array of PAnsiChar): integer;
  var i, error: integer;
  var p: array[0..6] of PAnsiChar;
  var o: array[0..6] of PPAnsiChar;
begin
  for i:=0 to 6 do
            p[i]:= param[i]; 
  dpstate_callPluginFunction(plugin, method, synchronous, p[0], p[1], p[2], p[3], p[4], p[5], p[6], @o[0], @o[1], @o[2], @o[3], @o[4], @o[5], @o[6]);

  for i:=0 to 6 do
            if o[i] <> Nil then
                returnParam[i]:= o[i]^; 
end;

我现在的问题是,输出“returnParam”总是包含“Adress xxxxxx out of bounds”。 我很乐意快速回答:)

2 个答案:

答案 0 :(得分:9)

我认为你混合了动态数组和开放数组。哪些措辞相同,但在不同的背景下。 http://rvelthuis.de/articles/articles-openarr.html

试试这个:

type TPAnsiCharDynArray = array of PAnsiChar;

function callPluginFunction(plugin, method: PAnsiChar; 
         synchronous :boolean; param: array of PAnsiChar;
     out returnParam: TPAnsiCharDynAttay): integer;

这一行也存在问题:

for i:=0 to 6 do
         p[i]:= param[i]; 

要么你确定你的数组总是0..6 - 那么使用动态数组是没有意义的。

type TDLLVectorIndex = 0..6; 
     TDLLPCharArray = array [TDLLVectorIndex] of PAnsiChar;

function callPluginFunction(const plugin, method: PAnsiChar; 
         synchronous :boolean; 
         const param: TDLLPCharArray;
         out returnParam: TDLLPCharArray): integer;

不要忘记参数修饰符const/var/out来记录您的调用合约并使值通过引用传递而不是克隆并作为值传递

或者您不知道确切的维度 - 那么您不应该假设6幻数并迭代到传入的数组的实际大小。

for i:=0 to High(param) do
        p[i]:= param[i]; 

然而,这似乎不是你的情况,但是你明确地声明参数“god-only-knows-what-length-array”并将其与硬编码魔法常数一起使用时非常引人注目。


同样,如果你制作PAscal桥,那么使用PAscal字符串比使用C char指针更好。

function callPluginFunction(const plugin, method: AnsiString; 
         synchronous :boolean; 
         .....

追踪C bool类型。它真的是单字节布尔值吗?或者是一些Windows bool,可能需要1,2或甚至4个字节?它有时也有轻微的二进制不兼容性,实际上是true,+ 1或-1。

但如果没有bool不确定性,你在应用参数限定符之后的声明会更好看。

type fn_dpstate_callPluginFunction = 
   function(const plugin, method: PAnsiChar; synchronous: boolean;
   const  p0, p1, p2, p3, p4, p5, p6: PAnsiChar; 
   var o0, o1, o2, o3, o4, o5, o7: PAnsiChar): integer; cdecl;
var dpstate_callPluginFunction: fn_dpstate_callPluginFunction;

在C ++中传递参数by-ref不受欢迎,而在C中它可能不存在。但是平均Pascal代码传递参数by-ref被认为比传递指针更常见,更安全。


这将重新编码:

type TDLLVectorIndex = 0..6; 
     TDLLPCharArray = array [TDLLVectorIndex] of PAnsiChar;

function PConnect.callPluginFunction(const plugin, method: AnsiString; 
         synchronous :boolean; 
         const p: TDLLPCharArray;
         out o: TDLLPCharArray): integer;

  var Error: integer;
begin
  Error :=
    dpstate_callPluginFunction( PAnsiChar(plugin), PAnsiChar(method), synchronous, 
        p[0], p[1], p[2], p[3], p[4], p[5], p[6],
        @o[0], @o[1], @o[2], @o[3], @o[4], @o[5], @o[6]);
  Result := Error + 10;
// or something like that - you did had the reason
// to declare the var and declare function return type afterall
end;

procedure TForm1.btn_pluginFunctionClick(Sender: TObject);
var param, returnParam: TDLLPCharArray;
begin
  FillChar(param, 0, SizeOf(TDLLPCharArray)); // maybe redundant, but to be on safe side
  param[0]:= 'Param1';
  param[1]:= 'Param2';
  connector.callPluginFunction('dpserverplugin', 'showconfigdialog', true, param, returnParam);
....

PS。最后但同样重要的是,您的连接器内部是否有任何真实的成员变量?我指的是非静态的,那些在几个连接器实例之间有所不同?如果不是,您可能会callPluginFunction成为class function而根本不创建连接器类的实例。

  param[0]:= 'Param1';
  param[1]:= 'Param2';
  PConnect.callPluginFunction('dpserverplugin', 'showconfigdialog', true, param, returnParam);

PPS。大卫对合同非常正确。特别是关于“谁分配记忆?”即使DLL只返回字符串常量,也有一个很好的问题:加载DLL,获取指向字符串的指针,卸载DLL,尝试使用指针 - 访问冲突。因此,虽然上面的桥梁是我认为正确的界面翻译,但由于大卫指出的问题,它不一定在更大的图片中工作。

PPPS。数组数据类型声明可能看起来很好看,但它克服了我认为的Delphi novadays的愚蠢限制:why two aliases to "array of string" treated differently?

PPPPS。使用out参数是有争议的。例如,David认为,由于Delphi实际上并没有为大多数数据类型实现它们,因此几乎总是使用var参数。我个人认为使用out参数有助于记录您的合同,并且有利于与FPC和其他编译器兼容。

答案 1 :(得分:2)

另一个小问题是“布尔”类型。 Pascal布尔值通常假设false = 0 true = 1并且未定义,非常类似于GTK gbooleans。

C布尔值通常假设0 =假,其他任何东西=真。

最近的免费帕斯卡有两种不同尺寸的完整套装。布局8..boolean64用于帕斯卡方面,bytebool,wordbool,longbool等用于C方。