(Delphi)使用函数指针参数调用DLL

时间:2009-11-30 11:10:31

标签: delphi dll function pointers

我坚持调用外部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);   

非常感谢!

4 个答案:

答案 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;