2009-08-05 22 views
18

ho il seguente codice:Impossibile eseguire il cast di oggetti COM di tipo di eccezione

public void Test(IMyInterface iInterface) 
{ 
    iInterface.CallMethod (); 
} 

che funziona bene. Tuttavia, se cambio il codice da filettare:

private IMyInterface myInterface; 
public void Test(IMyInterface iInterface) 
{ 
    myInterface = iInterface; 
    new Thread (new ThreadStart (CallInterfaceMethod)).Start (); 
} 

public void CallInterfaceMethod () 
{ 
    myInterface.CallMethod () 
} 

Quando uso il filo che riceve l'eccezione:

impossibile cast di oggetti COM di tipo 'Sistema .__ ComObject' interfacciare tipo 'IMyInterface' . Questa operazione non è riuscita perché la chiamata QueryInterface sul componente COM per l'interfaccia con IID "{GUID}" non è riuscita a causa dell'errore di follow: Nessuna interfaccia supportata

Ma l'interfaccia deve essere supportata correttamente? Qualcuno ha qualche idea su cosa sta succedendo qui?

+0

http://blogs.msdn.com/b/oldnewthing/archive/2004/12/13/281910.aspx – EricLaw

risposta

20

Questa brutta, sgradevole eccezione deriva da un concetto noto come COM marshalling. L'essenza del problema sta nel fatto che per consumare oggetti COM da qualsiasi thread, il thread deve avere accesso alle informazioni sul tipo che descrivono l'oggetto COM.

Nello scenario descritto, il motivo per cui non riesce sul secondo thread è perché il secondo thread non ha informazioni sul tipo per l'interfaccia.

Si potrebbe provare ad aggiungere il seguente al codice:

[ComImport] 
[Guid("23EB4AF8-BE9C-4b49-B3A4-24F4FF657B27")] 
public interface IMyInterface 
{ 
    void CallMethod(); 
} 

In sostanza la dichiarazione di cui sopra indica al COM .NET framework caricato per caricare le informazioni sul tipo con tecniche tradizionali dal Registro di sistema e individuare la libreria di tipo associato e andare da li.

È inoltre necessario limitare la creazione dell'oggetto COM a un singolo thread (per impedire il marshalling dei thread) per risolvere il problema.

Per riassumere, questo errore riguarda le informazioni sul tipo e il marshalling del thread. Assicurarsi che ogni thread che desidera accedere all'oggetto COM disponga delle informazioni pertinenti per rimuovere unmarshal dell'oggetto dal thread di origine.

PS: questo problema è risolto in .NET 4.0 utilizzando una tecnica chiamata "Tipo Equivalenza"

+0

Grazie per la risposta. La tua esplosione ha senso e anche la ricerca della decodifica di ComImport sul MSDN sembra avere un senso. Saluti. – Kyle

+0

È un piacere :) Ho già avuto a che fare con questo problema ed è stato un incubo provare a risolverlo, fino a quando la lampadina non si è spenta e ho finito col consumare l'oggetto COM dal thread che lo ha creato. –

+1

[+1] Grazie mille ragazzi! Per me l'errore era che mancava [STAThread]. Questa domanda e risposta mi hanno portato a trovarlo dopo aver letto il problema del threading. – Marc

-1

Bene, per esempio, si sta effettuando una chiamata incrociata a un oggetto senza bloccarlo, questo causerà automaticamente alcuni problemi. Il codice dovrebbe essere più simile:

private IMyInterface myInterface; 
private static readonly object _myObjectLock = new object(); 

public void Test(IMyInterface iInterface) 
{ 
    myInterface = iInterface; 
    new Thread (new ThreadStart (CallInterfaceMethod)).Start (); 
} 

public void CallInterfaceMethod () 
{ 
    lock(_myObjectLock) 
    { 
     myInterface.CallMethod (); 
    } 
} 

Da quello che ho capito, l'errore che hai elencato a volte può verificarsi quando la risorsa non è possibile accedere, che, con un'operazione di cross-thread come questo, molto probabilmente accadrà. Non citarmi su di esso, però, non sono un esperto di COM.

Sinceramente, non penso che mi avvicinerei a chiamare questo metodo in questo modo, troppi rischi nel farlo. Hai preso in considerazione l'utilizzo di un ParameterizedThreadStart e il passaggio dell'oggetto in questo modo? Avresti comunque bisogno di bloccare in sicurezza i tuoi oggetti per le operazioni cross-thread, ma sarebbe più sicuro.

Inoltre, verificare che la classe "myInterface" possa ancora chiamare il metodo "CallMethod()". Le interfacce non hanno implementazione, è possibile che si verifichino problemi quando si imposta "myInterface = iInterface".

+0

Rispondere per la risposta, ma l'utilizzo di ParameterizedThreadStart non funziona (con o senza il blocco). Inoltre ho controllato, myInterface può ancora "CallMethod" una volta che è stato impostato (myInterface = iInterface). – Kyle

+0

Ci sono due bit di disinformazione in questa risposta: ** 1. ** L'accesso agli oggetti cross-thread non * sempre * causa automaticamente problemi. Il blocco non dovrebbe essere necessario con accessi in sola lettura, ad esempio. ** 2. ** Riguardo l'ultimo paragrafo, non è possibile passare le interfacce come oggetti, quindi anche se si assegna un oggetto non null a una variabile con un tipo di interfaccia (statico) e il codice viene compilato, tutti questi metodi possono essere chiamato. Pensa al tipo statico (interfaccia) come a una sorta di facciata per un altro tipo di implementazione. – stakx

3

Ho ricevuto un consiglio e mi ha aiutato!

Trova nel thread principale (Program.cs) la riga [STAThread] e cambialo in [MTAThread].

+0

Ha funzionato come un fascino. Per favore, spieghi il cambiamento? –

0

Ho sviluppato un'applicazione C# che utilizza 7-zip tramite le interfacce COM. Mi sono imbattuto in questa cosa divertente in cui sono stato in grado di estrarre archivi da un thread di lavoro in un'istanza, ma non un altro, ottenendo questa stessa eccezione.

Ho scoperto che, finché si inizializza l'oggetto COM offendente nel thread in cui è utilizzato, non viene generata alcuna eccezione. La mia soluzione era quella di disporre gli oggetti che utilizzavano le interfacce COM e inizializzarle di nuovo quando le passavano tra i thread.

Problemi correlati