Пожалуйста, помогите, как конвертировать код C в код Delphi (qsBarcode) - PullRequest
3 голосов
/ 20 мая 2011

Мне нужно использовать файл DLL из qsBarcode http://www.qsbarcode.de/en/index.htm (вот ссылка для скачивания http://www.qsbarcode.de/en/download/qsbar39.zip). DLL будет декодировать растровое изображение, содержащее штрих-код 39 в строку.

В их примере есть только VB и C, но мне нужно использовать его в Delphi. Вот официальный пример кода в C:

#include <windows.h>
#include <stdio.h>

typedef int (WINAPI * CODE39_PROC)(char *, char *);

int main(int argc, char* argv[])
{
    HINSTANCE       hinstLib; 
    CODE39_PROC     ProcAdd; 
    BOOL            fFreeResult; 

    char            cFileName[512] = "\0";
    char            cResult[512] = "\0";
    int             iReturn = 0;


    if(argc < 2) return 0; //no bitmap filename in argv[1]

    strcpy(cFileName,argv[1]);

    hinstLib = LoadLibrary("qsBar39"); 
    if (hinstLib == NULL) return -1; //can't load lib

    ProcAdd = (CODE39_PROC) GetProcAddress(hinstLib, "ReadCode39"); 
    if (NULL == ProcAdd) return -1; //can't access Proc

    //dll Proc call
    iReturn = (ProcAdd) (cFileName, cResult); 
    printf("%s", cResult);

    fFreeResult = FreeLibrary(hinstLib); 

    return iReturn;
}

и это то, что я пытаюсь кодировать в Delphi

unit uRead;

interface

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

type
  TDLLFunc = function(namafile: PChar; hasil:PChar):integer;
  TForm2 = class(TForm)
    JvFilenameEdit1: TJvFilenameEdit;
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
   DLLFunc: TDLLFunc = nil;

var
  Form2: TForm2;
  DLLHandle: THandle;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
   feedback : integer;
   hasil:PChar;
begin
   DLLHandle := LoadLibrary('qsBar39.dll');
   if (DLLHandle < HINSTANCE_ERROR) then
     raise Exception.Create('library can not be loaded or not found. ' + SysErrorMessage(GetLastError));

   try
     { load an address of required procedure}
     @DLLFunc := GetProcAddress(DLLHandle, 'ReadCode39');

     {if procedure is found in the dll}
     if Assigned(DLLFunc) then
       feedback := DLLFunc(PChar(JvFilenameEdit1.Text), PChar(hasil));
     showmessage(hasil);
   finally
     {unload a library}
     FreeLibrary(DLLHandle);
   end;

end;

end.

Когда я выполняю этот код и отлаживаю, hasil содержит только # $ 11'½ в то время как он должен вернуть некоторый символ в изображении штрих-кода (вы можете получить изображение файла в zip-файле). Пожалуйста, помогите мне, спасибо.


последнее обновление:

@ 500, спасибо, я поставил stdcall

@ dthorpe, спасибо, сделано

На самом деле совет отличный, мой код должен работать нормально, но я по ошибке поставил JvFilenameEdit1.text вместо JvFilenameEdit1.FileName, мой плохой:)

Еще раз спасибо за совет, поэтому вот рабочий код:

unit uRead;

interface

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

type
  TDLLFunc = function(namafile: PAnsiChar; hasil:PAnsiChar):integer; stdcall;
  TForm2 = class(TForm)
    JvFilenameEdit1: TJvFilenameEdit;
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
   DLLFunc: TDLLFunc = nil;

var
  Form2: TForm2;
  DLLHandle: THandle;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
   feedback : integer;
   hasil: array [0..512] of char;
begin
   DLLHandle := LoadLibrary('qsBar39.dll');
   if (DLLHandle < HINSTANCE_ERROR) then
     raise Exception.Create('library can not be loaded or not found. ' + SysErrorMessage(GetLastError));

   try
     { load an address of required procedure}
     @DLLFunc := GetProcAddress(DLLHandle, 'ReadCode39');

     {if procedure is found in the dll}
     if Assigned(DLLFunc) then
        feedback := DLLFunc(PAnsiChar(JvFilenameEdit1.FileName), @hasil);

     edit1.Text := StrPas(@hasil);

   finally
     {unload a library}
     FreeLibrary(DLLHandle);
   end;

end;

end.

Ответы [ 3 ]

6 голосов
/ 20 мая 2011

На вашем месте я бы воспользовался возможностью, чтобы обернуть этот вызов функции в более похожую на Delphi оболочку.

function ReadCode39(FileName, Result: PAnsiChar): LongBool; stdcall;
  external 'qsBar39';

function ReadCode(const FileName: string): string;
var
  cResult: array [0..512-1] of AnsiChar;
begin
  if not ReadCode39(PAnsiChar(AnsiString(FileName)), @cResult[0]) then
    raise Exception.Create('ReadCode39 failed');
  Result := string(cResult);
end;

Примечания:

  1. Я использую неявный импорт DLL (используя external), а не явный GetProcAddress.Это значительно уменьшает объем стандартного кода.
  2. Я преобразую обработку ошибок целочисленного кода в стиле C в исключение Delphi.На основании вашего комментария я предполагаю, что ненулевое возвращаемое значение означает успех.Более старые версии C не имеют логического типа и используют 0 для обозначения false, а каждое ненулевое значение оценивается как true.Естественный способ сопоставить это с булевым типом Delphi - LongBool.Это означает, что ваш код вызова не должен беспокоиться о кодах ошибок.
  3. Все преобразования в и из строк с нулевым символом в конце обрабатываются в одной подпрограмме, и ваш код вызова снова не должен беспокоиться о таких пустяках.
  4. Я написал код так, чтобы он был переносимым между версиями Delphi как для ANSI, так и для Unicode.

Это позволяет вашему вызывающему коду читать намного более четко:

procedure TForm2.Button1Click(Sender: TObject);
var
  hasil: string;
begin
  hasil := ReadCode(JvFilenameEdit1.Text);
  ShowMessage(hasil);
end;
3 голосов
/ 20 мая 2011

Придерживайтесь стандартного вызова;в конце объявления TDLLFunc указывается компилятору, что он использует соглашение о вызовах WINAPI, и, как указывает Дорин, если вы используете версию Delphi на основе юникода, вы, вероятно, захотите использовать PAnsiChar.

2 голосов
/ 20 мая 2011

В дополнение к стандартному вызову, упомянутому в другом ответе, вам также необходимо выделить место для указателей pchar, которые вы передаете в DLLFunc. Обратите внимание, что в коде C переменная cResult определена как char cResult[512];. Это означает, что вызывающая сторона резервирует пространство для буфера символов из 512 символов и передает адрес этого буфера в функцию DLL.

Вы не делаете эквивалент в вашем коде Delphi.

Измените код Delphi, чтобы определить hasil как массив символов:

var hasil: array [0..512] of char;

затем передайте адрес hasil вызову DLLFunc:

DLLFunc(PChar(JvFilenameEdit1.Text), @hasil);
...