在Delphi中实现DLL回调

时间:2016-08-05 16:58:45

标签: delphi dll callback delphi-10-seattle

我正在尝试连接软件定义的接收器DLL,需要回调以提供接收的I / Q数据。

我可以调用DLL,但我无法弄清楚如何编写回调代码。

这是主要模块的一部分:

uses
  uReceiverHackRFDLLWrapper;

procedure TForm1.btnIDClick(Sender: TObject);
var
  strptr: PAnsiChar;
begin
  hackrf_board_init();
  strptr := hackrf_board_id_name(0);
  lblName.Caption := 'Name: ' + strptr;
end;

这是DLL的包装器:

unit uReceiverHackRFDLLWrapper;

interface

uses Windows, uSETITypes, sysutils;

{$MINENUMSIZE 4}

type
  // Trtlsdr_read_async_cb_t = procedure(buf: PAnsiChar; len: UINT32; ctx: Pointer);
  // [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  // public unsafe delegate int hackrf_sample_block_cb_fn(hackrf_transfer* ptr); /* Return 0 if OK or -1 if error to stop */
  THackrf_sample__block_cb_fn = function(var hackrf_transfer: Pointer): integer;

function hackrf_board_id_name(index: integer): Pointer; stdcall;
function hackrf_board_init(): integer; stdcall;
function hackrf_open(var dev: THackRF_dev): integer; stdcall;

function hackrf_start_rx(dev: THackRF_dev; cb: THackrf_sample__block_cb_fn; rx_ctx: Pointer): integer;

implementation

var
  DLLLoaded: Boolean = False;
  SaveExit: Pointer;
  DLLHandle: THandle;

function SampleCallBack(var hackrf_transfer: Pointer): integer;
var
  i: integer;
begin
  i := 12345;
end;

function GetModuleFileNameStr(Instance: THandle): string;
var
  buffer: array [0 .. MAX_PATH] of Char;
begin
  GetModuleFileName(Instance, buffer, MAX_PATH);

  Result := extractfilepath(buffer);
end;

function hackrf_board_id_name; external 'libhackrf.dll' name 'hackrf_board_id_name';
function hackrf_board_init; external 'libhackrf.dll' name 'hackrf_init';
function hackrf_open; external 'libhackrf.dll' name 'hackrf_open';

// [DllImport(LibHackRF, EntryPoint = "hackrf_start_rx", CallingConvention = CallingConvention.StdCall)]
// public static extern int hackrf_start_rx(IntPtr dev, hackrf_sample_block_cb_fn cb, IntPtr rx_ctx);
function hackrf_start_rx; external 'libhackrf.dll' name 'hackrf_start_rx';

initialization

DLLHandle := LoadLibrary('libhackrf.dll');

end.

任何人都有一个我可以使用的回调的简单示例吗?

1 个答案:

答案 0 :(得分:4)

基于libHackRF API documentationsource code,包装器单元中存在一些错误。

首先,所有DLL函数都使用cdecl作为Windows上的调用约定, not stdcall

其次,hackrf_start_rx()函数未使用任何调用约定声明,因此它使用Delphi的默认register约定。与THackrf_sample__block_cb_fn相同。您需要在其声明中添加cdecl

第三,对var Pointer hackrf_transfer参数使用THackrf_sample__block_cb_fn与实际hackrf_sample_block_cb_fn回调类型的签名不匹配。正确的回调声明应该更像是这样:

THackrf_sample__block_cb_fn = function(var ptr: hackrf_transfer): integer; cdecl;

hackrf_transfer本身需要声明如下(假设它尚未在uSETITypes单位中声明):

type
  phackrf_device = ^hackrf_device;
  hackrf_device = record
  end;

  ...

  hackrf_transfer = record
    device: phackrf_device;
    buffer: PByte;
    buffer_length: Integer;
    valid_length: Integer;
    rx_ctx: Pointer;
    tx_ctx: Pointer;
  end;

Trtlsdr_read_async_cb_t来自哪里?这不是API的一部分。

现在有了所有这些说法,一个正确的包装单元应该看起来更像这样:

unit uHackRF;

interface

{$MINENUMSIZE 4}

type
  hackrf_error = (
    HACKRF_SUCCESS = 0,
    HACKRF_TRUE = 1,
    HACKRF_ERROR_INVALID_PARAM = -2,
    HACKRF_ERROR_NOT_FOUND = -5,
    HACKRF_ERROR_BUSY = -6,
    HACKRF_ERROR_NO_MEM = -11,
    HACKRF_ERROR_LIBUSB = -1000,
    HACKRF_ERROR_THREAD = -1001,
    HACKRF_ERROR_STREAMING_THREAD_ERR = -1002,
    HACKRF_ERROR_STREAMING_STOPPED = -1003,
    HACKRF_ERROR_STREAMING_EXIT_CALLED = -1004,
    HACKRF_ERROR_OTHER = -9999,
  );

  hackrf_board_id = (
    BOARD_ID_JELLYBEAN  = 0,
    BOARD_ID_JAWBREAKER = 1,
    BOARD_ID_HACKRF_ONE = 2,
    BOARD_ID_INVALID = 0xFF,
  );

  hackrf_usb_board_id = (
    USB_BOARD_ID_JAWBREAKER = 0x604B,
    USB_BOARD_ID_HACKRF_ONE = 0x6089,
    USB_BOARD_ID_RAD1O = 0xCC15,
    USB_BOARD_ID_INVALID = 0xFFFF,
  );

  rf_path_filter = (
    RF_PATH_FILTER_BYPASS = 0,
    RF_PATH_FILTER_LOW_PASS = 1,
    RF_PATH_FILTER_HIGH_PASS = 2,
  );

  transceiver_mode_t = (
    TRANSCEIVER_MODE_OFF = 0,
    TRANSCEIVER_MODE_RX = 1,
    TRANSCEIVER_MODE_TX = 2,
    TRANSCEIVER_MODE_SS = 3,
    TRANSCEIVER_MODE_CPLD_UPDATE = 4
  );

  phackrf_device = ^hackrf_device;
  hackrf_device = record
  end;

  hackrf_transfer = record
    device: phackrf_device;
    buffer: PByte;
    buffer_length: Integer;
    valid_length: Integer;
    rx_ctx: Pointer;
    tx_ctx: Pointer;
  end;

  read_partid_serialno_t = record
    part_id: array[0..1] of UInt32;
    serial_no: array[0..3] of UInt32;
  end;

  hackrf_device_list = record
    serial_numbers: PPAnsiChar;
    usb_board_ids: ^hackrf_usb_board_id;
    usb_device_index: PInteger;
    devicecount: Integer;

    usb_devices: PPointer;
    usb_devicecount: Integer;
  end;

  hackrf_device_list_t = hackrf_device_list;
  phackrf_device_list_t = ^hackrf_device_list_t;

  hackrf_sample_block_cb_fn = function(var transfer: hackrf_transfer): Integer; cdecl;

function hackrf_init: Integer; cdecl;
function hackrf_exit: Integer; cdecl;

function hackrf_device_list: phackrf_device_list_t; cdecl;
function hackrf_device_list_open(list: phackrf_device_list_t; idx: Integer; var device: phackrf_device): Integer; cdecl;
procedure hackrf_device_list_free(list: phackrf_device_list_t); cdecl;

function hackrf_open(var device: phackrf_device): Integer; cdecl;
function hackrf_open_by_serial(const desired_serial_number: PAnsiChar; var device: phackrf_device): Integer; cdecl;;
function hackrf_close(device: phackrf_device): Integer; cdecl;

function hackrf_start_rx(device: phackrf_device; callback: hackrf_sample_block_cb_fn; rx_ctx: Pointer): Integer; cdecl;
function hackrf_stop_rx(device: phackrf_device): Integer; cdecl;

function hackrf_start_tx(device: phackrf_device; callback: hackrf_sample_block_cb_fn; tx_ctx: Pointer): Integer; cdecl;
function hackrf_stop_tx(device: phackrf_device): Integer; cdecl;

{ return HACKRF_TRUE if success }
function hackrf_is_streaming(device: phackrf_device): Integer; cdecl;

function hackrf_max2837_read(device: phackrf_device; register_number: UInt8; var value: UInt16): Integer; cdecl;
function hackrf_max2837_write(device: phackrf_device; register_number: UInt8; value: UInt16): Integer; cdecl;

function hackrf_si5351c_read(device: phackrf_device; register_number: UInt16; var value: UInt16): Integer; cdecl;
function hackrf_si5351c_write(device: phackrf_device; register_number: UInt16; value: UInt16): Integer; cdecl;

function hackrf_set_baseband_filter_bandwidth(device: phackrf_device; const bandwidth_hz: UInt32): Integer; cdecl;

function hackrf_rffc5071_read(device: phackrf_device; register_number: UInt8; var value: UInt16): Integer; cdecl;
function hackrf_rffc5071_write(device: phackrf_device; register_number: UInt8; value: UInt16): Integer; cdecl;

function hackrf_spiflash_erase(device: phackrf_device): Integer; cdecl;
function hackrf_spiflash_write(device: phackrf_device; const address: UInt32; const length: UInt16; const data: PByte): Integer; cdecl;
function hackrf_spiflash_read(device: phackrf_device; const address: UInt32; const length: UInt16; data: PByte): Integer; cdecl;

{ device will need to be reset after hackrf_cpld_write }
function hackrf_cpld_write(device: phackrf_device; const data: PByte; const total_length: UInt32): Integer; cdecl;

function hackrf_board_id_read(device: phackrf_device; var value: UInt8): Integer; cdecl;
function hackrf_version_string_read(device: phackrf_device; version: PAnsiChar; length: UInt8): Integer; cdecl;

function hackrf_set_freq(device: phackrf_device; const freq_hz: UInt64): Integer; cdecl;
function hackrf_set_freq_explicit(device: phackrf_device; const if_freq_hz, lo_freq_hz: UInt64; const path: rf_path_filter): Integer; cdecl;

{ currently 8-20Mhz - either as a fraction, i.e. freq 20000000hz divider 2 -> 10Mhz or as plain old 10000000hz (double)
    preferred rates are 8, 10, 12.5, 16, 20Mhz due to less jitter }
function hackrf_set_sample_rate_manual(device: phackrf_device; const freq_hz, divider: UInt32): Integer; cdecl;
function hackrf_set_sample_rate(device: phackrf_device; const freq_hz: Double): Integer; cdecl;

{ external amp, bool on/off }
function hackrf_set_amp_enable(device: phackrf_device; const value: UInt8): Integer; cdecl;

function hackrf_board_partid_serialno_read(device: phackrf_device; var read_partid_serialno: read_partid_serialno_t): Integer; cdecl;

{ range 0-40 step 8d, IF gain in osmosdr  }
function hackrf_set_lna_gain(device: phackrf_device; value: UInt32): Integer; cdecl;

{ range 0-62 step 2db, BB gain in osmosdr }
function hackrf_set_vga_gain(device: phackrf_device; value: UInt32): Integer; cdecl;

{ range 0-47 step 1db }
function hackrf_set_txvga_gain(device: phackrf_device; value: UInt32): Integer; cdecl;

{ antenna port power control }
function hackrf_set_antenna_enable(device: phackrf_device; const value: UInt8): Integer; cdecl;

function hackrf_error_name(errcode: hackrf_error): PAnsiChar; cdecl;
function hackrf_board_id_name(board_id: hackrf_board_id): PAnsiChar; cdecl;
function hackrf_usb_board_id_name(usb_board_id: hackrf_usb_board_id): PAnsiChar; cdecl;
function hackrf_filter_path_name(const path: rf_path_filter): PAnsiChar; cdecl;

{ Compute nearest freq for bw filter (manual filter) }
function hackrf_compute_baseband_filter_bw_round_down_lt(const bandwidth_hz: UInt32): UInt32; cdecl;
{ Compute best default value depending on sample rate (auto filter) }
function hackrf_compute_baseband_filter_bw(const bandwidth_hz: UInt32): UInt32;

implementation

const
  LibHackRF = 'libhackrf.dll';

function hackrf_init; external LibHackRF name 'hackrf_init';
function hackrf_exit; external LibHackRF name 'hackrf_exit';

function hackrf_device_list; external LibHackRF name 'hackrf_device_list';
function hackrf_device_list_open; external LibHackRF name 'hackrf_device_list_open';
procedure hackrf_device_list_free; external LibHackRF name 'hackrf_device_list_free';

function hackrf_open; external LibHackRF name 'hackrf_open';
function hackrf_open_by_serial; external LibHackRF name 'hackrf_open_by_serial';
function hackrf_close; external LibHackRF name 'hackrf_close';

function hackrf_start_rx; external LibHackRF name 'hackrf_start_rx';
function hackrf_stop_rx; external LibHackRF name 'hackrf_stop_rx';

function hackrf_start_tx; external LibHackRF name 'hackrf_start_tx';
function hackrf_stop_tx; external LibHackRF name 'hackrf_stop_tx';

function hackrf_is_streaming; external LibHackRF name 'hackrf_is_streaming';

function hackrf_max2837_read; external LibHackRF name 'hackrf_max2837_read';
function hackrf_max2837_write; external LibHackRF name 'hackrf_max2837_write';

function hackrf_si5351c_read; external LibHackRF name 'hackrf_si5351c_read';
function hackrf_si5351c_write; external LibHackRF name 'hackrf_si5351c_write';

function hackrf_set_baseband_filter_bandwidth; external LibHackRF name 'hackrf_set_baseband_filter_bandwidth';

function hackrf_rffc5071_read; external LibHackRF name 'hackrf_rffc5071_read';
function hackrf_rffc5071_write; external LibHackRF name 'hackrf_rffc5071_write';

function hackrf_spiflash_erase; external LibHackRF name 'hackrf_spiflash_erase';
function hackrf_spiflash_write; external LibHackRF name 'hackrf_spiflash_write';
function hackrf_spiflash_read; external LibHackRF name 'hackrf_spiflash_read';

function hackrf_cpld_write; external LibHackRF name 'hackrf_cpld_write';

function hackrf_board_id_read; external LibHackRF name 'hackrf_board_id_read';
function hackrf_version_string_read; external LibHackRF name 'hackrf_version_string_read';

function hackrf_set_freq; external LibHackRF name 'hackrf_set_freq';
function hackrf_set_freq_explicit; external LibHackRF name 'hackrf_set_freq_explicit';

function hackrf_set_sample_rate_manual; external LibHackRF name 'hackrf_set_sample_rate_manual';
function hackrf_set_sample_rate; external LibHackRF name 'hackrf_set_sample_rate';

function hackrf_set_amp_enable; external LibHackRF name 'hackrf_set_amp_enable';

function hackrf_board_partid_serialno_read; external LibHackRF name 'hackrf_board_partid_serialno_read';

function hackrf_set_lna_gain; external LibHackRF name 'hackrf_set_lna_gain';

function hackrf_set_vga_gain; external LibHackRF name 'hackrf_set_vga_gain';

function hackrf_set_txvga_gain; external LibHackRF name 'hackrf_set_txvga_gain';

function hackrf_set_antenna_enable; external LibHackRF name 'hackrf_set_antenna_enable';

function hackrf_error_name; external LibHackRF name 'hackrf_error_name';
function hackrf_board_id_name; external LibHackRF name 'hackrf_board_id_name';
function hackrf_usb_board_id_name; external LibHackRF name 'hackrf_usb_board_id_name';
function hackrf_filter_path_name; external LibHackRF name 'hackrf_filter_path_name';

function hackrf_compute_baseband_filter_bw_round_down_lt; external LibHackRF name 'hackrf_compute_baseband_filter_bw_round_down_lt';
function hackrf_compute_baseband_filter_bw; external LibHackRF name 'hackrf_compute_baseband_filter_bw';

end.

现在,你可以写一个回调:

uses
  uHackRF;

var
  device: phackrf_device = nil;

type
  ELibHackRFError = class(Exception)
  public
    ErrorCode: Integer;
    constructor CreateError(Err: Integer);
  end;

  constructor ELibHackRFError.CreateError(Err: Integer);
  begin
    inherited CreateFmt('LibHackRF Error %d', [Err]);
    ErrorCode := Err;
  end;

function HackRFCheck(Res: Integer);
begin
  if Res < 0 then
    raise ELibHackRFError.CreateError(Res);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  HackRFCheck(hackrf_init());
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if device <> nil then
    hackrf_close(device);
  hackrf_exit();
end;

procedure TForm1.btnIDClick(Sender: TObject);
var
  strptr: PAnsiChar;
begin
  strptr := hackrf_board_id_name(BOARD_ID_JELLYBEAN);
  lblName.Caption := 'Name: ' + StrPas(strptr);
end;

function RxCallback(var transfer: hackrf_transfer): Integer; cdecl;
begin
  // use transfer members as needed...
  // transfer.rx_ctx is a pointer to the TForm1 object...
  //...
  Result := HACKRF_SUCCESS;
end;

procedure TForm1.btnOpenClick(Sender: TObject);
begin
  if device = nil then
    HackRFCheck(hackrf_open(device)); // or hackrf_device_list_open() or hackrf_open_by_serial()
end;

procedure TForm1.btnCloseClick(Sender: TObject);
begin
  if device <> nil then
  begin
    HackRFCheck(hackrf_close(device));
    device = nil;
  end;
end;

procedure TForm1.btnStartRxClick(Sender: TObject);
begin
  if device <> nil then
    HackRFCheck(hackrf_start_rx(device, RxCallback, Self));
end;

procedure TForm1.btnStopRxClick(Sender: TObject);
begin
  if device <> nil then
    HackRFCheck(hackrf_stop_rx(device));
end;