2014-07-16 5 views
3

Je n'arrive plus à savoir si les objets COM de C# marshal se trouvent entre les threads. À cette fin, j'ai une application qui charge un ensemble de fichiers dans une tâche parallèle. J'utilise le StaTaskScehduler pour charger les fichiers en utilisant l'objet COM. Une fois l'objet COM chargé, je stocke l'objet dans une liste centrale. Ensuite, j'essaie d'effectuer un traitement sur ces données, en utilisant STATaskScheduler. Cependant, à ce stade, j'ai rencontré un problème. Je reçois une exception comme suit:C# Marshalling Objets COM entre les threads

An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in MadCat.exe 

Additional information: COM object that has been separated from its underlying RCW cannot be used 

Maintenant, je crois comprendre que je reçois cette erreur parce que l'objet n'a pas été mobilisées dans le nouveau fil. Je pensais que c'était quelque chose que C# faisait pour vous?

Comment puis-je créer un objet COM threaded appartement dans un thread, puis l'utiliser à partir d'un autre thread?

Est-ce que j'aboie le mauvais arbre ici? Ne devrais-je même pas utiliser l'appartement Sta pour mes discussions? Je peux garantir que l'objet n'est jamais accessible à partir de plusieurs threads simultanément. Toutes les pensées ont beaucoup apprécié.

Modifier: L'objet COM est défini comme suit:

[ 
    coclass, 
    threading(apartment), 
    vi_progid([Namespace.Class]), 
    progid([Namespace.Class].6), 
    version(6.0), 
    uuid(GUID_C[Class]), 
    helpstring([Class]" Class") 
] 

donc par ma compréhension ceci est un appartement objet fileté, non? Je viens d'essayer d'utiliser un planificateur de tâches modifié qui ne définit pas l'état de l'appartement (MTA par défaut?). Cet objet semble alors fonctionner quand je le crée dans un fil et l'utilise d'un autre. Est-ce sûr ou reviendra-t-il me mordre d'une autre façon?

modèle de filetage de COM a toujours confondu l'enfer hors de moi:/

+0

J'ai seulement vu cette erreur quand le code a essayé d'appeler dans l'objet COM après qu'il ait été libéré par 'Marshal.ReleaseComObject'. Mais je n'ai jamais essayé d'utiliser un objet COM à thread STA sur plusieurs threads, ce qui à ma connaissance ne fonctionnera pas. Autrement dit, vous ne pouvez utiliser qu'un tel objet COM sur le thread qui l'a créé. En outre, s'il est documenté en tant que thread STA, il doit être utilisé sur un thread STA.Si vous devez utiliser l'objet de cette manière, je pense que vous devrez ajouter une synchronisation de thread: les autres threads doivent demander au thread créateur d'appeler dans l'objet COM. – groverboy

+0

Les fois où j'ai vu cette erreur sont des instances où 'Marshal.ReleaseComObject' a été appelé sur une instance créée à partir d'un encapsuleur COM appelable suivi d'une tentative d'appeler une méthode sur cette instance. –

+0

COM (pas .NET) effectue le marshaling pour vous si les objets COM sont correctement déclarés dans le registre en ce qui concerne la façon dont ils gèrent le multithreading (Both, Apartment, etc.). Comment sont déclarés les objets que vous utilisez? –

Répondre

3

Il semble que vous utilisez StaTaskScheduler Stephen Toub comme une partie d'une certaine logique « stateful », où vos objets COM vivent dans StartNew limites. Si c'est le cas, assurez-vous de créer et d'utiliser ces objets sur le même thread STA StaTaskScheduler et nulle part ailleurs. Alors vous ne devriez pas vous inquiéter du tout pour le marshaling COM. Inutile de dire que vous devez créer StaTaskScheduler avec un seul thread, c'est-à-dire numberOfThreads:1.

Voici ce que je veux dire:

var sta = new StaTaskScheduler(numberOfThreads:1); 

var comObjects = new { Obj = (ComObject)null }; 

Task.Factory.StartNew(() => 
{ 
    // create COM object 
    comObjects.Obj = (ComObject)Activator.CreateInstance(
     Type.GetTypeFromProgID("Client.ProgID")); 
}, CancellationToken.None, TaskCreationOptions.None, sta); 

//... 

for(int i=0; i<10; i++) 
{ 
    var result = await Task.Factory.StartNew(() => 
    { 
     // use COM object 
     return comObjects.Obj.Method();  
    }, CancellationToken.None, TaskCreationOptions.None, sta); 
} 

Si Obj.Method() retours autres objets COM, vous devez conserver le résultat dans le même « appartement » et accès StaTaskScheduler à partir de là aussi:

var comObjects = new { Obj = (ComObject)null, Obj2 = (AnotherComObject)null }; 
//... 
await Task.Factory.StartNew(() => 
{ 
    // use COM object 
    comObjects.Obj2 = comObjects.Obj.Method();  
}, CancellationToken.None, TaskCreationOptions.None, sta); 

Si vous devez également gérer des événements provenant de Obj, vérifiez les éléments suivants:

+0

Id plutôt pouvoir, d'une façon ou d'une autre, passer les objets COM entre les threads. Quelque chose comme le vieux CoMarshallInterthreadInterfaceInStream ...? – Goz

+0

J'ai aussi mis à jour ma question avec un peu plus d'infos! – Goz

+0

@Goz, alors peut-être que vous n'avez pas besoin de 'StaTaskScheduler'. Si vous créez un objet COM STA sur un thread MTA, COM va créer un appartement STA implicite (pour cela et d'autres objets STA comme cela) et marshaler un proxy à l'objet STA à votre client MTA. C'est pourquoi cela fonctionne comme décrit dans votre mise à jour. – Noseratio