現在VB.NETにて、エクセルにDBの値を帳票として吐き出す処理を行っています。
その際のエクセルのプロセスが残る問題にて行き詰っております。
こちらの掲示板の過去ログや、紹介されておりました
花ちゃんさんのサイトを拝見し、ほぼ解放の処理を終えることができたのですが、
エクセルのヘッダーへの値のセットと、
改ページを行う処理についてプロセスが残ったままになっています。
'ヘッダーへ値セット
xlSheet.PageSetup.CenterHeader = "シート名" ①
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
xlSheet = Nothing
'改ページの挿入
xlRange = xlSheet.Range("H" & CStr(lngRpCnt + 41)) xlSheet.HPageBreaks.Add(before:=xlRange) ②
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
xlRange = Nothing
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
xlSheet = Nothing
ヘッダーへの値のセットではxlSheetの解放、
改ページの挿入ではxlSheet、xlRangeの解放をそれぞれ行っていますが
この他に解放しなければならないものがあるのでしょうか?
①②それぞれ1行をコメントにすると、プロセスは終了しています。
どのようにすればプロセスを終了させることができるのでしょうか?
ご教授お願い致します。
基本的に、『.』を連続して使うのは赤信号だと思ってください。
(1) の方は、PageSetupオブジェクトの参照が解放されていませんし、
(2) の方は、HPageBreaksコレクションの参照が解放されていません。
それぞれを変数に受けてから、ReleaseComObject を呼び出しましょう。
魔界の仮面弁士さん、ご回答ありがとうございます。
>それぞれを変数に受けてから、ReleaseComObject を呼び出しましょう。
過去ログの魔界の仮面弁士さんの書き込みにあったのですが、
.Cells が返す物は、Rangeオブジェクトなので、
CellsだけでなくRangeの解放が必要なように、
PageSetupオブジェクトとHPageBreaksコレクションも何か
オブジェクトを返しているのに、それを解放していないという
ことでよいのでしょうか?
色々自分なりに調べたつもりですが、
各オブジェクトとコレクションを最終的にどの変数に受けて
どの参照を解放してやればよいかが分かりません。
ご教授いただけないでしょうか?
>ご教授いただけないでしょうか?
久々に、正しい使い方を見ました。
>> (2) の方は、HPageBreaksコレクションの参照が解放されていません。
さらに(2)の方は、HPageBreakオブジェクトの参照も解放されていませんね。
>> 基本的に、『.』を連続して使うのは赤信号だと思ってください。
これはたとえば、Excelのオブジェクトで、
hoge.hage.hige() '『.』が2回連続して使われている!!
というようなメソッド呼び出しがあったとしたら、
A = hoge.hage 'hageオブジェクトを変数Aに受け取る
A.hige() 'hageクラスのhigeメソッドを呼び出す
Marshal.ReleaseComObject(A) 'hageオブジェクトを解放
Marshal.ReleaseComObject(hoge) 'hogeオブジェクトを解放
のように書く必要がある、という事になります。
メソッドの呼び出しだけでなく、プロパティ操作も同様です。
また、その hige メソッドが、別のCOMオブジェクトを返す場合には、
A = hoge.hage
B = A.hige()
Marshal.ReleaseComObject(B)
Marshal.ReleaseComObject(A)
Marshal.ReleaseComObject(hoge)
のように、戻り値の解放処理も必要となります。
もう少し具体的に書くと、
xlSheet.PageSetup.CenterHeader = 〜
のようなプロパティ操作の場合は、
A = xlSheet.PageSetup 'PageSetupオブジェクトを受け取る
A.CenterHeader = 〜
Marshal.ReleaseComObject(A)
というイメージになりますし、
xlSheet.HPageBreaks.Add(before:=xlRange)
の方に関しては、
B = xlSheet.HPageBreaks 'HPageBreaksコレクションを受け取る
C = B.Add(before:=xlRange) 'HPageBreakオブジェクトを受け取る
Marshal.ReleaseComObject(C)
Marshal.ReleaseComObject(B)
のようになる、という事ですね。
> 過去ログの魔界の仮面弁士さんの書き込みにあったのですが、
どれの事でしょう?
魔界の仮面弁士さん、大変ご丁寧な回答ありがとうございます。
細かくご指摘頂き、行わなければいけない事が把握できました。
ただ、もう少しお付き合い頂きたいのですが、
>A = xlSheet.PageSetup 'PageSetupオブジェクトを受け取る
という部分のAというのは、
Dim A As Object で宣言するのでしょうか?
それともDim A As Excel.〜で宣言すべきなのでしょうか?
xlSheet等はDim xlSheet As Excel.Worksheetと宣言しています。
こちらも魔界の仮面弁士さんの過去ログを拝見すると、
厳密な型付け(As Excel.〜) と、そうでないもの(As System.Object)が
混じってしまうのは良くないと書かれておられました。
同様に、
>B = xlSheet.HPageBreaks 'HPageBreaksコレクションを受け取る
>C = B.Add(before:=xlRange) 'HPageBreakオブジェクトを受け取る
についても、具体的にはどの型で受け取ればよいのか
ご教授いただけませんでしょうか?
参考にさせていただいた魔界の仮面弁士さんの過去ログは
下記のページの書き込みです。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200412/04120059.txt
ご指摘頂いた部分を改善して、ヘッダーの方は
とりあえずDim objheader As Objectで宣言の後解放してやると
うまくプロセスが終了致しました。
'ヘッダーへの値のセット
Dim objheader As Object
objheader = xlSheet.PageSetup
objheader.CenterHeader = "シート名"
System.Runtime.InteropServices.Marshal.ReleaseComObject(objheader)
objheader = Nothing
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
xlSheet = Nothing
ただ、改ページの方なのですが、書かれていたことをそのまま実行してみようと
CollectionとObjectで宣言を行い、記入したところ、
下記★の部分で青い波線が引かれ、エラーになってしまいました。
やはり宣言に問題があるのでしょうか。。
'改ページの挿入
Dim B As Collection
Dim C As Object
xlRange = xlSheet.Range("H" & CStr(lngRpCnt + 41))
B = xlSheet.HPageBreaks 'HPageBreaksコレクションを受け取る
★C = B.Add(before:=xlRange) 'HPageBreakオブジェクトを受け取る
> Dim B As Collection
これは使えません。
Excel 固有のコレクション型と、.NET の Collection クラスとの間には
互換性がありませんので、この場合は単なる Object 型を使ってください。
もしも厳密な型を利用したいのであれば、
Microsoft.Office.Interop.Excel.HPageBreaks インターフェイス型です。
# Excel PIA (microsoft.office.interop.excel.dll)を参照設定している場合
魔界の仮面弁士さんの仰るとおり、Object型で宣言し、
全て解放処理を行ったのですが、まだプロセスが残ります。
下記★の行をコメントにするとプロセスは終了します。
解放の順番がおかしいのかもと思い色々試しましたが違うようですし、
B.Add(before:=xlRange)を更にオブジェクトに入れるのかと
試してみましたがうまく行きません。。
どこに問題があるのか、正直お手上げ状態になってしまったのですが
何かまだ解放されていないオブジェクトがあるのでしょうか?
'改ページの挿入
Dim B As Object
Dim C As Object
B = xlSheet.HPageBreaks 'HPageBreaksコレクションを受け取る
xlRange = xlSheet.Range("H" & CStr(lngRpCnt + 41))
★C = B.Add(before:=xlRange) 'HPageBreakオブジェクトを受け取る
pbsMRComObject(C)
pbsMRComObject(B)
pbsMRComObject(xlRange)
pbsMRComObject(xlSheet)
Excel 2003 のレイトバインドで試してみました……なるほど、残りますね。
とりあえず、Worksheet.HPageBreaks の代わりに、Range.PageBreak を
用いれば残らないようです。回避策として試してみてください。
======= 問題のコード(原因調査中) =======
Dim X As Object= CreateObject("Excel.Application")
X.Visible = True
Dim Bs As Object = X.Workbooks
Dim B As Object = Bs.Add()
Dim Ss As Object = B.Worksheets
Dim S As Object = Ss(1)
Dim Rs As Object = S.Rows
Dim R As Object = Rs(3)
'----> コメントを外すと残る!
Dim Hs As Object = S.HPageBreaks
'Dim H As Object = Hs.Add(R)
MessageBox.Show("改ページを挿入しました。")
'Marshal.ReleaseComObject(H)
Marshal.ReleaseComObject(Hs)
'<----
Marshal.ReleaseComObject(Hs)
Marshal.ReleaseComObject(R)
Marshal.ReleaseComObject(Rs)
Marshal.ReleaseComObject(S)
Marshal.ReleaseComObject(Ss)
B.Close(False)
Marshal.ReleaseComObject(B)
Marshal.ReleaseComObject(Bs)
X.Quit()
Marshal.ReleaseComObject(X)
MessageBox.Show("プロセスは残っていますか?")
======= 残らないコード =======
Dim X As Object = CreateObject("Excel.Application")
X.Visible = True
Dim Bs As Object = X.Workbooks
Dim B As Object = Bs.Add()
Dim Ss As Object = B.Worksheets
Dim S As Object = Ss(1)
Dim Rs As Object = S.Rows
Dim R As Object = Rs(3)
'----> これなら残らない
R.PageBreak = -4135 'Trueを代入してもOK
MessageBox.Show("改ページを挿入しました。")
'<----
Marshal.ReleaseComObject(R)
Marshal.ReleaseComObject(Rs)
Marshal.ReleaseComObject(S)
Marshal.ReleaseComObject(Ss)
B.Close(False)
Marshal.ReleaseComObject(B)
Marshal.ReleaseComObject(Bs)
X.Quit()
Marshal.ReleaseComObject(X)
MessageBox.Show("プロセスは残っていますか?")
レイトバインドではなく、厳密な型(Excel PIA)を用いた場合には
HPageBreaks.Add しても、プロセスは残らないようです。
Dim X As Excel.Application = DirectCast(CreateObject("Excel.Application"), Excel.Application)
X.Visible = True
Dim Bs As Excel.Workbooks = X.Workbooks
Dim B As Excel.Workbook = Bs.Add()
Dim Ss As Excel.Sheets = DirectCast(B.Worksheets, Excel.Sheets)
Dim S As Excel.Worksheet = DirectCast(Ss(1), Excel.Worksheet)
Dim Rs As Excel.Range = S.Rows
Dim R As Excel.Range = DirectCast(Rs(3), Excel.Range)
'----> どちらの方法でも残らない
'R.PageBreak = Excel.XlPageBreak.xlPageBreakManual
'MessageBox.Show("改ページを挿入しました。")
'----
Dim Hs As Excel.HPageBreaks = S.HPageBreaks
Dim H As Excel.HPageBreak = Hs.Add(R)
MessageBox.Show("改ページを挿入しました。")
Marshal.ReleaseComObject(H)
Marshal.ReleaseComObject(Hs)
'<----
Marshal.ReleaseComObject(R)
Marshal.ReleaseComObject(Rs)
Marshal.ReleaseComObject(S)
Marshal.ReleaseComObject(Ss)
B.Close(False)
Marshal.ReleaseComObject(B)
Marshal.ReleaseComObject(Bs)
X.Quit()
Marshal.ReleaseComObject(X)
MessageBox.Show("プロセスは残っていますか?")
Range.PageBreakを用い、魔界の仮面弁士さんに書いていただいたコードを
利用してソースを修正し、無事プロセスを終了させることができました。
魔界の仮面弁士さん、長らくお付き合い頂き、
非常に丁寧かつ分かりやすい解説、大変勉強になりました。
また、今回の質問の件以外にも、過去の魔界の仮面弁士さんの書き込みを
非常に多く活用させていただきました。
この場をお借りしてお礼申し上げます。
本当にありがとうございました。
> ======= 問題のコード(原因調査中) =======
ようやく分かりました。
どうやら、レイトバインドの場合、
Dim Hs As Object = S.HPageBreaks
Dim H As Object = Hs.Add(R)
の処理を行った時点で、変数 R の参照カウントが
増加してしまっていたようです。
ツイート | ![]() |