玄関へお回り下さい。
.NETからExcelの基本的な操作方法 (6個)               (SNo.062)
サンプル投稿用掲示板の方に、VB.NET(VB2005/VB2008/VB2010)から Excel VBA を使って操作するサンプルをいろいろ投稿したのに伴い、こちらのサンプルとの共通性を図る為にも今回書き直してみました。
ご使用にあたっては、Excelのプロセスが正常に終了しない理由 をよく読んでから実行願います。
このサンプルは、VB2010 から Excel を操作する時に必要な起動・終了処理を紹介しており、VB2010 から Excel を操作している間に、ユーザーが勝手に Excel を閉じるとエラーが発生するので、操作中は、ユーザーが勝手にExcel を閉じれないように設定を追加しております。
尚、VB から Excel を操作する為のサンプル(Tips)は、ここをはじめ、サンプル投稿用掲示板VB2005用掲示板他いろいろなところに投稿してありますので、サイト内検索等で探して下さい。
サンプル投稿用掲示板のカテゴリ別サンプル一覧からでも見る事ができます。
又、Excel の起動・終了に関する設定は、実行環境用とテスト環境用の両方を掲載しておりますので。できれば、開発時は、テスト環境用の起動・終了に関する設定を使って頂いた方がプロセスの解放状況がよく解りますので便利かと思います。
これらのサンプルをご使用になられる場合は、必ず、下記の Excelのプロセスが正常に終了しない理由をご覧になってから試すようにして下さい。
Excelのプロセスが正常に終了しない理由(その1)
Excelのプロセスが正常に終了しない理由(その2)

※ 掲載サンプルは、Excel 2002 の当時に書いたものやExcel 2007 以降のバージョンで書いたもの等入り混じっておりますので、ご使用のバージョンによっては、エラー等が表示される場合があるかもしれませんので、動作環境等をご確認下さい。(ご使用のバージョンに合せて変更して下さい)
※ 下記コードの表示は、自作の【Excel Com オブジェクトの解放漏れチェックツール】の結果を自作のツールでリッチテキストをHTMLファイルに変換した結果を掲載しております。
標準のIDE上のコードのカラー化に加え、Excel Com オブジェクト等もカラー化しております。

'===================================================================================================
'SampleNo:062  2010.05.17   A 2012.02.28
'タイトル:Excelの起動及び終了方法(062) - VB2010
'動作確認:WindowsVista VB2010(VS2010 Pro) Framework 4 / ターゲットCPU:X86 Excel 2007/Excel 2010
'[Option Compare Text] [Option Explicit On] [Option Infer On] [Option Strict On]で設定
'---------------------------------------------------------------------------------------------------
'プロジェクト→参照の追加→COM→Microsoft Excel *.* ObjectLibrary を参照設定しておいてください。
'ソースコードは、保存オプションの詳細設定で、日本語(シフトJIS)-コードページ932 で保存しております。
'========1=========2=========3=========4=========5=========6=========7=========8=========9=========0

Imports Microsoft.Office.Interop

Public Class Form1

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
  Call ExcelOpen("", "")   '新規ファイルをオープンして、Excel を起動
  '既存のファイルをオープンして、Excel を起動する場合 (ファイルのフルパス , シート名)
  'Call ExcelOpen(System.IO.Path.GetFullPath("..\..\..\data\DBTest.xls"), "Sheet1")
'---------------------------------------------------------------------------------------------------

  Dim xlRange As Excel.Range
  Dim strDat(2, 0) As Object
  xlRange = xlSheet.Range("A5:A7")  'データの入力セル範囲
  strDat(0, 0) = "10"         'データの作成
  strDat(1, 0) = "20"
  strDat(2, 0) = "=Sum(A5:A6)"    '計算式
  xlRange.Value = strDat       'セルへデータの入力

  '使い終わった時点で、xlRange オブジェクトを解放 
  MRComObject(xlRange)

  '確認のために、5秒間表示して終了処理へ
  System.Threading.Thread.Sleep(5000)

'---------------------------------------------------------------------------------------------------
  'Excelファイルを上書き保存(True 又省略すれば)して終了処理を実行(名前をつけて保存も同じ)
  Call ExcelClose(IO.Path.GetFullPath(".\Test.xlsx"), False) 'False の場合保存しないで終了

  'Excel.EXE がタスクマネージャーに残っていないか調査(テスト環境用の場合のみ追加して下さい)
  Call ProcessCheck() '正常に動作する事が確認できたらこの行は、コメントにして下さい。

End Sub


下記、Excel の起動・終了に関する設定は、実装用とテスト環境用の両方を掲載しておきますので、条件付きのコンパイルで切り替えるなり、コードを入れ替えるなり、組み替えるなり、お好きな方法でご使用下さい。
(不慣れな内は、テスト環境用のコードを使って開発される事をお薦め致します。)


#Region "Excel の起動・終了に関する設定(実装用)"

'---------- Private な変数の宣言 -----------------------------------
Private xlApp As Excel.Application
Private xlBooks As Excel.Workbooks
Private xlBook As Excel.Workbook
Private xlSheets As Excel.Sheets
Private xlSheet As Excel.Worksheet
Private frgClose As Boolean     'ユーザーが Excel を閉じようとしたかのフラグ

Private Sub ExcelOpen(ByVal FilePath As String, ByVal SheetName As String)
'Excel のオープン処理用プロシージャ
  frgClose = False  '起動中は、ユーザーが Excel を閉じれないように
  xlApp = New Excel.Application

  'Excel の WorkbookBeforeClose イベントを取得
  AddHandler xlApp.WorkbookBeforeClose, AddressOf xlApp_WorkbookBeforeClose

  xlBooks = xlApp.Workbooks
  If FilePath.Length = 0 Then
   '新規のファイルを開く場合
   xlBook = xlBooks.Add
   xlSheets = xlBook.Worksheets
   xlSheet = CType(xlSheets.Item(1), Excel.Worksheet)
  Else
   '既存のファイルを開く場合
   xlBook = xlBooks.Open(FilePath)
   xlSheets = xlBook.Worksheets
   xlSheet = CType(xlSheets(SheetName), Excel.Worksheet)
  End If
  xlApp.Visible = True
End Sub

Private Sub xlApp_WorkbookBeforeClose(ByVal Wb As Excel.Workbook, ByRef Cancel As Boolean)
'VB2010 から Excel の WorkbookBeforeClose イベントを監視してユーザーが Excel を閉じれないようにする
'(VB2010 から Excel を操作している途中でユーザーが勝手に Excel を閉じるとエラーが発生するので)
  If frgClose = False Then
   Cancel = True   'ユーザーが Excel を閉じようとしたので、処理をキャンセルする
  Else
   Cancel = False  'キャンセルしない(閉じる)
  End If
End Sub

Private Sub ExcelClose(ByVal FilePath As String, Optional ByVal CancelSave As Boolean = True)
'Excelファイルを上書き保存して終了処理用プロシージャ
  frgClose = True        'True : 閉じる事ができる(プログラムからの終了なので)
  xlApp.DisplayAlerts = False  '保存時の問合せのダイアログを非表示に設定
  If CancelSave Then
   Dim kts As String = System.IO.Path.GetExtension(FilePath).ToLower()
   Dim fm As Excel.XlFileFormat
   '拡張子に合せて保存形式を変更
   Select Case kts
     Case ".csv"  'CSV (カンマ区切り) 形式
      fm = Excel.XlFileFormat.xlCSV
     Case ".xls"  'Excel 97〜2003 ブック形式
      fm = Excel.XlFileFormat.xlExcel8
     Case ".xlsx"  'Excel 2007〜ブック形式
      fm = Excel.XlFileFormat.xlOpenXMLWorkbook
     Case ".xlsm"  'Excel 2007〜マクロ有効ブック形式
      fm = Excel.XlFileFormat.xlOpenXMLWorkbookMacroEnabled
     Case Else   '必要なものは、追加して下さい。
      fm = Excel.XlFileFormat.xlWorkbookDefault
      MessageBox.Show("ファイルの保存形式を確認して下さい。")
   End Select
   Try
     xlBook.SaveAs(Filename:=FilePath, FileFormat:=fm)  'ファイルに保存
   Catch ex As Exception
     MessageBox.Show(ex.Message)
   End Try
  End If
  MRComObject(xlSheet)     'xlSheet の解放
  MRComObject(xlSheets)     'xlSheets の解放
  xlBook.Close()        'xlBook を閉じる
  MRComObject(xlBook)      'xlBook の解放
  MRComObject(xlBooks)     'xlBooks の解放
  xlApp.Quit()         'Excelを閉じる
  MRComObject(xlApp)      'xlApp を解放
End Sub

'今まで使っていた方法では、Option Strict On の時にエラーとなったので、下記の
'魔界の仮面弁士さんの投稿を使用させて頂きました。詳しくは、下記を参照してください。
' http://hanatyan.sakura.ne.jp/vbnetbbs/wforum.cgi?mode=allread&no=6370#6374

'VB2005/VB2008/VB2010 用
''' <summary>
''' COMオブジェクトの参照カウントをデクリメントします。
''' </summary>
''' <typeparam name="T">(省略可能)</typeparam>
''' <param name="objCom">
''' COM オブジェクト持った変数を指定します。
''' このメソッドの呼出し後、この引数の内容は Nothing となります。
''' </param>
''' <param name="force">
''' すべての参照を強制解放する場合はTrue、現在の参照のみを減ずる場合はFalse。
''' </param>
Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T,
                   Optional ByVal force As Boolean = False)
 If objCom Is Nothing Then
  Return
 End If
 Try
  If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
   If force Then
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
   Else
    System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
   End If
  End If
 Finally
  objCom = Nothing
 End Try
End Sub

#End Region

End Class


#Region "Excel の起動・終了に関する設定(テスト環境用)"

'---------- Private な変数の宣言 -----------------------------------
Private xlApp As Excel.Application
Private xlBooks As Excel.Workbooks
Private xlBook As Excel.Workbook
Private xlSheets As Excel.Sheets
Private xlSheet As Excel.Worksheet

Private Sub ExcelOpen(ByVal FilePath As String, ByVal SheetName As String)
'Excel のオープン処理用プロシージャ
  'メッセージボックス等がExcelの裏に隠れないようにする為に
  Me.TopMost = True

  frgClose = False  '起動中は、ユーザーが Excel を閉じれないように
  xlApp = New Excel.Application
  'Excel の WorkbookBeforeClose イベントを取得
  AddHandler xlApp.WorkbookBeforeClose, AddressOf xlApp_WorkbookBeforeClose

  xlBooks = xlApp.Workbooks
  If FilePath.Length = 0 Then
   '新規のファイルを開く場合
   xlBook = xlBooks.Add
   xlSheets = xlBook.Worksheets
   xlSheet = DirectCast(xlSheets.Item(1), Excel.Worksheet)
  Else
   '既存のファイルを開く場合
   xlBook = xlBooks.Open(FilePath)
   xlSheets = xlBook.Worksheets
   xlSheet = DirectCast(xlSheets(SheetName), Excel.Worksheet)
  End If
  xlApp.Visible = True
End Sub

Private frgClose As Boolean  'ユーザーが Excel を閉じようとしたかのフラグ

Private Sub xlApp_WorkbookBeforeClose(ByVal Wb As Excel.Workbook, ByRef Cancel As Boolean)
'VB2010 から Excel の WorkbookBeforeClose イベントを監視してユーザーが Excel を閉じれないようにする
  If frgClose = False Then
   Cancel = True    'ユーザーが Excel を閉じれないように
  Else
   Cancel = False
  End If
End Sub

Private Sub ExcelClose(ByVal FilePath As String, Optional ByVal CancelSave As Boolean = True)
'Excelファイルを上書き保存して終了処理用プロシージャ
  frgClose = True        'プログラムからExcel を閉じた時のフラグ
  xlApp.DisplayAlerts = False  '保存時の問合せのダイアログを非表示に設定
  If CancelSave Then
   Dim kts As String = System.IO.Path.GetExtension(FilePath).ToLower()
   Dim fm As Excel.XlFileFormat
   '拡張子に合せて保存形式を変更(使用する Excel のバージョンに注意)
   Select Case kts
     Case ".csv"  'CSV (カンマ区切り) 形式
      fm = Excel.XlFileFormat.xlCSV
     Case ".xls"  'Excel 97〜2003 ブック形式
      fm = Excel.XlFileFormat.xlExcel8
     Case ".xlsx"  'Excel 2007〜ブック形式
      fm = Excel.XlFileFormat.xlOpenXMLWorkbook
     Case ".xlsm"  'Excel 2007〜マクロ有効ブック形式
      fm = Excel.XlFileFormat.xlOpenXMLWorkbookMacroEnabled
     Case Else   '必要なものは、追加して下さい。
      fm = Excel.XlFileFormat.xlWorkbookDefault
      MessageBox.Show("ファイルの保存形式を確認して下さい。")
   End Select
   Try
     xlBook.SaveAs(Filename:=FilePath, FileFormat:=fm)  'ファイルに保存
   Catch ex As Exception
     MessageBox.Show(ex.Message)
   End Try
  End If
  MRComObject(xlSheet)     'xlSheet の解放
  MRComObject(xlSheets)     'xlSheets の解放
  xlBook.Close()        'xlBook を閉じる
  MRComObject(xlBook)      'xlBook の解放
  MRComObject(xlBooks)     'xlBooks の解放
  xlApp.Quit()         'Excelを閉じる
  MRComObject(xlApp)      'xlApp を解放
End Sub

Private Sub ProcessCheck()
'タスクマネージャーに、Excel.exe が残っていないか確認(テスト環境でのみ使用の事)
  '以前は、Loop しながら5秒間程繰り返し確認していたのだが、その間に解放される場合が
  'ある事が判明したので、下記のように1回きりの確認でもデクリメント処理がきちんと
  '行われていたら解放される事が解ったので下記のように厳密に判定する事にしました。
  System.Threading.Thread.Sleep(500)
  Application.DoEvents()
  If Process.GetProcessesByName("Excel").Length = 0 Then
   '先にフォームを閉じるとエラーが発生するので
   '必要により表示するようにして下さい。
   MessageBox.Show(Me, "Excel.EXE は解放されました。")
   Exit Sub
  End If
  If Process.GetProcessesByName("Excel").Length >= 1 Then
   Dim ret As DialogResult
   ret = MessageBox.Show(Me, "まだ Excel.EXE が起動しています。強制終了しますか?", _
                           "確認", MessageBoxButtons.YesNo)
   If ret = Windows.Forms.DialogResult.Yes Then
     Dim localByName As Process() = Process.GetProcessesByName("Excel")
     Dim p As Process
     '起動中のExcelを取得
     For Each p In localByName
      'Windou の無い(表示していない)Excel があれば強制終了させる
      '画面に表示している Excel は、終了させないので必要なら手動で終了して下さい。
      If System.String.Compare(p.MainWindowTitle, "", True) = 0 Then
        'Excel.EXE のプロセスを削除
        p.Kill()
      End If
     Next
   End If
  End If
End Sub

Public Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
  Dim IDEEnvironment As Boolean = False 'メッセージボックスを表示させたい場合は、True に設定
  If objCom Is Nothing Then
   If IDEEnvironment = True Then
     'テスト環境の場合は下記を実施し、後は、コメントにしておいて下さい。
     MessageBox.Show(Me, "Nothing です。")
   End If
   Return
  End If
  Try
   If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
     Dim count As Integer
     If force Then
      count = System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
     Else
      count = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
     End If
     If IDEEnvironment = True AndAlso count <> 0 Then
      Try
        'テスト環境の場合は下記を実施し、後は、コメントにしておいて下さい。
        MessageBox.Show(Me, TypeName(objCom) & " 要調査! デクリメントされていません。")
      Catch ex As Exception
        MessageBox.Show(Me, " 要調査! デクリメントされていません。")
      End Try
     End If
   Else
     If IDEEnvironment = True Then
      'テスト環境の場合は下記を実施し、後は、コメントにしておいて下さい。
      MessageBox.Show(Me, "ComObject ではありませんので、解放処理の必要はありません。")
     End If
   End If
  Finally
   objCom = Nothing
  End Try
End Sub

#End Region

注意!
MRComObject は、元々 http://support.microsoft.com/kb/317109/ja の NAR の実装を元に私が少し変更して使っていたのですが、今まで使っていた方法では、Option Strict On の時にエラーとなったので、当サイトの掲示板の 魔界の仮面弁士さん の投稿を使用させて頂いたものなので、そう言った経緯を無視して、転載・引用されると、魔界の仮面弁士さん 等にもご迷惑をおかけする事にもなりますので、絶対に転載されないようにお願い致します。
又、MRComObject の名前は、変更しておりませんが中身が何度となく変更しておりますので、転載先まで変更する事ができず、問題があっても困りますので、必要ならリンクを貼るようにして下さい。

又、Excelのプロセスが正常に終了しない理由 等をよく理解しないまま、MRComObject を使用すれば、全て解放されるかのように誤解されて使用されている場合もあるので、よく読んでからご使用下さい。


2004/02/28
2008/08/10
2012/05/18
2012/07/01

VBレスキュー(花ちゃん)
VB.NET2003  VB2005