2009-10-29 3 views
3

J'ai créé un objet COM (DLL) in-process en utilisant ATL. Notez qu'il s'agit d'un objet et non d'un contrôle (donc pas de fenêtre ou d'interface utilisateur). Mon problème est que j'essaie de déclencher un événement à partir d'un deuxième thread et que je reçois un 'Catastrophic failure' (0x8000FFFF). Si je tire l'événement de mon thread principal, alors je ne reçois pas l'erreur. Le deuxième thread appelle CoInitializeEx mais cela ne fait aucune différence. J'utilise le modèle de thread d'appartement, mais passer à Free Threaded n'aide pas.Exécution d'un événement COM à partir d'un autre thread

Le fait que j'essaie de le faire à partir d'un deuxième fil est évidemment crucial. Existe-t-il un moyen facile de le faire ou vais-je devoir mettre en place une forme de messagerie à fenêtre cachée?

Par exemple, dans mon fichier source de l'objet principal:

STDMETHODIMP MyObject::SomeMethod(...) 
{ 
    CreateThread(NULL, 0, ThreadProc, this, 0, NULL); 
    // Succeeds with S_OK 
    FireEvent(L"Hello, world!"); 
    return S_OK; 
} 

DWORD WINAPI ThreadProc(LPVOID param) 
{ 
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 
    MyObject* comObject = reinterpret_cast<MyObject*>(param); 
    // Fails with 0x8000FFFF 
    comObject->FireEvent(L"Hello, world!"); 
} 

void MyObject::FireEvent(BSTR str) 
{ 
    ... 
    // Returns 0x8000FFFF if called from ThreadProc 
    // Returns S_OK if called from SomeMethod 
    pConnection->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL); 
} 

Répondre

4

bases COM

Dans STA vit votre objet sur un seul thread (le fil). Ce thread est celui sur lequel il a été créé, ses méthodes sont exécutées et ses événements sont activés. Le STA s'assure que deux méthodes de votre objet ne sont pas exécutées simultanément (parce qu'elles doivent être exécutées sur The Thread, donc c'est une bonne conséquence).

Cela ne signifie pas que votre objet n'est pas accessible à partir d'autres threads. Ceci est fait en créant des proxies de votre objet pour chaque thread autre que The Thread. Sur The Thread, vous empaquetez un IUnknown avec CoMarshalInterThreadInterfaceInStream et sur l'autre thread vous déballez avec CoGetInterfaceAndReleaseStream qui crée réellement le proxy sur l'autre thread. Ce proxy utilise le message pump pour synchroniser les appels vers votre objet, les appels qui sont toujours exécutés sur The Thread, donc The Thread doit être libre (non occupé) pour exécuter un appel depuis un autre thread.

Dans votre cas, vous souhaitez que votre objet puisse exécuter des méthodes sur un thread et déclencher des événements sur un autre thread. Donc ce a pour arriver dans MTA, donc votre objet doit vivre dans MTA, donc votre classe doit être libre. Les threads appartiennent à exactement un appartement, donc un thread ne peut pas être en MTA et STA simultanément. Si votre objet vit dans MTA chaque fois qu'un objet STA essaie de l'utiliser, il devra créer un proxy.Donc, vous obtenez un léger frais généraux. Ce que j'imagine, c'est que vous pensez à une "technique" très astucieuse pour décharger votre thread principal, et faire des "async" événements, qui ne voleront pas à la fin :-)) Si vous y pensez là-bas a à un auditeur sur ce second fil [travailleur] ...

BTW, cette ligne

MyObject* comObject = reinterpret_cast<MyObject*>(param); 

peut se faire que dans le MTA.

+0

Très bonne explication. Merci beaucoup. – Rob

0

Je pense que le vrai problème est pas ce que votre composant est configuré avec, mais le processus d'accueil. De nombreux hôtes, comme ceux du modèle objet Office, vivent dans un seul appartement fileté, auquel cas il n'est pas autorisé à les appeler à partir de n'importe quoi sauf du thread principal.
Si tel est le cas, vous pouvez laisser COM effectuer le travail en utilisant le modèle d'appartement à un seul thread et en déplaçant l'appel réel vers une fonction dans une CoClass et en invoquant cette fonction à partir de votre thread. Cela ne fait que passer les messages de la fenêtre dans les coulisses, mais vous évite de l'implémenter vous-même.

Threading in COM (wikipedia):
Le modèle Appartement monothread (STA) est un modèle très couramment utilisé. Ici, un objet COM se trouve dans une position similaire à l'interface utilisateur d'une application de bureau. Dans un modèle STA, un seul thread est dédié pour piloter les méthodes d'un objet, c'est-à-dire qu'un seul thread est toujours utilisé pour exécuter les méthodes de l'objet. Dans un tel arrangement, les appels de méthode à partir de threads à l'extérieur de l'appartement sont rassemblés et automatiquement mis en file d'attente par le système (via une file d'attente de messages Windows standard). Ainsi, il n'y a pas d'inquiétude concernant les conditions de course ou le manque de synchronicité, car chaque appel de méthode d'un objet est toujours exécuté jusqu'à son achèvement avant qu'un autre soit appelé.

Voir également Message Pumping dans le même article.

+0

Avez-vous plus de détails sur ce qui serait impliqué ici? – Rob

+0

Mis à jour la réponse avec plus d'informations - ne pas avoir le temps maintenant d'explorer msdn pour des détails concis :) –

+0

Pour raccourcir si: si votre application hôte est un STA vous devez appeler à partir du thread propriétaire ou invoquer l'appel sur le bon fil à travers COM (marshalling). Sinon, vous devez prendre soin d'appeler du bon fil vous-même. –

Questions connexes