none
Bindung von unmanaged DLLs in Excel AddIn RRS feed

  • Frage

  • Hallo Forum,
    seit geraumer Zeit schlage ich mich mit der Bindung von DLLs eines Drittanbieters herum, die statisch gelinkte Abhängigkeiten beinhalten.
    Die Bindung dieser DLLs muss aus verswchiedenen Gründen spät erfolgen.
    Mein Programm ist ein Excel AddIn.
    Offensichtlich hat Excel eine Eigenschaft zu bindende assemblies irgendwo temporär hin zu kopieren, was mit den statischen Abhängigkeiten Probleme macht.

    Gibt es einen Weg, wie man dieses Verhalten Excel abgewöhnt?
    Ich habe schon FUSLOGVW.exe bemüht um mehr darüber heraus zu bekommen.
    Folgender Log zeigt was ich meine:

    LOG: Explizite IJW-Bindung. Dateipfad:C:\Programme\EPLAN\Electric P8\1.9.10\BIN\Eplan.EplApi.AFu.dll.
    LOG: Die IJW-Assemblybindung hat einen anderen Pfad zurückgegeben: C:\Dokumente und Einstellungen\User\Lokale Einstellungen\Anwendungsdaten\assembly\dl3\9HVVD91E.RWZ\OCAMDONH.VM7\2b776793\0064222f_3199c901\Eplan.EplApi.AFu.dll. Verwenden Sie die gelieferte Datei.
    
    Wie kann ich dieses Verhalten unterbinden?
    Hier mein Code zum Laden der Assemblies:

    public static object CreateInstanceFromExternalType( string assemblyPath, string typeName )
            {
                try
                {
                    // Externe Assembly laden
                    Environment.CurrentDirectory = System.IO.Path.GetDirectoryName( assemblyPath );
                    Assembly externalAssembly = Assembly.LoadFile( assemblyPath, new Evidence( Assembly.GetExecutingAssembly().Evidence ) );
    
                    // Typinformationen abrufen
                    Type externalType = externalAssembly.GetType( typeName );
                    
                    // Instanz erzeugen und zurückgeben
                    return Activator.CreateInstance( externalType );
                }
                catch ( Exception ex ) 
                {
                    ErrorLog.SaveMessage( "ExternalTypeHelper - CreateInstanceFromExternalType(...)",
                        String.Format( "File: {0}{1}TypeName: {2}{1}{3}", assemblyPath, Environment.NewLine, typeName, ex.Message ) );
                    throw ex;
                }
            }

    Vielen Dank im Voraus...
    Tele
    Mittwoch, 19. August 2009 10:10

Antworten

  •             AppDomain domain = AppDomain.CurrentDomain;
                foreach (Assembly assembly in domain.GetAssemblies())
                {
                    Console.WriteLine("{0}", assembly.FullName, assembly.CodeBase);
                }
    
    Hallo,

    entschuldige, da wollte ich Dich auf einen falschen Weg schicken.

    Um die die DLL handelt sich anscheind doch um eine managed Assembly,
    so dass  das vorhin geschriebene nicht zutrifft.

    Ich habe erst beim zweiten Blick auf Deine Antwort hin, die zweite Meldung
    in ihrer Gesamtheit gesehen, die leider aus dem Rähmchen fällt.

    Bei dem Pfad handelt sich um den lokalen Assembly Cache.
    Nicht Excel sondern auch Visual Studio usw. nutzen das als lokalen
    Cache für Assemblies zur Laufzeit. Mehr darüber findest Du in
    http://it-republik.de/dotnet/artikel/Zwischenlager-0550.html

    Das das zweite Laden fehlschlägt dürfte für das AddIn vermutlich
    daran liegen, das Excel die Assembly bereits geladen hat.
    Ich nutzte .NET nicht für Excel, so dass ich das jetzt nicht mal eben nachstellen kann.

    Nun wird eine Assembly nicht anhand ihres Dateinamens sondern anhand  ihrer Signatur unterschieden.
    Wenn die Assembly signiert ist, anhand ihres Strong Name  ansonsten, reicht wiederum der Name alleine (PublicKeyToken=null).
    Suzanne Cook beschreibt das unter
    http://blogs.msdn.com/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx
    und
    http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx

    Um die in der aktuellen AppDomain geladenen Assemblies zu ermitteln,
    kannst Du verwenden:
                AppDomain domain = AppDomain.CurrentDomain;
                foreach (Assembly assembly in domain.GetAssemblies())
                {
                    Console.WriteLine("{0}", assembly.FullName, assembly.CodeBase);
                }
    
    darunter sollte sich auch Eplan.EplApi.AFu.dll befinden.
    Und über die darüber ermittelte Assembly solltest Du den Typ erzeugen.
    Mittels Assembly.GetTypes kannst Du die in der Assembly enthaltenen Typen
    abfragen, falls ich doch noch etwas übersehen habe.

    Gruß Elmar



    Mittwoch, 19. August 2009 19:21
    Beantworter
  • Hallo Carsten,

    mehr als eine Version einer Assembly gibt keinen Sinn.
    Denn dann wären so erzeugte Objekte nicht mehr vom gleichen Typ,
    neben anderen Dingen, wie Speicherverbrauch uvm.

    Hierbei handelt es nicht um den GAC sondern um einen Download Cache.
    Und verhindern kann man das nicht, denn dabei geht es um Sicherheit,
    die beim .NET Framework groß geschrieben wird.

    Und hier wiederum auch darum, dass die richtige Assembly nur einmal geladen wird.

    Die detailliertesten Informationen dazu hat Richard Grimes in seinem
    .NET Fusion Workshop zusammengetragen.
    Für den Cache Fall gilt 3.4 Loading an Assembly from the Internet
    Wie dort erläutert wird, hat sich da im Laufe der .NET Evolution das eine
    oder andere Detail geändert (mittlerweile gibt es weitere Cache Orte).

    Wird die Assembly denn als bereits geladen angezeigt?
    Denn dann kannst Du die Instanz direkt darüber erzeugen.

    Gruß Elmar




    Donnerstag, 20. August 2009 10:28
    Beantworter
  • Hallo Carsten,

    das ist zwar letztendlich Sache des Herstellers.
    Aber ein Programm/.NET Assembly sollte seine Datenablage nicht vom Programmspeicherort abhängig machen.

    .NET bietet reichlich Möglichkeiten eine Speicherung unabhängig davon zu realisieren.
    Z. B. über Environment.GetFolderPath auf ein Applikationsverzeichnis zuzugreifen.
    Was für Vista/Windows 7 ohnehin bei schreibendem Zugriff viele Probleme vermeidet, siehe z. B.
    http://social.msdn.microsoft.com/Forums/en-US/windowscompatibility/thread/74cee849-ed48-4de5-ac4b-0b35b7852ecd

    Wenn der Hersteller kooperativ ist, sollte man das vorzugsweise an der Stelle ändern.

    Gruß Elmar

    Donnerstag, 20. August 2009 14:48
    Beantworter

Alle Antworten

  • Hallo Tele,

    eine unmanaged DLL kannst Du auf diese Weise nicht laden, das geht nur für .NET Assemblies,
    die die notwendigen Metadaten mitliefern.

    Für unmanged Funktionen mußt Du eine Methodensignatur mit dem DLLImport Attribut erstellen.
    Und die Signatur muß den Vorgaben entsprechen, mehr dazu findest Du unter
    Verwenden nicht verwalteter DLL-Funktionen

    Handelt es sich bei der DLL um ein COM+ Bibliothek käme zum Einsatz:
    Verfügbarmachen von COM-Komponenten für .NET Framework

    Gruß Elmar
    Mittwoch, 19. August 2009 15:43
    Beantworter
  • Hallo Elmar, zunächst erstmal vielen Dank für die Antwort. Ich muss gestehen, dass ich noch nicht viel Ahnung von diesem Thema habe. Der Hersteller hatte es so beschrieben. Wenn Du schreibst, dass ich eine unmanaged dll nicht so laden kann, heisst das dann, dass es generell nich funktioniert? Ich Frage das, weil auf meinem PC das ganze funktioniert. Ich bekomme zwar diese Logs aber es läuft halt irgendwie... Wenn dem aber nicht so ist, kann es dann sein, dass die dlls Managed sind, die wiederum unmanaged verwenden? Ich werde mir morgen mal intensiv Deine Links anschauen und mich ggf. Nochmal melden. Danke und Guss, Carsten
    Mittwoch, 19. August 2009 17:51
  •             AppDomain domain = AppDomain.CurrentDomain;
                foreach (Assembly assembly in domain.GetAssemblies())
                {
                    Console.WriteLine("{0}", assembly.FullName, assembly.CodeBase);
                }
    
    Hallo,

    entschuldige, da wollte ich Dich auf einen falschen Weg schicken.

    Um die die DLL handelt sich anscheind doch um eine managed Assembly,
    so dass  das vorhin geschriebene nicht zutrifft.

    Ich habe erst beim zweiten Blick auf Deine Antwort hin, die zweite Meldung
    in ihrer Gesamtheit gesehen, die leider aus dem Rähmchen fällt.

    Bei dem Pfad handelt sich um den lokalen Assembly Cache.
    Nicht Excel sondern auch Visual Studio usw. nutzen das als lokalen
    Cache für Assemblies zur Laufzeit. Mehr darüber findest Du in
    http://it-republik.de/dotnet/artikel/Zwischenlager-0550.html

    Das das zweite Laden fehlschlägt dürfte für das AddIn vermutlich
    daran liegen, das Excel die Assembly bereits geladen hat.
    Ich nutzte .NET nicht für Excel, so dass ich das jetzt nicht mal eben nachstellen kann.

    Nun wird eine Assembly nicht anhand ihres Dateinamens sondern anhand  ihrer Signatur unterschieden.
    Wenn die Assembly signiert ist, anhand ihres Strong Name  ansonsten, reicht wiederum der Name alleine (PublicKeyToken=null).
    Suzanne Cook beschreibt das unter
    http://blogs.msdn.com/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx
    und
    http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx

    Um die in der aktuellen AppDomain geladenen Assemblies zu ermitteln,
    kannst Du verwenden:
                AppDomain domain = AppDomain.CurrentDomain;
                foreach (Assembly assembly in domain.GetAssemblies())
                {
                    Console.WriteLine("{0}", assembly.FullName, assembly.CodeBase);
                }
    
    darunter sollte sich auch Eplan.EplApi.AFu.dll befinden.
    Und über die darüber ermittelte Assembly solltest Du den Typ erzeugen.
    Mittels Assembly.GetTypes kannst Du die in der Assembly enthaltenen Typen
    abfragen, falls ich doch noch etwas übersehen habe.

    Gruß Elmar



    Mittwoch, 19. August 2009 19:21
    Beantworter
  • Hmm...
    nun gut, sowohl load als auch loadfrom und loadfile laden ja nicht mehr die wirkliche Datei.

    Kann man denn irgendwie verhindern, dass die DLL's in den GAC geladen werden?

    Gruss Carsten
    Donnerstag, 20. August 2009 08:26
  • Hallo Carsten,

    mehr als eine Version einer Assembly gibt keinen Sinn.
    Denn dann wären so erzeugte Objekte nicht mehr vom gleichen Typ,
    neben anderen Dingen, wie Speicherverbrauch uvm.

    Hierbei handelt es nicht um den GAC sondern um einen Download Cache.
    Und verhindern kann man das nicht, denn dabei geht es um Sicherheit,
    die beim .NET Framework groß geschrieben wird.

    Und hier wiederum auch darum, dass die richtige Assembly nur einmal geladen wird.

    Die detailliertesten Informationen dazu hat Richard Grimes in seinem
    .NET Fusion Workshop zusammengetragen.
    Für den Cache Fall gilt 3.4 Loading an Assembly from the Internet
    Wie dort erläutert wird, hat sich da im Laufe der .NET Evolution das eine
    oder andere Detail geändert (mittlerweile gibt es weitere Cache Orte).

    Wird die Assembly denn als bereits geladen angezeigt?
    Denn dann kannst Du die Instanz direkt darüber erzeugen.

    Gruß Elmar




    Donnerstag, 20. August 2009 10:28
    Beantworter
  • Hallo Elmar,
    ja, die Assembly wird als geladen angezeigt.
    Ich glaube aber nicht, dass das das Problem ist.
    Laut Aussage des Herstellers sind die statischen Abhängigkeiten das Problem.
    Und das kann ich dann auch ein Stück weit nachvollziehen.
    Wenn eine Assembly in ein temporäres verzeichnis geladen wird, kann es natürlich keine weiteren Assemblies finden, wie im selben Verzeichnis stehen sollten, wenn sie nicht mit geladen werden.
    Merkwürdiger Weise soll das ganze ja wunderbar funktionieren, wenn Excel nicht als ausführendes Programm läuft sondern eine eigenständige exe die DLL's aufruft / bindet.

    Ich bin grade dabei das zu testen, wird aber noch einiges dauer :(
    Donnerstag, 20. August 2009 11:18
  • Hallo Carsten,

    das ist zwar letztendlich Sache des Herstellers.
    Aber ein Programm/.NET Assembly sollte seine Datenablage nicht vom Programmspeicherort abhängig machen.

    .NET bietet reichlich Möglichkeiten eine Speicherung unabhängig davon zu realisieren.
    Z. B. über Environment.GetFolderPath auf ein Applikationsverzeichnis zuzugreifen.
    Was für Vista/Windows 7 ohnehin bei schreibendem Zugriff viele Probleme vermeidet, siehe z. B.
    http://social.msdn.microsoft.com/Forums/en-US/windowscompatibility/thread/74cee849-ed48-4de5-ac4b-0b35b7852ecd

    Wenn der Hersteller kooperativ ist, sollte man das vorzugsweise an der Stelle ändern.

    Gruß Elmar

    Donnerstag, 20. August 2009 14:48
    Beantworter
  • *lach*
    das ist das Problem...
    die wollen nur Kohle machen und da sind "Einzelschicksale" leider nicht zu berücksichtigen.
    Dieser arrogante Haufen verkauft Dir gerne Lizenzen um 15k€ pro Stück aber dann war's das auch mit Kooperation.
    Leider kann ich nicht drauf verzichten.

    Ich bin grade dabei das ganze mit einer standalone-Anwendung aufzubauen, die dann von außen auf Excel zugreift und dann das Handling mit Eplan (der CAD Software) übernimmt.
    Vielleicht klappt das ja.

    Gruß Carsten
    Freitag, 21. August 2009 06:32