Von verschiedenen Threads auf Com-Server zugreifen



  • Hallo,

    ich bin gerade dabei einen Corba-Server schreiben der Methoden bereit stellt, welche wiederrum dann auf einen Com-Serve zugreifen. Ich habe nur das Problem das die bereitgestellten Methoden vom Client mit unterschiedlichen Threads aufgerufen werden und ich dann nicht auf das selbe Com-Objekt zugreifen kann. Ich erhalte dann immer eine Exception.

    Also Beispiele wären diese Codefragemente...

    Datei.h

    #import "libid:E60738C2-DEE8-41E3-B538-9306544CB32F" //MyWrapper
    CComPtr<MyWrapper> pMyWrapper;

    Datei.cpp

    //Wird beim starten des Corba-Servers mit aufgerufen
    void init()
    {
    hr = CoInitialize(NULL);

    hr=pUnknown.CoCreateInstance(__uuidof(BEISPIELLib: :MyWrapper));

    hr=pUnknown->QueryInterface(&pMyWrapper);

    pMyWrapper->Methode1();
    }

    //Wird über Corba aufgerufen
    void deInit()
    {
    pMyWrapper->MEthode2();
    }

    Ich initialsiere beim starten des Com-Server die Mehtode init, welche dann als Thread läuft.
    Wenn ich dann über den Corba-Client die Methode2 aufrufe erhalte ich einen Exception bei pMyWrapper->Methode2(). Wie kann ich sicherstellen das ich von unterschiedlichen Threads auf das selbe Com-Objekt zugreife?
    Hat jemand eine Idee?

    Ich habe es bereits mit folgendes versucht...
    http://www.microsoft.com/germany/msd....mspx?mfr=true

    Am Ende der Methode1 gebe ich folgendes ein

    CoMarshalInterThreadInterfaceInStream (__uuidof(pMyWrapper), pMyWrapper, &pStream);

    und in der Methode2 rufe ich dann gleich beim start

    CoGetInterfaceAndReleaseStream (pStream, __uuidof(pMyWrapper), (void**) &pMyWrapper);
    Aber auch das bringt nicht.

    Gruß
    cos



  • Wenn in dem Programm keine GUI sowie keine ActiveX Komponenten vorkommen dann initialisiere COM einfach mit CoInitializeEx(0, COINIT_MULTITHREADED) (in jedem Thread!), dann kannste COM Interface Pointer einfach von Thread zu Thread weiterreichen und verwenden.

    Ansonsten musst du die Interfaces rum-marshallen, z.B. über CoMarshalInterface, CoMarshalInterThreadInterfaceInStream oder den Global Interface Table.



  • Funktioniert das mit dem CoInitializeEx(0, COINIT_MULTITHREADED); auch mit Com-Servern die als singlethreaded entwickelt worden sind?

    Denn ich binde 3 Com-Server ein (3 DLLs). Und bei bei einem mache ich beim QueryInterface so...

    IMyModule* m_module = NULL;	
    IMyLogger* m_logger = NULL;
    
    hr = pMyJob->QueryInterface(__uuidof(IMyModule),(void**)&m_module);
    if(hr == S_OK)
    {
      cout << "MyJob: QueryInterface erfolgreich" << endl;
    }
    

    Da erhalte ich aber immer ein E_NOINTERFACE.
    Bei den anderen beiden geht es. Da übergebe ich dem QueryInterface aber auch nur __uuidof(IxyzJob).

    Eine Idee?



  • Und wie könnte ich dann den Com-Interface-Pointer weitergeben?

    Außerdem habe ich das mit dem CoInitializeEx versucht.

    hr = CoInitialize(NULL); //das geht auch mit dem 3 com-Server
    hr = CoInitializeEx(0, COINIT_MULTITHREADED); //!das hier nicht!
    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); //das geht euch mit dem 3 com-Server



  • Funktioniert das mit dem CoInitializeEx(0, COINIT_MULTITHREADED); auch mit Com-Servern die als singlethreaded entwickelt worden sind?

    Jain. Wenn der Prozess der CoInitializeEx(0, COINIT_MULTITHREADED) macht nur Client von Komponenten ist die als singlethreaded (apartment-threaded) entwickelt worden sind dann funktioniert es oft aber nicht immer. Ein Beispiel für wo es nicht geht sind die meisten ActiveX Komponenten (ActiveX == apartment-threaded). Wenn dagegen der Code dieser Komponenten in einem neuen Server "neu verpackt" wird kann man auf keinen Fall einfach COINIT_MULTITHREADED - der Code der "alten" Komponenten würde damit wohl kaum klarkommen.

    Aber... wenn du mit CoInitializeEx(NULL, COINIT_MULTITHREADED) Probleme hast dann würde ich vorschlagen erstmal CoInitialize zu lassen, denn auf anderem Wege lässt sich das Problem vermutlich schneller lösen.

    BTW: CoInitialize(NULL) und CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) sind äquivalent, ist egal welches von beiden du nimmst. Daher auch nicht weiter verwunderlich dass es bei dir dasselbe Verhalten zeigt 😉

    Soviel zu COINIT_MULTITHREADED.

    ----

    (...) Da erhalte ich aber immer ein E_NOINTERFACE.

    Erstmal: bist du sicher dass das Interface auch implementiert ist, denn E_NOINTERFACE deutet eher darauf hin dass es nix mit Threading zu tun hat, sondern das Interface einfach ... nicht implementiert ist.

    Davon abgesehen: aus welchem Thread rufst du pMyJob->QueryInterface auf? "OK" ist es nur wenn es der gleiche Thread ist der auch das Interface in pMyJob "auf legalem Weg" erstanden hat. Also entweder über CoCreateInstance oder über einen anderen Aufruf auf ein Interface welches diesem Thread geöhrt. Oder auf einem der anderen "legalen" Wege (Marshaling z.B.).

    Wenn du aber ein Interface in Thread A bekommst (woher auch immer) und in Thread B verwenden willst dann musst du es "marshal-en". Der einfachste mir bekannte Weg ist über den Global Interface Table. Sieht inetwa so aus:

    DWORD MarshalFooInGIT(IFoo* foo) // "foo" muss für den Thread der dashier aufruft natürlich erstmal gültig sein
    {
        DWORD cookie = 0;
    
        IGlobalInterfaceTable git = 0;
        HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &git);
    
        if (SUCCEEDED(hr))
        {
            // OK, wir haben das Interface auf den GIT
            hr = git->RegisterInterfaceInGlobal(foo,IID_IFoo, &cookie);
            if (SUCCEEDED(hr))
            {
                // OK, wir haben das interface im GIT registriert
                assert(cookie != 0);
            }
            else
                cookie = 0;
    
            git->Release();
        }
    
        return cookie;
    }
    
    // Solange du das Interface im GIT registriert lässt kannst du das cookie nun rumreichen,
    // quasi als "handle" auf das Interface.
    // "Rausholen" kannst du das Interface mit dem Cookie so:
    
    void UseFoo(DWORD fooCookie) // kannst du aus einem beliebigen Thread aufrufen, solange dieser *irgendwie* COM initialisiert hat
                                // (CoInitialize, CoInitializeEx oder zur Not OleInitialize)
    {
        IGlobalInterfaceTable git = 0;
        HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &git);
    
        if (SUCCEEDED(hr))
        {
            // OK, wir haben das Interface auf den GIT
            IFoo* foo = 0;
            hr = git->GetInterfaceFromGlobal(fooCookie, IID_IFoo, (void**) &foo);
            if (SUCCEEDED(hr))
            {
                // OK, wir haben das IFoo interface :) :)
                foo->FOOOOO();
                foo->Release(); // müssen wir freigeben, standard COM Regeln (was wir "getten" müssen wir auch "releasen")
            }
            git->Release();
        }
    }
    
    // Und "rauslöschen" aus dem GIT geht so:
    bool RemoveFooFromGIT(DWORD fooCookie) // kannst du AFAIK auch aus einem beliebigen Thread aufrufen solange dieser COM initialisiert hat
    {
        IGlobalInterfaceTable git = 0;
        HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &git);
    
        if (SUCCEEDED(hr))
        {
            hr = git->RevokeInterfaceFromGlobal(fooCookie);
            git->Release();
        }
    
        return SUCCEEDED(hr);
    }
    

    Ich übernehme keine Garantie für den Code da oben, hab das einfach so hier reingetippt, aber inetwa so geht das. (Ich hab' das schonmal nach dem Schema verwendet und hat funktioniert, also weit daneben kanns nicht sein, bloss hab ich den Code von damals jetzt nicht hier).



  • Hallo Hustbaer,

    danke für die Infos. D.h. wenn bei mir das mit dem CoInitializeEx(Null, Multithreaded) nicht funktioniert muss ich es marshallen und CoInitialize(Null) verwenden. Richtig?
    Und wenn ich drei Com-Server habe muss ich für jeden den einen Cookie machen und diese dann rumreichen.
    Das QueryInterface rufe ich übrigens aus dem Corba-Server aus auf. Dieser initialisiert dann alles und dann ruft der Corba-Client aus der selben Klasse verschiedene Methoden auf die Methoden des Com-Servers aufrufen.
    Ich werde das aber testen und wenns nicht klappt melde ich mich nochmal.
    Aber erstmal Danke.

    Gruß
    cos


  • Mod

    So ist es!
    Ich würde übrigends immer marshallen.
    Wenn später aus technischen Gründen auf free theaded umgestellt wird, mach dies keinen Nachteil. Es kostet keine Zeit, weil einfach der Zeiger aus der GIT returniert wird ohne Overhead.
    Muss man sein Konzept aber später in ein STA Modell zwängen ist es gut, wenn immer schon gemarshallt wurde.



  • Hallo,

    ich versuche das gerade mit dem Cookie, aber wenn ich mir die Daten aus dem Cookie hole bleibt mein Programm stehen.
    Zur Info. Ich initialisere den Com-Server im Corba-Server jetzt so...

    hr = CoInitialize(NULL);
    
    	//MyWrapper
    	hr=pUnknown.CoCreateInstance(__uuidof(MyWrapper)); 
    
      //als CComPtr<IAsamWrapper> pMyWrapper; in der *.h deklariert
    	hr=pUnknown->QueryInterface(&pMyWrapper); 
    
    	//in der selben Methode mache ich das hier; also selber Thread
    	cookie = 0; //Im Header deklariert: DWORD cookie;
    	git = 0; //Im Header deklariert: IGlobalInterfaceTable* git;
    
    	HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &git);
    
    	// OK, wir haben das Interface auf den GIT
    	hr = git->RegisterInterfaceInGlobal(pMyWrapper,__uuidof(MyWrapper), &cookie);
    	if (SUCCEEDED(hr))
    	{
    		// OK, wir haben das interface im GIT registriert
    		assert(cookie != 0);
    	}
    	else
    		cookie = 0;
    
    	git->Release();
    

    die beiden anderen Com-Server werden in der selben Methode initialsiert. Kann ich da dann den selben Cookie nehmen? Oder wäre es besser wenn ich einfach cookie1, cookie2 und cookie3 anlegen würden?

    Im Com Corba-Client rufe ich eine andere Methode auf mit folgenden Inhalt:

    CoInitialize(NULL);
    
    git = 0;
    HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &git);
    
    if (SUCCEEDED(hr))
    {
      CComPtr<IMyWrapper> pMyWrapper = 0;
      // OK, wir haben das Interface auf den GIT
      hr = git->GetInterfaceFromGlobal(cookie, __uuidof(MyWrapper), (void**) &pMyrapper);
      if (SUCCEEDED(hr))
      {
    
    	   hr = pMyWrapper->DeInit();
      }
      git->Release();
    }
    

    und bei git->GetInterfaceFromGlobal bleibt mein Server und Client einfach stehen. Ohne Fehlermeldung.

    Woran liegt das? Was mache ich falsch?



  • 1.) Dass du den GIT jedesmal neu anlegst und freigibst ist schonmal richtig, nur würde ich eine lokale Variable dafür verwenden, keine globale. Sonst kommen sich wieder verschiedene Threads beim Zugriff darauf in die Quere.

    2.) Natürlich musst du wenn du 3 Sachen in den GIT steckst auch 3 Cookies verwenden - das Cookie ist ja sozusagen das "Handle" auf das Interface.

    Die beiden Punkte sind IMO falsch und gehören korrigiert. Allerdings hab' ich diese Fehler auch nie gemacht und kann daher nicht sagen ob das "Stehenbleiben" daran liegt oder an etwas anderem.



  • Ich verwende im Moment erstmal nur "einen" Com-Server, also auch nur einen Cookie und habe habe nun auch den git als lokale Variabele initalisiert. Kann der Fehler auch daran liegen das ich dne git so initialisiere?

    IGlobalInterfaceTable* git = 0;

    Denn wenn ich einen Zeige verwende erhalte ich beim Übersetzen einen Fehler....


  • Mod

    Du musst jedes Interface über die GIT marschallen.
    Das hängt nicht davon ab ob dies ein Server ist...


Anmelden zum Antworten