2013-03-11 20 views
10

Ho appena "guadagnato" il privilegio di mantenere una libreria legacy codificata in C# nel mio lavoro corrente.Rendi il mio assembly assembly COM asincrono

questa DLL:

  • espone i metodi per un grande sistema legacy realizzato con Uniface, che non ha altra scelta che chiamare oggetti COM.
  • funge da collegamento tra questo sistema legacy e un'altra API di sistema.
  • Utilizza WinForm per l'interfaccia utente in alcuni casi.

più visivamente, mi pare di capire i componenti:

*[Big legacy system in Uniface]*== [COM] ==>[C# Library]== [API Managed] ==>*[Big EDM Management System]*

La domanda è: : Uno dei metodi in questa libreria C# richiede troppo tempo per essere eseguito e I "dovrebbe" renderlo asincrono!

Sono abituato a C#, ma non a COM affatto. Ho già fatto la programmazione concorrente, ma COM sembra aggiungere un sacco di complessità ad esso e tutte le mie prove finora finire in uno:

  • Un incidente senza alcun messaggio di errore affatto
  • mio Dll solo parzialmente funzionante (visualizzando solo parte della sua interfaccia utente e quindi chiudendo), e ancora non mi ha dato alcun errore

Sono a corto di idee e risorse su come gestire i thread all'interno di una DLL COM, e vorrei apprezzare qualsiasi suggerimento o aiuto.

Finora, la maggior parte del codice ho cambiato a fare il mio metodo asincrono:

// my public method called by the external system 
public int ComparedSearch(string application, out string errMsg) { 
    errMsg = ""; 
    try { 
    Action<string> asyncOp = AsyncComparedSearch; 
    asyncOp.BeginInvoke(application, null, null); 
    } catch (ex) { 
    // ... 
    } 
    return 0; 
} 

private int AsyncComparedSearch(string application) { 
    // my actual method doing the work, that was the called method before 
} 

Qualsiasi suggerimento o una risorsa utile sarebbe apprezzato. Grazie.

UPDATE 1:

Di seguito le risposte e gli indizi sottostanti (in particolare circa la SynchronizationContext, e con l'aiuto di this example) sono stato in grado di refactoring il mio codice e renderlo al lavoro, ma solo quando chiamato da un altro Applicazione finestra in C# e non tramite COM. Il sistema legacy incontra un errore piuttosto oscuro quando chiamo la funzione e non fornisco dettagli sull'arresto.

UPDATE 2:

Ultimi aggiornamenti nelle mie prove: sono riuscito a far funzionare il multithreading quando le chiamate vengono effettuate da un progetto di test, e non dal sistema Uniface. Dopo diversi tentativi, tendiamo a pensare che il nostro sistema legacy non supporti bene il multithreading nella sua attuale configurazione.Ma non è questo il punto della questione più :)

Questo vuole essere un exerpt del codice che sembra funzionare:

string application; 
SynchronizationContext context; 

// my public method called by the external system 
public int ComparedSearch(string application, out string errMsg) { 
    this.application = application; 
    context = WindowsFormsSynchronizationContext.Current; 
    Thread t = new Thread(new ThreadStart(AsyncComparedSearchAndShowDocs)); 
    t.Start(); 
    errMsg = ""; 
    return 0; 
} 

private void AsyncComparedSearch() { 
    // ANY WORK THAT AS NOTHING TO DO WITH UI 
    context.Send(new SendOrPostCallback(
     delegate(object state) 
     { 
      // METHODS THAT MANAGE UI SOMEHOW 
     } 
    ), null); 
} 

Ora stiamo valutando altre soluzioni che modificare questa assemblea COM, come incapsulare questo libreria in un servizio di Windows e creazione di un'interfaccia tra il sistema e il servizio. Dovrebbe essere più sostenibile ..

+3

+1 per questa domanda ben organizzata. – 3yanlis1bos

+0

Puoi fornire un campione veramente piccolo che si bloccherà? Non sono sicuro di cosa stai facendo esattamente per farlo andare in crash :) (Se è colpa tua, o se è il sistema legacy che non può gestire il threading) – Onkelborg

+0

Stai dicendo che l'eccezione non viene mai catturata o che non lo fa? non contiene alcun messaggio? –

risposta

2

È difficile dirlo senza conoscere ulteriori dettagli, ma qui ci sono alcuni problemi.

Esegui il delegato su un'altra discussione tramite BeginInvoke ma non lo aspetti. Il tuo blocco try\catch non catturerà nulla poiché è già passato mentre la chiamata remota è ancora in esecuzione. Invece, è necessario inserire il blocco try\catch all'interno di AsyncComparedSearch.

Poiché non si attende la fine dell'esecuzione del metodo remoto (EndInvoke o tramite callback) non sono sicuro di come si gestiscono i risultati della chiamata COM. Immagino quindi di aggiornare la GUI da AsyncComparedSearch. Se è così, è sbagliato, dato che è in esecuzione su un altro thread e non dovresti mai aggiornare la GUI da nessuna parte ma dal thread della GUI - molto probabilmente si verificherà con un crash o un altro comportamento imprevisto. Pertanto, è necessario sincronizzare l'aggiornamento della GUI al thread GUI. In WinForms è necessario utilizzare Control.BeginInvoke (non confonderlo con Delegate.BeginInvoke) o in un altro modo (ad esempio SynchronizationContext) per sincronizzare il codice sul thread della GUI. Io uso qualcosa di simile a questo:

private delegate void ExecuteActionHandler(Action action); 

public static void ExecuteOnUiThread(this Form form, Action action) 
{ 
    if (form.InvokeRequired) { // we are not on UI thread 
    // Invoke or BeginInvoke, depending on what you need 
    form.Invoke(new ExecuteActionHandler(ExecuteOnUiThread), action); 
    } 
    else { // we are on UI thread so just execute the action 
    action(); 
    } 
} 

poi mi chiamano in questo modo da qualsiasi thread:

theForm.ExecuteOnUiThread(() => theForm.SomeMethodWhichUpdatesControls()); 

Inoltre, leggere this answer per alcuni avvertimenti.

+0

Grazie per la risposta e il collegamento. La logica in 'AsyncComparedSearch' è piuttosto complessa e non si adatta al post. Ho più blocchi 'try/catch' in esso e non catturano nulla. Proverò a mettere i dettagli che contano in una modifica della domanda. E hai ragione riguardo l'handle della GUI, questo è gestito da "AsyncComparedSearch". Oggi cercherò in quella direzione e ti farò sapere. – David

+0

Ho aggiunto una nota sulla sincronizzazione alla risposta –

+0

Seguendo gli indizi forniti nella risposta e il collegamento su 'SynchronizationContext', sono riuscito a far funzionare correttamente la mia funzione, ma solo quando la chiamata è stata fatta da un altro progetto C#, e non quando la chiamata viene effettuata dal sistema legacy tramite COM. Almeno, c'è un'evoluzione positiva qui. – David