Question

Venez à travers ce qui semble à première vue comme un MT-question, mais je suis en train de comprendre en détail le modèle STA utilisé par COM +.

En effet, j'ai un composant COM + héritage, écrit en VB6, qui remet en natif (à savoir, non-COM) DLL Win32 écrit en C ++.

quelques problèmes avec elle, j'ai ajouté un code de débogage intermittente (et impossible à reproduire dans les tests) pour savoir ce qui se passait et a constaté que lorsque les problèmes surviennent, je devais enregistrer des messages intercalées dans le fichier - il sous-entendu que la DLL a été appelé par deux fils à la fois.

Maintenant, l'enregistrement va à un, il semble donc fichier par thread basé sur _getpid () et GetCurrentThreadId () que lorsque le code dans la DLL C ++ est appelé, il se fait appeler deux fois sur le même fil en même temps. Ma compréhension de STA dit suggère que ce pourrait être le cas comme COM Marshalls les cas individuels d'objets sur un seul thread et suspend l'exécution reprend à son gré.

Unfortuantly Je ne sais pas où aller d'ici. Je lis que je devrais appeler CoInitialiseEx () dans DllMain () pour dire que ce COM est une DLL STA, mais d'autres endroits que c'est valable uniquement pour les DLL COM et n'aura aucun effet dans une DLL native. La seule autre option consiste à envelopper des parties de la DLL comme des sections critiques pour sérialiser l'accès (en ce qui a frappé la performance qui a le menton).

Je pourrais essayer de retravailler la DLL, mais il n'y a pas d'état partagé ou vars mondial - tout est dans les variables locales en théorie chaque appel doit obtenir sa propre pile, mais je me demande si le modèle STA est d'avoir un peu bizarre au fond effet sur ce sujet et tout simplement ré-entrer dans la DLL déjà chargé au même point d'entrée comme un autre appel. Unfortuantly, je ne sais pas comment prouver ou tester cette théorie.

Les questions sont essentiellement:

  1. Lorsqu'un composant STA COM + appelle une DLL native, il n'y a rien dans le modèle STA empêcher le « fil » actif étant suspendu et le contrôle étant passé à un autre « fil » au milieu d'un appel DLL?
  2. est CoInitialiseEx () la bonne façon de résoudre ce problème, ou non?
  3. Si aucune (1) ou (2) sont des hypothèses "bonnes", ce qui se passe?
Était-ce utile?

La solution

Dans un serveur COM appartement thread, chaque instance de la classe COM est garanti à des accès par un seul fil. Ce moyen l'instance est thread-safe. Cependant, de nombreux cas peuvent être créés simultaniously, en utilisant différents threads. Maintenant, en ce que le serveur COM est concerné, votre DLL native ne pas faire quelque chose de spécial. Il suffit de penser kernel32.dll, qui est utilisé par tous les exécutables - COM-il initialize lorsqu'il est utilisé par un serveur COM

Du point de vue de la DLL, vous devez vous assurer que vous êtes thread-safe, comme les différentes instances peuvent vous appeler en même temps. STA ne vous protégera pas sur ce cas. Puisque vous dites que vous n'utilisez pas toutes les variables globales, je ne peux que supposer que le problème est ailleurs, et arrive juste pour montrer les circonstances qui semblent pointer vers les choses COM. Êtes-vous sûr que vous n'avez pas des problèmes de mémoire C ++ bon vieux?

Autres conseils

Je soupçonne que votre problème est que quelque part au plus profond de la DLL appelée, il a fait un appel COM sortant vers un autre appartement (un autre fil dans le même processus, ou un objet dans le MTA, ou un autre processus entièrement). COM permet à un thread STA attendre le résultat d'un appel sortant pour recevoir un autre appel entrant, la traiter de manière récursive. Il est prévu que pour les conversations en cours entre les mêmes objets - à savoir A appelle B, B appelle un retour, A appelle B à nouveau - mais peut recevoir des appels d'autres objets si vous avez remis à un pointeur d'interface pour plusieurs clients, ou le client a partagé le pointeur d'interface à un autre client. En général, il est une mauvaise idée de distribuer des pointeurs d'interface à un objet mono-thread à plusieurs threads clients, car ils ne doivent attendre les uns des autres. Créer un objet travailleur par thread.

COM ne peut pas suspendre et reprendre l'exécution à volonté sur un fil - un nouvel appel entrant sur un thread STA ne peut arriver par la pompe de message. Lorsque « bloqué » en attente d'une réponse, le thread STA est en fait pompage des messages, vérifier avec le filtre de message (voir IMessageFilter) si le message doit être manipulé. Toutefois, les gestionnaires de messages ne doivent pas faire un nouvel appel sortant - si elles COM renvoie une erreur de RPC_E_CANTCALLOUT_INEXTERNALCALL ( «Il est illégal d'appeler tandis que le filtre de message à l'intérieur »)

Des problèmes similaires pourraient se produire si vous avez une pompe de message (GetMessage / DispatchMessage) partout dans la DLL native. J'ai eu des problèmes avec les DoEvents de VB dans les procédures d'interface.

CoInitializeEx ne doit être appelé par le créateur d'un fil, car seulement ils savent ce que leur comportement de pompage de message sera. Il est probable que si vous essayez de l'appeler dans DllMain il échouera tout simplement, que votre DLL native est appelée en réponse à un appel COM, de sorte que l'appelant doit avoir en fin de compte déjà appelé CoInitializeEx sur le fil afin de faire l'appel. Le faire dans la notification DLL_THREAD_ATTACH, pour les fils nouvellement créés, pourraient travailler superficiellement mais provoquer le programme dysfonctionnement si les blocs COM quand il doit pomper et vice-versa.

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