Funckja DLL z C do Delphi

0

Witam

Walczę z taką funkcją:

extern QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);

gdzie QRcode:

typedef struct {
int version; ///< version of the symbol
int width; ///< width of the symbol
unsigned char *data; ///< symbol data
} QRcode;

a QRecLevel i QRencodeMode to enumy mające wartosci 1,2 itp.

Moje mapowanie, które niestety nie działa to:

 TArrayOfByte = array of Byte;
  PArrayOfByte = ^TArrayOfByte;

  TQRcode = record
    version       :  integer;
    width         :  integer;
    symbolDataPtr :  PArrayOfByte;
  end;

oraz mapowanie funkcji właściwej:

QRcode_encodeStringFun : function ( content: PAnsiChar ; version : Integer; level : Integer; mode : Integer ; casesensitive : Integer) : TQRcode ; stdcall;

i przykładowe wywołanie:

var
  Data : AnsiString;
  QRCodeOutcome : TQRcode;

begin
     Try
       Data := '1234567890';
       QRCodeOutcome := QRcode_encodeStringFun ( PAnsiChar (Data) , 0 , 0 , 0 , 0);
     except
     on E : Exception do
       ShowMessage(E.ClassName+' error raised, with message : '+E.Message);
     end;
end;

PS: Enumy w C doczytałem ze są typu int czyli przekazuje je z delphi dobrze.

0

A czy:

unsigned char *data; ///< symbol data

To nie będzie po prostu PChar?

Poza tym w Pascalu istnieją enumy, więc czemu zamiast nich używasz int'a?

0

Co znaczy "Nie działa"? Dostajesz śmieci? EAccessViolation? nie znajduje funkcji w bibliotece? Masz dostęp do kodu tej biblioteki? Czy funkcja jest napisana w języku C czy C++? Jeżeli C++ to powinna być eksportowana jako extern "C".

0

Nie znam się na C++ ale... Czy to nie będzie cdecl?

nie działa

Idealny opis tego co się dzieje po uruchomieniu (albo i nie) programu. Zakładam że to problem z char *x bo sam się nie możesz zdecydować co to ma być. Nie pokazujesz też kodu który inicjalizuje tablicę (na 90% jest błędny już w czasie przypisania).

0
 TArrayOfByte = array of Byte;
  PArrayOfByte = ^TArrayOfByte;
 
  TQRcode = record
    version       :  integer;
    width         :  integer;
    symbolDataPtr :  PArrayOfByte;
  end;

źle. powinno być symbolDataPtr : PByte;

I prawdopodobnie cdecl zamiast stdcall.

0

Dziekuje za odpowiedzi. Btw uzywam Delphi 5.

  1. Nie uzywam enum w Delphi bo z tego co wiem to sa one typu Byte a enumy w C sa int czyli niezgodne stad na sztywno integer.

  2. Kod jest w C a mapowanie niedziala-dostaje smieci. Nie leci exception (chociaz funkcja przewiduje kilka takich typow) czyli mniemam ze parametry przekazuje dobrze tylko rezultat zle czytam.

  3. Nie rozumiem o jakie inicjalizowanie tablicy chodzi-jest przyklad wywolania.

  4. Do ostatniej odp: sprobuje zastapic ten stdcall jutro w pracy. Natomiast nie rozumiem tego PByte przez Ciebie zmienionego. Generalnie wg opisu tam ma byc ciag 8 bitowych wartosci. Co mi da PByte? To nie moze byc wskaznik do jednej wartosci 8 bitow....czy zle zrozumialem?

Pisze z komorki wiec mam trudnosci z cytowaniem za co przepraszam.
PS: string nie moze byc jako parametr bo C string musi konczyc ssie nullem.

0

PByte to wskaźnik na "ciąg ośmiobitowych wartości" bajta. Do danych odwołujesz się wówczas przez operator dereferencji czyli ^. np.

  x := symbolDataPtr^; //pobiera pierwszą wartość ciągu do x.

Aby pobrać kolejną musisz dodać do symbolDataPtr sizeof(Byte).

0

unsigned char * to wskaźnik, czyli w Delphi dokładnie ^byte albo to samo znaczące pbyte.

Za to array of byte to delphiowa tablica dynamiczna, nie mająca dokładnego odpowiednika w C, więc na pewno nie będzie dobrze.

możesz ewentualnie dać

TArrayOfByte = array[0..100000] of Byte;

gdzie górny indeks nie jest specjalnie istotny, i potem użyć twojego PArrayOfByte.

EDIT, doczytałem:

extern QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);

ta funkcja zwraca wskaźnik. A u ciebie w Delphi nie.

0
type PQRcode = ^TQRcode;
     TQRcode = record
         version:integer;
         width:integer;
         data:pbyte;
     end;

function QRcode_encodeString(astring:pansichar; version, level, hint:integer; casesensitive:integer):PQRcode; cdecl; external 'blabla.dll';

ponieważ funkcja zwraca wskaźnik, doczytaj skąd on się bierze — gdzie jest przydzielana pamięć i jak trzeba ją zwolnić.

0

PByte to wskaźnik na "ciąg ośmiobitowych wartości".

PByte to wskaźnik na bajta, nic więcej. Nie rób herezji.
I poprawniej będzie PChar, bo jak nazwa wskazuje to jest pointer na chara, który można sobie łatwo przerabiać na stringa i z niego.

0

Witam

Mimo zastosowanych rad niestety nie udało mi się doprowadzić do działania ww biblioteki (biblioteka zawiesza się lub leci Access Valation.

Ponieważ biblioteka była pisana w C pod unixem - twórca dołączył adnotację by w przypadku chęci korzystania z niej w MS Windows skompilować ją pod Cygwinem.

Zrobiłem to (mogę zamieścić ktok po kroku co właściwie zrobiłem ale z grubsza:
./configure make make install a potem na wynikowym .a użyłem skryptu a2dll i w wyniku dostałem dll. Jeżeli do niej zajerzę np jakimś dependency walkerem to widzę że jedyną zależnością jest dll z cygwina wiec mniemam że zrobiłem to dobrze.

Adresy funkcji są zwracane poprawnie czyli są poprawnie skompilowane,

Czy coś w powyższego może być powodem fiaska użycia tej dllki?

PS: Kod mam i wiem że jest napisany poprawnie i działa.

0

PS: Kod mam i wiem że jest napisany poprawnie i działa.
Skoro działa to w czym problem? ;-)

Pokaż kod po zastosowaniu rad.

0

Przez działa miałem na myśli że ta biblioteka to dojrzała implementacja:)

Kod to dllka w której znajduje się funkcja QRCodeEncodeSymbol.
Do testów przygotowałem procedurkę Test, która jest wołana jako ostatnia instrukcja przy 'montowaniu'. Uprościłem tą dllkę i jest w niej tylko ta część odpowiedzialna za wywołanie problematycznej instrukcji.


uses
  Windows,
  SysUtils,
  Dialogs,
  Classes,
  Forms,
  OLE2;

type

 { Kod w C:
  typedef struct
	int version;         ///< version of the symbol
	int width;           ///< width of the symbol
	unsigned char *data; ///< symbol data
  QRcode;
  }

  TQRcode = record
    version       :  Integer;
    width         :  Integer;
    symbolDataPtr :  PByte;
  end;

  PQRcode = ^TQRcode;



var
FDLLInstance : THandle;

  { Kod w C :
   extern QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);
   }
QRcode_encodeStringFun : function ( content: PAnsiChar ; version : Integer; level : Integer; mode : Integer ; casesensitive : Integer) : PQRcode ; cdecl;



procedure NotImplemented(); stdcall;
begin
end;


function QRCodeEncodeSymbol (dataToSymbol : AnsiString)  : PQRcode ; stdcall;
var
  PQRCodeOutcome : PQRcode;

begin
     Result := nil;
     Try
       //Parametr 2 :	Wersja - 0 automatyczna
       //Parametr 3 :	QR_ECLEVEL_L = 0, ///< lowest
       //Parametr 4 :	QR_MODE_NUM = 0,   ///< Numeric mode
       //Parametr 5 :	0 - jezeli 1 to rozroznia male/duze znaki
       PQRCodeOutcome := QRcode_encodeStringFun ( PAnsiChar(dataToSymbol) , 0 , 0 , 0 , 0);

      if not ( PQRCodeOutcome = nil )then
       begin
         ShowMessage(IntToStr (PQRCodeOutcome.version) );
         ShowMessage(IntToStr (PQRCodeOutcome.width) );
         Result := PQRCodeOutcome;
       end;
     except
     on E : Exception do
       ShowMessage(E.ClassName+' error raised, with message : '+E.Message);
     end;
end;


function LoadQRCodeGenLibrary() : Boolean;

begin
   FDLLInstance := LoadLibrary( 'libqrencode.dll' );
   if FDLLInstance = 0 then
   begin
     FDLLInstance := INVALID_HANDLE_VALUE;
   end;
   if ( FDLLInstance <> INVALID_HANDLE_VALUE ) then
   begin
      QRcode_encodeStringFun := GetProcAddress(FDLLInstance , 'QRcode_encodeString');

    if ( Assigned ( QRcode_encodeStringFun ) ) then
    begin
    Result := true;
    end//end if
    else
    begin
      Result := false;
    end;//end else
  end//end if
  else
  begin
    Result := false;
  end;//end if else
end;//end LoadQRCodeGenLibrary

function FreeQRCodeGenLibrary() : Boolean;
begin
    FreeLibrary( FDLLInstance );
    FDLLInstance := INVALID_HANDLE_VALUE;
    Result := true;
end;



procedure DLLEntryPoint(dwReason: DWord);
var
  FDLLQRCodeActive : Boolean;

begin
  try
    case dwReason of
      DLL_PROCESS_ATTACH:
        begin
          CoInitialize(nil);
          FDLLQRCodeActive := LoadQRCodeGenLibrary();
          if not (FDLLQRCodeActive) then
          begin
            ShowMessage ('Blad w ladowaniu');
          end;
          //Tutaj odp komunikat jezeli false
        end;//DLL_PROCESS_ATTACH

      DLL_PROCESS_DETACH:
        begin
          if ( FDLLInstance = 0 ) OR (FDLLInstance = INVALID_HANDLE_VALUE) then
          begin
                Exit;
          end;
          FreeQRCodeGenLibrary();
          coUninitialize;
        end;//DLL_PROCESS_DETACH
    end;//case
  except
    Application.HandleException(Application);
  end;//trye
end;//DLLEntryPoint


procedure Test();
begin
    QRCodeEncodeSymbol('0123456789');
end;

exports
  NotImplemented index 1;

begin
  try
    DLLProc := @DllEntryPoint;
    DllEntryPoint(DLL_PROCESS_ATTACH);
    ShortDateFormat := 'dd-mm-yyyy';
    Test();
  except
    Application.HandleException(Application);
  end;
end

Wstawiłem również plik nagłówkowy z biblioteki w C :
http://wyslijto.pl/plik/qf02n1rmhc

Mogę również wstawić skompilowaną biblioteke w C.

Bardzo prosze o pomoc.

PS: Na chwilę obecną wywoływana funkcja zwraca null

0
    if ( Assigned ( QRcode_encodeStringFun ) ) then
    begin
    Result := true;
    end//end if
    else
    begin
      Result := false;
    end;//end else

weź trochę ten kod ogarnij... ;-)

Result := Assigned(QRcode_encodeStringFun);

A twój problem wynika z tego, że olałeś wszystkie enumy, i wrzucasz zera. Jako przedostatni parametr należy podać QR_MODE_8.

PS. pamiętaj też o wywołaniu QRcode_free() na zwróconym wyniku.

0

Hmmm....dlaczego QR_MODE_8?

Przecież jak mam liczbę składającą się z np. 20 cyfr to mogę je zakodować numerycznie a nie 8 bitowo. Wydajniejszy sposób kodowania - więcej zmieszczę. A kodowanie numeryczne w QR COde w tej bibliotece ma akurat 0.

Co do ogarnięcia kodu to nie wiem dlaczego ale zawsze tak pisałem - ifbeginendelsebeginend :)

EDIT: Przez chwilę myślałem ze z tym wyborem kodowania to może być (w końcu metoda nazywa się ...String - i jakoś 8 bitow pasuje) ale przecież widać że można tam podać parametr. Chyba ze mam jakoś te 012345 zmapowac jakoś przed wsadzeniem ich do PAnsiCHar?

0

@Azarien Dzięki za wytrwałość w pomaganiu mi. Twoja ostatnia rada pomogła i koduje:)

CO ciekawe dałem 2 czyli QR_MODE_8 i zakodowało. I zakodowało tak jakby typ kodowania byl numeric (oczywiście kodowałem same cyferki).

Dodałem kilka innych znaków i zakodował jak dla 8 bitow.

Czyli tak jakby robił jakiś autodetect.

Wynik końcowy porównywalem z:
http://www.jaxo-systems.com/barshow/?lang=en_US#

Wersje przeskakują na wyższe na poziomach ilosci znaków odpowiednich do najbardziej optymalnego kodowania.

To mi wystarczy - dziękuje.

PS: Ale i tak dllka skompilowana pod cygwinem nie dziala tylko dopiero mingw+msys i konwersja za pomoca gcc do dll zadziałała.

1 użytkowników online, w tym zalogowanych: 0, gości: 1