投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/8/10 10:54:27
> range = sheet.Range("B" & i + 5 & "")
range オブジェクトには、「B5セル」を現す Range オブジェクトが格納されています。

それを解放しないまま、
> range = sheet.Range("C" & i + 5 & "")
にて、その参照先を「C5セル」のインスタンスに差し替えています。これが原因ですね。

解放処理は「変数」ではなく、「変数が指し示すインスタンス」に対して行われますので、
別のインスタンスに差し替えるのであれば、差し替える前のインスタンスも ReleaseComObject せねばなりません。


なお、連続した領域であるならば、Range("B5:F5") などの範囲で取得し、
Value プロパティに「配列」で渡す形にするのがお奨めです。
通信回数を減らすことができるため、処理速度がUPしますし、
COM 解放の手間も減らすことができます。



> ''5秒だけ表示する
> System.Threading.Thread.Sleep(1000)
これでは 5 秒になっていませんよね。

そもそも先にも述べたように、この場所での Sleep の利用は厳禁です。
(メッセージループを阻害するため、Windows Forms の UI スレッドから利用すべきでは無い)

どうしても待ち合わせをしたいなら、Timer コンポーネントを利用するか、
もしくは、『Await task.Delay(5000)』などを使うようにします。


> Catch ex As Exception
>  Throw ex
これは悪手です。このような例外処理を書いてはいけません。

これでは例外のスタックトレースが握りつぶされますので、
どこで発生した問題なのかが分からなくなってしまいます。

別の例外に変換するのであればさておき、例外をそのまま再スローしたいだけであれば、
「Throw ex」ではなく「Throw」とするべきです。

また、今回のようにそもそも例外処理を行わないのであれば、
Catch 句無しの「Try~Finally~End Try」を使うのが望ましいです。



> Finally
>   Marshal.ReleaseComObject(range)
>   Marshal.ReleaseComObject(sheet)
>   Marshal.ReleaseComObject(sheets)
>   Marshal.ReleaseComObject(book)
>   Marshal.ReleaseComObject(books)
>   '閉じる
>  app.Quit()
>   Marshal.ReleaseComObject(app)
>
> End Try

これも非常にまずいです。

たとえばこの処理だと、Book1.xlsx を開けずに処理が中断した場合、
app と books のみが設定され、それ以外は Nothing の状態に陥ります。

しかし ReleaseComObject にNothing を渡すことはできませんので、
その時点で、NullReferenceException の例外が発生して
Finally 句が中断されます。結果、COM オブジェクトは一切解放されません。

ということはつまり、Finally 句としては何の役目も果たしていない、ということです。

しかも Catch 句でスタックトレースが潰されているので、処理としては
Try ブロックを使わずに書いたケースよりも、むしろ悪化しているほどです。


Finally 句で対処するのであれば、
 Marshal.ReleaseComObject(sheets)
ではなく、それぞれを
 If sheets IsNot Nothing AndAlso Marshal.IsComObject(sheets) Then
  Marshal.ReleaseComObject(sheets)
 End If
と書くべきです。これなら、Finally ブロック内での例外の発生を抑えられます。

上記で IsComObject は呼ばなくても構いませんが、COM オブジェクトではなく
マネージオブジェクトを返すような修正が入った場合には、ReleaseComObject に
渡すわけには行かなくなるので、念のための保険です。


とはいえ、If 文を毎回書くのは面倒だと思いますので、解放処理を行うために
http://hanatyan.sakura.ne.jp/dotnet/Excel01.htm
の末尾にある MRComObject メソッドを用意しておくのも良いでしょう。
そうすれば、下記のように簡潔に書けます。
Finally
    MRComObject(range)
    MRComObject(sheet)
    MRComObject(sheets)
    MRComObject(book)
    MRComObject(books)
    If app IsNot Nothing Then
        app.Quit()
    End If
    MRComObject(app)
End Type