2009-04-22 10 views
-1

J'écris une application qui utilise une bibliothèque COM. J'ai une bibliothèque de classes qui encapsule les appels à la bibliothèque COM et ajoute plus de fonctionnalités. Finalement, cela fonctionnera comme un service Windows. Pour l'instant je l'ai testé en utilisant un harnais de test Winforms.Le service lève une exception lors de la tentative d'appel d'une bibliothèque COM

Lorsque la bibliothèque de classes est créée par le faisceau de test, tout semble fonctionner correctement. Les problèmes commencent quand j'essaie de l'exécuter en tant que service. Il est créé OK et même le premier appel à COM est OK. L'objet COM déclenche alors un événement que je gère et en réponse au résultat dans l'événement j'appelle une autre fonction dans la bibliothèque COM. La fonction est invoquée avec succès dans le cas où je le lance du faisceau de test, mais lors de l'exécution en tant que service une exception est levée:

System.InvalidCastException occurred Message="Unable to cast COM object of type '' to interface type ''. This operation failed because the QueryInterface call on the COM component for the interface with IID '{350ADD2A-18CB-4D9C-BE28-48D53F14E6FB}' failed due to the following error: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))."

Je vois qu'il ya des problèmes de filetage. Dans le cas du faisceau de test, tous ces appels se produisent sur le thread principal et, dans le cas du service Windows, le remplacement Service OnStart et le gestionnaire d'événements COM se trouvent sur des threads différents. J'ai essayé différentes choses sans succès. Aucune suggestion?

+0

Pouvez-vous fournir un exemple de code? –

Répondre

1

Certains composants COM ne doivent être accessibles que par des threads STA. Si tel est le cas pour le vôtre, vous pouvez faire votre travail COM dans un thread STA comme ceci:

RunInSTAThread(() => com_object.DoSomething()); 

private static void RunInSTAThread(ThreadStart thread_start) 
     { 
      Exception threadEx = null; 
      ThreadStart wrapped_ts =() => 
             { 
              try 
              { 
               thread_start(); 
              } 
              catch (Exception ex) 
              { 
               MethodInfo preserveStackTrace = 
                typeof(Exception).GetMethod("InternalPreserveStackTrace", 
                       BindingFlags.Instance | BindingFlags.NonPublic); 
               preserveStackTrace.Invoke(ex, null); 
               threadEx = ex; 
              } 
             }; 
      Thread thread = new Thread(wrapped_ts); 
      thread.SetApartmentState(ApartmentState.STA); 
      thread.Start(); 
      thread.Join(); 
      if (threadEx != null) 
      { 
       throw threadEx; 
      } 
     } 

Cela peut ne pas être la meilleure utilisation de fils (un nouveau thread pour chaque appel) pour votre situation, mais c'est un point de départ.

+0

@Jeremy Lew: C'est aussi complètement inutile. Lors de la création d'un RCW, le CLR placera l'objet dans son propre thread si la création se produit sur un thread non-STA. Vous ne pompez pas non plus de messages sur le thread STA, ce qui est un MUST pour traiter les objets COM STA. – casperOne

+0

Voici quelques bons points. Je ne comprends toujours pas pourquoi cela fonctionne à partir de Winforms et pas à partir du Service. –

0

Le service Win s'exécute-t-il sous le même compte d'utilisateur que l'application Winforms?

+0

Il fonctionne en tant que LocalSystem. –

+0

Je viens de l'essayer en tant qu'utilisateur et le résultat est à peu près le même. –

0

C'est maintenant résolu. J'ai dû réarranger les discussions. Maintenant, l'objet COM et tous les appels sont sur le même thread et il n'y a pas de problèmes inter-threading. Encore la question de savoir comment traiter dans le cas commun reste peu claire.

-1

Je viens de fermer le projet et l'a rouvert et l'erreur a été résolue