Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Avertissement
Marshaling entre appartements à partir du code .NET : Lorsqu’une application .NET crée un objet COM en cours de processus dont le ThreadingModel ne correspond pas à l’appartement appelant, COM crée l’objet dans un autre appartement et renvoie un proxy. L’appel de méthodes via ce proxy entraîne une surcharge de marshaling et peut entraîner des blocages si le thread de l’appartement cible est bloqué.
Modèle d’échec courant :
// A .NET console app defaults to MTA (no explicit CoInitializeEx).
// Creating an Apartment-threaded COM object forces COM to spin up
// a hidden STA thread. If that STA thread has no message pump,
// calls that require marshaling back to it will hang.
// Fix: If your COM object requires STA, mark Main with [STAThread]:
[STAThread]
static void Main(string[] args)
{
var comObj = new MyCOMObject(); // Created in the STA — no proxy needed
}
Bonnes pratiques pour les clients .NET de serveurs COM intégrés au processus :
- Vérifier la valeur du registre de l’objet
ThreadingModelsousHKCR\CLSID\{...}\InprocServer32 - Utilisez
[STAThread]au niveau de votre point d’entrée si vous utilisez des objets à thread cloisonné (WinForms/WPF le font automatiquement) - N’appelez jamais
Thread.Join()niTask.Wait()dans un thread STA — utilisezawaitou traitez les messages avecDispatcher.PushFrame()
Un serveur in-process n’appelle pas CoInitialize, CoInitializeEx ou OleInitialize pour marquer son modèle de threading. Pour les objets DLL ou les objets intégrés au processus qui prennent en charge le multithreading, vous devez définir le modèle de threading dans le Registre. Le modèle par défaut lorsque vous ne spécifiez pas de modèle de multithreading est un seul thread par processus. Pour spécifier un modèle, vous ajoutez la valeur ThreadingModel à la clé InprocServer32 dans le Registre.
Les DLL qui prennent en charge l’instanciation d’un objet de classe doivent implémenter et exporter les fonctions DllGetClassObject et DllCanUnloadNow. Lorsqu’un client souhaite une instance de la classe prise en charge par la DLL, un appel à CoGetClassObject (directement ou via un appel à CoCreateInstance) appelle DllGetClassObject pour obtenir un pointeur vers son objet de classe lorsque l’objet est implémenté dans une DLL. DllGetClassObject doit donc être en mesure de fournir plusieurs objets de classe ou un seul objet sécurisé pour les threads (essentiellement en utilisant simplement InterlockedIncrement/InterlockedDecrement pour les compteurs de références internes).
Comme son nom l’indique, DllCanUnloadNow est appelé pour déterminer si la DLL qui l’implémente est en cours d’utilisation, ce qui permet à l’appelant de le décharger en toute sécurité si ce n’est pas le cas. Les appels à CoFreeUnusedLibraries depuis n’importe quel thread passent toujours par le thread de l’apartment principal pour appeler DllCanUnloadNow.
Comme les autres serveurs, les serveurs « in-process » peuvent être à thread unique, à thread cloisonné ou à thread libre. Ces serveurs peuvent être utilisés par n’importe quel client OLE, quel que soit le modèle de thread utilisé par ce client.
Toutes les combinaisons d’interopérabilité entre les modèles de gestion des threads sont autorisées entre les clients et les objets intégrés au processus. L’interaction entre un client et un objet in-process qui utilise différents modèles de thread est exactement semblable à l’interaction entre les clients et les serveurs hors processus. Pour un serveur in-process, lorsque le modèle de thread du client et du serveur in-process diffère, COM doit s’interposer entre le client et l’objet.
Lorsqu’un objet in-process qui prend en charge le modèle monothread est appelé simultanément par plusieurs threads d’un client, COM ne peut pas autoriser les threads clients à accéder directement à l’interface de l’objet (l’objet n’a pas été conçu pour ce type d’accès). Au lieu de cela, COM doit s’assurer que les appels sont synchronisés et sont effectués uniquement par le thread client qui a créé l’objet. Par conséquent, COM crée l’objet dans l’appartement principal du client et exige que tous les autres appartements clients accèdent à l’objet à l’aide de proxys.
Lorsqu'un cloisonnement à threads libres (modèle cloisonné multithread) d'un client crée un serveur « in-process » à threads cloisonnés, COM lance un thread « hôte » à thread unique du modèle cloisonné dans le client. Ce thread hôte crée l’objet et le pointeur de l’interface est marshalé vers l’appartement libre du client. De même, lorsqu'un cloisonnement à thread unique dans un modèle cloisonné client crée un serveur in-process à thread unique, COM lance un thread hôte à thread unique (cloisonnement à thread unique sur lequel l'objet sera créé et ensuite transféré au cloisonnement à thread unique du client).
Note
En général, si vous concevez une interface personnalisée sur un serveur in-process, vous devez également fournir le code de marshaling pour que COM puisse marshaler l'interface entre les cloisonnements du client.
COM permet de protéger l’accès aux objets fournis par une DLL à thread unique en exigeant l’accès à partir du même appartement client dans lequel ils ont été créés. En outre, tous les points d’entrée DLL (comme DllGetClassObject et DllCanUnloadNow) et les données globales doivent toujours être accessibles par le même appartement. COM crée ces objets dans l’appartement principal du client, ce qui permet à l’appartement principal d’accéder directement aux pointeurs de l’objet. Les appels provenant des autres cloisonnements utilisent le marshaling interthread pour passer du proxy au stub dans le cloisonnement principal, puis à l'objet. Cela permet à COM de synchroniser les appels à l’objet. Les appels interthread sont lents. Il est donc recommandé de réécrire ces serveurs pour prendre en charge plusieurs appartements.
Comme un serveur in-process à thread unique, un objet fourni par une DLL de modèle cloisonné doit être accessible par le même cloisonnement client que celui à partir duquel il a été créé. Toutefois, les objets fournis par ce serveur peuvent être créés dans plusieurs appartements du client. Le serveur doit donc implémenter ses points d’entrée (comme DllGetClassObject et DllCanUnloadNow) pour une utilisation multithread. Par exemple, si deux appartements d’un client essaient de créer deux instances de l’objet in-process simultanément, DllGetClassObject peut être appelé simultanément par les deux appartements. DllCanUnloadNow doit être écrit afin que la DLL ne décharge pas pendant que le code est toujours en cours d’exécution dans la DLL.
Si la DLL fournit une seule instance de la fabrique de classes pour créer tous les objets, l’implémentation de la fabrique de classes doit également être conçue pour une utilisation multithreadée, puisqu’elle sera utilisée par plusieurs appartements client. Si la DLL crée une instance de la fabrique de classes chaque fois que DllGetClassObject est appelé, la fabrique de classes n’a pas besoin d’être thread-safe.
Les objets créés par la fabrique de classes n'ont pas besoin d'être thread-safe. Une fois créé par un thread, l’objet est toujours accessible via ce thread et tous les appels à l’objet sont synchronisés par COM. Le modèle cloisonné d'un client qui crée cet objet obtiendra un pointeur direct sur l'objet. Les appartements clients qui sont différents de l’appartement dans lequel l’objet a été créé doivent accéder à l’objet via des proxys. Ces proxys sont créés lorsque le client marshale l’interface entre ses appartements.
Lorsqu’une DLL en cours de processus a sa valeur ThreadingModel définie sur « Both », un objet fourni par cette DLL peut être créé et utilisé directement (sans proxy) dans des appartements client à thread unique ou multithreadés. Toutefois, il peut être utilisé directement dans l’appartement dans lequel il a été créé. Pour donner l'objet à un autre cloisonnement, l'objet doit être marshalé. L’objet DLL doit implémenter sa propre synchronisation et être accessible en même temps par plusieurs appartements clients.
Pour améliorer les performances de l’accès multithread libre aux objets DLL intra-processus, COM fournit la fonction CoCreateFreeThreadedMarshaler. Cette fonction crée un objet de marshaling libre de threads qui peut être agrégé avec un objet de serveur in-process. Lorsqu'un cloisonnement client dans le même processus doit accéder à un objet dans un autre cloisonnement, l'agrégation du marshaleur libre de threads fournit au client un pointeur direct sur l'objet serveur, plutôt que sur un proxy, lorsque le client marshale l'interface de l'objet dans un autre cloisonnement. Le client n’a pas besoin d’effectuer de synchronisation. Cela fonctionne uniquement dans le même processus ; le marshaling standard est utilisé pour une référence à l’objet envoyé à un autre processus.
Important
Remplacement moderne pour CoCreateFreeThreadedMarshaler : Pour le nouveau code ciblant Windows 8.1 ou version ultérieure, préférez RoGetAgileReference et l’interface IAgileReference. Ceux-ci offrent un moyen plus sûr de transmettre des références d’objets à travers des appartements sans les pièges du marshaleur sans fil libre (qui contourne entièrement la protection de l’appartement et peut masquer les bogues de threading). Pour les objets WinRT, l’agilité est la valeur par défaut : les objets WinRT prennent automatiquement en charge IAgileObject, sauf s’ils s’y soustraient explicitement.
Un objet fourni par une DLL in-process qui ne prend en charge que le threading libre est un objet libre de threads. Il implémente sa propre synchronisation et est accessible en même temps par plusieurs threads clients. Ce serveur ne gère pas les interfaces entre les threads, de sorte qu'il ne peut être créé et utilisé directement (sans proxy) que par les cloisonnements multithreads d'un client. Les cloisonnements à thread unique qui le créent y accèdent via un proxy.
Rubriques connexes