statische DLL

    statische DLL

    Guten Morgen Kollegen,
    ich habe ein Problem mit einer DLL in einem meiner Projekt. Das Projekt besteht aus mehreren Units, welch dynamisch erstellt werden und dann auch wie freigegeben. In einer dieser Units verwende ich eine DLL,
    welche ich statisch einbinde. Alles OK. Aber wenn ich das Programm starte und dann ohne eine Unit zu öffnen wieder beende, bekomme ich eine Schutzverletzung bei Adres .....
    Ich habe lange gesucht und dann den Fehler lokalisiert. Kommentiere ich den DLL aufruf aus geht alles.

    Ich finde den Fehler nicht:
    Ich rufe 2 Funktionen auf mit den Konventionen cdecl, stdcall. Leider habe ich nicht den Quellcode zur Verfügung, da ich auf arbeit bin.

    In einem anderen Programm mit der selben DLL geht alles und der Aufruf ist der gleiche.

    Hat jemand eine Idee????? ;(
    Thx
    Tossi
    Unverständlich.
    Eine Unit ist Teil des Quelltextes, sie ist drin oder nicht drin.
    Aber die Schutzverletzung scheint auf eine noch nicht existierende Variable hinzuweisen.
    Deshalb mal die Erzeugungsreihenfolge im Projektquelltext überprüfen.
    ism
    Morgen ist Heute schon Gestern
    hey,
    sorry vielleicht habe ich mich falsch ausgedrückt. Du hast recht.
    Aber ich meinte, jede Unit ist ein eigenes Formular und wird von mir zur Laufzeit erzeugt und wieder freigegeben. Soll heißen wenn das Programm startet existiert dieser Proggrammteil noch gar nicht. Er wird auch nicht automatisch erzeugt. Delphi Version XE7
    Thx
    Tossi
    Du meinst, ein zur Laufzeit erzeugtes Formular bindet statisch eine DLL? Ich bin mir nicht sicher, ob das überhaupt funktioniert, IIRC werden statisch gebundene DLLs direkt beim Programmstart eingebunden. Hast Du es mal testhalber mit dynamischer Bindung versucht?
    10 Minuten Nachdenken ersparen oftmals 10 Stunden Fehlersuche.

    Tossi65 schrieb:

    Aber ich meinte, jede Unit ist ein eigenes Formular und wird von mir zur Laufzeit erzeugt und wieder freigegeben. Soll heißen wenn das Programm startet existiert dieser Proggrammteil noch gar nicht. Er wird auch nicht automatisch erzeugt.


    Das ist falsch. Eine Unit ist eine Art Container vor Quellcode. Du kannst in der Unit machen, was du willst - bei VCL/FMX-Anwendungen ist es eben üblich, dass eine Unit die Definition einer Klasse, nämlich der Form enthält. Das ist aber reine Konvention. Du kannst eine Unit theoretisch völlig leer machen (also leere Interface und Implementation-Sektion) oder aber zig Klassen, Funktionsdefinition oder Typdefinition reinstecken. Das ist alles möglich (ob es ratsam ist, ist eine andere Sache).
    Sobald die Unit zum Projekt gehört - sie also vom Hauptprogram in der Uses-Sektion eingebunden wird oder aber von irgendeiner anderen Unit, die (möglicherweise auf verschachtelte Art) zum Projektgehört in einer der beiden Uses-Sektionen refereziert wird -, ist sie bekannt. Insbesondere ist es ja möglich, dass eine Unit eine Initialization oder Finialization-Sektion besitzt. Diese wird am Programmstart und -ende ausgeführt, völlig unabhängig davon, ob du den Rest der Unit irgendwo verwendest. Und das einbinden statischer DLLs ist quasi so eine Art "Pre-Initialization"-Abschnitt. Sobald deine Unit im Projekt ist und eine DLL statisch einbindet, wird das am Programmstart ausgeführt.

    Eine ganz andere Sache ist es, ob der Aufruf der DLL-Funktion auch ausgeführt wird. Wenn du dies nur in der Unit tust und diesen Aufruf auskommentierst, dann wird die Funktion natürlich nicht aufgerufen. Das heißt aber nicht, dass die DLL deswegen nicht trotzdem an das Programm gebunden wäre (nagle mich nicht fest, vielleicht optimiert der Linker das Binden weg, wenn überhaupt kein Funktionsaufruf stattfindet). Wenn beim Aufruf eine Exception auftritt, dann kann das zwei Gründe - auch kombiniert - haben:
    • ein Fehler von dir: Du rufst die DLL-Funktion nicht so auf, wie sie es gerne möchte.
    • ein Fehler in der DLL: Auch externer Code ist nicht über jeden Zweifel erhaben. Möglicherweise ist die Funktion auch fehlerfrei implementiert, aber falsch dokumentiert, sprich, du kannst die Funktion gar nicht so aufrufen, wie sie es gerne hätte, weil du nicht weißt, wie.
    Um das zu beurteilen ist Code aber unumgänglich.
    Master of the EDH ;)
    Vielen Dank für die ausführliche Erklärung. Jetzt ergibt es eine Sinn.

    Ich empfinde es besser, wenn die DLL dynamisch eingebunden wird. Ich habe es versucher aber es will nicht:

    Delphi-Code

    1. type
    2. TForm1 = class(TForm)
    3. Button1: TButton;
    4. procedure Button1Click(Sender: TObject);
    5. private
    6. { Private-Deklarationen }
    7. procedure CallDLLFunction(const dllname: string);
    8. public
    9. { Public-Deklarationen }
    10. end;
    11. type
    12. InitFunc = function (Owner:Pointer):Word; stdcall;// external 'pdfmngr.dll';
    13. ShowFunc = function (S: PChar):Word; stdcall; // external 'pdfmngr.dll';
    14. // function InitPDFViewer(Owner:Pointer):Word; stdcall; external 'pdfmngr.dll';
    15. // function PVShowform(S: PChar):Word; stdcall; external 'pdfmngr.dll';
    16. var
    17. Form1: TForm1;
    18. implementation
    19. {$R *.dfm}
    20. procedure TForm1.Button1Click(Sender: TObject);
    21. var
    22. s : String;
    23. begin
    24. s := 'e:\Projekte\test\Honecker Test\DLLTest\Win32\Debug\1 Lieferschein 69341.pdf';
    25. // if InitPDFViewer(Pointer(Self)) = 0 then PVShowform(PChar(s));
    26. CallDLLFunction('pdfmngr.dll');
    27. end;
    28. procedure TForm1.CallDLLFunction(const dllname: string);
    29. var
    30. hDLL: THandle;
    31. InitFunction: InitFunc;
    32. ShowFunction: InitFunc;
    33. s : String;
    34. buf: array [0..144] of Char;
    35. begin
    36. //s := 'e:\Projekte\test\Honecker Test\DLLTest\Win32\Debug\1 Lieferschein 69341.pdf';
    37. s := AnsiQuotedStr('1 Lieferschein 69341.pdf','"');
    38. // Get a handle to the DLL module.
    39. // das Handle zum DLL Modul ermitteln.
    40. hDLL := LoadLibrary(StrPCopy(buf, dllname));
    41. // If the handle is valid, try to get the function address.
    42. // Wenn das Handle gültig ist, versuche die Adresse der Funktion zu ermitteln
    43. if hDLL <> 0 then
    44. begin
    45. // Return the address of the specified exported (DLL) function.
    46. // Adresse der Dll-Funktion ermitteln
    47. try
    48. @InitFunction := GetProcAddress(hDll, 'InitPDFViewer');
    49. @ShowFunction := GetProcAddress(hDll, StrPCopy(buf, 'PVShowform'));
    50. // If the function address is valid, call the function.
    51. // Wenn die Funktion gefunden wurde...
    52. if (@InitFunction <> nil) then
    53. begin
    54. if(InitFunction(Pointer(Self)) <> 0) then
    55. if (@ShowFunction <> nil) then
    56. ShowFunction(PChar(s));
    57. end
    58. else
    59. raise Exception.CreateFmt('Unable to link to function %s in DLL %s!',
    60. [dllname]);
    61. finally
    62. // Free the DLL module.
    63. // Dll wieder freigeben.
    64. FreeLibrary(hDLL);
    65. hDLL := 0;
    66. InitFunction := nil;
    67. ShowFunction := nil;
    68. end;
    69. end
    70. else
    71. raise Exception.CreateFmt('Unable to load DLL %s!'#13#10 +
    72. 'Reason: ', [dllname]);
    73. end;

    ich bekomme beim Aufruf immer wieder eine Exception auf einen Nullzeiger. Ich komme nicht weiter.
    Thx
    Tossi
    Moin,

    ich habe der Lesbarkeit halber Deinen Code einmal in Delphi-Tags gepackt.

    Wo tritt die Exception denn genau auf?
    An so einer ganzen Unit rumzuraten ist doch etwas mühselig.
    Gruß Thomas

    Admin werden ist nicht schwer, sein ......
    Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
    Hallöle... 8o

    Delphi-Code

    1. procedure TForm1.Button1Click(Sender: TObject);
    2. ...
    3. CallDLLFunction('pdfmngr.dll'); // besser: C:\Blubb\pdfmngr.dll
    4. end;
    5. procedure TForm1.CallDLLFunction(const dllname: string);
    6. ...
    7. hDLL := LoadLibrary(StrPCopy(buf, dllname)); // relativer Dateiname?
    8. end;

    ...ein relativer Dateiname ist meistens keine gute Idee. :/

    Hat hDLL das Handle?

    Ansonsten gilt das was Thomas gesagt hatte... :)
    Zusätzlich zu allem, was bisher gesagt wurde:
    • Laut deinem Profil verwendest du Delphi 7. Damit ist das soweit in Ordnung, wenn das aber nicht aktuell ist und du Delphi >= 2009 hast (auf was der Pfad Win32\Debug hindeutet), solltest du mit Unicode aufpassen.
    • ShowFunction sollte wohl vom Typ ShowFunc sein (unglückliche Namensgebung - TShowFunction wäre konventioneller).
    • Stimmt die Aufrufkonvention? Ist das StdCall? Du übergibst der InitFunction den Objektzeiger der aktuellen Form - das lässt mich vermuten, dass die DLL auch mit Delphi geschrieben wurde. Ist StdCall dann explizit als Export angegeben worden?
    • Wenn die DLL nicht mit exakt derselben Delphi-Version erstellt wurde: Was soll die DLL-Funktion mit Self anfangen? Da in einer Klasse, zumindest über Umwege der Polymorphie häufig neue Eigenschaften hinzukommen, manches vielleicht umgeordnet ist, kommt mir das sehr spanisch vor.
    • Dieses ganze StrPCopy-rumgemurkse ist nicht nötig. StrPCopy ist sinnvoll, wenn du einen Delphi-String hast und den in einen nullterminierten String konvertieren willst, wobei dir die Adresse, an der dieser String landen soll, schon gesagt wurde. Ich würde hier einfach einen ganz gewöhnlichen Cast nach PChar machen.
    • Eine DLL nur für eine Funktion dynamisch zu laden und sofort wieder zu entladen ist unkonventionell. Natürlich kannst du das machen, aber wenn du diese DLL häufiger verwendest oder gar eine gemischte Speicherverwaltung hast, dann ist das Entladen der DLL nicht unbedingt ratsam.
    • In neueren Delphis gibt es die Möglichkeit, eine Funktion zu deklarieren als würde sie statisch geladen, nur mit dem "delayed" Schlüsselwort. Damit wird die Bindung nicht beim Programmstart durchgeführt, sondern beim ersten Aufruf der Funktion, ohne dass du dich darum kümmern musst. Das ist quasi so ein Mittelding, "semidynamische" Bindung.
    Master of the EDH ;)
    Guten Morgen,
    sorry für meine Ungenauigkeit. Ich benutze Delphi XE7 und arbeite unter Win32Debug.

    Der Fehler passiert hier:

    Delphi-Code

    1. if (@InitFunction <> nil) then
    2. begin
    3. if(InitFunction(Pointer(Self)) <> 0) then
    4. if (@ShowFunction <> nil) then
    5. ShowFunction(PChar(s)); // Krawumm!!!!
    6. end

    Ich habe es auch schon mit dem kompletten Pfad zur DLL versucht, dasselbe Ergebnis.
    hdll hat einen gültigen Wert, die Funktionen haben auch einen Zeiger darin.

    Sorry mit DLL's habe ich dynamisch noch nicht gearbeitet.

    Vielen Dank.
    Thx
    Tossi
    Moin... 8o
    Honecker Test

    ...das kann nicht funktionieren. :whistling:

    Ich habe das mal ausgedünnt...versuche das mal.

    Delphi-Code

    1. unit Unit1;
    2. interface
    3. type
    4. InitFunc = function(Owner: Pointer): Word; stdcall;
    5. ShowFunc = function(S: PChar): Word; stdcall;
    6. TForm1 = class(TForm)
    7. Button1: TButton;
    8. procedure Button1Click(Sender: TObject);
    9. private
    10. { Private-Deklarationen }
    11. procedure CallDLLFunction(const dllname: string);
    12. public
    13. { Public-Deklarationen }
    14. end;
    15. var
    16. Form1: TForm1;
    17. implementation
    18. {$R *.dfm}
    19. procedure TForm1.Button1Click(Sender: TObject);
    20. begin
    21. CallDLLFunction(PChar(ExtractFilePath(ParamStr(0)) + 'pdfmngr.dll')); // oder Konstante mit Pfad. Hier: Programmordner
    22. end;
    23. procedure TForm1.CallDLLFunction(const dllname: string);
    24. var
    25. hDLL: THandle;
    26. InitFunction: InitFunc;
    27. ShowFunction: ShowFunc; // hier stand auch InitFunc ... ??? Das kann nicht passen.
    28. s: string;
    29. begin
    30. s := AnsiQuotedStr('1 Lieferschein 69341.pdf', '"');
    31. hDLL := LoadLibrary(dllname);
    32. if hDLL <> 0 then
    33. begin
    34. try
    35. @InitFunction := GetProcAddress(hDLL, 'InitPDFViewer');
    36. @ShowFunction := GetProcAddress(hDLL, 'PVShowform');
    37. if (@InitFunction <> nil) then
    38. begin
    39. if (@ShowFunction <> nil) then
    40. ShowFunction(PChar(s));
    41. end
    42. else
    43. raise Exception.CreateFmt('Unable to link to function %s in DLL %s!', [dllname]);
    44. finally
    45. FreeLibrary(hDLL);
    46. hDLL := 0;
    47. InitFunction := nil;
    48. ShowFunction := nil;
    49. end;
    50. end
    51. else
    52. raise Exception.CreateFmt('Unable to load DLL %s!'#13#10 + 'Reason: ', [dllname]);
    53. end;
    54. end.

    Ich tippe darauf das das Holen der Process Adresse schiefgeht. Ansonsten hänge mal die DLL incl. Code (wenn du es veröffentlichen darfst) und einen Lieferschein an.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „haentschman“ ()

    Hall haentschman,
    danke für die Hilfe. Aber bei
    FreeLibrary(hDLL)
    kracht es:
    ---------------------------
    Benachrichtigung über Debugger-Exception
    ---------------------------
    Im Projekt Project1.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x00000000: read of address 0x00000000' aufgetreten.
    ---------------------------
    Anhalten Fortsetzen Hilfe
    ---------------------------
    Thx
    Tossi
    Hallo...
    ...Häää? 8|
    Dann nimm mal alles raus und dann zeilenweise wieder rein. Dann finden wir den Übeltäter. :whistling:

    Delphi-Code

    1. hDLL := LoadLibrary(dllname);
    2. if hDLL <> 0 then
    3. begin
    4. try
    5. //@InitFunction := GetProcAddress(hDLL, 'InitPDFViewer');
    6. //@ShowFunction := GetProcAddress(hDLL, 'PVShowform');
    7. //if (@InitFunction <> nil) then
    8. //begin
    9. //if (@ShowFunction <> nil) then
    10. //ShowFunction(PChar(s));
    11. //end
    12. //else
    13. //raise Exception.CreateFmt('Unable to link to function %s in DLL %s!', [dllname]);
    14. finally
    15. FreeLibrary(hDLL);
    16. //hDLL := 0;
    17. //InitFunction := nil;
    18. //ShowFunction := nil;
    19. end;
    20. end
    21. else
    22. raise Exception.CreateFmt('Unable to load DLL %s!'#13#10 + 'Reason: ', [dllname]);

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „haentschman“ ()

    Tossi65 schrieb:

    Hall haentschman,
    danke für die Hilfe. Aber bei
    FreeLibrary(hDLL)
    kracht es:
    ---------------------------
    Benachrichtigung über Debugger-Exception
    ---------------------------
    Im Projekt Project1.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x00000000: read of address 0x00000000' aufgetreten.
    ---------------------------
    Anhalten Fortsetzen Hilfe
    ---------------------------



    Hmm..

    Könnte es sein, das die DLL noch intern mit dem PVShowform beschäftigt ist oder gar eine eigene Form anzeigt und deshalb nicht entladen werden kann?

    oder, was mir gerade aufgefallen ist: Es wird zwar die funktion InitPDFViewer gelinkt, jedoch nicht aufgerufen. Kann es sein, dass dies vor dem SHowForm erfolgen muss?