(Delphi) Вызов DLL с параметром указателя на функцию - PullRequest
4 голосов
/ 30 ноября 2009

Я застрял с вызовом внешней 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 ]

11 голосов
/ 30 ноября 2009

Если вы не уверены, что делаете, всегда начинайте с самого буквального перевода. Прототип функции говорит, что получает указатель на указатель на символ, поэтому вам следует использовать:

type
  PTR_Allocate = procedure(param1: ^^Char; param2: ^LongWord); cdecl;

Как только вы убедитесь, что это правильно, , затем начните заменять вещи на их более Delphi-подобные эквиваленты. Если вы пропустите этот первый шаг, вы, возможно, никогда не получите его правильно, потому что вы просто будете продолжать вносить изменения в то, что начиналось неправильно.

Итак, вы уверены, что вышеприведенное верно? Не совсем. Char в Delphi может иметь разные значения в зависимости от версии продукта. Вы используете 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;

Указатель на AnsiChar является настолько распространенным типом, что Delphi уже имеет имя для него: PAnsiChar. Используйте идиоматическое имя:

type
  PTR_Allocate = procedure(out param1: PAnsiChar; var param2: LongWord); cdecl;

И, наконец, вы, возможно, захотите взять на себя смелость и понять, что персонажи вообще задействованы. Вы четко выделяете память для произвольных байтовых буферов, поэтому байт, вероятно, является лучшим выбором, чем любой тип символов. Последние версии 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 голос
/ 30 ноября 2009

Я предвижу проблему с объявлением 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;
0 голосов
/ 08 февраля 2012

Вам нужно только изменить тип указателя на 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;
0 голосов
/ 30 ноября 2009

Как уже указывалось в шунти, и Allocate, и Deallocate должны быть процедурами в delphi. Кроме того, параметр psize должен быть указателем, подобным состоянию объявления c. И помните, что вы должны реализовать обе функции, чтобы заставить это работать должным образом, или один диспетчер памяти выделит, а другой освободит ту же память, поэтому реализация, показанная в вашем примере, будет продолжать давать сбои, даже если реализация Allocate верна.

...