Domanda

Ho un'applicazione che utilizza le assemblee di Office interoperabilità. Sono consapevole del "Runtime Callable Wrapper (RCW)" gestito dal runtime. Ma io non sono molto sicuro di come il conteggio dei riferimenti viene incrementato. MSDN dice:

RCW mantiene solo un riferimento alla avvolto oggetto COM indipendentemente dal numero di client gestiti che lo chiamano.

Se ho ben capito, il seguente esempio,

using Microsoft.Office.Interop.Word;

static void Foo(Application wrd)
{
    /* .... */
}

static void Main(string[] args)
{
    var wrd = new Application();
    Foo(wrd);
    /* .... */
}

sto passando il wrd esempio per un altro metodo. Ma questo non incrementa il conteggio di riferimento interno. Quindi mi chiedo su quali scenari il conteggio dei riferimenti viene incrementato? Qualcuno può punto di uno scenario in cui il conteggio dei riferimenti viene incrementato?

Inoltre ho letto alcuni blog che dice evitano il ricorso ad doppi punti durante la programmazione con oggetti COM. Qualcosa di simile, wrd.ActiveDocument.ActiveWindow. L'autore sostiene che compilatore crea variabili separate per contenere i valori, che incrementerà il contatore di riferimento. IMHO, questo è sbagliato e il primo esempio lo dimostra. E 'corretto?

Qualsiasi aiuto sarebbe grande!

È stato utile?

Soluzione

Sono stato alla ricerca questa domanda troppo, lavorando su un COM / NET-Interop-centric, combattendo perdite, si blocca e si blocca.

Risposta breve: ogni volta che l'oggetto COM viene passato da ambiente COM a .NET

.

Risposta lunga:

  1. Per ogni oggetto COM c'è un oggetto RCW [Test 1] [Rif 4]
  2. conteggio dei riferimenti viene incrementato ogni volta che l'oggetto viene richiesto dall'interno oggetto COM (chiamando proprietà o il metodo in oggetto COM che oggetto di ritorno COM, il conteggio di oggetto di riferimento COM restituito verrà incrementato di uno) [Test 1]
  3. count
  4. Riferimento non viene incrementato di fusione ad altre interfacce COM dell'oggetto o spostando il riferimento RCW intorno [Test 2]
  5. conteggio dei riferimenti viene incrementato ogni volta che un oggetto viene passato come parametro in caso sollevato dal COM [Ref 1]

Su un lato nota: Si dovrebbe SEMPRE di rilascio COM oggetti appena finito di usarli. Lasciare questo lavoro al GC può portare a perdite, comportamento imprevisto e deadlock eventi. Questo è dieci volte più importante se si accede oggetto non sul thread STA è stato creato su. [Ref 2] [Rif 3] [dolorosa esperienza personale]

Sono la speranza ho coperto tutti i casi, ma COM è un osso duro. Cin cin.

Test 1 - riferimento count

private void Test1( _Application outlookApp )
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();
    var explorer3 = outlookApp.ActiveExplorer();
    var explorer4 = outlookApp.ActiveExplorer();

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True

Test 2 -. Riferimento conteggio cont

private static void Test2(_Application outlookApp)
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();

    var explorer3 = explorer2 as _Explorer;
    var explorer4 = (ExplorerEvents_10_Event)explorer2;
    var explorerObject = (object)explorer2;
    var explorer5 = (Explorer)explorerObject;

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True

Fonti I relè sulla oltre la mia esperienza e la sperimentazione:

1. Johannes Passando di -! Regole di conteggio RCW riferimento = COM Riferimento regole di conteggio

2. Eran Sandler - Runtime Callable Wrapper Internals e le insidie ??più comuni

3. Eran Sandler - Marshal.ReleaseComObject e CPU Spinning

4. MSDN - Runtime Callable Wrapper

Altri suggerimenti

Non ho visto il codice per il RCW - nemmeno sicuro che sia parte del SSCLI - ma ho dovuto implementare un sistema simile per il monitoraggio di durata degli oggetti COM in SlimDX e ha dovuto fare un bel po 'di ricerche il RCW. Questo è quello che mi ricordo, speriamo che sia ragionevolmente accurata, ma la prendo con un tocco di sale.

Quando il sistema vede prima un puntatore interfaccia COM, si va solo a una cache per vedere se c'è un RCW per questo puntatore di interfaccia. Presumibilmente la cache sarebbe utilizzando riferimenti deboli, in modo da non impedire la messa a punto e la raccolta del RCW.

Se c'è un involucro dal vivo per quello puntatore, il sistema restituisce l'involucro - se l'interfaccia è stata ottenuta in un modo che incrementato conteggio dei riferimenti dell'interfaccia, presumibilmente il sistema RCW chiamerebbe Release () a questo punto. Ha trovato un wrapper dal vivo, quindi sa che wrapper è un solo riferimento e vuole mantenere esattamente un riferimento. Se non c'è involucro vivo nella cache, si crea uno nuovo e lo restituisce.

L'involucro chiama rilascio sul puntatore a interfaccia COM sottostante (s) dal finalizzatore.

L'involucro si trova tra voi e l'oggetto COM, e gestisce tutti i parametri marshalling. Questo permette anche permettono di prendere il risultato grezzo di qualsiasi metodo di interfaccia che si è un altro puntatore di interfaccia ed eseguire tale puntatore attraverso il sistema di caching RCW per vedere se esiste ancora prima di restituire il puntatore di interfaccia avvolto.

Purtroppo non ho una buona comprensione di come le maniglie del sistema RCW generazione oggetto proxy per l'invio di materiale attraverso i domini applicativi o appartamenti filo; non era un aspetto del sistema avevo bisogno di copiare per SlimDX.

Non dovrebbe essere necessario alcun trattamento speciale. Il runtime mantiene un solo riferimento all'oggetto COM. La ragione di questo è che il GC tiene traccia di tutti i riferimenti gestiti, in modo che quando il RCW va fuori del campo di applicazione e viene raccolto, viene rilasciato il riferimento COM. Quando si passa intorno a un riferimento gestito, il GC è il monitoraggio per voi -. Questo è uno dei più grandi vantaggi di un runtime GC basata sul vecchio schema AddRef / Release

Non è necessario chiamare manualmente Marshal.ReleaseComObject meno che non vogliate rilascio più deterministico.

La soluzione accettata è valida, ma ecco alcune informazioni aggiuntive.

Un RCW contiene uno o più riferimenti nativi di interfaccia di oggetti COM internamente per il suo oggetto COM.

Quando un RCW rilascia il suo oggetto COM sottostante, sia a causa di ottenere garbage collection o per Marshal.ReleaseComObject() sempre chiamato su di esso, rilascia tutte le sue interfacce degli oggetti COM detenuti internamente.

Ci sono in realtà molti conteggi dei riferimenti qui - uno determinare quando di .NET RCW dovrebbe rilasciare le sue interfacce degli oggetti COM sottostanti, e poi ciascuna di quelle interfacce COM prime ha il suo conteggio di riferimento come in una regolare COM

.

Ecco il codice per ottenere conteggio dei riferimenti interfaccia IUnknown prime COM:

int getIUnknownReferenceCount(object comobject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    return Marshal.Release(iUnknown);
}

e si può ottenere lo stesso per le altre interfacce COM dell'oggetto utilizzando Marshal.GetComInterfaceForObject().

In aggiunta ai metodi elencati nel accettato soluzione , possiamo anche aumentare il numero di riferimento .NET RCW artificialmente chiamando qualcosa come Marshal.GetObjectForIUnknown().

È qui esempio di codice facendo uso di questa tecnica per ottenere conteggio dei riferimenti RCW di un dato oggetto COM:

int comObjectReferenceCount(object comObject)
{
    var iUnknown = Marshal.GetIUnknownForObject(comObject);
    Marshal.GetObjectForIUnknown(iUnknown);
    Marshal.Release(iUnknown);
    return Marshal.ReleaseComObject(comObject);
}

È necessario chiamare Marshal.ReleaseComObject sulla variabile wrd per rilasciare il riferimento all'applicazione di parola.

In questo modo se Word non è visibile e si chiude l'applicazione, l'exe scaricherà così a meno che non avete fatto visibile all'utente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top