2012-04-25 6 views
2

私は次のコードを持っています(オンラインチュートリアルから入手しました)。コードが動作していますが、私はExcelのCOMオブジェクトを処理する方法があると思われます。は適切ではありません。本当にGC.Collectを呼び出す必要がありますか?または、このExcel comオブジェクトを処分する最善の方法は何ですか?VB.NETを使用してExcel comオブジェクトを処分する適切な方法はありますか?

Public Sub t1() 
    Dim oExcel As New Excel.Application 
    Dim oBook As Excel.Workbook = oExcel.Workbooks.Open(TextBox2.Text) 

    'select WorkSheet based on name 
    Dim oWS As Excel.Worksheet = CType(oBook.Sheets("Sheet1"), Excel.Worksheet) 
    Try 

     oExcel.Visible = False 
     'now showing the cell value 
     MessageBox.Show(oWS.Range(TextBox6.Text).Text) 

     oBook.Close() 
     oExcel.Quit() 

     releaseObject(oExcel) 
     releaseObject(oBook) 
     releaseObject(oWS) 
    Catch ex As Exception 
     MsgBox("Error: " & ex.ToString, MsgBoxStyle.Critical, "Error!") 
    End Try 
End Sub 

Private Sub releaseObject(ByVal obj As Object) 
    Try 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) 
     obj = Nothing 
    Catch ex As Exception 
     obj = Nothing 
    Finally 
     GC.Collect() 
    End Try 
End Sub 
+4

[C#でExcel interopオブジェクトを適切にクリーンアップする方法](http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c)の可能な複製-sharp) –

+0

@Petr Abdulin、C#は私にとっては外国語です。正確に重複しません。私は、いわゆる重複で受け入れられた答えを理解するのに苦労しています。 –

+0

'GC.Collect()'を呼び出す必要は決してありません。 – Seph

答えて

6

@PanPizza C#とVB.NETはWorksheets sheets = ...Dim sheets Worksheets = ...なり、行の末尾から;を除去する、非常に類似しています。プログラミングをより良くすることに興味がある場合は、両方の間でどのように移行するかを実際に学ぶべきです。多くの.NETの例は、どちらか一方のみで提供されており、実際には自分自身を制限しています。 How do I properly clean up Excel interop objects?これは常にから決して直接、ワークシートにステップダウンし、その後単一のサブオブジェクトにステップダウンして、常にこのDim oWS AS Excel.Worksheet = oExcel.Worksheets.Open(...)を決してしないブックへのステップダウンと手段「2つのドットを使用しないでください」:この回答で述べたように

Excel.Application

一般的なルールとして、作成したアイテムとは逆の順序でアイテムをリリースする必要があります。さもなければ、あなたは他の参照の下から足を引っ張っており、彼らは正しく割り当てを解除しません。

お知らせは、Excelアプリケーション(oExcel)、その後、Excelワークブック(oBook)そして最後にExcelワークシート(oWS)を作成する方法、あなたは逆の順序でそれらを解放する必要があります。

このようにあなたのコードは次のようになります。

oBook.Close() 
    oExcel.Quit() 

    releaseObject(oWS) 
    releaseObject(oBook) 
    releaseObject(oExcel) 
Catch ex As Exception 

、ちょうどそれが必要ないですSub releaseObject(ByVal obj As Object)

Finally 
    GC.Collect() 

から完全にこのコードを削除し、GCは、自然に発生すると、アプリケーションが瞬時に解放することを期待しないでください.NETには割り当てられていないメモリがプールされるため、メモリにOSを要求するのではなく、このメモリ内のオブジェクトを簡単にインスタンス化できます。

+0

説明のためにありがとう –

+0

@Seph私はあなたの根本的な参照の説明が好きでしたが、VSTOではそれはそれ以上のものです:http://jake.ginnivan.net/vsto-com-interop –

+0

@Sephコメントをありがとう。私は 'GC.Collect()'は自然に発生するのでほとんどの場合は必要ではないことに同意しますが、COMオブジェクトを使って作業しているときに必要になることがあります。私にとって、すべてのオブジェクトを解放した後、私は 'GC.Collect()'と 'GC.WaitForPendingFinalizers()'を続けて呼び出さなければなりませんでした。それについてのいくつかのコメント[ここ](https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/)があります。 – dustinrwh

1

私はこれを検索して検索しましたが、マイクロソフト独自のソリューションでさえ機能しません(あなたが見たい場合はHere)。私はExcelテンプレートにデータをエクスポートするvb.netアプリケーションを持っています。理想的には、ユーザーがExcelウィンドウを閉じるとプロセスが終了するが、Microsoftの記事で述べたように、vb.netはまだそれを参照しているため、プロセスは終了しない。

For Each p As Process In Process.GetProcesses 
    If p.ProcessName = "EXCEL.EXE" Then p.Kill 
Next 

しかし、これはエクセルのすべてのインスタンスを殺すし、ユーザーが得るだろうと開いた他のExcelのウィンドウを持っていることがあります。

は、あなたは以下のようにこれを行うための手順があり、プロセスを自分を殺すために必要があります保存せずにシャットダウン、私はこの(私が使用しているワークブックは「トップ5の問題テンプレート」と呼ばれている)を作ってみた:

For Each p As Process In Process.GetProcesses 
    If InStr(p.MainWindowTitle, "Top 5 Issues Template") <> 0 Then p.Kill 
Next 

これはウィンドウ名ではなく、プロセス名で検索し、唯一殺しますそれに関連するプロセス。これは、私が何かをつぶすことなくExcelを正しく終了させる唯一の方法です。

0

私の鍵は、GarbageCollector(GC)に私が何かクリーンアップしたかったことを知らせることでした。私はこれが通常必要ではないことを認識しますが、COMオブジェクトで作業する場合、時には必要になります。Collect()WaitForPendingFinalizers()を呼び出すことによってクリーンアップするGCを頼む、オブジェクトを解放した後、より多くの情報https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/

については、このリンクを参照してください。上記のリンクは、メモリからCOMオブジェクトを完全に削除するために、これらのmehtodを2回呼び出す必要があることを示しています。私の場合は、一度これらのメソッドを呼び出すと機能しましたが、それを2度呼び出す価値があります。

oBook.Close() 
oExcel.Quit() 

releaseObject(oExcel) 
releaseObject(oBook) 
releaseObject(oWS) 

GC.Collect() 
GC.WaitForPendingFinalizers() 
GC.Collect() 
GC.WaitForPendingFinalizers() 
4

まず - あなた決して Excelの相互運用を行う際Marshal.ReleaseComObject(...)またはMarshal.FinalReleaseComObject(...)を呼び出す必要があります。混乱する反パターンですが、.NETからCOM参照を手動でリリースする必要があることを示すMicrosoftを含むこれに関する情報は間違っています。実際には、.NETランタイムとガベージコレクタはCOM参照を正しく追跡し、クリーンアップします。あなたのコードでは、これは全体のreleaseObject(...) Subを削除して呼び出すことができることを意味します。

第2に、プロセスが終了したときに(Excelプロセスが終了するように)アウトプロセスCOMオブジェクトへのCOM参照がクリーンアップされるようにするには、ガベージコレクタが実行されていることを確認する必要があります。 GC.Collect()GC.WaitForPendingFinalizers()の呼び出しでこれを正しく実行します。 2度呼び出すことは安全です。最後にも確実にサイクルが確実にクリーンアップされます。

第3に、デバッガで実行しているとき、ローカル参照はメソッドの終了時まで人為的に有効に保たれます(ローカル変数検査が機能するようになります)。だからGC.Collect()呼び出しは同じメソッドからrng.Cellsのようなオブジェクトのクリーニングには有効ではありません。 COM相互運用機能を実行するコードをGCクリーンアップから別々のメソッドに分割する必要があります。

一般的なパターンは次のようになります。

Sub WrapperThatCleansUp() 

    ' NOTE: Don't call Excel objects in here... 
    '  Debugger would keep alive until end, preventing GC cleanup 

    ' Call a separate function that talks to Excel 
    DoTheWork() 

    ' Now Let the GC clean up (twice, to clean up cycles too) 
    GC.Collect()  
    GC.WaitForPendingFinalizers() 
    GC.Collect()  
    GC.WaitForPendingFinalizers() 

End Sub 

Sub DoTheWork() 
    Dim app As New Microsoft.Office.Interop.Excel.Application 
    Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add() 
    Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1") 
    app.Visible = True 
    For i As Integer = 1 To 10 
     worksheet.Cells.Range("A" & i).Value = "Hello" 
    Next 
    book.Save() 
    book.Close() 
    app.Quit() 

    ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed 
End Sub 

MSDN上とStackOverflowの上で多くの記事を含むこの問題に関する誤った情報や混乱、多くのがあります。

最終的に、私は詳細を知り、適切なアドバイスを見つけ出すことができました。この投稿はhttps://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/であり、一部のStackOverflowの回答ではデバッガの下で参照が維持されています。

関連する問題