2010-10-17 5 views
5

Ho un Excel plug-in (scritto in C#) con una variabile statica che è al centro di una cache di dati Singleton:Excel richiama il server di automazione .NET da due diversi AppDomain?

static DataCache _instance; 

Questo si accede tramite tre diversi percorsi di codice:

  1. I gestori di eventi su barra a nastro VSTO inizializzano l'istanza e la leggono per la visualizzazione nelle finestre di dialogo di supporto
  2. Un server RTD (una classe dichiarata [ComVisible] e implementa l'interfaccia IRtdServer) utilizza i dati per le formule di RTD
  3. Un insieme di chiamate di automazione (implementate in un'altra classe dichiarata [ComVisible]) operano anche sui dati. Questi sono chiamati tramite codice VBA che viene richiamato quando si fa clic sui pulsanti nel foglio di lavoro di Excel.

EDIT (# 3):

A seconda dell'ordine in cui questi percorsi di codice vengono prima richiamati, trovo che il mio codice viene eseguito in due AppDomain separati.

Tutti gli accessi dai gestori di eventi barra a nastro si verificano in un AppDomain denominato "MyPlugIn.vsto". Se questo è il primo accesso al mio oggetto COM, tutte le chiamate successive (incluse le chiamate RTD) si verificano nello stesso AppDomain.

Tuttavia, se il primo accesso avviene tramite l'interfaccia RTD, la chiamata e tutte le chiamate RTD successive si verificano in un AppDomain denominato "DefaultDomain". (Questo accade quando si carica un documento salvato con formule RTD incorporate.) Le chiamate successive per inizializzare e manipolare DataCache tramite la barra degli strumenti si verificano ancora nell'AppDomain "MyPlugIn.vsto". Ciò significa che le formule di RST vengono sempre eseguite come se DataCache non fosse inizializzato (poiché la variabile statica impostata in un AppDomain non è inizializzata nell'altro).

Sembra che Excel o VSTO stiano creando un AppDomain quando VSTO viene inizializzato. Gli oggetti creati tramite interoperabilità COM prima di questo campo di inizializzazione nell'AppDomain predefinito, mentre gli oggetti creati successivamente vengono inseriti nell'AppDomain VSTO.

Come posso garantire che la stessa istanza DataCache viene utilizzato, non importa quale dominio di applicazione il mio oggetto server RTD viene creato in?

+0

Cosa intendi per "il mio oggetto singleton non è correttamente condiviso"? È solo l'inizializzazione dell'oggetto, come suggerisce @mhttk, o stai affermando che thread diversi vedono diversi stati in quella variabile (che sembra molto strana), o qualcos'altro? – Rory

+0

@Rory - in un thread, _instance viene inizializzata. Nelle chiamate successive da quello stesso thread, è ancora inizializzato come previsto. Tuttavia, quando un altro thread tenta di accedervi (alcuni minuti più tardi - questo non è un problema di temporizzazione) è nullo e deve essere reinizializzato per essere utilizzato da quel thread. – Eric

+0

È piuttosto strano, vero? Nella mia esperienza di.Interop NET COM (con Internet Explorer che è simile ma ovviamente diverso), ciò non accade. È una cosa normale con gli appartamenti COM? Sei sicuro che le chiamate siano all'interno dello stesso processo? – Rory

risposta

1

La variabile statica non è certamente condivisa tra AppDomains, quindi quello che stai vedendo è come previsto, visti i diversi AppDomain.

penso che funziona in questo modo:

Il VSTO aggiuntivo viene eseguito nel proprio dominio di applicazione. Se il factory di classe COM per il proprio oggetto cache (o server RTD) viene creato all'interno di tale AppDomain, verrà caricato nell'AppDomain chiamante. L'accesso successivo a tale classe COM lo troverà già caricato nel processo e utilizzerà l'istanza esistente.

Tuttavia, se la prima attivazione viene attivata da Excel stesso, ad es. mediante una chiamata RTD, l'oggetto COM implementato .NET verrà caricato nell'AppDomain predefinito del processo. Non hai il controllo su questa parte del processo di caricamento a meno che non crei uno shim non gestito, poiché "il tuo codice" non è in esecuzione quando avviene il caricamento.

Alcuni suggerimenti al largo della parte superiore della mia testa:

  1. fare alcune funzioni wrapper per la RST chiamate che sono esposti dal .NET aggiuntivo. In questo modo, puoi assicurarti che la classe RTD sia caricata prima di chiamare Application.RTD di Excel per eseguire la vera configurazione RTD.

  2. rendere l'accesso dal server RTD alla cache reale attraverso le funzioni definite dall'utente - in questo modo Excel chiamerà nel dominio di applicazione che ha la vera cache, anche se non è il dominio di applicazione attuale, in cui la vita del server di RST.

  3. Cercare di ottenere l'oggetto del componente aggiuntivo tramite Application.AddIns .... esiste un modo per ottenere l'oggetto COM effettivo del componente aggiuntivo e utilizzare una qualche interfaccia per accedere alla cache ..

  4. Creare uno shim non gestito (cercare nel web "wizard COM shim") per il server RTD. In qualche modo è possibile capire come caricare VSTO AppDomain e quindi caricare il server RTD in AppDomain.

  5. Vedere se è possibile caricare il componente aggiuntivo VSTO nell'AppDomain predefinito. Non lo so, ma forse c'è un flag o un interruttore che dice al "Microsoft Office Systems loader" (o qualsiasi altra parte venga chiamata ora) per non creare l'AppDomain isolato.

  6. La risposta giusta: utilizzare Excel-Dna (dichiarazione di non responsabilità: sono lo sviluppatore). Supporta Ribbons, RTD e UDF nel componente aggiuntivo gestito, senza necessità di registrazione e tutto viene inserito nei componenti aggiuntivi AppDomain. È gratuito, ma ci vorrà un po 'di tempo e impegno per portare i tuoi contenuti - RTD è banale, ma se usi molti oggetti helper di VSTO per la barra multifunzione e l'accesso ai fogli (tabelle, ecc.), Dovrai pensare a un po '.

Spero che questo ti dia qualche idea.

--Govert--

-2

prima, si consiglia di dichiarare la propria istanza come:

static DataCache _instance = new DataCache(); 

in questo modo (non l'unico di sicuro), si sa _instance viene generato thread-safe. C'è molta copertura sul tema per filo single sicuri, ma questo sembra una delle soluzioni più semplici.

La seconda cosa può essere che si desidera provare a utilizzare è una struttura come

Lock (_lockObject) 
{ 
... 
} 

sia per lettura e scrittura. Ciò renderà le vostre letture e scritture al sicuro da diversi thread.

Infine, ma questa è pura speculazione, si può provare con la creazione di un oggetto separato per le chiamate COM che risiede in uno STA e accede tua libreria.

Buona fortuna!

+0

Mi dispiace, questo non risolve il mio problema. Ho modificato la domanda per chiarire che passano diversi minuti tra quando un thread inizializza l'istanza e altri thread tentano di usarlo. Questo non è un problema di sincronizzazione dei thread, è qualcosa su come funziona l'implementazione .NET/COM. – Eric