Hatena::ブログ(Diary)

周回遅れのブルース

2010-12-24

C# で PowerPoint のイベントを検知する。

| 01:33 | C# で PowerPoint のイベントを検知する。を含むブックマーク

C# Advent Calendar jp: 2010 の24日目の記事です。

C#と.NETに関するTips ですから C# 言語特有の記事を書こうかとも思ったのですが、日頃 VB.NET の方が使う頻度が高く、C# の言語機能を抑え切れてないため断念しました。(こんなんでよく Microsoft MVP For Development Tools Visual C# を頂けたよなぁ・・・謎)

そこで本日は私の昔からの得意分野である、相互運用ネタでいきたいと思います。以前 MSDN フォーラムで話が上がった C++フォームアプリからPowerPointのイベント取得について の内容を発展させ、C#PowerPoint アプリケーションのイベントを検知する方法を書いてみました。


f:id:hilapon:20101223190520j:image



まずは簡単なサンプル

まずはコンソールアプリケーションの簡単なサンプルです。プレゼンテーションを開いた時と閉じるときを検知するイベントです。

参照設定に Office と Microsoft.Office.Interop.PowerPoint を追加します。私の環境では Office2010 がインストールされているため、バージョンは各々 14.0.0.0 を選択しています。

using System;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;

class Program {
    static void Main(string[] args) {

        var pp = new PowerPointEventHandlar();
        pp.Start();

        Console.ReadKey();
    }
}

class PowerPointEventHandlar {

    Microsoft.Office.Interop.PowerPoint.Application _app;

    public void Start() {

        _app = new Microsoft.Office.Interop.PowerPoint.Application();
        _app.Visible = MsoTriState.msoTrue;
        _app.PresentationOpen += PresentationOpen;
        _app.PresentationClose += PresentationClose;
    }

    // プレゼンテーションを開いた時のイベントハンドラ
    private void PresentationOpen(Presentation Wn) {
        Console.WriteLine("PresentationOpen\n");
    }

    // プレゼンテーションを閉じた時のイベントハンドラ
    private void PresentationClose(Presentation Wn) {
        Console.WriteLine("PresentationClose\n");
    }
}


WindowsForms でイベント発生時にコントロールを設定する。

MSDN フォーラムの記事では、イベント発生時にメッセージを表示させているだけですが、今回はリストボックスに発生したイベント名を表示させようと思いました。

で、引っかかったのが以下のコード。イベントが発生してもリストボックスにイベント名は追加されません。

// スライドショウ次のスライド移動時のイベントハンドラ
private void SlideShowNextSlide(SlideShowWindow Wn) {
    this.listBox1.Items.Add("SlideShowNextSlide");
}

考えてみれば PowerPoint のイベントは別プロセス・別スレッドで発生しているのだから、自身のスレッドのコントロールに対して正常に動作しないのは当然といえます。

そこで Invoke メソッドを使ってスレッドを起動し、リストボックスに追加する文字列は Static 変数にして、コントロールを操作してみました。これでどうにか動作しました。

参考記事:@IT : .NET TIPS Windowsフォームで別スレッドからコントロールを操作するには


以下、Microsoft.Office.Interop.PowerPoint.Application が公開するすべてのイベントを検知するようにしたサンプルです。イベントはひととおり試そうと思ったものの時間がなくて半分くらいしか試しておりません。よって各イベントのコメントも適当。正確でないのも混ざってます(汗

でも自分で作っときながらなんですが、いったいどういうシーンでこのプログラムを使うのか、さっぱり想像がつきませんw


using System;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;

namespace PowerPointApplication {
    public partial class Form1 : Form {

        Microsoft.Office.Interop.PowerPoint.Application _app;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Form1() {
            InitializeComponent();
        }

        /// <summary>
        /// ボタンクリック時のイベントハンドラ
        /// </summary>
        private void button1_Click(object sender, EventArgs e) {

            _app = new Microsoft.Office.Interop.PowerPoint.Application();

            _app.AfterNewPresentation += AfterNewPresentation;
            _app.AfterPresentationOpen += AfterPresentationOpen;
            _app.ColorSchemeChanged += ColorSchemeChanged;

            ((EApplication_Event)_app).NewPresentation += NewPresentation;
            _app.PresentationBeforeClose += PresentationBeforeClose;
            _app.PresentationBeforeSave += PresentationBeforeSave;
            _app.PresentationClose += PresentationClose;
            _app.PresentationCloseFinal += PresentationCloseFinal;
            _app.PresentationNewSlide += PresentationNewSlide;
            _app.PresentationOpen += PresentationOpen;
            _app.PresentationPrint += PresentationPrint;
            _app.PresentationSave += PresentationSave;
            _app.PresentationSync += PresentationSync;
            _app.ProtectedViewWindowActivate += ProtectedViewWindowActivate;
            _app.ProtectedViewWindowBeforeClose += ProtectedViewWindowBeforeClose;
            _app.ProtectedViewWindowBeforeEdit += ProtectedViewWindowBeforeEdit;
            _app.ProtectedViewWindowDeactivate += ProtectedViewWindowDeactivate;
            _app.ProtectedViewWindowOpen += ProtectedViewWindowOpen;
            _app.SlideSelectionChanged += SlideSelectionChanged;
            _app.SlideShowBegin += SlideShowBegin;
            _app.SlideShowEnd += SlideShowEnd;
            _app.SlideShowNextBuild += SlideShowNextBuild;
            _app.SlideShowNextClick += SlideShowNextClick;
            _app.SlideShowNextSlide += SlideShowNextSlide;
            _app.SlideShowOnNext += SlideShowOnNext;
            _app.SlideShowOnPrevious += SlideShowOnPrevious;
            _app.WindowActivate += WindowActivate;
            _app.WindowBeforeDoubleClick += WindowBeforeDoubleClick;
            _app.WindowBeforeRightClick += WindowBeforeRightClick;
            _app.WindowDeactivate += WindowDeactivate;
            _app.WindowSelectionChange += WindowSelectionChange;

            _app.Visible = MsoTriState.msoTrue;
        }

        private delegate void ListBoxAddItemDelegate();
        
        private void worker() {
            this.Invoke(new ListBoxAddItemDelegate(this.ListBoxAddItem));
        }

        private void ListBoxAddItem() {
            this.listBox1.Items.Add(value);
        }

        private static string value;

        // 新規プレゼンテーション作成後のイベントハンドラ
        private void AfterNewPresentation(Presentation Pres) {
            value = "AfterNewPresentation";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーションを開いた直後のイベントハンドラ
        private void AfterPresentationOpen(Presentation Pres) {
            value = "AfterPresentationOpen";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // 配色変更時のイベントハンドラ
        private void ColorSchemeChanged(SlideRange SldRange) {
            value = "ColorSchemeChanged";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // 新規作成時のイベントハンドラ
        private void NewPresentation(Presentation Pres) {
            value = "NewPresentation";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼン終了直前のイベントハンドラ
        private void PresentationBeforeClose(Presentation Pres, ref bool Cancel) {
            value = "PresentationBeforeClose";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼン保存直前のイベントハンドラ
        private void PresentationBeforeSave(Presentation Pres, ref bool Cancel) {
            value = "PresentationBeforeSave";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // パワーポイント終了時のイベントハンドラ
        private void PresentationClose(Presentation Pres) {
            value = "PresentationClose";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // パワーポイント完全終了時のイベントハンドラ
        private void PresentationCloseFinal(Presentation Pres) {
            value = "PresentationCloseFinal";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // 新規スライド追加時のイベントハンドラ
        private void PresentationNewSlide(Slide Sld) {
            value = "PresentationNewSlide";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーションを開いた時のイベントハンドラ
        private void PresentationOpen(Presentation Pres) {
            value = "PresentationOpen";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーション印刷時のイベントハンドラ
        private void PresentationPrint(Presentation Pres) {
            value = "PresentationPrint";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーション保存時のイベントハンドラ
        private void PresentationSave(Presentation Pres) {
            value = "PresentationSave";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーション 時のイベントハンドラ
        private void PresentationSync(Presentation Pres, MsoSyncEventType SyncEventType) {
            value = "PresentationSync";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void ProtectedViewWindowActivate(ProtectedViewWindow ProtViewWindow) {
            value = "ProtectedViewWindowActivate";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void ProtectedViewWindowBeforeClose
            (ProtectedViewWindow ProtViewWindow, 
             PpProtectedViewCloseReason ProtectedViewCloseReason, ref bool Cancel) {
            value = "ProtectedViewWindowBeforeClose";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void ProtectedViewWindowBeforeEdit(ProtectedViewWindow ProtViewWindow, ref bool Cancel) {
            value = "ProtectedViewWindowBeforeEdit";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void ProtectedViewWindowDeactivate(ProtectedViewWindow ProtViewWindow) {
            value = "ProtectedViewWindowDeactivate";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void ProtectedViewWindowOpen(ProtectedViewWindow ProtViewWindow) {
            value = "ProtectedViewWindowOpen";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライド変更時のイベントハンドラ設定
        private void SlideSelectionChanged(SlideRange SldRange) {
            value = "SlideSelectionChanged";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライドショウ開始時のイベントハンドラ設定
        private void SlideShowBegin(SlideShowWindow Wn) {
            value = "SlideShowBegin";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライドショウ終了時のイベントハンドラ設定
        private void SlideShowEnd(Presentation Pres) {
            value = "SlideShowEnd";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーション開始時のイベントハンドラ設定
        private void SlideShowNextBuild(SlideShowWindow Wn) {
            value = "SlideShowNextBuild";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライドショウ次へクリック時のイベントハンドラ
        private void SlideShowNextClick(SlideShowWindow Wn, Effect nEffect) {
            value = "SlideShowNextClick";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライドショウ次のスライド移動時のイベントハンドラ
        private void SlideShowNextSlide(SlideShowWindow Wn) {
            value = "SlideShowNextSlide";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // スライドショウ次のスライド移動時のイベントハンドラ
        private void SlideShowOnNext(SlideShowWindow Wn) {
            value = "SlideShowOnNext";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        // プレゼンテーション開始時のイベントハンドラ設定
        private void SlideShowOnPrevious(SlideShowWindow Wn) {
            value = "SlideShowOnPrevious";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        //
        private void WindowActivate(Presentation Pres, DocumentWindow Wn) {
            value = "WindowActivate";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        //
        private void WindowBeforeDoubleClick(Selection Sel, ref bool Cancel) {
            value = "WindowBeforeDoubleClick";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        private void WindowBeforeRightClick(Selection Sel, ref bool Cancel) {
            value = "WindowBeforeRightClick";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        //
        private void WindowDeactivate(Presentation Pres, DocumentWindow Wn) {
            value = "WindowDeactivate";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }

        //
        private void WindowSelectionChange(Selection Sel) {
            value = "WindowSelectionChange";
            Thread t = new Thread(new ThreadStart(worker));
            t.Start();
        }
    }
}



メタデータをソースとして表示する

私は現場で VBC# を 6/4 (・・・いや 7/3 だろうな) の割合で使ってます。作業してる感覚としては一長一短で、どちらが断然優れてるというのはないのですが、COM との相互運用に関してはコード定義ウィンドウを使える分、C# に若干軍配が上がると思ってます。

例えば以下のコード。「_app.PresentationOpen += PresentationOpen;」PresentationOpen にカーソルを当てて F12 もしくは右クリックして「定義へ移動」をクリックします。


public void Start() {
    _app = new Microsoft.Office.Interop.PowerPoint.Application();
    _app.Visible = MsoTriState.msoTrue;
    _app.PresentationOpen += PresentationOpen;
    _app.PresentationClose += PresentationClose;
}

するとコード定義ウィンドウが開き、メタデータから取得した EApplication_Event のメンバーの宣言もしくは型情報を展開します。


#region アセンブリ Microsoft.Office.Interop.PowerPoint.dll, v2.0.50727
// C:\Program Files (x86)\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.PowerPoint.dll
#endregion

using System.Runtime.InteropServices;

namespace Microsoft.Office.Interop.PowerPoint {
    [TypeLibType(16)]
    [ComVisible(false)]
    [ComEventInterface(typeof(EApplication), typeof(EApplication_EventProvider))]
    public interface EApplication_Event {
        event EApplication_AfterNewPresentationEventHandler AfterNewPresentation;
        event EApplication_AfterPresentationOpenEventHandler AfterPresentationOpen;
        event EApplication_ColorSchemeChangedEventHandler ColorSchemeChanged;
        event EApplication_NewPresentationEventHandler NewPresentation;
        event EApplication_PresentationBeforeCloseEventHandler PresentationBeforeClose;
        event EApplication_PresentationBeforeSaveEventHandler PresentationBeforeSave;
        event EApplication_PresentationCloseEventHandler PresentationClose;
        event EApplication_PresentationCloseFinalEventHandler PresentationCloseFinal;
        event EApplication_PresentationNewSlideEventHandler PresentationNewSlide;
        event EApplication_PresentationOpenEventHandler PresentationOpen;
        event EApplication_PresentationPrintEventHandler PresentationPrint;
        event EApplication_PresentationSaveEventHandler PresentationSave;
        event EApplication_PresentationSyncEventHandler PresentationSync;
        event EApplication_ProtectedViewWindowActivateEventHandler ProtectedViewWindowActivate;
        event EApplication_ProtectedViewWindowBeforeCloseEventHandler ProtectedViewWindowBeforeClose;
        event EApplication_ProtectedViewWindowBeforeEditEventHandler ProtectedViewWindowBeforeEdit;
        event EApplication_ProtectedViewWindowDeactivateEventHandler ProtectedViewWindowDeactivate;
        event EApplication_ProtectedViewWindowOpenEventHandler ProtectedViewWindowOpen;
        event EApplication_SlideSelectionChangedEventHandler SlideSelectionChanged;
        event EApplication_SlideShowBeginEventHandler SlideShowBegin;
        event EApplication_SlideShowEndEventHandler SlideShowEnd;
        event EApplication_SlideShowNextBuildEventHandler SlideShowNextBuild;
        event EApplication_SlideShowNextClickEventHandler SlideShowNextClick;
        event EApplication_SlideShowNextSlideEventHandler SlideShowNextSlide;
        event EApplication_SlideShowOnNextEventHandler SlideShowOnNext;
        event EApplication_SlideShowOnPreviousEventHandler SlideShowOnPrevious;
        event EApplication_WindowActivateEventHandler WindowActivate;
        event EApplication_WindowBeforeDoubleClickEventHandler WindowBeforeDoubleClick;
        event EApplication_WindowBeforeRightClickEventHandler WindowBeforeRightClick;
        event EApplication_WindowDeactivateEventHandler WindowDeactivate;
        event EApplication_WindowSelectionChangeEventHandler WindowSelectionChange;
    }
}

さらに EApplication_PresentationOpenEventHandler の上にカーソルを置き、F12 もしく「定義へ移動」を実行すると、EApplication_PresentationOpenEventHandler の宣言が表示されます。これで PresentationOpen イベントに渡すパラメータが判ります。


#region アセンブリ Microsoft.Office.Interop.PowerPoint.dll, v2.0.50727
// C:\Program Files (x86)\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.PowerPoint.dll
#endregion

using System;
using System.Runtime.InteropServices;

namespace Microsoft.Office.Interop.PowerPoint {
    [TypeLibType(16)]
    [ComVisible(false)]
    public delegate void EApplication_PresentationOpenEventHandler(Presentation Pres);
}

これ VB.NET にはない機能なのでけっこう便利。VB.NET だと「定義に移動」でオブジェクトブラウザが起動するのでそれはそれで便利なのですが、宣言を纏めてコピーして持ってきたいときなんかはコード定義ウィンドウが使える C# の方が便利なように思えます。


参考記事:

Metadata as Source

コード定義ウィンドウ


 

トラックバック - http://d.hatena.ne.jp/hilapon/20101224/1293122037