オートメーションで作成したExcel Bookが閉じられたことをリアルタイムで取得する方法

このQ&Aのポイント
  • Visual Studio 2008 C#を使い、オートメーションで作成したExcel Bookが閉じられたことをリアルタイムで取得する方法を教えてください。
  • フォームに複数のコントロールを配置した状態で、開いたExcel Bookが閉じる瞬間をとらえてボタンの「Enabled」プロパティを制御したいです。
  • BeforeBookCloseイベントの処理はExcel Bookに対して行われ、もとのC#のコントロールに対しては意味をなさないようです。プロセス間通信やスレッド間通信が必要な可能性があります。
回答を見る
  • ベストアンサー

オートメーションで作成したExcel Book

お世話になります。 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; } } }

質問者が選んだベストアンサー

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.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 ); を実行 といった具合でいいと思います …

ichhabehunger
質問者

お礼

ありがとうございました。 なんとなく 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; } } }

関連するQ&A

  • なぜ、clickイベントが起こってしまうのか? 回避するにはどのように

    なぜ、clickイベントが起こってしまうのか? 回避するにはどのようにしたら? お世話になります。 Visual Studio 2008 C#での開発です。 以下のような簡単なコードでの実験なのですが、 フォームには radioボタン2個とボタンが配置されています。 やりたいことは  1.button1がクリックされたらbutton1自身を使用不可にする  2.radioButton1がクリックされたらbutton1を再度使用可にする です。 しかし、どうもbutton1のクリックイベントの途中でradioButton1の クリックイベントが発生してしまい、思ったような動作になりません。 どのようにしたらbutton1のクリックイベントの途中のradioButton1の クリックイベントの発生を止めることができるのでしょうか? よろしくお願いします。 namespace WindowsFormsApplicationTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Console.WriteLine("1:button1がクリックされました"); Console.WriteLine("2:button1のenabled=" + button1.Enabled); button1.Enabled = false; Console.WriteLine("6:button1のenabled=" + button1.Enabled); } private void radioButton1_Click(object sender, EventArgs e) { Console.WriteLine("3:radioButton1がクリックされました"); Console.WriteLine("4:button1のenabled=" + button1.Enabled); button1.Enabled = true; Console.WriteLine("5:button1のenabled=" + button1.Enabled); } } } 上のコードでの実験結果 1:button1がクリックされました 2:button1のenabled=True 3:radioButton1がクリックされました 4:button1のenabled=False 5:button1のenabled=True 6:button1のenabled=True

  • 【VB.net】フォーム間でのやり取りについて

    先日、同じような質問をさせて頂いたのですが、 解決に至らなかったため、もう少し質問内容を工夫しますので、再度質問させてください。 ・実行したいプログラム フォーム1のボタン1をクリックすると、フォーム2を開くと同時にフォーム2ボタン1がvisible=Trueになる。 フォーム1のボタン2をクリックすると、フォーム2を開くと同時にフォーム2のボタン1がvisible=Falseになり、ボタン2がvisible=Trueになる。 ・書いたコード ---------------- Public Class Form1 Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click Dim f As New Form2 f.Owner = Me f.Show() DirectCast(Me.Owner, Form2).Button1.Visible = True DirectCast(Me.Owner, Form2).Button2.Visible = False End Sub Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click Dim f As New Form2 f.Owner = Me f.Show() DirectCast(Me.Owner, Form2).Button1.Visible = False DirectCast(Me.Owner, Form2).Button2.Visible = True End Sub End class ---------------- ・問題点 「nullreferenceexceptionはハンドルされませんでした オブジェクト参照がオブジェクト インスタンスに設定されていません。」 というエラーが発生します。 「DirectCast(Me.Owner, Form2).Button1.Visible = True」の部分のエラーです。 手書きで申し訳ありませんが、書きたいプログラムの参考画像も載せておきます。 数日前より調べては試し調べては試しと試行錯誤していますが、 正直手詰まりになっており、非常に困っております。 この他にも、フォーム間で命令を出しあいたい場面が多々あるのですが、 プロパティを変える程度のコードでつまずいていて、先に進めません。 手直しした簡単なサンプルコードを書いて頂けると非常に助かります。 以上、何卒よろしくお願い致します。

  • VB、前回の続き→http://okwave.jp/qa/q59129

    VB、前回の続き→http://okwave.jp/qa/q5912905.html 前回は詳しく教えていただきありがとうございました。 皆様に教えて頂いたコードを自分なりに色々と試しつつ おかげでラジオボタンから1から9の数字を選んでボタンを押すと選んだ数字がボタンに表示される→もう一度押すと表示された数字も消えて、選んでいたラジオボタンもセレクトされていない状態に戻る事ができるためのコードが組めました(前回質問したときは3つでしたが、今回9つのラジオボタンでやっています。) ボタン1の場合 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ElseIf Button1.Text <> "" Then Label1.Text = "0" Button1.Text = "" RadioButton1.Enabled = True RadioButton2.Enabled = True RadioButton3.Enabled = True RadioButton4.Enabled = True RadioButton5.Enabled = True RadioButton6.Enabled = True RadioButton7.Enabled = True RadioButton8.Enabled = True RadioButton9.Enabled = True どのラジオボタンを選んでボタン1にどんな数字が表示されても、もう一度クリックすると数字は消えてラジオボタンは選んでいない状態、そして横のラベルの和は0になっています。 9つのボタン全てにこのようにコードを入れていったのですが問題が起こってしまいました。 なぜか、1のラジオボタンを選んだ時は何も起こらないのですが、2から9までの数字のラジオボタンを選んだ際、自動で”1”とボタンに表示されてしまいます(例えば2のラジオボタンを選択すると、押していないのにButton2に”1”と表示される。) これはどのように解決すればいいのでしょうか?毎回丁寧なご回答を下さっている方、本当に本当にありがとうございます。この場をかりて改めてお礼を言わせていただきます。

  • Current、Openがそれぞれ2回発動する

    テーブルには適当なデータが入っています。 フォーム1を作り、コマンドボタンを1個だけ設置して、フォーム2を開くようにするために Private Sub コマンド0_Click() DoCmd.OpenForm Form_フォーム2.Name End Sub としました。 フォームはテーブルをレコードソースとしています。 フォーム2のモジュールに Private Sub Form_Current() Debug.Print "Form_Current" End Sub Private Sub Form_Open(Cancel As Integer) Debug.Print "Form_Open" End Sub としたのですが、結果は Form_Open Form_Current Form_Open Form_Current になります。 よって、二つのイベントの中にいくつかのプロシージャーが入っているのですが 2回ずつ実行されてしまい、求めている結果と異なってしまいます。 これを回避する方法はありますか? どうしてもフォーム1のコマンドボタンを押して、フォーム2をひらきたいのです。 よろしくお願いします。

  • VBAで別のExcelブックのボタンのマクロ登録

    Excelブック1とExcelブック2があります。 Excelブック1はVBAで 1)Excelブック2を開いて 2)Excelブック1のワークシート(Sheet1)と  モジュールシート(Module1)を  Excelブック2にコピーします。 3)ワークシート(Sheet1)にはコントロール(ボタン)が貼り付けてあって  このボタンをクリックするとモジュールシート(Module1)の中の  Excute_Button()というプロシージャ(マクロ)を呼び出すため  Excelブック1のVBAで  wb.Worksheets("Sheet1").Shapes.Range(Array("Button 1")).Select  Selection.OnAction = "Excute_Button"  と記述してボタンのマクロの登録をします。   (wbはExcelブック2を指しています) 4)Excelブック2を保存して閉じる。 という処理をしています。 この後、Excelブック1を閉じて、 Excelブック2を開いてワークシート(Sheet1)のボタンをクリックすると Excelブック1が開いてしまいます。 Excelブック2のボタンのマクロの登録のマクロ名を見てみると Excelブック1のファイル名!Excute_Button となっています。 Excelブック1のVBAで、ここを単にExcute_Buttonだけにするには どのようにすればよいでしょうか。

  • 三目並べのコードについて。ビジュアルベーシック2008

    三目並べのコードについて。ビジュアルベーシック2008 現在三目並べのコードにて苦戦しています。 ×が3つ揃えば勝ち、Oが3つ揃えば勝ち、というメッセージが表示されるコードも完了しました。 しかし引き分け、つまり9つのボタンの内3つ揃わなかった場合、どのようなコードにすればいいでしょう?色々やっているのですが、メッセージボックスすら表示されません。 また、勝ち、や引き分け、のメッセージ表示後、自動的にリセットしたく Private Sub reset()         Button1.Text = "" Button1.Enabled = True Button2.Text = "" Button2.Enabled = True Button3.Text = "" Button3.Enabled = True Button4.Text = "" Button4.Enabled = True Button5.Text = "" Button5.Enabled = True Button6.Text = "" Button6.Enabled = True Button7.Text = "" Button7.Enabled = True Button8.Text = "" Button8.Enabled = True Button9.Text = "" Button9.Enabled = True を Private Sub win() If Button1.Text = "X" And Button2.Text = "X" And Button3.Text = "X" Then MessageBox.Show("You are the winner with X!") Call disablebuttons()        Call reset() ・・・以下続く とCALLで呼び出しているのですが、9つきちんとリセットされてくれず必ず9つのボタンのうち一つだけ窪み(何も表示されない状態)になってしまいます。一体どうすればよいでしょう? また、ボタンにカーソルが触れると色が変わるようにしたいのですが、もしお知恵を貸していただけるようでしたらよろしくお願いいたします。

  • フォーカスの移動について

    フォーム1をロードしたときに、フォーム2を呼び出します。2つのフォームを呼び出すとき、フォーム1がメインのフォームで、フォーム2が呼び出されたフォームです。そのときに、フォーム1の操作を無効にし、フォーム2の操作を有効にするために、 Form1.Enabled=False Form2.Enabled=True を使いました。そのときにどうしてもフォーム2にフォーカスが移動しないのですが、どうしたらよろしいですか?

  • Visual-C#の event キーワードの「意義」が分かりません

    Visual-C#というものを初めて触っている者です。 フォーム上にボタン等のコントロールを配置して、ボタンをダブルクリックすると、イベントハンドラをユーザが記述するコード部分(partial class)が表示されてくるのですが(Form1.cs)、このpartial classの残りの、ユーザが編集する必要のないコードはForm1.Designer.cs内に自動生成されます。 このForm1.Designer.cs内に、たとえば this.Button1.Click += new System.EventHandler( this.Button1_Click );  ←(1) のようなコードが自動生成されているわけです。 ここで、Button1とはButtonクラスのオブジェクトであり、ClickとはButtonクラスの(Controlクラスから継承した)Clickイベントだということです。 http://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.click(VS.85).aspx public event EventHandler Click  ←(2) ここで使用されているEventHandlerとは何かというと、delegateです。 http://msdn.microsoft.com/ja-jp/library/system.eventhandler.aspx public delegate void EventHandler(Object sender, EventArgs e)  ←(3) 「イベントとは、コンピュータ・プログラムの実行に際し、何らかのアクションが発生した際にプログラムに発信される信号(これをメッセージと呼んだりする)をいう。」(『ウィキペディア(Wikipedia)』) この「イベント」を、.NetFrameworkのクラスライブラリでは、(2)のように「EventHandler」という名前のデリゲートクラス型の変数として定義しているわけですが、(2)の中の「event」というキーワードの「意義」が分かりません。 Controlクラス定義内で(2)式のように、コントロールの「クリック」イベントを、「EventHandler」という名前のデリゲートクラス型の変数と定義するというのなら、それはそれで分かるのですが、なぜ更に「event」というキーワードを付ける必要があるのかが、よく分からないのです。

  • actionscript3.0でボタン操作無効化

    flashのHPを作っております。 (mc)ボタンを配置しクリックすると画面の背景が変わる操作をしたいと思っており(top、new、main・・・など) 下記のHPのような http://www.mediacreator.jp/tutorials/show_tutorial.asp?id=69&pn=3 操作ボタンを作っております。 上記HPは2.0で製作されているため、3.0に改変し、 アクションを下のようにしました。 var buttonNames= new Array("button1", "button2", "button3", "button4"); button1.buttonMode = true; button2.buttonMode = true; button3.buttonMode = true; button4.buttonMode = true; button1.addEventListener(MouseEvent.CLICK, button1click); button2.addEventListener(MouseEvent.CLICK, button2click); button3.addEventListener(MouseEvent.CLICK, button3click); button4.addEventListener(MouseEvent.CLICK, button4click); function resetButton(buttonName){ for(var i = 0; i < buttonNames.length; i++){ if(buttonNames[i] != buttonName){ this[buttonNames[i]].gotoAndStop(1); this[buttonNames[i]].enabled = true; } } } function button1click(event:MouseEvent):void { resetButton(button1); this.gotoAndStop(2); this.enabled = false; } function button2click(event:MouseEvent):void { resetButton(button2); this.gotoAndStop(2); this.enabled = false; } function button3click(event:MouseEvent):void { resetButton(button3); this.gotoAndStop(2); this.enabled = false; } function button4click(event:MouseEvent):void { resetButton(button4); this.gotoAndStop(2); this.enabled = false; } コンパイルエラーは無くなったのですが、ボタン自体が動いてくれません。 どこが間違っているか分かる方、お教え下さい!

  • フォームを表示してからメッセージボックスを表示する

    メッセージボックスが表示される前に、フォームを表示させたいです。アクセスです。 ナビゲーションウインドウからフォームをダブルクリックしてフォームを開くのですが、 今のままだとメッセージボックスが表示されてからフォームが表示されてしまいます。 フォームを表示してからメッセージボックスを表示するにはどうすればいいでしょう? Private Sub Form_Open(Cancel As Integer) MsgBox "Form_Open" End Sub だと、先ほど述べたように、メッセージボックスが表示されてからフォームが表示されてしまいます。 フォームを開いたときのイベントは複数あるようで、 Private Sub Form_Activate() MsgBox "Form_Activate" Debug.Print "Form_Activate" End Sub Private Sub Form_Current() MsgBox "Form_Current" Debug.Print "Form_Current" End Sub Private Sub Form_GotFocus() MsgBox "Form_GotFocus" Debug.Print "Form_GotFocus" End Sub Private Sub Form_Open(Cancel As Integer) MsgBox "Form_Open" Debug.Print "Form_Open" End Sub Private Sub Form_Load() MsgBox "Form_Load" Debug.Print "Form_Load" End Sub をすると、 ・Form_Load ・Form_Activate ・Form_GotFocus ・Form_Open ・Form_Current の順に開きますが、やはりフォームが最後に表示されてしまいます。 どうにかして先にフォームを表示させる方法はないでしょうか? ご回答よろしくお願いします。