Problem mit Def und External

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!

  • In meiner Profanversion (XProfan9) ergibt das hier einen dicken Programmabsturz:


    Ist das in aktuellen Versionen von Profan noch so?
    Grund dafür dürfte sein, dass die comctl32.dll bereits unter einem anderen Pfad vom Prozess geladen wurde.

    Bei External passiert übrigens das gleiche.

    Da die PSAPI.DLL ebenfalls in mehreren Versionen auf einem PC vorhanden sein kann, könnte das zu gewaltigen "Verwicklungen" führen.

  • Na ja - aber in External?



    Wie du siehst, ist das auch nicht der Grund des Absturzes...
    Bei manchen DLLs scheinen "Def" und External die DLL aus dem Systemverzeichnis nachzuladen, in diesenm Fall wird nicht nachgeladen.

  • Hallo,


    das ist leider eine (meines Erachtens sehr dumme) Eigenart von Windows:
    Ist ein Modul (meist eine DLL) erst einmal geladen, dann wird beim neuerlichen Laden desselben Moduls (selbst wenn es eigentlich eine andere Version in einem anderen Pfad ist) nur der Zähler des geladenen Moduls hochgezählt. Es wird offensichtlich der interne Modulname zur Unterscheidung herangezogen und nicht der Pfad.


    Das hat uns in der Firma, in der ich arbeite und wo Programme an über 2000 Mitarbeiter auf ihre Notebooks verteilt werden, schon einiges an Kopfzerbrechen bereitet. Es ging da um eine Steuer-Berechnungs-DLL, die von verschiedenen Programmen genutzt wurde und dummerweise mehrmals auf dem System vorhanden war. Bei einem Update wurde ein Speicherort übersehen und je nachdem, welches Programm zuerst aufgerufen wurde, wurde in allen Programmen falsch oder richtig gerechnet.


    External() kann da auch nicht helfen, denn es werden intern ja immer die gleichen Windows-APIs benutzt. Wenn der Programmierer einen Pfad angibt, wird dieser an die API übergeben.


    COMCTL32.DLL ist in diesem Zusammenhang ein besonders "beliebter" Kandidat. Es gibt die eine oder anderer Anwendung, die eine eigene Version mitbringt.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Roland - das kann so aber nicht stimmen, denn folgendes funktioniert wieder perfekt:


    Wie man hier sieht, wird die DLL mit dem falschen Pfad sehr wohl zusätzlich geladen (anderes Handle und anderer Pfad => andere DLL). Das Mapping des Pfades vernachlässigen wir jetzt mal.
    Da baut also nicht Windows den Mist - oder sehe ich das falsch?

    [Blockierte Grafik: http://s2.postimage.org/XPWYi.jpg]

  • Hallo Andreas,


    SORRY, ich habe wohl nicht genau genug geschaut und geschrieben. Jetzt sehe ich, was im ersten Listing das Problem ist: Du verwendest nur die API GetModuleHandle(). Diese lädt ein Modul aber nicht, sondern ermittelt nur das Handle eines geladenen Moduls aufgrund des Modul-Namens. Hier spielt der Pfad offensichtlich keine Rolle.


    Im letzten Beispiel nutzt Du UseDLL (hinter dem die Api LoadLibrary() steckt). Diese lädt ein Modul in den Speicher, wenn es noch nicht im Speicher ist und setzt anderenfalls den Zähler hoch. Hier wird offensichtlich der Pfad berücksichtigt.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Nein Roland, denn folgendes funktioniert ja auch:


    Hier lädt XProfan die PSAPI.DLL nach, obwohl ich hier nicht USEDLL angewendet habe....

    [Blockierte Grafik: http://s3.postimage.org/cbjt0.jpg]

    Warum verhält sich XProfan bei der Comctl32.dll anders? Das liegt doch nicht an Windows???
    Dass das nicht an Windows liegen kann, sieht man doch hier, oder?

  • Zu GetModuleHandle:


    Zitat aus MSDN: "Retrieves a module handle for the specified module. The module must have been loaded by the calling process.
    ...
    If lpModuleName does not include a path and there is more than one loaded module with the same base name and extension, you cannot predict which module handle will be returned. To work around this problem, you could specify a path, use side-by-side assemblies, or use GetModuleHandleEx to specify a memory location rather than a DLL name.
    ..."


    Die ComCtl32.dll ist üblicherweise schon immer geladen. Die PSAPI.DLL offensichtlich nicht. Daher bringt GetModuleHandle() da auch die 0 als Ergebnis = Modul ist nicht geladen.


    Und nun kommt's: GetModuleInformation() verwendet die PSAPI.DLL und lädt diese, wenn sie zuvor nicht geladen wurde. Das lässt sich zeigen, indem Du die Programmzeilen zur Ermittlung des Handles und Namens der PSAPI.DLL nach diesem Aufruf nochmal einbaust.


    Zitat aus MSDN: "The GetModuleInformation function does not retrieve information for modules that were loaded with the LOAD_LIBRARY_AS_DATAFILE flag. For more information, see LoadLibraryEx. Starting with Windows 7 and Windows Server 2008 R2, Psapi.h establishes version numbers for the PSAPI functions. The PSAPI version number affects the name used to call the function and the library that a program must load.
    If PSAPI_VERSION is 2 or greater, this function is defined as K32GetModuleInformation in Psapi.h and exported in Kernel32.lib and Kernel32.dll. If PSAPI_VERSION is 1, this function is defined as GetModuleInformation in Psapi.h and exported in Psapi.lib and Psapi.dll as a wrapper that calls K32GetModuleInformation.
    Programs that must run on earlier versions of Windows as well as Windows 7 and later versions should always call this function as GetModuleInformation."


    Ich hoffe, das hilft Dir weiter.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • OK, dann anders:
    Wie du siehst, wird hier aber ein Pfad übergeben. Warum sollte sich also LoadLibrary, wenn es innerhalb von External aufgerufen wird, anders veralten als LoadLibrary, wenn es über UseDLL aufgerufen wird???

    Was tut External genau? Welche API ruft External auf bzw. welchen Delphibefehl?

    PS:

    Zitat


    Und nun kommt's: GetModuleInformation() verwendet die PSAPI.DLL und lädt diese, wenn sie zuvor nicht geladen wurde. Das lässt sich zeigen, indem Du die Programmzeilen zur Ermittlung des Handles und Namens der PSAPI.DLL nach diesem Aufruf nochmal einbaust.


  • Zitat von AHT;751409

    OK, dann anders:
    Wie du siehst, wird hier aber ein Pfad übergeben. Warum sollte sich also LoadLibrary, wenn es innerhalb von External aufgerufen wird, anders veralten als LoadLibrary, wenn es über UseDLL aufgerufen wird???

    Was tut External genau? Welche API ruft External auf bzw. welchen Delphibefehl?


    Hallo Andreas,
    was soll der Quellcode bezüglich Deiner Frage demonstrieren? Weder External() noch UseDLL() kommen vor.
    Es wird in beiden Fällen API verwandt.
    Zum genauen Aufruf in XProfan kann ich vielleicht heite Abend antworten. Am Arbeitsplatz liegt mir der Quellcode nicht komplett vor.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Frank: Wir hatten mal das Problem, dass beim Beenden eines Quellcodes, der vorher die PSAPI.DLL geladen hatte, das Programm mit Zugriffsverletzung abstürzte - und das nur auf deinem Rechner. Ich wollte mal nachsehen, ob auf deinem Rechner die PSAPI.DLL evtl. bereits schon vorher über den Hook, der Auslöser des Problems war, geladen wird. Das ist nicht der Fall.

    roland: Ob nun External oder Def ist eigentlich egal - in beiden Fällen wird durch das Aufrufen der jeweiligen API die DLL nachgeladen, wenn sie nicht bereits geladen ist. Ist sie geladen, wird, so denke ich, der Loadcount um 1 erhöht. Beim Laden passiert ein Fehler - das was die DLL lädt, kümmert sich nicht in ausreichender Weise um den Pfad der DLL.
    PS: Nach dem Aufruf der API wird die DLL auch wieder entladen, bzw. der Loadcount verringert.

    Wenn ich nichts übersehen habe, kann derjenige, der da falsch lädt, eigentlich nicht Windows selbst sein - dann bleibt im Prinzip nur Profan oder Delphi über.

    Im Augenblick vermute ich bei dem Fehler, der bei Frank passiert ist und der Sache mit der COMCTL32 einen Zusammenhang, denn auch die PSAPI.DLL ist oft in mehreren Versionen auf einem Rechner vorhanden - und bei anderen DLLs sind mir solche Sachen noch nie passiert.
    Wenn so etwas wie bei Frank passieren kann, kann im Prinzip alles passieren - und da möchte ich schon genau wissen, was da die Ursache ist und was dort abläuft.

  • Hallo,
    ich habe noch mal nachgesehen. Es ist so wie ich vermutet habe:
    (Pfad ist ein Delphi-String mit dem Namen der DLL incl. des Pfades, wie vom Programmierer im Programm angegeben.)

    Code
    X := LoadLibrary(Addr(Pfad));
      If X <> 0 Then Begin
        Proc := GetProcAddress(X,Addr(Funktion));
        <...>
        <Schieben der Parameter auf den Stack und Aufruf der Funktion>
        <...>
        FreeLibrary(X);
      End;


    So funktioniert es mit Def und mit External, sowie den anderen Aufrufmöglichkeiten.
    Reine API, ohne dass XProfan oder Delphi da etwas machen.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Hallo,
    und hier noch mal ein Testprogramm, das demonstriert, dass immer die gemeinte DLL angezogen wird:


    Hinweis: Da das Handle jedesmal freigegeben wird, kann es vorkommen, dass das gleiche Handle bei einem weiteren Aufruf wieder verwandt wird. GetModuleFilename ermittelt aber jedes Mal den korrekten Programmpfad zur DLL. Lässt man das freeDLL weg, wird natürlich jedes Mal ein neues Handle vergeben.
    BTW: Bei mir ist die DLL auf C:, einer ziemlich "nackten" Windows XP Installation, 8 mal vertreten. (Mein "Arbeits-XP" liegt auf E: und kommt auf 12 Vorkommen. Meine Programmpartitionen D: (Anwendungen) und F: (Entwicklung) habe ich jetzt nicht überprüft.)


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Darauf wollte ich ja hinaus, deine Aussage hier kann also nicht stimmen:

    Zitat


    das ist leider eine (meines Erachtens sehr dumme) Eigenart von Windows:
    Ist ein Modul (meist eine DLL) erst einmal geladen, dann wird beim neuerlichen Laden desselben Moduls (selbst wenn es eigentlich eine andere Version in einem anderen Pfad ist) nur der Zähler des geladenen Moduls hochgezählt. Es wird offensichtlich der interne Modulname zur Unterscheidung herangezogen und nicht der Pfad.


    Trotzdem passiert hier beim Anwenden von External eine Zugriffsverletzung, wenn External (bzw. 'DEF') die DLL selbst lädt - warum?

  • Zitat von AHT;751862

    Darauf wollte ich ja hinaus, deine Aussage hier kann also nicht stimmen:


    Ja, die war so nicht korrekt. Das erwähnte ich bereits. Unser Problem war damals wohl, dass die Pfadangabe fehlte ...


    Zitat

    Trotzdem passiert hier beim Anwenden von External eine Zugriffsverletzung, wenn External (bzw. 'DEF') die DLL selbst lädt - warum?


    Davon hast Du bislang nichts erwähnt. Poste mal einen Drei-Zeiler (dürfen auch 10 sein, aber mehr braucht man für einen External()-Aufruf wohl nicht), bei dem das passiert. Dann schaue ich mal, was da schief läuft.


    Eine Zugriffsverletzung sollte eigentlich nur dann unvermeidlich sein, wenn man eine Funktion der DLL mit DEF oder External() aufruft, die z.B. zuvor einen initialisierten Datenbereich im Adressraum der DLL erwartet. Wenn also Daten zwischen mehreren DLL-Aufrufen im Adressraum derselben erhalten bleiben sollen, muss zuvor ein UseDLL() (und abschließend ein FreeDLL) erfolgen. Manche DLLs erwarten dann auch noch eine spezielle Initialisierung, bevor weitere Funktionsaufrufe von Erfolg gekrönt sind. Das steht dann aber in der Doku der DLL.


    Gruß
    Roland

    (Intel Duo E8400 3,0 GHz / 4 GB RAM / 250 GB HDD / ATI Radeon HD4770 512 MB / Windows Vista - ausgemustert zum Verkauf)
    AMD Athlon II X2 2,9 GHz / 8 GB RAM / 500 + 1000 GB HDD / ATI Radeon 3000 (onboard) / Windows 10(64) - XProfan X4


    http://www.xprofan.de

  • Zitat

    Davon hast Du bislang nichts erwähnt.


    Doch, ich rede die ganze Zeit von nichts anderem.

    Zitat

    Eine Zugriffsverletzung sollte eigentlich nur dann unvermeidlich sein, wenn man eine Funktion der DLL mit DEF oder External() aufruft, die z.B. zuvor einen initialisierten Datenbereich im Adressraum der DLL erwartet.


    Nein, nicht nur.
    Folgendes passiert...
    External lädt die zweite COMCTL32. Die Funktion ImageList_Create wird korrekt in der zweiten COMCTL32 aufgerufen und ein Handle auf die Imageliste wird zurückgegeben. Danach wird die zweite COMCTL32 von External wieder entladen.
    Das Handle übergebe ich dann an SendMessage - doch leider ist die zweite COMCTL32 bereits wieder entladen und der relative Pointer, der sich hinter dem Handle verbirgt und der sich auf die Ladeadresse der COMCTL32 bezieht, ist nicht mehr valide. Die Zugriffsverletzung erfolgt dann beim Aufruf von SendMessage.

    Diese Sache lässt sich mit ein paar Zeilen Code problemlos beweisen.