我遇到以下情况:
我想从我的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”。 我很乐意快速回答:)
答案 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方。