Question

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 (il n'a donc pas de fenêtre ni d'interface utilisateur.) Mon problème est que je tente de déclencher un événement à partir d'un deuxième thread et que je reçois un «échec catastrophique» (0x8000FFFF). Si je déclenche l'événement à partir de mon thread principal, 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 Apartment, mais le passage à Free Threaded n'aide pas.

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

Par exemple, dans le fichier source de mon 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);
}
Était-ce utile?

La solution

Principes de base de COM

Dans STA, votre objet vit sur un seul thread (The Thread). Ce thread est celui sur lequel il a été créé, ses méthodes sont exécutées et ses événements sont déclenchés. Le STA veille à ce qu'aucune des deux méthodes de votre objet ne soit exécutée simultanément (car elles doivent être exécutées sur The Thread, il s'agit donc d'une conséquence intéressante).

Cela ne signifie pas que votre objet ne peut pas être accédé depuis d'autres threads. Cela se fait en créant des mandataires de votre objet pour chaque thread autre que The Thread. Sur le fil, vous emballez un IUnknown avec CoMarshalInterThreadInterfaceInStreamInStream et sur l’autre fil que vous décompressez avec CoGetInterfaceAndReleaseStream qui crée réellement le proxy sur l'autre thread. Ce proxy utilise la pompe de messages pour synchroniser les appels sur votre objet, les appels toujours exécutés sur le thread, de sorte que le 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, cela doit se produire dans MTA, votre objet doit donc résider dans MTA et votre classe doit donc être à thread libre. Les threads appartiennent à exactement un appartement, donc un thread ne peut pas être simultanément dans MTA et STA. Si votre objet réside dans MTA chaque fois qu'un objet STA tente de l'utiliser, il devra créer un proxy. Donc, vous obtenez une légère surcharge.

Ce que je suppose, c’est que vous pensez à une "technique" très intelligente. pour décharger votre thread principal et effectuer des "& as; activités asynchrones "" événements, qui ne voleront pas à la fin :-)) Si vous y réfléchissez, il y a un auditeur sur ce deuxième fil [travailleur] ...

Btw, cette ligne

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

peut être effectué dans MTA uniquement.

Autres conseils

Je pense que le vrai problème n'est pas la configuration de votre composant, mais le processus hôte. De nombreux hôtes, tels que ceux du modèle d’objet Office, résident dans un seul appartement threadé, auquel cas il n’est pas autorisé à les appeler à partir 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, en déplaçant l'appel réel vers une fonction dans une CoClass et en appelant cette fonction à partir de votre thread.
Cela ne fait que transmettre les messages de fenêtre en coulisse, mais vous évite de mettre cela en œuvre vous-même.

Filetage dans COM (wikipedia): < br> Le modèle STA (Single-Threaded Apartment) est un modèle très 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é à la gestion des 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 en provenance de threads situés en dehors de l'appartement sont organisés et automatiquement mis en file d'attente par le système (via une file d'attente de messages Windows standard). Ainsi, vous ne vous inquiétez pas des conditions de concurrence ou de l’absence de synchronicité, car chaque appel de méthode d’un objet est toujours exécuté jusqu’à son achèvement, avant qu’un autre ne soit appelé.

Voir aussi Message Pumping dans le même article.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top