Помогите с подключением к DLL

zeon11
Дата: 29.07.2019 19:10:52
Добрый день!

Пытаюсь подключиться к libpioneer3.dll, - DLL для работы с фискальным аппаратом Пионер 114ф.

В описании разработчик приводит пример инициализации сессии работы с ККТ по IP адресу на C++:

int pioneer3_open (const char ∗host , uint16 timeout , const char∗ &response ) 


Параметры:
host IP-адрес ККТ
timeout таймаут ожидания ответа
response строка с результатом выполнения
Возвращает:
отрицательное число код ошибки
положительное число идентификатор сессии работы с ККТ, в дальнейшем
используется во всех командах.

Я попробовал перевести на Delphi:
unit Un_Pioner;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    EditAddress: TEdit;
  
   
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
 
  private
    { Private declarations }
    Procedure ShowDLLError(IDSession: Integer);
  public
    { Public declarations }
  end;

type
  Tpioneer3_version           = procedure(var Amajor: Integer; var Aminor: Integer; var Amicro: Integer; var Abuild: Integer); stdcall;
  Tpioneer3_open              = function(AIp_Addr: PChar;     ATimeOut: Integer; var AResponce: PChar):Integer; stdcall;
 ..................................................

var
  Form1: TForm1;
  dll_handle:        THandle;
  IDSession : integer;

  pioneer3_version           : Tpioneer3_version          ;
  pioneer3_open              : Tpioneer3_open             ;
 
......................................

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var Responce: PChar; IPAddress: PChar;
begin            
  IPAddress:=PChar(EditAddress.Text);  //  '192.168.0.150'
  if Assigned(pioneer3_open)
       then begin
              IDSession:=pioneer3_open(IPAddress, 5000, Responce);
               // Здесь возвращает IDSession = -2
              if IDSession<0 then ShowDLLError(IDSession);
              ShowMessage(Responce);
            end;
end;

procedure TForm1.FormShow(Sender: TObject);
  var Amajor, Aminor, Amicro, Abuild : Integer;
begin
  dll_handle := LoadLibrary('libpioneer3.dll');

  if (dll_handle <= 0) then ShowMessage('LoadLibrary: libpioneer3.dll not found.')
    else
      begin
         @pioneer3_version           := GetProcAddress(dll_handle, 'pioneer3_version');
         @pioneer3_open              := GetProcAddress(dll_handle, 'pioneer3_open');
..................................................................

         if Assigned(pioneer3_version)
              then begin
                     pioneer3_version(Amajor, Aminor, Amicro, Abuild);
                     Label1.Caption:='Версия библиотеки Пионер: ' + IntToStr(Amajor)+'.'+IntToStr(Aminor)+'.'+IntToStr(Amicro)+'.'+IntToStr(Abuild);
                   end;
       end;
end;

procedure TForm1.ShowDLLError(IDSession: Integer);
var Mess: String;
begin
  case IDSession of
   -1: Mess:='Неправильный handle';
   -2: Mess:='Ошибка подключения к ККТ';
   -3: Mess:='Ошибка передачи данных в ККТ';
   -4: Mess:='Таймаут получения ответа из ККТ';
   -5: Mess:='Ошибка в размере пакета данных';
   -6: Mess:='Неправильная контрольная сумма пакета данных';
   -7: Mess:='Неправильный заголовок пакета данных';
   -8: Mess:='Ошибка в параметрах команды (при разборе JSON)';
    else Mess:='Не определённая производителем ККМ ошибка';
   end;
  showmessage(Mess+'/код возврата из функции библиотеки:'+IntToStr(IDSession));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeLibrary(dll_handle);
end;


end.


При запуске программы подключение к DLL проходит, о чём свидетельствует получение версии DLL,
однако попытка инициализации работы с ККТ по IP адресу дает "Ошибка подключения к ККТ" (-2).
Пинг аппарата по IP проходит.
Похоже, я напортачил с передачей параметров в функцию pioneer3_open, но разобраться где - не хватает опыта.

Помогите ПОЖ.!
Dimitry Sibiryakov
Дата: 29.07.2019 19:24:38

Во-первых, в описании на С++ ничего не сказано о stdcall, следовательно это cdecl.
Во-вторых, параметр timeout - 16-ти разрядный, а твой Integer явно шире.

Posted via ActualForum NNTP Server 1.5

zeon11
Дата: 29.07.2019 20:12:00
Dimitry Sibiryakov
Во-первых, в описании на С++ ничего не сказано о stdcall, следовательно это cdecl.
Во-вторых, параметр timeout - 16-ти разрядный, а твой Integer явно шире.



Да, в документации нашел:

Все команды имеют спецификацию вызова вида:
extern ”C” __declspec ( dllimport ) _cdecl
В дальнейшей документации при описании формата команд эта спецификация
опущена.


Переписал
type
  Tpioneer3_version  = procedure(var Amajor: Integer; var Aminor: Integer; var Amicro: Integer; var Abuild: Integer); cdecl;
  Tpioneer3_open     = function(const AIp_Addr: PChar; ATimeOut: UInt16;  var AResponce: PChar):Integer; cdecl;


Результат, увы, тот-же. Первая процедура выполняется, функция - нет.
X-Cite
Дата: 29.07.2019 20:38:37
3 параметр строка. А память под нее кто выделять должен?
ziv-2014
Дата: 29.07.2019 20:51:03
const char * = в делфи PAnsiChar
ziv-2014
Дата: 29.07.2019 20:52:16
Tpioneer3_open     = function(const AIp_Addr: PAnsiChar; ATimeOut: UInt16;  var AResponce: PAnsiChar):Integer; cdecl;
Dimitry Sibiryakov
Дата: 29.07.2019 21:10:20

zeon11
Tpioneer3_open = function(const AIp_Addr: PChar;

Вот этот const ты совершенно зря сюда впихнул. И да, если у тебя Delphi юникодная, то оно
должно быть AnsiChar всюду.

Posted via ActualForum NNTP Server 1.5

zeon11
Дата: 30.07.2019 04:26:10
Так заработало:

  Tpioneer3_open  = function(const AIp_Addr: PAnsiChar; ATimeOut: UInt16;  var AResponce: PAnsiChar):Integer; cdecl;
,

так тоже работает:
  Tpioneer3_open  = function(AIp_Addr: PAnsiChar; ATimeOut: UInt16;  var AResponce: PAnsiChar):Integer; cdecl;


Вызов функции:
procedure TForm1.Button1Click(Sender: TObject);
var Responce: PAnsiChar; IPAddress: PAnsiChar;
begin
  IPAddress:='192.168.0.150';
  if Assigned(pioneer3_open)
       then begin
              IDSession:=pioneer3_open(IPAddress, 5000, Responce);
              if IDSession<0 then ShowDLLError(IDSession);
              showMessage(Responce);
            end;
end;


Под память под строку с ответом, как пишут разработчики, выделяет и освобождает библиотека.

Спасибо всем, помогли.
Vizit0r
Дата: 30.07.2019 07:46:47
аж интересно стало - как длл узнает, что память под строку уже можно освобождать?
ziv-2014
Дата: 30.07.2019 09:34:57
zeon11,
Учти с таким подходом у тебя будут утечки памяти.