<案1> Imports Microsoft.Office.Interop Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim ex As New Excel.Application() With { .Visible = True }
<案2> Imports Excel = Microsoft.Office.Interop.Excel Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim ex As New Excel.Application() With { .Visible = True }
> Dim sheet As Microsoft.Office.Interop.Excel.Worksheets= ex.Worksheets Sheets や Worksheets といったコレクション オブジェクトは、「複数形」の型名なので sheet といった「単数形」の変数名にすると、後で読むときにややこしくないですか…?
> Dim sh As Microsoft.Office.Interop.Excel.Worksheet =sheet(“発注”) Worksheets プロパティはその戻り値として 実は Worksheets 型ではなく Sheets 型のオブジェクトを返します。 型が異なるため、Worksheets 型には代入できません。
そのため .NET から扱う場合は Dim sheet As Excel.Sheets = book.Worksheets のように書くことになります。
とはいえ、VB2008 以降なら型推論が使えるので、単に Dim sheets = book.Worksheets と書くのが簡単でしょう。
Worksheet に関しては、そこから Dim sheet1 = DirectCast(sheets("Sheet1"), Excel.Worksheet) とします。単に Dim sheet1 = sheets("Sheet1") にしてしまうと、As Object になってしまうため、DirectCast で本来の型に戻します。
Dim lastRow As Integer = sh.Cells(sh.Rows.Count, "B").End(Microsoft.Office.Interop.Excel.XlDirection.xlUp).row とrange.ClearContents()をコメントにし、 range = Cells.Range("B5:I" & lastRow)をrange = Cells.Range("B5:I5")に変更するとプロセスが残らず終了できるので、lastRowかrange.ClearContents()のどちらかが解放漏れしていてプロセスが残ってしまっていると考えて居るのですが、解放の仕方が分からない状態です。 お力を貸してください。
Private Sub btn作成_Click(sender As Object, e As EventArgs) Handles btn作成.Click If txtPath.Text = "" Then Exit Sub End If
Dim str As String = "" Dim strCount = 0
Dim ex As New Microsoft.Office.Interop.Excel.Application Dim books As Microsoft.Office.Interop.Excel.Workbooks = ex.Workbooks Dim wb As Microsoft.Office.Interop.Excel.Workbook Dim sheets As Microsoft.Office.Interop.Excel.Sheets Dim sh As Microsoft.Office.Interop.Excel.Worksheet
Dim Cells As Microsoft.Office.Interop.Excel.Range Dim range As Microsoft.Office.Interop.Excel.Range
Try For rw As Integer = 0 To dgv.RowCount - 1 If strCount = 0 Then wb = books.Open(txtPath.Text) sheets = wb.Worksheets sh = sheets("発注一覧") Cells = sh.Cells strCount = strCount + 1
'ファイル内のセル初期化 Dim lastRow As Integer = sh.Cells(sh.Rows.Count, "B").End(Microsoft.Office.Interop.Excel.XlDirection.xlUp).row If lastRow >= 5 Then range = Cells.Range("B5:I" & lastRow) range.ClearContents() End If
End If
strCount = strCount + 1 End If Next
wb.Save() wb.Close(False) ex.Quit()
Catch exc As Exception Finally If Cells IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(Cells) End If If range IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(range) End If If sheets IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets) End If If sh IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(sh) End If If books IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(books) End If If wb IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(wb) End If If ex IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(ex) End If
Cells = Nothing range = Nothing sheets = Nothing sh = Nothing books = Nothing wb = Nothing ex = Nothing
GC.Collect() GC.WaitForPendingFinalizers()
btn作成.Enabled = True
If strCount <> 0 Then MsgBox(“作成完了”) End If End Try
■No35502に返信(Excel難しいさんの記事) > Dim lastRow As Integer = sh.Cells(sh.Rows.Count, "B").End(Microsoft.Office.Interop.Excel.XlDirection.xlUp).row
先の回答がまるで反映されていないですよね…? Range オブジェクトすべてを変数にとり、使用後に廃棄しましょう。
'Dim lastRow As Integer = sh.Cells(sh.Rows.Count, "B").End(Microsoft.Office.Interop.Excel.XlDirection.xlUp).row Dim cells = sh.Cells Dim rows = sh.Rows Dim rng = cells(rows.Count, "B") Dim rngEnd = rng.End(Microsoft.Office.Interop.Excel.XlDirection.xlUp)
> range = Cells.Range("B5:I" & lastRow)を この処理自体には何の問題もありません。 実際のところ、 Dim lastRow As Integer = 5 range = Cells.Range("B5:I" & lastRow) であれば、問題は出ないですよね?
問題があったのは、lastRow を求めるための先の手続きです。
> Private Sub btn作成_Click(sender As Object, e As EventArgs) Handles btn作成.Click strCount が何をカウントしているのかの意図が不明瞭ですが、 提示されたコードでは「If 〜 Then」よりも「End If」の方が多く、文法的に意味が通りません。 正しいコードを提示しましょう。
それと、COM オブジェクトの扱いに根本的な誤解があるようです。 Dim o1 = sheet1.Cells Dim o2 = sheet1.Cells この場合、o1 と o2 は同一のオブジェクトを指しているように見えますが、 実際は別物であり、 If o1 Is o2 Then は False となります。
それゆえに Dim o As Excel.Range = Nothing For n = 1 To 2 o = sheet1.Cells : Next Marshal.ReleaseComObject(o) のような再代入処理が行われると、Range オブジェクトの解放漏れに繋がります。 ※Range 以外の COM オブジェクト(Workbooks とか Workbook とか Sheets とか Worksheet とか)も同様です。
ただし、 For n = 1 To 2 Dim o As Excel.Range = sheet1.Cells : Marshal.ReleaseComObject(o) Next のように、取得 → 解放の手続きをその都度行うのであれば、問題ありません。 (とはいえ、何度も取得しなおす方法では、実行コストが高くなってしまいますが)
> For rw As Integer = 0 To dgv.RowCount - 1 > If strCount = 0 Then > wb = books.Open(txtPath.Text) > sheets = wb.Worksheets > sh = sheets("発注一覧") ということで、この書き方は NG 。
Dim wb = books.Open(txtPath.Text) Dim sheets = wb.Worksheets Dim sh = sheets("発注一覧") For rw = 0 To dgv.RowCount - 1 : Next Marshal.ReleaseComObject(sh) Marshal.ReleaseComObject(sheets) wb.Save() wb.Close(False) Marshal.ReleaseComObject(wb) ex.Quit() Marshal.ReleaseComObject(ex)
それにしても、『For rw As Integer = 0 To dgv.RowCount - 1』って何の意味があるのでしょうか? dgv が DataGridView であるというのは想像がつきますが、 ループ内で変数 rw が一度も使われていませんし、ループさせる意味が皆無に見えるのですが。