none
Office ExcelCOMアドインでの印刷時イベントでキャンセルの値が反映されない RRS feed

  • 質問

  • 現在C#でVSOTを利用しない形でExcelのアドインを作成しているのですが
    Excelの印刷を行った際のイベントを補足して、強制的にキャンセルを行いたいと考えています。

    一応コードは作成してイベント補足できるようになったのですが、キャンセルを行ってもキャンセルができないのです。
    どなたか原因と対策を知っている方がいらっしゃれば教えてください。

    一応下記のようなコードです。

    ①Excelのイベントインタフェースを定義
    ---------
     [ComImport, ComVisible(true), Guid("00024413-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
     public interface IApplicationEvents
     {
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall), DispId(0x0000061d)]
      void NewWorkbook([In, MarshalAs(UnmanagedType.Interface)] object Wb);
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall), DispId(0x0000061f)]
      void WorkbookOpen([In, MarshalAs(UnmanagedType.Interface)] object Wb);
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall), DispId(0x00000620)]
      void WorkbookActivate([In, MarshalAs(UnmanagedType.Interface)] object Wb);
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall), DispId(0x00000623)]
      void WorkbookBeforeSave([In, MarshalAs(UnmanagedType.Interface)] object Wb, [In] bool SaveAsUI, [In] ref bool Cancel);
      [PreserveSig, MethodImpl(MethodImplOptions.InternalCall), DispId(0x00000624)]
      void WorkbookBeforePrint([In, MarshalAs(UnmanagedType.Interface)] object Wb, [In] ref bool Cancel);
     }
    --------------------------------

    ②上記のIApplicationEventsを実装する
      void IApplicationEvents.WorkbookBeforePrint(object Wb, ref bool Cancel)
      {
       Cancel = true;
      }

    ③メイン実装からCOMイベントの補足準備
       IConnectionPointContainer oConnPointContainer = (IConnectionPointContainer)excelapp;

       Type typeiae = typeof(IApplicationEvents);
       Attribute[] atributes = (Attribute[])typeiae.GetCustomAttributes(typeof(GuidAttribute), false);
       string iid = ((GuidAttribute)atributes[0]).Value;
       Guid guid = new Guid(iid);

       IConnectionPoint oConnectionPoint;
       oConnPointContainer.FindConnectionPoint(ref guid, out oConnectionPoint);
       oConnectionPoint.Advise(this, out m_Cookie);


    上記実装で、Excelで印刷を行った際にWorkbookBeforePrint()が呼ばれることを確認済ですが
    Cancel=trueにしても印刷ができてしまいます・・・

    ちなみに、もうひとつのパターンとして、いろいろウェブで検索を行い、①②を行わなくても
      [DispId(0x00000007)]
      public void Nandedaro_DocumentBeforePrint(object Doc, ref bool Cancel)
      {
           Cancel = true;
      }
    とするだけでもイベントを補足することができて、これだとちゃんと印刷のキャンセルができました。


    私自身C#といいますか、ExcelのCOMの扱いがよくわかってないのですが、上記2つのパターンの実装で、内部でどのように
    解釈されているのかがさっぱりわからず、なぜ、最初の実装ではキャンセルが反映されないのかがわからないのです。
    補足までに最終的にはWord等にも同じく印刷禁止をさせたいと思っているのですがWordの場合はそもそもExcelでうまくいった
    2パターン目の実装はまったく機能しませんでした(そもそもよばれない)
    このようなこともあり、最終的には最初の(現在キャンセルが反映されない)実装であれば印刷イベントの補足ができるので
    これで対応していと考えています。

    しかし、キャンセルが反映されない・・・・
    どなかた原因と対策を教えてください。
    どうかよろしくお願いいたします。





    2009年12月3日 1:44

回答

  • void WorkbookBeforePrint([In, MarshalAs(UnmanagedType.Interface)] object Wb, [In,Out] ref bool Cancel);

    System.Runtime.InteropServices.InAttributeだけだとCancelの値を書き換えてもExcel側には戻されません。
    参考 : 方向属性

    #古いtlbimpだったかExcelだったかでラッピングクラスを作るとInAttributeしか設定してくれないという問題がありました。

    2パターン目は判りません。
    #Word用の気がしますが、Excelでこんなのありました?
    • 回答としてマーク マルリン 2009年12月4日 4:15
    2009年12月3日 9:55

すべての返信

  • void WorkbookBeforePrint([In, MarshalAs(UnmanagedType.Interface)] object Wb, [In,Out] ref bool Cancel);

    System.Runtime.InteropServices.InAttributeだけだとCancelの値を書き換えてもExcel側には戻されません。
    参考 : 方向属性

    #古いtlbimpだったかExcelだったかでラッピングクラスを作るとInAttributeしか設定してくれないという問題がありました。

    2パターン目は判りません。
    #Word用の気がしますが、Excelでこんなのありました?
    • 回答としてマーク マルリン 2009年12月4日 4:15
    2009年12月3日 9:55
  • ありがとうございます。

    方向属性というものでデザインすることができるのですね。
    さっそく Out, OutAttribute 属性
    を追加して [In,Out] とするこでキャンセルが可能となりました。

     本当に助かりました。

    2パターン目なのですが、すみません。どのサイトから流用したのか、今となっては見つかることができない状況
    なのですが、
    メンバ関数の定義部分の先頭に、確かに[DispId(0x00000007)]と定義することで
    オーバーライド引数さえあっていればイベントが補足できておりました。
    どのようなテクノロジーがあるのかはさっぱりわからないのですが・・・

    ひとまず、1パターンで問題なく動作しましたので、この実装で進めたいと思います。

    以上。まだまだ勉強不足なので今後とも勉強を重ねていきたいと思います。
    本当にどうもありがとうございました。


    2009年12月4日 4:31
  • メンバ関数の定義部分の先頭に、確かに[DispId(0x00000007)]と定義することで
    オーバーライド引数さえあっていればイベントが補足できておりました。
    どのようなテクノロジーがあるのかはさっぱりわからないのですが・・・
    OLE オートメーションとか、COM とか、IDispatch とか、遅延バインディングとか、このあたりのキーワードが絡む事柄です。

    この手のインターフェースの呼び出しは、IDispatch インターフェースを通して、名前から ID を取得(GetIDsOfNames)し、ID を指定して関数を呼び出す(Invoke)するような仕組みになっています。
    DispId 属性ではその呼び出すための ID を直接指定する役割を持つため、名前が合っていなくても、特定の機能を呼び出すことができるのでしょう。

    ※これは私の理解に基づいて書いているため、細かい動作・仕様は間違っている可能性があります。
    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2009年12月4日 13:49
    モデレータ