Dan*_*ler 3 c delphi pascal pointers
我得到以下情况:
我想从我的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)
Run Code Online (Sandbox Code Playgroud)
"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;
Run Code Online (Sandbox Code Playgroud)
C功能调用加载:
@dpstate_callPluginFunction:= GetProcAddress(mConnectorLibrary, 'dpstate_callPluginFunction');
Run Code Online (Sandbox Code Playgroud)
功能调用声明:
function callPluginFunction(plugin, method: PAnsiChar; synchronous :boolean; param, returnParam:array of PAnsiChar): integer;
Run Code Online (Sandbox Code Playgroud)
应该调用函数的函数:
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;
Run Code Online (Sandbox Code Playgroud)
功能:
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;
Run Code Online (Sandbox Code Playgroud)
我的问题是,输出"returnParam"总是包含"Adress xxxxxx越界".我会很高兴快速回答:)
我认为你混合了动态数组和开放数组.哪些措辞相同,但在不同的背景下. 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;
Run Code Online (Sandbox Code Playgroud)
这条线也有问题:
for i:=0 to 6 do
p[i]:= param[i];
Run Code Online (Sandbox Code Playgroud)
要么你确定你的数组总是如此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;
Run Code Online (Sandbox Code Playgroud)
不要忘记参数修饰符const/var/out来记录您的调用契约并使值通过引用传递而不是克隆并传递为值
或者您不知道确切的维度 - 那么您不应该假设6幻数并迭代到传入的数组的实际大小.
for i:=0 to High(param) do
p[i]:= param[i];
Run Code Online (Sandbox Code Playgroud)
然而,这似乎不是你的情况,但是你明确地声明参数"god-only-knows-what-length-array"并将其与硬编码魔法常量一起使用是非常引人注目的.
同样,如果你制作PAscal桥,那么使用PAscal字符串比使用C char指针更好.
function callPluginFunction(const plugin, method: AnsiString;
synchronous :boolean;
.....
Run Code Online (Sandbox Code Playgroud)
追踪什么是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;
Run Code Online (Sandbox Code Playgroud)
在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);
....
Run Code Online (Sandbox Code Playgroud)
PS.最后但同样重要的是,您的连接器内部是否有任何真实的成员变量?我的意思是非静态的,那些在几个连接器实例之间有所不同?如果不是,你可能会创建callPluginFunction一个class function而不是创建连接器类的实例.
param[0]:= 'Param1';
param[1]:= 'Param2';
PConnect.callPluginFunction('dpserverplugin', 'showconfigdialog', true, param, returnParam);
Run Code Online (Sandbox Code Playgroud)
PPS.大卫对合同非常正确.特别是关于"谁分配记忆?" 即使DLL只返回字符串常量,也有一个很好的问题:加载DLL,获取指向字符串的指针,卸载DLL,尝试使用指针 - 访问冲突.因此,虽然上面的桥梁是我认为正确的界面翻译,但由于David指出的问题,它不一定在更大的图片中工作.
购买力平价.数组数据类型声明可能看起来很好,但它克服了我认为Delphi novadays的愚蠢限制:为什么两个别名的"字符串数组"处理不同?
PPPPS.使用out参数是有争议的.例如,David认为,由于Delphi实际上并没有为大多数数据类型实现它们,因此几乎总是应该使用var参数.我个人认为使用out参数有利于记录您的合同,并且有利于与FPC和其他编译器的兼容性.