Problem mit Isolated COM



  • Hallo erstmal,

    als Aufgabe hatte ich es, eine .Net DLL in ein MFC Projekt einzubinden. Das ganze läuft dann auch soweit. Jetzt soll ich allerdings auch noch das ganze ohne aufrufen von RegAsm.exe hinbekommen. Dafür habe ich mir ein paar Artikel bzgl. Isolated COM durchgelesen und das ganze hat sich dann auch erstmal recht einfach angehört.

    Wenn ich das jedoch so mache, wie z.B. in http://qualapps.blogspot.de/2007/06/isolated-com.html beschrieben erhalte ich erst einmal die Fehlermeldung, dass ein Fehler in dem Application Manifest wäre. Daher habe ich in den Projekteinstellungen bei "Embedded Manifest" Nein eingestellt, so dass ich das Manifest noch bearbeiten kann (aus irgendeinem Grund fehlt dort im AssemblyIdentity Tag immer das "version" und "type" Attribut, die laut http://msdn.microsoft.com/en-us/library/aa374219.aspx benötigt werden).

    Der Aufbau meiner kleinen TestApp ist im übrigen so ...
    MFC Anwendung importiert Native C++ DLL
    Native C++ DLL importiert Managed DLL

    Meine Manifeste sehen dann mittlerweile so aus ...

    ManifestTest.exe.manifest:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity name="ManifestTest.exe" version="1.0.0.1" type="win32">
      </assemblyIdentity>
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
          <requestedPrivileges>
    	    <requestedExecutionLevel level="asInvoker" uiAccess="false">
          </requestedExecutionLevel>
          </requestedPrivileges>
        </security>
      </trustInfo>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity name="NativeDll.dll" version="1.0.0.1" type="win32"/>
        </dependentAssembly>
      </dependency>
    </assembly>
    

    NativeDll.dll.manifest:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity name="NativeDll.dll"  version="1.0.0.1" type="win32">
      </assemblyIdentity>
      <file name="ManagedDll.dll" hashalg="SHA1">
        <comClass clsid="{0490E148-FFD2-4909-A4B8-3533D7F264D0}" tlbid="{75047FBB-93E4-4620-86C1-9BDDCDC74423}">
        </comClass>
    	<typelib tlbid="{75047FBB-93E4-4620-86C1-9BDDCDC74423}" version="1.0" helpdir="" flags="HASDISKIMAGE">
        </typelib>
      </file>
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
      </trustInfo>
      <comInterfaceExternalProxyStub name="IManagedInterface" iid="{0490E148-FFD2-4909-A4B8-3533D7F264F1}" tlbid="{75047FBB-93E4-4620-86C1-9BDDCDC74423}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}">
      </comInterfaceExternalProxyStub>
      <comInterfaceExternalProxyStub name="_ImplOfInterface" iid="{4AA9A33F-5062-3E68-A48F-CF10D3E63650}" tlbid="{75047FBB-93E4-4620-86C1-9BDDCDC74423}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}">
      </comInterfaceExternalProxyStub>
    </assembly>
    

    Im ManifestTest.exe.manifest habe ich noch manuell die "dependency" Tags hinzugefügt, da sonst beim Aufruf von CoCreateInstance immer der Fehler zurück kam, dass die Klasse nicht registriert sei. Jetzt erhalte ich dort immer den Fehler, dass ein Fehler in der DLL wäre (hier gibt es leider keine genauere Angabe und GetLastError() liefert hier dann eine 127 zurück).
    Da das Ganze ja funktioniert, wenn ich die managed DLL mit RegAsm.exe registriere, glaube ich aber irgendwie nicht daran, dass ein Fehler in der DLL ist.
    Ausserdem musste ich in dem NativeDll.dll.manifest noch beim name Attribut im file tag den Pfad zu der ManagedDll.dll entfernen. Zumindest stand im Anwendungs-Ereignislog, dass in dieser Zeile ein Fehler wäre und nachdem ich den Pfad zu der DLL entfernt habe und dort nur noch den Dateinamen habe stehen lassen, liess sich die Anwendungen starten (Dafür kopiere ich dann die ManagedDll.dll + .tlb neben die ManifestTest.exe damit es zu keinen Pfadproblemen kommt).
    Mache ich da noch irgendwas grundlegend falsch mit den Manifesten?

    Hier noch der Code aus der .Net Dll

    using System;
    using System.Runtime.InteropServices;
    
    namespace ManagedDll
    {
        [ComVisible(true)]
        [Guid("0490E148-FFD2-4909-A4B8-3533D7F264F1")]
        public interface IManagedInterface
        {
            int DoSth();
        }
    }
    
    using System;
    using System.Runtime.InteropServices;
    
    namespace ManagedDll
    {
        [ComVisible(true)]
        [Guid("0490E148-FFD2-4909-A4B8-3533D7F264D0")]
        public class ImplOfInterface : IManagedInterface
        {
            public int DoSth()
            {
                int a = 0 + 42;
                return a;
            }
        }
    }
    

    Was mich da halt auch ein wenig wundert ist, dass die GUID für die Klasse ImplOfInterface nirgendwo in der Manifest Datei auftaucht, sondern nur etwas für ein _ImplOfInterface Objekt. (Im grossen und ganzen lasse ich die Manifeste halt automatisch generieren, bis auf die genannten manuellen Änderungen)

    Und zum Schluss noch der Code aus der NativeDll.dll

    #include "stdafx.h"
    #include "NativeDll.h"
    #import "../ManagedDll/bin/Debug/ManagedDll.tlb" named_guids
    
    void MyNativeClass::foo()
    {
      try
      {
        CoInitialize(NULL);
    
        ManagedDll::IManagedInterface *cpi = NULL;
        DWORD dwLastError = GetLastError();
        dwLastError = 0;
    
        HRESULT hr = CoCreateInstance(ManagedDll::CLSID_ImplOfInterface, NULL, CLSCTX_INPROC_SERVER, ManagedDll::IID_IManagedInterface, reinterpret_cast<void**>(&cpi));
    
        dwLastError = GetLastError();
    
        if(cpi)
        {
          int result = cpi->DoSth();
          cpi->Release();
          cpi = NULL;
        }
        CoUninitialize();
      }
      catch (...)
      {
      }
    }
    

    Und zum Schluss noch ein Auszug aus einem SxsTrace.

    ...
    			INFORMATION: Startet die Assemblierungssuche.
    				INFORMATION: Die Assemblierung in WinSxS wurde nicht gefunden.
    				INFORMATION: Es wurde kein Manifest für die Kultur "de" gefunden.
    			INFORMATION: Beendet die Assemblierungssuche.
    INFORMATION: Manifestdatei "C:\dev\WCF\ManifestTest\ManifestTest\Debug\NativeDll.dll.MANIFEST" wird analysiert.
    	INFORMATION: Die Manifestsdefinitionsidentität ist "NativeDll.dll,type="win32",version="1.0.0.1"".
    INFORMATION: Die Generierung des Aktivierungskontextes war erfolgreich.
    Beendet die Generierung des Aktivierungskontextes.
    

    So ... dass war es dann aber auch erst einmal an Infos, die mir eingefallen wären und welche vlt. nützlich sein könnten.

    Falls jmd. meinen Fehler sieht (also ... der angebliche Fehler in der DLL), oder mir zumindest einen Hinweis geben kann wie ich diesen Fehler eingrenzen kann, wäre ich äusserst dankbar.



  • Du hast da glaube ich was missverstanden...
    1. Du verwendest eine C#-Assembly als COM; dafür braucht man schon mal ein spezielles Manifest
    2. Du verwendest die C#-Assembly in einer native-DLL, oder? Dann brauchst Du in der EXE schon mal kein Manifest für die natuve DLL. Das ist überflüssig.

    Also:
    1. Nur Deine native DLL braucht eine eingebettetes Manifest der FOrm:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     <assembly
       xmlns="urn:schemas-microsoft-com:asm.v1"
       manifestVersion="1.0">
       <assemblyIdentity
         type = "win32"
         name = "NativeDLL"
         version = "1.0.0.0"
         />
       <dependency>
         <dependentAssembly>
           <assemblyIdentity
             type="win32"
             name="ManagedDLL"
             version="1.0.0.0" />
         </dependentAssembly>
       </dependency>
     </assembly>
    

    Deine Managed-DLL braucht auch ein Manifest mit den passenden Einträge zu den COM-Klassen.
    Das kannst Du Dir auch erzeugen lassen.
    http://www.c-plusplus.net/forum/p2183918

    In dem obigen Link ist auch beschrieben, was man machen muss.

    Mein Vorschlag: Mach es zuerst mal mit einer Simplen Anwendung (EXE) und ManagedDLL, bevor Du es mit EXE, NativeDLL und ManagedDLL versuchst.... sishe obiger Link...



  • Wie Du richtig bemerkt hast, kenne ich mich mit den Manifesten wirklich nicht so besonders aus. Ich habe gerade auch einmal die native-Dll entfernt und erst einmal das Beispiel aus dem Post von sibourg nachgebaut. So funktioniert das ganze schon mal einwandfrei.
    Aber diesen Aufbau mit exe - nativeDll - managedDll hab ich ja auch nicht aus Jux-und-Dollerei gemacht. 🤡

    Daher hab ich nochmal rumgeproscht. So ... hier dann meine Manifeste in der Endfassung ... mit NativeDll.

    ManifestTest.exe.manifest

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity name="ManifestTest" version="1.0.0.1" type="win32"></assemblyIdentity>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity name="NativeDll" version="1.0.0.1" type="win32"></assemblyIdentity>
        </dependentAssembly>
      </dependency>   
    </assembly>
    

    NativeDll.manifest

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="NativeDll" version="1.0.0.1" type="win32"></assemblyIdentity>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity name="ManagedDll" version="1.0.0.0" processorArchitecture="msil"></assemblyIdentity>
      </dependentAssembly>
      </dependency> 
    </assembly>
    

    ManagedDll.manifest

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity name="ManagedDll" version="1.0.0.0" processorArchitecture="msil"></assemblyIdentity>
      <clrClass clsid="{0490E148-FFD2-4909-A4B8-3533D7F264D0}" progid="ManagedDll.ImplOfInterface" threadingModel="Both" name="ManagedDll.ImplOfInterface" runtimeVersion=""></clrClass>
      <file name="ManagedDll.dll" hashalg="SHA1"></file>
    </assembly>
    

    Dank Dir nochmal. Du hast mir heute den Tag gerettet!

    Nur noch eine kleine Frage:
    Du hattest geschrieben, dass ich für die EXE kein Manifest bräuchte. Als ich es zuerst ohne Manifest für die ManifestTest.exe probiert habe, bekam ich denoch den Fehler bei CoCreateInstance(), dass die Klasse nicht registriert sei und im Eventlog stand

    Fehler beim Generieren des Aktivierungskontextes für "C:\dev\WCF\ManifestTest\ManifestTest\Debug\ManifestTest.exe.Manifest". Die
    abhängige Assemblierung "NativeDll,type="win32",version="1.0.0.1"" konnte nicht gefunden werden. Verwenden Sie für eine detaillierte
    Diagnose das Programm "sxstrace.exe".
    

    (Die SxsTrace Datei blieb hierbei leer)

    Daher habe ich halt das Manifest für die EXE hinzugefügt und so funktioniert es. Wieso musste ich es denn in diesem Fall hinzufügen? Ich meine ... erst einmal bin ich glücklich das es irgendwie funktioniert, aber verstehen was man macht hat noch nie geschadet 😉


  • Mod

    Ich bezweifle, dass dieseMeldung dait zu tun hat.

    Das Problem beim Testen dieser Manifest Geschichten ist dass Windows hier kräftig cached. Hier wird nicht an Entwickler gedacht, dass sich Manifeste ändern können. In 99,999% der Fälle stimmt das ja auch.

    Siehe Fusion Cache
    http://blogs.msdn.com/b/vistacompatteam/archive/2006/11/13/manifest-and-the-fusion-cache.aspx
    http://blog.m-ri.de/index.php/2011/04/25/auf-was-man-unbedingt-achten-muss-wenn-man-an-manifesten-fur-assemblies-herumbastelt/



  • Du brauchst für die EXE nur kein Manifest, wenn Du die nativeDLL verwendest.
    Abgesehen davon sollte man heute immer ein Manifest haben 😉

    Das Manifest der native.DLL muss aber eingebettet sein!


Anmelden zum Antworten