Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Abfangen von Exceptions bei LoadLibrary (https://www.delphipraxis.net/126308-abfangen-von-exceptions-bei-loadlibrary.html)

igel457 22. Dez 2008 17:05


Abfangen von Exceptions bei LoadLibrary
 
Hallo,

in meiner 2D-Grafikbibliothek gibt es eine Klasse, die nach allen verfügbaren Plugin-DLLs sucht. Dabei wird jede DLL in einem angegebenen Verzeichnis einzeln geladen und dann überprüft, ob sie bestimmte Funktionen exportiert.

Mein Problem ist nun das folgende: Gibt es ein Problem beim Laden der Bibliothek (wird zum Beispiel eine DLL, die die DLL benötigt nicht gefunden), so wird eine Exception geworfen. Das will ich nicht - die Bibliothek soll einfach links liegen gelassen werden.

Das kuriose ist nun, dass die Exception nur geworfen wird, wenn das Programm nicht aus dem Debugger heraus ausgeführt wird.

Mein Code:
Delphi-Quellcode:
res := FindFirst(dir+'*Andorra*'+Extension, faAnyFile, searchrec);
ahandle := 0;
while (res = 0) do
begin
  try
    try
    {$IFDEF Win32}
      ahandle := Windows.LoadLibrary(PChar(dir+searchrec.Name)); //<--- Hier wird die Exception (siehe unten) geworfen (vermute ich)
    {$ELSE}
      ahandle := dynlibs.LoadLibrary(PChar(dir+searchrec.Name));
    {$ENDIF}
    except
      //Load the next module if something didn't work...
      Continue;
    end;

    if AHandle <> 0 then
    begin
      @fileinfo := GetProcAddress(ahandle, 'Andorra2DLibraryInformation');

      //If procedure exists, it must be an Andorra 2D Plugin Library
      if @fileinfo <> nil then
      begin
        //Read fileinfo
        fileinfo(info);

        //The library must be compatible
        if info.LibVersion = LibraryVersion then
        begin
          //Call callback and pass name and information
          CallBack(searchrec.Name, info);
        end;
      end;
    end;

  finally
    if AHandle <> 0 then
      FreeLibrary(AHandle);
  end;
  res := FindNext(searchrec);
end;
Eine Beispiel-Exception (tritt auf, wenn eine von AndorraDX93D.dll benötigte Komponente gelöscht wurde):
Code:
---------------------------
Novcl: NoVCL.exe - Komponente nicht gefunden
---------------------------
Die Anwendung konnte nicht gestartet werden, weil d3dx9_31.dll nicht gefunden wurde. Neuinstallation der Anwendung könnte das Problem beheben.
---------------------------
OK  
---------------------------
Der komplette Code:
http://andorra.cvs.sourceforge.net/v...ew=markup#l_50

Nach dieser Fehlermeldung läuft das Programm munter weiter. Dieses Verhalten hat schon einige Entwickler, die mit meiner Bibliothek arbeiten irritiert.
Ich schätze, dass das Problem darin liegt, dass die Exception in der geladenen DLL geworfen wird und daher von meinem Hauptprogramm nicht abgefangen werden kann.

Vielen Dank im Voraus,
Andreas

Apollonius 22. Dez 2008 17:24

Re: Abfangen von Exceptions bei LoadLibrary
 
Interessant. Weißt du näheres über die DLL? Hast du mal versucht, das mit einer eigenen DLL nachzustellen (am besten einer nicht-Delphi-DLL)?

igel457 22. Dez 2008 18:05

Re: Abfangen von Exceptions bei LoadLibrary
 
Ja, die DLL kenn ich näher, habe sie selbst geschrieben. Die AndorraDX93D.dll lädt die d3dx9_31.dll statisch.
Das gleiche Problem habe ich aber auch mit anderen DLLs, die DLLs statisch laden.

Muetze1 22. Dez 2008 19:17

Re: Abfangen von Exceptions bei LoadLibrary
 
Dir ist bewusst, dass durch dein Continue das FindNext() nie aufgerufen wird und er somit in einer Endlosschleife bei der gleichen DLL hängt?

Die Meldung scheint mir auch eher vom Windows PE Loader zu kommen aufgrund einer statischen Abhängigkeit anstatt von dem oben geposteten Quellcode.

Muetze1 27. Dez 2008 23:47

Re: Abfangen von Exceptions bei LoadLibrary
 
Wenigstens vielleicht mal irgendeine Reaktion?

igel457 31. Dez 2008 10:24

Re: Abfangen von Exceptions bei LoadLibrary
 
Hallo,

Danke für den Hinweis mit dem "continue" und "FindNext" - ich habe das behoben. Trotzdem weiß ich immer noch nicht, wie ich den PE-Loader davon überzeugen kann keine Exceptions zu werfen. Dass dieser die Quelle für das Problem ist, ist logisch.

Andreas

igel457 21. Sep 2009 20:13

Re: Abfangen von Exceptions bei LoadLibrary
 
Nach der Methode RTFM, die ich beim erstellen dieses Threads leider auf eine Googlesuche beschränkte, habe ich eine Lösung für das Problem gefunden:
Delphi-Quellcode:
{$IFDEF WIN32}
//Prevent the library from showing any error messages while loading.
SetErrorMode(SEM_FAILCRITICALERRORS);
{$ENDIF}
try
  FHandle := LoadLibrary(PChar(AName));
finally
  {$IFDEF WIN32}
  SetErrorMode(0);
  {$ENDIF}
end;
Siehe MSDN-Library durchsuchenLoadLibrary

Ich hoffe, dass dies jemandem weiterhilft, der das selbe Problem hat wie ich.

Chris.R 21. Sep 2009 21:13

Re: Abfangen von Exceptions bei LoadLibrary
 
Hallo,

dafür gibts doch die Funktion SafeLoadLibrary aus SysUtils. Die sichert sogar das FPU-Steuerwort.
Die Funktion hat 2 Parameter, der erste ist der Dateiname der DLL, der zweite Parameter gibt der ErrorMode an. Vorgabe ist dafür SEM_NOOPENFILEERRORBOX, um das Anzeigen eines Fehlerdialogs zu verhindern.

Ciao Chris

Muetze1 21. Sep 2009 21:16

Re: Abfangen von Exceptions bei LoadLibrary
 
Altbekannt, da der PE Loader abhängig von dem Modus still oder laut das ganze ausgibt. Aber anscheinend ist das damals bei mir wohl untergegangen.

Aber mal ein zusätzlicher Hinweis: DLLs die in MS Visual Studio C++ geschrieben wurden haben in ihrem Startup Code die Eigenheit die FPU Exception Mask umzustellen. Dadurch werden illegale Operationen still und heimlich von der FPU behandelt, also es wird NaN und INF berechnet anstatt einer Exception. Delphi wiederrum setzt Exceptions voraus und kommt dadurch spätestens bei irgendwelchen Fließkommaberechnungen in die Bedrängnis, wenn es von NaN oder INF etwas berechnen soll. Von daher sollte hier nochmals explizit Delphi-Referenz durchsuchenSafeLoadLibrary() erwähnt werden. Werden DLLs komplett statisch geladen kommt der Startup Code der App (somit Delphi der die Maske umsetzt) erst nach dem Startup Coder statisch gelinkten DLLs, da der PE Loader diese Abhängigkeiten vorher auflösen muss. Wenn eine DLL oder auch eine solche DLL Kette aber dynamisch geladen wird hat man ein Problem. Von daher der Hinweis.

Sorry nochmals dass ich damals nicht auf die WinAPI Funktion hingewiesen hatte.

@Chris.R: SafeLoadLibrary() wurde für die von mir beschriebene Gegebenheit eingeführt und mit Delphi 7 um den zweiten Parameter erweitert, da dieser zuvor nicht von außen steuerbar war.

igel457 21. Sep 2009 21:44

Re: Abfangen von Exceptions bei LoadLibrary
 
Danke für den Hinweis mit dem "SafeLoadLibrary" aus der Unit "SysUtils", ich wusste nicht, dass Delphi/FreePascal eine solche Methode bereits anbietet - eine Abhängigkeit zur WinAPI weniger in meinem Quellcode. :thumb:

Ich habe nur Delphi-DLLs getestet und vom Umbiegen des FPU-Steuerworts wusste ich nichts - das hat sich nun geändert.

Edit:
Da FreePascal "SafeLoadLibrary" unvollständig implementiert, habe ich das jetzt in eine neue "SafeLoad" Prozedur geschrieben:
Delphi-Quellcode:
uses
{$IFDEF WIN32}Windows{$ELSE}dynlibs{$ENDIF};

function AcLoadLibrary(AModule: string): TAcHandle;
var
  fpu_word: Word;
begin
  //Store the fpu control word as some dlls might corrupt it when they are not
  //loaded properly
  fpu_word := get8087cw;

  {$IFDEF WIN32}
  //On Windows prevent the PE-Loader from showing up any exceptions
  SetErrorMode(SEM_FAILCRITICALERRORS);
  {$ENDIF}

  try
    result := LoadLibrary(PChar(AModule));
  finally
    {$IFDEF WIN32}
    //Reset the error mode to default
    SetErrorMode(0);
    {$ENDIF}
    //Reset the FPU word
    set8087cw(fpu_word);
  end;
end;
Edit2:
SEM_NOOPENFILEERRORBOX, was bei "SafeLoad" der Default ist, unterdrückt das Anzeigen der Fehlermeldung leider nicht!


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz