COM Surrogate bzw. LOCAL_SERVER



  • Halli hallo,

    Ich hab gerade ein Problem mit einer COM-DLL die eine Ressource mit exklusiv-Rechten benutzt. Das führt natürlich zu Problemen, wenn mehrere Clients auf die Komponente zugreifen wollen. Nun dachte ich, dass ein COM-Surrogate die Lösung wäre, sodass die Clients dieselbe Instanz von dem COM-Objekt als LOCAL_SERVER teilen können.
    Allerdings funktioniert das irgendwie nicht so, wie ich es mir erhofft hatte. Und zwar bekommen die Clients unterschiedliche Schnittstellen-Zeiger. Das muss ja noch nichts heißen, da hängt ja das Surrogate und ein Marshaller dazwischen. Allerdings, wenn ich nun den Zustand des Objektes in den unterschiedlichen Clients abfrage bzw. verändere, bekommt der jeweils andere Client nichts davon mit. Also sind das im Moment immernoch unterschiedliche Instanzen pro Client (zumindest teilweise, weil ich keinen Fehler mehr erhalte, dass die Ressource schon in Benutzung wäre. Wie das sein kann, verstehe ich eigentlich auch nicht).
    Hat jemand eine andere Idee (abgesehen davon, dass man sich einen eigenen Server schreibt, der das Teil wrapped), wie man dasselbe COM-Objekt mit mehreren Clients teilen kann?


  • Mod

    Bei einem LOCAL_SERVER als EXE sollte das genau so sein. Ich benutze schon seit Jahren solche "COM-Singletons", mit dem sich mehrere Anwendungen Funktionen teilen.



  • Also ich habe gerade zwei unterschiedliche Verständnisfragen und eine Vorgehensfrage dazu:

    Die Exe definiert meinem Verständnis nach ein IClassFactory, auf das man ultimativ zuerst zugreift. Wenn die Exe nun gestartet wird, ruft sie CoRegisterClassObject auf, damit alle Clients am Ende einen Zeiger auf _dasselbe_ IClassFactory-Objekt bekommen (auch wenn der Zeiger lokal wegen Marshalling anders aussieht), ja? Darüber kann man CreateInstance aufrufen. Aber bedeutet nicht CreateInstance, dass eben immer eine neue Instanz des Objekts, was der Client eigentlich haben will, erzeugt wird?
    Oder ist man in der ClassFactory so frei, einfach ein Refcounted-Interface auf dasselbe Objekt mehrmals rauszureichen? Will meinen, muss CreateInstance wirklich eine neue Instanz erzeugen, oder darf es auch eine alte wiederverwenden?

    Wenn das da oben so geht, kann man der Standard-DLLSurrogate (Dllhost.exe) dasselbe Verhalten beibringen?

    Mir bleibt wahrscheinlich nichts anderes übrig, als ein Custom-Surrogate für die DLL zu erzeugen, oder? Die Dll kommt von extern, ich habe nur eine Schnittstellenbeschreibung zur Verfügung 😞

    Viele Grüße


  • Mod

    Es steht der Classfactory vollkommen frei, was sie zurück gibt. In der ATL Implementierung gibt es ja auch ein Singleton.

    Ich gehe noch einen Schritt weiter. Ich verwende die IROT. D.h. ich trage das Singleton dort ein. Jede Anwendung kann also in der IROT nachsehen ob das entsprechende Objekt im System schon existiert.

    Ich würde die DLL wrappen. Das heißt ich würde eine einfache EXE bauen die eine einzige Klasse und Methode hat wie GetMyDLLTarget). Diese EXE instanziiert das DLL Objekt (inprocess) und gibt den Zeiger auf dieses Objekt an den Aufrufenden zurück. Zugleich hält es eine Kopie des Zeigers auf die DLL...

    Den Rest mach COM für Dich.



  • Alles klar, vielen Dank, das sollte ich hinbekommen.
    Aber eigentlich schade, dass Windows nicht allgemein so ein SingletonSurrogate mitbringt!


  • Mod

    Decimad schrieb:

    Alles klar, vielen Dank, das sollte ich hinbekommen.
    Aber eigentlich schade, dass Windows nicht allgemein so ein SingletonSurrogate mitbringt!

    Vielleicht tut es das und es ist nur verborgen in der Doku... 😞
    Ich bin (leider) nicht allwissend.


  • Mod

    Ich habe mich aus Neugierde nochmal in das Thema eingelesen.
    Eigentlich müsste bei Verwendung des DLLHosts in jedem Flall ein und dieselbe DLL für alle Instanzen verwendet werden.

    Eigentlich haben viele eher das umgekehrte Problem wie ich gelesen habe. Viele wollen mehrere Instanzen Out of Process....



  • Hallo Martin,

    danke erstmal. Also ja, dieselbe DLL wird auf jeden Fall verwendet. Aber hier kommt es jetzt wohl auf die Nomenklatur an. Es werden dennoch 2 unterschiedliche Objekte von der ClassFactory, die da surrogiert wird, erzeugt.

    Mein Code sieht jetzt gerade so aus:

    _com_ptr_t<_com_IIID<IClassFactory, &__uuidof(IClassFactory)>> class_factory_ptr;
    HRESULT hr = CoGetClassObject(CLSID_Object, CLSCTX_LOCAL_SERVER, 0,IID_IClassFactory, (void**)static_cast<IClassFactory**>(&class_factory_ptr) );
    if(FAILED(hr))
    {
        bool arrrrr = true;
    } else {
        class_factory_ptr.GetInterfacePtr()->CreateInstance( NULL, IID_Object, (void**)static_cast<IObject**>(&object_ptr) );
    }
    

    Es macht jetzt auch gar keinen Unterschied, ob ich überhaupt den Umweg über die ClassFactory mache, ich bekomme immer das IObject*-Interface zu zwei verschiedenen Objekten.
    Vielleicht ist ja auch an meinem COM-Code etwas grundlegend falsch? CLSID_Object und IID_Object sind auf jeden Fall die GUIDs aus der .tlh-Datei, die #import erzeugt.


  • Mod

    Na dann tippe ich mal darauf, dass Deine DLL die Classfactory mit REGCLS_SINGLEUSE oder REGCLS_MULTI_SEPARATE registriert.



  • Hey,

    vielleicht denke ich gerade viel zu C++-Objektorientiert. Aber ich stelle mir das gerade so vor, dass dieses Flag nur anzeigt, ob man dieselbe Instanz der Factory erhält? Will meinen: man hat ja so auch keinen Einfluss darauf, in welcher Art die Factory selber dann die benötigten Interfaces "beschafft"?

    Oder anders ausgedrückt: Ich stelle mir so eine Factory gerade so vor, wie ich mir eben eine Factory als Pattern im eigenen C++-Code vorstelle. Die Schnittstelle sagt ja dort auch nichts über konkrete Zusammenhänge mit Objekten aus. Dort könnte man also auch speziell eine SingletonFactory implementieren, aber sieht ja der Schnittstelle nicht an, was eine abstrakte Factory dann tut.

    Woher kommt eigentlich die IClassFactory-Schnittstelle in der dll? Also so wie es für mich aussieht, handelt es sich bei meiner dll um eine von VB6 erzeugte. Hat der Programmierer da selber eine ClassFactory programmieren müssen, oder wird die ClassFactory automatisch aus den Schnittstellenbeschreibungen der VB-Klassen generiert und dort hätte man schon zur Zeit der DLL-Erstellung angeben müssen, dass man gerne ein Singleton hätte?

    Und noch eine andere Frage: Gibt es eine Möglichkeit herauszufinden, was hinter den Marshalled LOCAL_SERVER-Interfaces steckt? Also ob in dem LOCAL_SERVER-Prozess dieselben Interfaces gemeint sind, auch wenn man in eigenen Prozessen unterschiedliche Zeiger halt?

    Viele Grüße,
    Deci


  • Mod

    Nein!
    REGCLS_SINGLEUSE bedeutet, dass die Factory nach der ersten Benutzung sich deregistriert... 😉

    After an application is connected to a class object with CoGetClassObject, the class object is removed from public view so that no other applications can connect to it. This value is commonly used for single document interface (SDI) applications. Specifying this value does not affect the responsibility of the object application to call CoRevokeClassObject; it must always call CoRevokeClassObject when it is finished with an object class.



  • Hrmmm, wenn dem so wäre, dann würde ich doch kein zweites Objekt von der Klasse erzeugen können? Man kann doch eigentlich nur eine Factory pro CLSID registrieren, will meinen, wenn sich jemand dieser ClassFactory besorgt, wird niemand zweites mehr eine Factory für diese Klasse bekommen.
    Aber ich habe ja zwei unterschiedliche Instanzen von IID_Object, also 2 unterschiedliche Objekte, aber beide von der gleichen Klasse (es gibt überhaupt nur eine Klasse auf meinem System, die das besagte IObject implementiert). Also muss ich doch zweimal die CLSID_Object-Factory benutzt haben, die mir das Objekt rausgehauen hat, das ich dann über IUnknown nach IID_Object gefragt habe?


  • Mod

    Das stimmt nicht. Weil keine Factory existiert wird eine zweite Instanz der DLL geladen. Es ist doch dann genauso, als wenn noch nie eine Instanz erzeugt wurde.

    Es werden doch nicht durch COM prophylaktisch alle Factories erzeugt. Das genau ist ja schon der Unterschied zum Factory Pattern.



  • Oh, ich hatte das jetzt so verstanden, dass COM die dll/exe halt on-demand wie in der Registry eingestellt lädt, aber dann diese eine dll das ist, was überall reingemapped wird oder im surrogate oder dem exe-Local-Server-Prozess läuft. Dass sozusagen die Factory immer wiederverwendet wird, es sei denn, das will man unterbinden, dann war's das mit der Klasse. Late-Loading oder wie man das bezeichnen sollte.
    Na gut, dann kann ich wohl von außen nichts weiter tun. Wer weiß, ob die ClassFactory überhaupt damit klar käme, dass man mehrmals CreateInstance aufruft, oder wenn ja, ob dann immer die selbe Instanz für das IObject-Interface rausgeschmissen würde...


  • Mod

    Der sicherste Weg ist es bestimmt. Das Ganze selbst zu wrappen, wie ich es beschrieben habe. Da es sich aber um ein Objekt handelt ist es natürlich dennoch spannend wie dann der Zugriff aus mehreren Interface Zeigern läuft.



  • Ach, die Schnittstelle ist einfach, was das eigentlich Objekt macht, ist auch total trivial. Mir wurde nur explizit untersagt, die dll durch eine reverse-engineered (ich zitiere: "hacked") Eigenentwicklung zu substituieren. Deswegen muss die Software jetzt also mit ein paar VB6-dlls und der COM-dll installiert werden. Synchronisation wird kein großes Problem. Schöne neue Welt! 😃


  • Mod

    Dann hast Du Dir die Antwort gegeben. Bau ein COM Singleton in einem out of process Server, der nichts anderes macht als dieses Objekt in die IROT zu legen.

    Dann können sich theoretisch sogar andere Prozesse dieses Objekt aus der IROT ziehen. Marshalling macht COM für Dich.



  • Das mit dem Singleton war jetzt der Plan, auch wenn ich jetzt noch nicht daran gedacht habe, IRunningObjectTable zu verwenden und mich erstmal über Moniker schlau lesen muss (hatte jetzt eigentlich vor, einen einfachen Wrapper drumzubasteln, andere CLSID, aber unterstützt dann eben auch IObject. Wollte nur noch ein bissl erzählen, statt hier einfach auf stumm zu schalten. Da freut sich meiner Erfahrung nach der Unterstützer mehr 😉


  • Mod

    🙂



  • Martin Richter schrieb:

    Dann hast Du Dir die Antwort gegeben. Bau ein COM Singleton in einem out of process Server, der nichts anderes macht als dieses Objekt in die IROT zu legen.

    Dann können sich theoretisch sogar andere Prozesse dieses Objekt aus der IROT ziehen. Marshalling macht COM für Dich.

    Was ist, wenn das Objekt aus der ROT an den Client zurückgegeben wurde? Wie funktioniert das mit der Referenzzählung, wie wird so die Lebensdauer des Prozesses kontrolliert? So einfach ist das nicht, meine ich jedenfalls momentan noch. Mir scheint, als müsstest Du die Schnittstelle für dieses Vorhaben exakt nachbauen, damit Du entsprechend CoAddRefServerProcess/CoReleaseServerProcess matchen kannst. Die implementierten Methoden delegieren dann einfach nur.

    Ich glaube fast, ich würde da einfach einen Dienst drumrum stricken. Die IClassFactory erstellt das Objekt und ruft in CreateInstance einfach QueryInterface auf. Endet der Dienst, gibt es ein einfaches CoDisconnectObject und gut. Und da der Client scheinbar ohnehin nur CoCreateInstance aufruft, bringt Dir die ROT außer Gefummel auch keine Vorteile.

    Falls ich völlig neben der Spur bin, bitte ich um Aufklärung!



  • ... oder man baut sich ein eigenes Surrogate, ISurrogate zum implementieren ist ja nun auch kein Hexenwerk. 🙂


Anmelden zum Antworten