tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルエクセルを2度目に起動するとエラーになってしまいます。
記事No5410
投稿日: 2007/04/24(Tue) 17:29
投稿者take
プログラムからエクセルを起動し、エクセルの終了はユーザーがエクセルの終了ボタンを
クリックすることにより行っています。
この時にエクセルの”WorkbookDeactivate”イベントを取り、オブジェクトの解放を
行おうとしています。

下記のようにすると、”WorkbookDeactivate”でXlappを解放する際に、
”呼び出しのターゲットが例外をスローしました。”というエラーが出ますが、
エクセルのプロセスは消えます。しかし、2度目にエクセルを起動しようとすると、
”呼び出しのターゲットが例外をスローしました。”というエラーになってしまいます。

エラーになった場合、違う変数を用意して、”New Excel.Application”すると
エクセルは起動します。
どうもXlappがうまく解放されていないようなのですが、ぜんぜんわかりません。
XlappにNothingを設定しようとしても、やはりエラーになります。
なにがいけないのでしょうか、どなたか御教示をお願いします。


  Private WithEvents Xlapp As Excel.Application
  Private Book As Excel.Workbooks

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Try
      Xlapp = New Excel.Application '1度目はOKなのですが、2度目がうまくいきません。
    Catch ex As Exception
      MsgBox("Open : " & ex.Message)
      End
    Finally
    End Try
    Book = Xlapp.Workbooks
    Book.Add()
    Xlapp.Visible = True
  End Sub

  Private Sub Xlapp_WorkbookDeactivate(ByVal Wb As Excel.Workbook) Handles Xlapp.WorkbookDeactivate
    Try
      MRComObject(Book)
    Catch ex As Exception
      MsgBox("Book : " & ex.Message)
    End Try
    Try
      MRComObject(Xlapp) '終了時ここでエラーがでますが、プロセスは解放されます。
    Catch ex As Exception
      MsgBox("xlapp : " & ex.Message)
    End Try
  End Sub

'花ちゃんさんのプログラムを使わせて頂いています。

  Private Sub MRComObject(ByRef objCom As Object)
    'COM オブジェクトの使用後、明示的に COM オブジェクトへの参照を解放する
    Try
      '提供されたランタイム呼び出し可能ラッパーの参照カウントをデクリメントします
      If Not objCom Is Nothing AndAlso System.Runtime.InteropServices. _
                                                Marshal.IsComObject(objCom) Then
        Dim I As Integer
        Do
          I = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
        Loop Until I <= 0
      End If
    Catch
    Finally
      '参照を解除する
      objCom = Nothing
    End Try
  End Sub

[ツリー表示へ]
タイトルRe: エクセルを2度目に起動するとエラーになってしまいます。
記事No5411
投稿日: 2007/04/24(Tue) 18:50
投稿者魔界の仮面弁士
> この時にエクセルの”WorkbookDeactivate”イベントを取り、オブジェクトの解放を
> 行おうとしています。
そのタイミングで解放するという事自体が不味いのでは。
未確認ですが、デリゲートが参照している分など、
逆に残しておいた方が良い参照も含まれているかも知れません。

> エクセルの終了はユーザーがエクセルの終了ボタンをクリックすることにより行っています。
終了処理をユーザ操作に任せるのであれば、イベントをとらえて処理するのではなく、
単に起動だけ行い、Excel への参照のみを解放するだけで良いのでは。

Module Sample
  Sub Main
    Dim O As Object = CreateObject("Excel.Application")
    O.Visible = True
    Dim S As Object = O.Workbooks
    Dim B As Object = S.Add()

    System.Runtime.InteropServices.Marshal.ReleaseComObject(B)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(S)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(O)

    MsgBox("終了")
  End Sub
End Module

>         Do
>           I = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
>         Loop Until I <= 0
どうしても RelaseComObject を複数回呼ばなければならない場面というのもありますが、
かといって、常にそのような方法で解放するのは、避けた方が良いかと。
使用中の参照をうっかり解放してしまうと、逆に問題があるでしょうし。

[ツリー表示へ]
タイトルRe^2: エクセルを2度目に起動するとエラーになってしまいます。
記事No5414
投稿日: 2007/04/25(Wed) 11:30
投稿者take
ありがとうございました。

プログラムは、メインフォームでエクセル、CADを立ち上げ、処理別に作成した子フォームでエクセル、CADのイベント
(Change,Click等)をひろいエクセル、CADに書き込みを行っています。

そのため、現在はメインフォームで起動したエクセル、CADを子フォームに渡して処理を行っています。
エクセルデータの検索処理の高速化を図りたかったため、DAOを使いデータの一部をメモリに読み込み、
その後にエクセルを起動しています。データが大きくなると、これら処理に時間がかかるため、
起動したエクセルを参照したままにしてあります。

それから、エクセルの変更をすぐにインデックスの変更に反映させたいため、
および、エクセルのセル内にフォーカスがあったり、エクセルのメニューを開いている状態で、
子フォームの処理をしようとするとエラーになってしまうため、エクセルの状態を常に監視しています。

また、データが多くなると、インデックスの書き換えに時間がかかるため、
エクセルを切り離して作業し、プログラムから再起動したり、エクセルを起動していることを忘れ、
別のブックを起動した際にはエクセルを切り離したり、ということも行っております。

お教えの方法だと、エクセルの終了でプロセスも解放され、2度目のエクセル起動も問題なく行えます。
しかし、エクセルを監視するため実行中は参照を保持したいので、解放が起動と違う場所になって
しまいます。
”WorkbookDeactivate”イベントではダメで、”WorkbookBeforeClose”でもダメでした。
どのように解放すれば2度目の起動がうまくいくのでしょうか。

実は、やってはいけないことと思いつつ、適当なタイミングでエクセルを”Kill”していました。
うまく解放されないエクセルは”Main title”が無いのでそれを見つけて”Kill”していました。
エクセル2000では不具合は感じなかったのですが、エクセル2003では、あるタイミングでメニューの”オートSUM”
をクリックすると、不具合が発生します。
安易な方法にたより、大量に作りこんでしまいました。
天罰だと思います。

申し訳ありません、どうかよろしくお願いします。

[ツリー表示へ]
タイトルRe^3: エクセルを2度目に起動するとエラーになってしまいます。
記事No5455
投稿日: 2007/05/09(Wed) 17:29
投稿者七誌
どうも。
上記の障害を私自身のPC環境で再現し(プログラムをそのままコピーし、実行しました。)
、それに基づき変更を加えてみました。
下記の修正部分を実施すると、私の環境では エクセル開く⇒エクセル閉じる⇒もっかいエクセル
開く⇒OK(5回くらい左記手順実施してもエラー発生なし)になりましたが。
あんまり障害状況を見ていないので、これで駄目ならすいません。
戯言と思ってください。

ちなみに、追加した参照設定は
<COM>
Microsoft Excel 10.0 Object Library
Microsoft Office 10.0 Object Library
Microsoft Office 9.0 Object Library
くらいです。

Private Sub Xlapp_WorkbookDeactivate(ByVal Wb As Excel.Workbook) Handles Xlapp.WorkbookDeactivate
    Try
      MRComObject(Book)
    Catch ex As Exception
      MsgBox("Book : " & ex.Message)
    End Try
    Try
      MRComObject(Xlapp) '終了時ここでエラーがでますが、プロセスは解放されます。
⇒ 上記行を Xlapp = Nothing 

    Catch ex As Exception
      MsgBox("xlapp : " & ex.Message)
    End Try
  End Sub

[ツリー表示へ]
タイトルRe^4: エクセルを2度目に起動するとエラーになってしまいます。
記事No5460
投稿日: 2007/05/09(Wed) 23:22
投稿者take
ありがとうございます。

ただ、Xlapp = Nothing では私の環境ではエクセルのプロセスが開放されないのですが・・・。
連続してエクセルを起動、終了するとその都度エクセルのプロセスが増えてしまいます。
プログラムを終了させればプロセスは消えますが・・・。

プログラムからだけエクセルを起動するのであれば、プロセスが残っていても、最終的に
プロセスが消えれば良いかもしれませんが、プログラムから起動したエクセルを終了させ、
プロセスが残っている状態で、エクセルファイルクリック等によりプログラム以外からエクセルを
起動すると、エクセルに不具合が生じてしまいます。

また、魔界の仮面弁士さんのご指摘にあったようにRelaseComObjectを複数回呼ぶ方法は、
複数のエクセルファイルを開いて作業していた場合に、うまくなかったようでした。

いずれにしてもありがとうございました。

[ツリー表示へ]