2016-07-21 14 views
0

Outlook 2010のVSTOを作成しています(2010-2016で動作する必要があります)。私が知る限り、イベントハンドラは決して削除されないという奇妙な問題に遭遇しています。これは、愚かで無駄なイベントハンドラを繰り返し呼び出すことを避けることができないことを意味します。Outlook VSTOで重複イベントハンドラを呼び出さない

問題のコードは、エクスプローラのSelectionChangeイベントのイベントハンドラで発生します。ハンドラは、選択がMailItemかどうかをチェックし、そうであれば、ReplyReplyAll、およびForwardイベントにハンドラがあることを確認します。ある項目を複数回選択することができるので、hereのパターンに従って、SelectionChangeハンドラは最初にReply/ReplyAll/Forwardイベントハンドラを削除します(イベントハンドラを2回フックしないようにします。クラス実装)。

問題は、これは、SelectionChangeイベントハンドラの起動時に1回呼び出されることを防ぐことではありません。これは、Reply(または他の応答アクション)イベントハンドラが1回呼び出されることを防ぎません。これは急速に黙々と呼び出されます。私はそれが同期の問題かもしれないと思ったので、私はイベントハンドラを取り除いて、lockブロックに追加して無駄にしました。

private void SelectionChangeHandler() 
    { 
     Outlook.Selection sel = Application.ActiveExplorer().Selection; 
     // First make sure it's a (single) mail item 
     if (1 != sel.Count) 
     { // Ignore multi-select 
      return; 
     } 
     // Indexed from 1, not 0. Stupid VB-ish thing... 
     Outlook.MailItem mail = sel[1] as Outlook.MailItem; 
     if (null != mail) 
     { 
      Outlook.ItemEvents_10_Event mailE = mail as Outlook.ItemEvents_10_Event; 
      lock (this) 
      { // For each event, remove the handler then add it again 
       mailE.Forward -= MailItemResponseHandler; 
       mailE.Forward += MailItemResponseHandler; 
       mailE.Reply -= MailItemResponseHandler; 
       mailE.Reply += MailItemResponseHandler; 
       mailE.ReplyAll -= MailItemResponseHandler; 
       mailE.ReplyAll += MailItemResponseHandler; 
      } 
      ProcessMailitem(mail); 
     } 
    } 

そして、あまりにも何度も呼び出されるイベントハンドラ:

private void MailItemResponseHandler (object newItem, ref bool Cancel) 
    { // We need to get the responded-to item 
     // NOTE: There really needs to be a better way to do this 
     Outlook.MailItem old = GetCurrentMail(); 
     if (null == old) 
     { // No mail item selected 
      return; 
     } 
     MessageBox.Show(old.Body); 
    } 

この関数は、最終的には、ダイアログボックスを開くよりも、はるかに有益な何かをするだろうが、それは "のための便利なチェックでした元の正しいメッセージが見つかりましたか? "私は同じダイアログボックスを何度も何度も繰り返してはいけません。

何か間違っていますか?これはOutlookまたはVSTOのバグですか?誰でも、重複したイベントハンドラの呼び出しを避ける方法を知っていますか?

答えて

0

イベントハンドラは、Outlookが動作するMAPIアイテムに実際には添付されていません。代わりに、COMオブジェクトをラップするRuntime Callable Wrapper(RCW)という.NETオブジェクトにアタッチされています。 RCWが動作する方法のため、同じオブジェクトであると思われるものへの複数の参照を取得する - たとえば、activeExplorer.Selection()[1]を2回取得することで、異なるCOMオブジェクトの周りに複数のRCWが得られます。これは、イベントを削除しようとしていたOutlook.MailItem(またはOutlook.ItemEvents_10_Event)がイベントを実際に持っていなかったことを意味します。 SelectionChangeHandlerが発砲されるたびに新しく作られました。

関連して、RCWラップCOMオブジェクトへの唯一の参照はRCW自体であるため、RCWを参照するすべての変数を有効範囲外にする(そうでなければそのRCWを参照しなくなる)ことによりRCWがガベージコレクションされますCOMオブジェクトは解放され、削除されます)。これは、問題のコード上の2つの関連する影響があります

  1. ガベージコレクションが即時、古いRCWS(およびCOMオブジェクト)ではないので、既存のイベントハンドラでは、上の長引くし、Outlookがまだ彼らのCOMオブジェクトのイベントをトリガすることができ。これは、イベントハンドラが複数回呼び出される理由です。
  2. RCWはSelectionChangeHandlerの下部にあるので、ガベージコレクションがすべてのRCW(およびイベントハンドラ)を掃討し、すべてのCOMオブジェクトを解放するまでは時間の問題でした。その時点で、その電子メールにはイベントは添付されません。
    • 実際には、私のテストは十分に短い時間枠で行われていましたが、私は複数のRCWを何も持っていないよりもメールの項目を選択し、それと相互作用しない(または何かを選択する)実際には、ガベージコレクションの掃引ではMailItemResponseHandlerが呼び出されず、が返信されました。

@DmitryStreblechenkoは私にこれを動作するように右方向にプッシュを与えたが、それは把握するために、いくつかの実験をしました。まず、関連するMailItemがグローバルに参照される必要があったため、イベントRCWへの参照が範囲外にならず、また重要なことに、SelectionChangeHandlerが再び呼び出されたときにRCWが直接参照される可能性があります。 I変数selectedMailを改名などのようなクラスレベルでそれを参照:

Outlook.ItemEvents_10_Event selectedMail; 

私はそれが現在選択された単一MailItemで呼び出されるたびに、それは最初selectedMailからのすべてのイベントハンドラを削除するようSelectionChangeHandler修飾、及び新しい選択項目にはselectedMailが表示されます。以前はselectedMailで参照されていたRCWはガベージコレクションの対象となりますが、実際には気にしないようにイベントハンドラはありません。 SelectionChangeHandlerは、今度はselectedMailで参照される新しいRCWに関連するイベントハンドラを追加します。ドミトリの回答やコメントをもとに

private void SelectionChangeHandler() 
    { 
     Outlook.Selection sel = activeExplorer.Selection; 
     // First make sure it's a (single) mail item 
     if (1 != sel.Count) 
     { // Ignore multi-select 
      return; 
     } 
     // Indexed from 1, not 0. Stupid VB-ish thing... 
     Outlook.MailItem mail = sel[1] as Outlook.MailItem; 
     if (null != mail) 
     { 
      if (null != selectedMail) 
      { // Remove the old event handlers, if they were set, so there's no repeated events 
       selectedMail.Forward -= MailItemResponseHandler; 
       selectedMail.Reply -= MailItemResponseHandler; 
       selectedMail.ReplyAll -= MailItemResponseHandler; 
      } 
      selectedMail = mail as Outlook.ItemEvents_10_Event; 
      selectedMail.Forward += MailItemResponseHandler; 
      selectedMail.Reply += MailItemResponseHandler; 
      selectedMail.ReplyAll += MailItemResponseHandler; 
      if (DecryptOnSelect) 
      { // We've got a live mail item selected. Process it 
       ProcessMailitem(mail); 

      } 
     } 
    } 

、私が代わりにイベントハンドラを削除するために考えられて前にselectedMailの古い値にMarshal.ReleaseComObject(selectedMail)を呼び出してみました。これは少し助けましたが、COMオブジェクトは即座にリリースされませんでした。または、返信前に短時間に複数の電子メールを選択した場合、イベントが複数回発生していたため、Outlookは依然としてイベントハンドラを呼び出すことができます。

解決策はまだあります。インスペクタを開いてエクスプローラで選択内容を変更せずに返信すると、正常に動作します(MailItemResponseHandlerが呼び出されます)。しかし、インスペクタを開いたままにしておくと、エクスプローラに戻って別の電子メールを選択してインスペクタに戻り、返信を押しても動作しません。その電子メールが選択解除されたときに関連する電子メールのインスペクタが開いている場合は、イベントハンドラを削除しないようにする必要があります(電子メールがエクスプローラでまだ選択されていない限り、インスペクタが閉じられたら削除する必要があります) 。厄介だが、私はそれを解決するだろう。

1

まず、mailE変数はガベージコレクションされないように、ローカルではなくクラスレベルで宣言する必要があります。

第2に、イベントを設定する前に、Marshal.ReleaseComObjectを使用して古い値(mailE)を解放する必要があります。

+0

OK、私はおそらく、変数が 'SelectionChangeHandler'の呼び出しの間にガベージコレクションを取得することと関係していると思います。これは、イベントハンドラの登録抹消が機能しないことを意味します。しかし、 'mailE'が別の' MailItem'を指し示すようになったらすぐにガベージコレクションを取得できませんか?いずれにしても 'ReleaseComObject'と何が関係しているのかよくわかりません。あなたは何が起こっているのか分かっているようですので、これらのステップがなぜ機能するのかをもう少し説明できますか?なぜ私は盲目的に変更を適用していないのですか?ありがとう! – CBHacking

+1

いいえ、私が言っているのは、変数がローカルであり、予測不可能な時点でガベージコレクションされるということです。そうであれば、変数がクラスレベルにあることによって生きていなければイベントは発生しません。しかし、それがリリースされる前に、2つ目の通知を取得し、2つの変数(同じ項目を指す両方)から同時にイベントをシンクすることは可能です。それが修正しようとしている問題です。 –

+0

ああ、私は見ていると思う。選択されたMailItemオブジェクトは、以前と同じ.NETオブジェクトではなく、同じOutlookオブジェクトを参照し、イベント処理はOutlookオブジェクトのレベルにあるため、イベントハンドラの削除は機能しません。古いCOMオブジェクトをリリースすることで、.NETオブジェクトを効果的に分解したり、少なくとも不活性化したりすることで、複数の「ライブ」.NETオブジェクトから離れることができます。私が正しいと分かっていれば、イベントハンドラの削除は必要ありません。私はこれを今正しく持っていますか? – CBHacking

関連する問題