C++ Bibliothek in Lazarus einbinden

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

Hallo,

ich bin gerade dabei mein Lazarus Programm (Win32 GUI) zu debuggen, weil es plötzlich (ab Version Lazarus 1.6.2, Zur Zeit 1.6.4) nicht mehr die Bibliotheksfunktionen der externen C++ dll einbindet und direkt nach dem kompilieren bzw externen Aufruf abstürzt. Ob es etwas mit der Lazarus Version zu tun hat, möchte ich hier herausfinden. Der Absturz wird mit der Fehlermeldung: "Die Anwendung konnte nicht korrekt gestartet werden (0xc000007b). ...." begleitet und die GUI öffnet sich nicht.

Die Bibliothek (DLL) wird von einem MinGW Compiler auf Netbeans 8.2 (mit GCC 6) mit den Compileroptionen "-static-libgcc -static-libstdc++ -DHAVE_STRUCT_TIMESPEC" im Standard C++14 umgesetzt. Die zu exportierenden DLL Funktionen werden mit folgendem Syntax exportiert:

Code: Alles auswählen

#ifdef __cplusplus
extern "C" {
#endif
 
int32_t extern_nsifun(const char* password, int32_tcall_id);
const char* extern_getversion(); 
int32_textern_testfun(int32_t i);
 
#ifdef __cplusplus
}
#endif


Auf der Lazarus-Seite versuche ich die Funktionen statisch einzubinden und zwar im UNIT Teil "interface". Die Einbindung geht wie folgt:
const DLL_NAME='liblibfraes.dll';

Code: Alles auswählen

function extern_nsifun(password:Pchar;call_id:int32):int32; cdecl; external DLL_NAME; 
function extern_getversion:Pchar; cdecl; external DLL_NAME; 
function extern_testfun(ii:int32):int32; cdecl; external DLL_NAME;
 


Gibt es einen anderen Weg an die Funktionen zu kommen - Stichwort dynamisches einbinden, da bin ich mir noch unsicher ? Gibt es eine spezielle Compiler-Einstellung, dich ich übersehen habe? Hat es was mit der Versionierung von Lazarus zu tun?

ich bitte um Antwort, da ich hier nicht weiter komme...

Viele Grüße, Mojo

Benutzeravatar
theo
Beiträge: 10497
Registriert: Mo 11. Sep 2006, 19:01

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von theo »

Kann FPC jetzt mit C++? Ich dachte das geht gar nicht ohne Interface Lib?
ftp://ftp.freepascal.org/pub/fpc/docs-p ... Pascal.pdf

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

Solange reine C Funktionen aufgerufen werden schon.
@Mojo:
Versuche herauszufinden, wie die Funktionen tatsächlich heissen (Stichwort "Name mangling") und gib den Namen in der "name" Klausel an.
https://www.freepascal.org/docs-html/cu ... 890007.1.1
Marco kann sicher mehr dazu sagen.
Edit:
Das passt vielleicht auch zum Thema:
https://answers.microsoft.com/en-us/win ... 111?auth=1
As mentioned in several links in a Google search, this error is most likely a result of a 32-bit (x86) executable trying to load a 64-bit (x64) DLL.

Gibt es einen anderen Weg an die Funktionen zu kommen - Stichwort dynamisches einbinden, da bin ich mir noch unsicher ?

https://www.freepascal.org/docs-html/cu ... index.html

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

Hallo mse,

Danke für Deine schnelle Antwort.

Ich habe über pexports den direkten Namen der Funktion herausgelesen. Über das C-interface wird der korrekte Name exportiert: extern_testfun.

Ich habe den statischen Aufruf um den name-Zusatz ergänzt (und die typen C=long, Lazarus=longint ersetzt) - Leider ohne Erfolg: Den Index konte ich noch nicht herausfinden.

Code: Alles auswählen

function extern_testfun(ii:longint):longint; cdecl; external DLL_NAME name 'extern_testfun';


Des weiteren habe ich konkret für ein 32 Bit Umgebung die DLL ausgegeben. (Ich benutze Windows 8 64bit)

Ich habe das LazarusProjekt mal mit der betreffenden Test - DLL angehängt um das Problem zu verdeutlichen. Vllt hat ja noch jemand eine zündende Idee? Die Testfunktion soll nichts anderes machen, als den übergebenen longint-Wert als Rückgabewert wieder herauszugeben - also eigentlich simpel.

Code: Alles auswählen

long extern_testfun(long i){
    return(i);
}


Viele Grüße,
Mojo

DLL_TEST.zip
Lazarusprojekt mit DLL
(5.61 MiB) 89-mal heruntergeladen

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

Ich würde versuchen, die Funktion mit mit den Funktionen aus dynlibs zu laden.
https://www.freepascal.org/docs-html/cu ... index.html
(loadlibrary(), getprocedureaddress()) und die Fehlermeldungen auszuwerten (getlasterror()).

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

Linux "nm" findet in libtest.dll

Code: Alles auswählen

 
6ea414b0 T _extern_testfun
6ea414f8 t __GLOBAL__sub_I_extern_testfun
 

Marco kann sicher Licht ins Dunkel bringen.

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

Hallo mse,

ich habe mal beide Varianten ausprobiert: "_extern_testfun" und "__GLOBAL__sub_I_extern_testfun" - es kam wieder die gleiche Fehlermeldung :( . Interessant, dass pexports und nm unterschiedliche exportfunktionen anzeigen - siehe Anhang.

Kannst du mir ein Beispiel geben, wie ich die Funktion per loadlibrary aufrufen und benutzen kann? :?:

Vielen Dank und Grüße, Mojo

dlltest.jpg

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

https://www.freepascal.org/docs-html/cu ... brary.html
https://www.freepascal.org/docs-html/cu ... dress.html

Code: Alles auswählen

 
uses
 dynlibs,ctypes,windows;
 
var
 extern_testfun: function(ii: clong): clong cdecl;
 handle: tlibhandle;
 
[...]
 
 handle:= loadlibrary('libtest.dll');
 if handle = 0 then begin
  writeln('loadlibrary error: ',getlasterror());
 end
 else begin
  writeln('loadlibrary OK');
  extern_testfun:= getprocedureaddress(handle,'extern_testfun');
  if assigned(extern_testfun) then begin
   writeln('getprocedureaddress OK');
   if extern_testfun(123) = 123 then begin
    writeln('extern_testfun OK');
   end;
  end
  else begin
   writeln('getprocedureaddress error: ',getlasterror());
  end;
 end;
 

(ungeprüft).

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

Hallo mse,

Danke für deinen CODE.

Ich habe den Code mal probiert und bekomme die Bibliothek nichtgeladen - ich bekomme immer einen dynlibs.NILHANDLE zurück - kann ich mir nicht erklären :(
DLL_TEST2.jpg


Ich habe das Projekt und den Code begefügt zur kontrolle. Woran kann das liegen, dass ich die Bibliothek nicht geladen bekomme?

DLL_TEST_dynlib.ZIP
(5.82 MiB) 87-mal heruntergeladen


Code: Alles auswählen

Procedure TForm1.test_extern_testfun;
 
type Textern_testfun = function(ii: clong): clong; cdecl;
 
var MyHandle : TLibHandle = dynlibs.NilHandle;
    MyFunc   : Textern_testfun;
 
begin
 MyHandle:= loadlibrary(pchar('libtest.dll'));
 if MyHandle = dynlibs.NilHandle then begin
  memo1.lines.add('loadlibrary error: dynlib.Nilhandle');
 end
 else begin
   memo1.lines.add('loadlibrary OK');
   MyFunc := Textern_testfun(getprocedureaddress(MyHandle,'extern_testfun'));
   if assigned(MyFunc) then
    begin
    memo1.lines.add('getprocedureaddress OK');
    if MyFunc(123) = 123 then
     begin
      memo1.lines.add('extern_testfun OK');
     end;
    end
  else begin
    memo1.lines.add('Funktion nicht gefunden ');
  end;
 end;
FreeLibrary(MyHandle);
end;   


Viele Grüße, Mojo

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

Ich habe hier im Moment kein Windows. Was bringt getlasterror()?
Um die FPC Version von getprocedureaddress() aufzurufen muss die unit Reihenfolge geändert werden, sorry:

Code: Alles auswählen

 
uses
 windows,dynlibs,ctypes;
 

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

getlasterror() bringt direkt nach loadlibrary(...) einen Wert von 193 - was auch immer das zu bedeuten hat.

Thandor
Beiträge: 153
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 3.0 mit FPC 3.2.2 (32Bit + 64bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Thandor »

Probiere mal spaßeshalber "stdcall;" statt "cdecl;"

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von mse »

https://msdn.microsoft.com/en-us/librar ... 82(v=vs.85).aspx

Code: Alles auswählen

 
ERROR_BAD_EXE_FORMAT
    193 (0xC1)
    %1 is not a valid Win32 application.
 

Kannst du denn die DLL in einer anderen nicht FPC Windows Applikation verwenden?

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

stdcall bringt auch nix - ich versuche mal die DLL in einem anderen Format auszugeben.

Mojo
Beiträge: 7
Registriert: Mi 26. Jul 2017, 08:34

Re: C++ Bibliothek in Lazarus einbinden

Beitrag von Mojo »

Also - geht nun.

Ich würde mal Licht ins Dunkle bringen: Der Fehler lag im GCC Compiler, der die DLL rausgegeben hat. Auf einem 64Bit System sollte man auch den Compiler für win64 installieren 8) . Warum der Compiler trozdem funktioniert hat weiß ich nicht. Nunmehr geht das Projekt mit einem GCC-7-WIN64 und gibt eine für Lazarus 1.6.4 FPC3.0.2 eine lesbare DLL aus, welche sowohl dynamisch als auch statisch eingebunden werden kann.

DLL statisch einbinden funktioniert mit folgendem Syntax:

Code: Alles auswählen

uses ctypes
...
Const DLL_NAME = 'libtest.dll';
 
// externe Funktionen deklarieren
function extern_testfun(ii:clong):clong; cdecl; external DLL_NAME name 'extern_testfun';


DLL dynamisch einbinden funktioniert mit folgendem Syntax:

Code: Alles auswählen

uses windows,dynlibs,ctypes;
...
Procedure TForm1.test_extern_testfun;
 
type Textern_testfun = function(ii: clong): clong; cdecl;
 
var MyHandle : TLibHandle = dynlibs.NilHandle;
    MyFunc   : Textern_testfun;
begin
  MyHandle:= loadlibrary(PChar('libtest.dll'));
  memo1.lines.add(inttostr(getlasterror()));
  if MyHandle = dynlibs.NilHandle then begin
  memo1.lines.add('loadlibrary error: dynlib.Nilhandle');
 end
 else begin
   memo1.lines.add('loadlibrary OK');
   MyFunc := Textern_testfun(getprocedureaddress(MyHandle,'extern_testfun'));
   if assigned(MyFunc) then
    begin
    memo1.lines.add('getprocedureaddress OK');
    if MyFunc(123) = 123 then
     begin
      memo1.lines.add('extern_testfun OK');
     end;
    end
  else begin
    memo1.lines.add('Funktion nicht gefunden ');
  end;
 end;
FreeLibrary(MyHandle);
end;


beides setzt in C++ ein C-Interface für die zu exportierenden Funktionen voraus, damit diese im Export der DLL lesbar sind, d.h. Ihre Namen so sind, wie sie programmiert wurden.
BSP:

Code: Alles auswählen

 
#define PRECTYPE1 long
#define DllExport  __declspec(dllexport)
#ifdef __cplusplus
extern "C" {
#endif
 
DllExport PRECTYPE1 extern_testfun(PRECTYPE1 i); //Testfunktion
 
#ifdef __cplusplus
}
#endif
 


An alle die mitgeholfen haben: vielen Dank dafür!

Viele Grüße, Mojo

Antworten