2009-09-04 7 views
1

Disons que je le code suivant qui est exécuté dans un thread d'une application COM multi-thread:Comment utiliser LoadLibrary dans plusieurs threads COM Accross?

// Thread 1 
struct foo { 
    int (*DoSomething)(void ** parm); 
}; 

HMODULE fooHandle = LoadLibrary("pathToFoo"); 
typedef int (*loadUpFooFP)(foo ** fooPtrPtr); 
loadUpFooFP loadUpFoo; 
loadUpFoo = (loadUpFooFP)GetProcAddress(fooHandle, "fooLoader"); 
foo * myFoo; 
loadUpFoo(&myFoo); 

Tout cela fonctionne très bien et je peux alors appeler

myFoo->DoSomething(&parmPtr); 

Cela fonctionne aussi ! Maintenant, un autre fil vient et charge son foo:

// Thread 2 
foo * myFooInThread2; 
loadUpFoo(&myFooInThread2); 

Et cela, aussi, fonctionne très bien. Dans fil 2, je peux appeler DoSomething:

// Thread 2 
myFooInThread2->DoSomething(&anotherParmPtr); 

Maintenant, lorsque le fil 1 éventuellement en va, j'ai un problème. Je remarque que déboguer dans Visual Studio que l'adresse de DoSomething ne peut plus être évaluée. Après le premier fil meurt, quand j'appelle:

myFooInThread2->DoSomething(&anotherParmPtr); 

Je reçois une violation d'accès. Le pointeur myFooInThread2 est toujours valide, mais pas le pointeur de fonction. Ce pointeur de fonction a été défini par un appel dans loadUpFoo qui à son tour était dans une DLL chargée par LoadLibrary.

Ma question est: où est-ce que je commence à chercher la raison pour laquelle cela échoue? Est-ce un problème avec la façon dont la DLL externe (que je charge avec LoadLibrary) définit le pointeur de fonction dans mon foo struct? Ou est-ce quelque chose à voir avec des threads différents utilisant la même bibliothèque? Ou, pourrait-il être lié d'une manière ou d'une autre à mon utilisation de COM dans cette application (l'appel de CoUninitialize dans le premier thread libérerait-il en quelque sorte cette mémoire ou bibliothèque)?

Je peux fournir plus de détails sur l'installation COM si cela peut être responsable. Merci!

Modifier: Merci pour les suggestions pour le moment. La structure foo est opaque - je ne connais pas vraiment son implémentation. La structure foo est déclarée dans un en-tête que j'importe. Il n'y a pas de méthodes de comptage de références que j'appelle explicitement et il n'y a pas d'autres interactions avec la bibliothèque chargée avec LoadLibrary. Je suis assez sûr que la structure foo n'est pas mappée en mémoire à une classe COM, mais comme je l'ai dit, elle est opaque et je ne peux pas dire avec certitude.

Les durées de vie des pointeurs foo sont correctement gérées (pas supprimées). La structure foo est une bibliothèque de chiffrement, donc je ne suis pas libre de divulguer plus. À ce stade, je suis convaincu qu'il n'y a rien de fondamentalement mauvais avec mon utilisation de LoadLibrary sur différents threads et dans une application COM (et je suppose que le nettoyage de la mémoire du pointeur de fonction est provoqué par quelque chose dans la bibliothèque en dehors de mon contrôle).

+3

Le code que vous avez montré n'utilise pas COM. C'est juste l'utilisation normale de DLL. –

Répondre

3

Il n'y a rien COM lié dans le code que vous avez montré. LoadLibrary n'est pas spécifique au thread, donc une fois que vous avez le handle de la lib, vous pouvez le réutiliser de tous vos threads. La même chose s'applique au pointeur vers la méthode fooLoader. Cependant, il pourrait certainement y avoir quelque chose de spécifique à COM à l'intérieur de fooLoader. En outre, ce qui n'est pas clair ici est quel est le contrôle à vie des instances de foo. Du fait que vous mentionnez COM et le comportement funky que vous voyez, j'ai le soupçon sournois que foo est en réalité une carte mémoire de vtable d'un objet COM et fooLoader est DllGetClassObject ou une autre méthode d'usine qui crée des objets COM.:-)

Dans tous les cas, l'explication la plus probable pour que l'instance de foo ne devienne pas valide serait qu'elle soit ref-comptée et que DoSomething appelle AddRef()/Release() pour l'auto-détruire.

Pour identifier exactement ce qui se passe, vous devrez fournir un peu plus d'informations sur ce que fooLoader fait et pourquoi vous pensez que votre code est lié à COM.

+0

C'est vrai qu'il n'y a rien de COM lié dans le code que j'ai fourni et je m'excuse de ne pas être plus clair. Tout le code que vous voyez (dans les deux threads) est exécuté dans les classes COM, c'est pourquoi j'ai mentionné COM. J'ai remarqué un comportement bizarre dans le passé qui avait à voir avec COM et comment j'ai utilisé CoInitialize/CoUninitialze, provenant de mon manque d'expérience avec COM. Supposons que je me trompe et que la bibliothèque opaque que je charge avec LoadLibrary utilise COM. Si tel est le cas, l'appel de CoUninitialize dans le thread qui a chargé la bibliothèque provoque-t-il une invalidation de la mémoire mappée? – Zach

+1

Non, CoUninitialize ne provoquera pas le démappage de la mémoire tant qu'il reste LoadMemory en attente sans l'appel Free correspondant. Cependant, si COM n'est pas initialisé sur ce thread et que fooLoader a utilisé COM pour remplir l'instance foo, le pointeur qu'il contient pourrait être un pointeur proxy et le proxy ne fonctionnerait pas correctement. –

0

La valeur de DoSomething est déterminée par la bibliothèque que vous chargez. Vous devriez être capable de déterminer où il pointe. Regardez la sortie de débogage dans Visual Studio. Il vous dira non seulement quand, mais aussi où les DLL sont chargées. Votre fonction pointe-t-elle vraiment vers la DLL à laquelle vous pensez qu'elle devrait pointer?

2

Par hasard, le thread 1 appelle-t-il FreeLibrary (ou ExitThreadAndFreeLibrary) lorsqu'il s'arrête? Si c'est le cas, vous essayez d'appeler du code qui n'est plus mappé dans le processus. Votre objet semble toujours bon, car les données d'instance existent toujours, mais le code de ses méthodes aurait disparu.

Si c'est le problème, vous pouvez changer le fil 1 pour ne pas libérer la bibliothèque.

Ou, vous pourriez avoir le deuxième thread également appeler LoadLibrary. LoadLibrary et FreeLibrary utilisez le comptage des références, donc si vous chargez une DLL 3 fois, il ne sera pas déchargé tant que vous ne l'aurez pas libéré 3 fois. Le comptage des références est par processus, vous pouvez donc charger la même bibliothèque à partir de différents threads. Les charges supplémentaires sont très faibles coûts.

+0

Cela ressemble vraiment à ce qui se passe, du moins d'un point de vue fonctionnel. Mais FreeLibrary n'est jamais appelé par moi dans ce cas que je peux voir (je place un point d'arrêt sur FreeLibrary dans le seul endroit où il se produit dans mon code). – Zach

Questions connexes