COM - con un grande potere derivano grandi responsabilità, ma a quale costo? (cercando consigli sulle pratiche di codifica quando si lavora con COM)

3

Qual è la prassi migliore (o comunemente accettata) su dove dichiarare le variabili oggetto COM (scope) e come gestirne la pulizia quando si utilizza la gestione strutturata degli errori?

Ho appena trascorso un sacco di tempo a imparare sulla Garbage Collection e sul Conteggio di riferimento e mi occupo di rilasciare oggetti COM in .NET.

Lavorare con COM è fastidioso ...

Il dilemma

Mi rendo conto che è importante rilasciare oggetti COM referenziati dopo averli terminati. Mi rendo anche conto che è anche importante anticipare e gestire gli errori.

Queste due cose insieme sembrano essere in disaccordo con ciò che mi viene sempre battuto come best practice: dichiarare le tue variabili dove sono usate.

Ora, al fine di garantire che i riferimenti vengano rilasciati quando vengono eseguiti con essi, sembra che abbia più senso eseguire questa operazione di pulizia all'interno della porzione Finally di un blocco Try / Catch.

Tuttavia, facendo ciò significa che non posso dichiarare queste variabili all'interno delle porzioni Try o Catch del blocco perché non saranno accessibili alla porzione Finally.

SO ....

Dove mi lascia in merito alle seguenti best practice?

Devo continuare a dichiarare le variabili all'inizio di ogni routine per pulirle usando Finally ?

C'è qualche altro metodo per accedere ai riferimenti COM alla fine di una procedura?

Sto solo raccogliendo pepe di merda per quanto riguarda le migliori pratiche?

Codice di esempio

Nell'Esempio 1, ho dichiarato tutte le mie variabili prima di entrare nel blocco Try / Catch. In questo modo posso assicurarmi che vengano puliti, indipendentemente da eventuali errori.

Nell'esempio 2, ho dichiarato le mie variabili dove sono utilizzate, ma ciò causa un problema con il potenziale non-rilascio di oggetti COM.

Imports System.Runtime.InteropServices

Module COMexamples   

    Sub Example1()
        Dim Com1 As SomeCOM
        Dim Com2 As AnotherCOM
        Try
            Com1 = New SomeCOM
            Com1.DoSomeStuff()
        Catch ex As Exception
            Com2 = New AnotherCOM
            Com2.CleanupTheMess()
        Finally
            If Not Com1 Is Nothing Then
                Com1.Close()
                Marshal.FinalReleaseComObject(Com1)
            End If
            If Not Com2 Is Nothing Then
                Com2.Quit()
                Marshal.FinalReleaseComObject(Com2)
            End If
        End Try
    End Sub

    Sub Example2()
        Try
            Dim Com1 As New SomeCOM
            Com1.DoSomeStuff()
            'If an exception is thrown, the object won't get released!
            Com1.Close()
            Marshal.FinalReleaseComObject(Com1)
        Catch ex As Exception
            Dim Com2 As New AnotherCOM
            Com2.CleanupTheMess()
            'Another exception here means we miss releasing this COM reference too!
            Com2.Quit()
            Marshal.FinalReleaseComObject(Com2)
        Finally
            'This is pretty much useless now as Com1 and Com2 are out of scope here
        End Try
    End Sub
End Module
    
posta CBRF23 15.07.2015 - 22:01
fonte

2 risposte

1

Devi dichiarare e creare l'oggetto prima del blocco try. Questo non è specifico per COM, ma un principio generale quando si utilizza una risorsa che deve essere esplicitamente deallocata in un blocco finale.

In altre parole: prima crea la risorsa, e se ciò riesce, prova a usare la risorsa. D'altra parte, se l'allocazione delle risorse fallisce, il blocco try / finally non viene mai eseguito.

Questo ti allevia anche dal controllare se Com1 o Com2 è assegnato nel blocco finally - sai per certo che se inserisci il blocco try che l'oggetto è stato inizializzato.

Sub Example()
    Dim Com1 As New SomeCOM
    Try
        Com1.DoSomeStuff()
    Catch
        Cleanup()
    Finally
        Com1.Close()
        Marshal.FinalReleaseComObject(Com1)
    End Try
End Sub

Sub Cleanup()
    Dim Com2 As New AnotherCOM
    Try
        Com2.CleanUpTheMess()
    Finally
        Com2.Quit()
        Marshal.FinalReleaseComObject(Com2)
    End Try
End Sub
    
risposta data 16.07.2015 - 10:26
fonte
4

Il tuo esempio 1 è la giusta strada da percorrere perché è logicamente corretto, che è molto più importante di un problema minore come la limitazione dell'ambito. Il tuo ambito è limitato a questa procedura, quindi è abbastanza buono. Se ti ha davvero infastidito, potresti suddividerlo in tre procedure, ma mi preoccuperei solo di questo se sembrava che la procedura fosse troppo lunga.

Imports System.Runtime.InteropServices

Module COMexamples   

Sub Example1()
    Try
        Example1A
    Catch
        Example1B
    End Try
 End Sub

Sub Example1A()
    Dim Com1 As SomeCOM
    Try
        Com1 = New SomeCOM
        Com1.DoSomeStuff()
    Finally
        If Not Com1 Is Nothing Then
            Com1.Close()
            Marshal.FinalReleaseComObject(Com1)
        End If
    End Try
End Sub

Sub Example1B()
    Dim Com2 As AnotherCOM
    Try
        Com2 = New AnotherCOM
        Com2.CleanupTheMess()
    Finally
        If Not Com2 Is Nothing Then
            Com2.Quit()
            Marshal.FinalReleaseComObject(Com2)
        End If
    End Try
End Sub
End Module
    
risposta data 15.07.2015 - 23:20
fonte