2013-04-11 10 views
5

Sto creando un AddIn barra multifunzione VSTO per Excel, e sto memorizzando alcune informazioni sullo stato della cartella di lavoro nella mia applicazione che uso per aggiornare Pulsanti visivi abilitati. Considerando che possono esistere più cartelle di lavoro, sto memorizzando questo oggetto stato in un dizionario nella classe ThisAddIn. Il mio problema è che non so come ottenere un Hash/chiave/guida univoci per la cartella di lavoro perché tutto quello che ottengo è un wrapper COM che modifica continuamente l'hash. abbastanza giusto, lo capisco perfettamente.Ottieni Hashcode per cartella di lavoro Excel in VSTO per abilitare i pulsanti in base allo stato

Una soluzione che ho utilizzato per un lungo periodo di tempo è stata creare un guid e memorizzarlo in CustomDocumentProperties per la cartella di lavoro e mappare lo stato in base a tale chiave. Almeno funziona, ma fallisce se creo una copia della cartella di lavoro e la apro nella stessa istanza dell'applicazione e ho più cartelle di lavoro con lo stesso guid ora ..

Ho appena avuto un'idea ora che suppongo di poter aggiornare questo Guid sull'evento Workbook_Open. Ma ancora questa sembra una soluzione poco raccomandabile.

La seconda soluzione che ho trovato qui: http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/04efa74d-83bd-434d-ab07-36742fd8410e/

quindi ho usato che il codice ragazzi e ha creato questo:

public static class WorkbookExtensions 
{ 
    public static IntPtr GetHashery(this msExcel.Workbook workbook) 
    { 
     IntPtr punk = IntPtr.Zero; 
     try 
     { 
      punk = Marshal.GetIUnknownForObject(workbook); 
      return punk; 
     } 
     finally 
     { 
      //Release to decrease ref count 
      Marshal.Release(punk); 
     } 
    } 
} 

Funziona molto bene per qualche minuto, fino a quando non inizia a darmi l'infame errore "L'oggetto COM che è stato separato dal relativo RCW sottostante non può essere utilizzato" quando si accede ad Application.ActiveWorkbook.

È un modo sicuro di fare riferimento all'oggetto COM della cartella di lavoro? E se avessi due applicazioni nastro con questo metodo per ottenere un singolo GUID di cartelle di lavoro? Cosa succede se una di queste applicazioni esegue il garbage collector sul mio oggetto stato, che chiama un finalizzatore per chiamare Marshal.FinalReleaseComObject (cartella di lavoro)? Esiste un modo per ottenere il numero di riferi per una cartella di lavoro in modo da non chiamare FinalRelease prima che le altre app della barra multifunzione abbiano terminato con esse? Quali sono le migliori pratiche per la pulizia degli oggetti COM delle cartelle di lavoro in VSTO per continuare a giocare correttamente con queste altre app?

Sicuramente non sono la prima persona a voler attivare i pulsanti in base allo stato della cartella di lavoro, come fanno tutti gli altri? Ho esaminato alcuni altri articoli qui su Stack Overflow, ma nessuno mi ha aiutato con la soluzione Guida alle cartelle di lavoro.

Sto utilizzando la Ribbon Designer e mi sto collegando agli eventi Carica e disattiva della cartella di lavoro.

Grazie in anticipo, la speranza ha incluso tutti i dettagli.

+1

** Oggetto COM che è stato separato dal suo R sottostante CW non può essere usato ** - sì io odio questo errore è un PITA, leggi questo: http://jake.ginnivan.net/vsto-com-interop –

+1

Perché rilasci il puntatore IUnknown nella stessa funzione? Prova a tenerlo e rilasciarlo solo quando la cartella di lavoro è stata chiusa o qualcosa del genere. –

+0

Brillante, grazie a @JeremyThompson! C'è molto da imparare da quell'articolo. Il VSTOContrib sembra essere perfetto per il mio prossimo progetto, ma non risolve i miei problemi immediati. @SimonMourier - Non sono molto esperto con il codice nativo, ma suppongo che se non rilasci il puntatore qui, perderò la traccia di esso per il rilascio successivo? – stuzor

risposta

5

Ho finito per risolvere questo semplicemente lanciando l'IntPtr a un lungo, e quindi lo smaltimento di IntPtr non mi riguarda. Non è necessario conservare IntPtr perché tutto ciò di cui ho veramente bisogno è qualcosa di unico nella cartella di lavoro.

Il seguente codice consente di memorizzare informazioni di stato specifiche della cartella di lavoro, pertanto è possibile aggiornare lo stato visivo dei pulsanti nella barra multifunzione in base a uno stato della cartella di lavoro dell'oggetto personalizzato. È possibile memorizzare qualsiasi informazione desiderata nella classe WorkbookState personalizzata, ma in genere si tratta di informazioni specifiche della sessione che non si desidera mantenere nel foglio di calcolo stesso.

estensioni Workbook separati:

public static class WorkbookExtensions 
{ 
    public static long GetHashery(this msExcel.Workbook workbook) 
    { 
     if (workbook == null) 
     { 
      throw new ArgumentNullException("workbook"); 
     } 

     IntPtr pUnknown = IntPtr.Zero; 
     try 
     { 
      pUnknown = Marshal.GetIUnknownForObject(workbook); 
      return pUnknown.ToInt64(); 
     } 
     finally 
     { 
      // GetIUnknownForObject causes AddRef. 
      if (pUnknown != IntPtr.Zero) 
      { 
       Marshal.Release(pUnknown); 
      } 
     } 
    } 
} 

Poi nella mia classe di VSTO/ExcelDna ThisAddIn I memorizzare una mappa di tutti gli stati cartella di lavoro con il metodo di cui sopra per trovare una unica chiave di cartella di lavoro hash:

private Dictionary<long, WorkbookState> _workbookStates = new Dictionary<long, WorkbookState>(); 
public WorkbookState WorkbookState 
{ 
    get 
    { 
     long hash = Application.ActiveWorkbook.GetHashery(); 
     WorkbookState state; 
     if (!_workbookStates.TryGetValue(hash, out state)) 
     { 
      state = _workbookStates[hash] = new WorkbookState(); 
     } 
     return state; 
    } 
} 

E naturalmente ora posso accedere al mio WorkbookState da qualsiasi punto della mia applicazione a nastro semplicemente chiamando ThisAddIn.WorkbookState

+1

Fornisce un altro hash per ActiveWorkbook quando viene chiamato da Task.Run. – smg

+0

ottengo due lunghezze diverse dal chiamare il metodo hash da foglio e nastro –

+0

Mmmm si ottengono buoni punti @ChrisHayes e user3009578. Da allora ho imparato che ActiveWorkbook dovrebbe essere chiamato solo dal thread dell'interfaccia utente. Poiché ActiveWorkbook può cambiare, fare riferimento a un thread diverso dal thread dell'interfaccia utente è una strategia non valida. Pertanto, ho trovato che la strategia migliore è quella di memorizzare nella cache ActiveWorkbook nel gestore di eventi action dell'utente e utilizzare ActiveWorkbook per tutti i thread. Se i thread in background devono accedere a ActiveWorkbook senza un'azione da parte dell'utente, memorizzarne la copia in cache ascoltando l'evento Application.ActiveWorkbookChanged. Non c'è bisogno di downvotare^_ ^ – stuzor

Problemi correlati