プロが教えるわが家の防犯対策術!

お世話になります。

Visual Studio 2008 C# を使い、オートメーションで作成した Excel Book が閉じられたことを
リアルタイムで取得したいのです。

具体的には、フォームに複数のコントロールを配置した状態で、開いた Excel Book が閉じる瞬間をとらえてボタンの「Enabled」プロパティを制御できればいいのです。

簡単のために Excel Book はフォームの「Load」イベントで開き、新しい Book を追加します。
フォームにはボタン1個だけを配置します。
そして、Excel Book の右上の「×」ボタンで Book が閉じられる時、デリゲートを通じて
BeforeBookClose イベントを用意すれば、Book が閉じられるタイミングを捕まえることはできます。

問題は、以下のコードでは、C#の出力ウィンドウに
「Delegate: BookClose イベントが呼ばれました」
「button1.Enabled = False」
と表示され、button1 の「Enabled」プロパティも確かに「true」になります。
しかし、次の行は実行されず、出力ウィンドウには
「'System.InvalidOperationException' の初回例外が System.Windows.Forms.dll で発生しました。」と表示されています。
つまり、それ以下のコードは実行されていません。
さらには、フォームにボタンやコンボなどを1個でも追加すると、もう、「button1.Enabled = true;」も実行されなくなります。これが追加するのがラベルだけなら大丈夫でした。

BeforeBookClose イベントの処理は Excel Book に対して行われ、もとのC#のコントロールに対しては意味をなしていない、とも思いましたが一概にそうでもなさそうで混乱しています。
プロセス間通信とかスレッド間通信とかが必要なのか、よくわかりません。

よろしくお願いします。

namespace test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

Excel.AppEvents_WorkbookBeforeCloseEventHandler EventDel_BeforeBookClose;
Excel.Application AppExcel = new Excel.Application();

private void Form1_Load(object sender, EventArgs e)
{
AppExcel.Visible = true;
Excel.Workbook WB = AppExcel.Workbooks.Add(Type.Missing);
EventDel_BeforeBookClose = new Excel.AppEvents_WorkbookBeforeCloseEventHandler(BeforeBookClose);
AppExcel.WorkbookBeforeClose += EventDel_BeforeBookClose;
button1.Enabled = false;
}

private void BeforeBookClose(Excel.Workbook Wb, ref bool Cancel)
{
System.Diagnostics.Debug.Print("Delegate: BookClose イベントが呼ばれました");
System.Diagnostics.Debug.Print("button1.Enabled = " + button1.Enabled.ToString());
button1.Enabled = true;
System.Diagnostics.Debug.Print("button1.Enabled = " + button1.Enabled.ToString());
AppExcel.WorkbookBeforeClose -= EventDel_BeforeBookClose;
}
}
}

A 回答 (1件)

ご考察の通り 異なるスレッド(プロセス)間のイベントのために引き起こされている現象だと思います



ボタンのEnabledプロパティおよび BeforeBookCloseのイベント削除用のハンドラを別に用意します
これを Delegateを通して Invokeで呼び出してあげればいいようです

private void myEnabelMethod()
{
  // これらの操作をするスレッドが違うと処理が続行されない
  // Excel側で例外を吸収してしまっているのかも
  // 以下の処理順は どちらが先でもOKなようです
  button1.Enabled = true;
  AppExcel.WorkbookBeforeClose -= EventDel_BeforeBookClose;
}

private Delegate void myEnabled();
private myEnabled myDelgateEnabled;

Form_Loadイベントやコンストラクタで
  myDelegateEnabeld = new myEnabled( myEnabledMethod );
を実行しておく

BefotreBookCloseイベントで
  Invoke( myDelegateEnabeld );
を実行

といった具合でいいと思います …
    • good
    • 0
この回答へのお礼

ありがとうございました。
なんとなく Invoke にはたどりついていたのですが、具体的な使い方がわかりませんでした。
以下のコードでばっちりでした。とても助かりました。ありがとうございました。

using System;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelEventTest成功
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private delegate void myEnabled();
private myEnabled myDelegateEnabled;
Excel.AppEvents_WorkbookBeforeCloseEventHandler EventDel_BeforeBookClose;
Excel.Application AppExcel;

private void myEnablMethod()
{
button1.Enabled = true;
AppExcel.WorkbookBeforeClose -= EventDel_BeforeBookClose;
}

private void BeforeBookClose(Excel.Workbook Wb, ref bool Cancel)
{
/*
* ここに C# のフォームに対する処理を記述しても動作しない。
* 異なるプロセス間の通信だからだと思われる。
*
* そこで、Invoke により解決を図る。
*
* しかし、このアプリが無事終了しないと、オバケの Excel は残る。
*
*/
System.Diagnostics.Debug.Print("Delegate: BookClose イベントが呼ばれました");
Invoke(myDelegateEnabled);
}

private void Form1_Load(object sender, EventArgs e)
{
myDelegateEnabled = new myEnabled(myEnablMethod);
EventDel_BeforeBookClose = new Excel.AppEvents_WorkbookBeforeCloseEventHandler(BeforeBookClose);
button1.Enabled = true;
}

private void button1_Click(object sender, EventArgs e)
{
AppExcel = new Excel.Application();
AppExcel.Visible = true;
Excel.Workbook WB = AppExcel.Workbooks.Add(Type.Missing);
AppExcel.WorkbookBeforeClose += EventDel_BeforeBookClose;
button1.Enabled = false;
}
}
}

お礼日時:2010/11/28 10:38

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!