我坚持调用外部DLL并将函数(指针)作为参数传递。 我最近有一些不同的问题,传递一些参数到DLL,你帮助。 希望,有人知道如何做到这一点....
这是DLL(cpp)中需要从Delphi调用的函数声明:
typedef void (*PTR_Allocate)(char**, unsigned long*);
typedef void (*PTR_Deallocate)(char*);
extern "C" export_dll_function void SetAllocateFunction(PTR_Allocate);
extern "C" export_dll_function void SetDeallocateFunction(PTR_Deallocate);
void Allocate(char** pbuffer, unsigned long* psize)
{
*psize = *psize * 2;
*pbuffer = new char[*psize];
}
void Deallocate(char* buffer)
{
delete[] buffer;
}
请你能帮助我在Delphi(7)中重写这个吗?
这是我尝试过的,它会引发异常(“外部异常”):
type
PByte = ^TByte;
TByte = array of byte;
TFunc = function(var pbuffer: PByte; var psize: Cardinal): integer; cdecl;
Procedure _SetAllocateFunction(var f: TFunc); cdecl;
implementation
function Allocate(var pbuffer: PByte; var psize: Cardinal): Integer; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;
var Func: TFunc;
Func := @Allocate;
_SetAllocateFunction(Func);
非常感谢!
答案 0 :(得分:11)
如果您不确定自己在做什么,请始终使用最文字翻译。函数原型说它接收一个指向char的指针,所以你应该使用它:
type
PTR_Allocate = procedure(param1: ^^Char; param2: ^LongWord); cdecl;
一旦你确定它是正确的,那么然后就会开始用类似Delphi的东西替换它们。如果你跳过这第一步,你可能永远无法做到正确,因为你只会继续改变那些错误的东西。
那么,你确定以上是对的吗?不完全的。 Delphi中的Char
可能具有不同的含义,具体取决于产品版本。您正在使用Delphi 7,但您可能会升级,因此您可能会与其他人共享此代码,因此您应该明确您想要的Char大小。需要单字节类型时使用AnsiChar。
type
PTR_Allocate = procedure(param1: ^^AnsiChar; param2: ^LongWord); cdecl;
现在我们可以开始让它看起来更像Delphi。一级指针参数可以用“var”或“out”指令替换。对每个参数执行此操作:
type
PTR_Allocate = procedure(out param1: ^AnsiChar; var param2: LongWord); cdecl;
Pointer-to-AnsiChar是一种常见的类型,Delphi已经为它命名了:PAnsiChar。使用惯用名:
type
PTR_Allocate = procedure(out param1: PAnsiChar; var param2: LongWord); cdecl;
最后,你可能希望对整个概念采取一些自由,即根本涉及到角色。你明确地为任意字节缓冲区分配内存,所以Byte可能是比任何字符类型更好的选择。最近的Delphi版本声明了一个指向字节的指针类型,因此请使用:
type
PTR_Allocate = procedure(out param1: PByte; var param2: LongWord); cdecl;
现在开始SetAllocateFunction
。它表示它接收PTR_Allocate
参数,该参数是指向函数的指针。 Delphi的过程类型是隐式指针,因此我们上面声明的类型已经完全适合Delphi等价。不要通过引用传递额外的“var”指令,否则即使在程序尝试分配任何内存之前,您也会遇到问题。这是其他答案忽略的内容。
procedure SetAllocateFunction(param: PTR_Allocate); cdecl;
不要在名称的开头添加下划线,除非您想要使调用自己的代码变得不方便。如果它是使用不同的名称从DLL导出的,那么在编写函数的实现时使用“name”子句:
procedure SetAllocateFunction; extern 'foo.dll' name '_SetAllocateFunction';
最后,如何实现分配功能。从与PTR_Allocate的签名匹配的东西开始,然后继续使用原始C ++代码中的尽可能翻译来实现它。
procedure Allocate(out pbuffer: PByte; var psize: LongWord; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;
您可以使用之前的函数进行设置:
SetAllocateFunction(Allocate);
注意我不需要单独的变量,我没有使用@
运算符。如果你需要使用@
运算符来提及函数指针,那么在大多数情况下,你做错了。你通常不需要它。使用该运算符可以隐藏程序中的错误,例如签名不匹配,因为默认设置是@
运算符无类型。使用它从函数指针中删除类型,并且无类型指针与Delphi中的所有内容兼容,因此它们适用于任何其他函数指针类型,包括具有错误签名的类型。
当编译器已经指示它已经尝试调用函数时,只在函数指针上使用@
,例如通过提及你没有足够的参数或者通过提到函数的返回类型。
答案 1 :(得分:1)
我预见到TByte作为动态数组类型的声明存在问题。动态数组本身就是一个指针。
将其声明为数组
type
PByte = ^TByte;
TByte = Array[0..1.) of Byte;
或删除PByte声明
type
TByte = Array of Byte;
TFunc = function(var buffer:TByte; var pSize: Cardinal): Integer; cdecl
function Allocate(var buffer:TByte; Var pSize: Cardinal): Integer; cdecl;
begin
SetLength(buffer,pSize*2);
end;
答案 2 :(得分:0)
正如shunty已经指出的那样,Allocate和Deallocate都应该是delphi中的程序。此外,psize参数应该是类似c声明状态的指针。 并且请记住,您必须实现这两个函数才能使其正常工作,或者一个内存管理器将分配而另一个将释放相同的内存,因此即使Allocate的实现正确,示例中显示的实现仍会失败。
答案 3 :(得分:0)
您只需要将指针类型更改为PByte = ^ byte;
type
PByte = ^byte;
TByte = array of byte;
TFunc = function(var pbuffer: PByte; var psize: Cardinal): integer; cdecl;
Procedure _SetAllocateFunction(var f: TFunc); cdecl;
implementation
function Allocate(var pbuffer: PByte; var psize: Cardinal): Integer; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;