トップ回答者
[再送] outlookアドインの関数呼び出し

質問
-
再投稿です。
visualstudio2015でoutlook2013のvsto2016でc#でプログラムを作成しました。
メールの新規作成で、添付ファイルを追加するときにmailItem.AttachmentAddを用いてメッセージボックスを表示させるプログラムを作成しました。
しかし、複数回添付を行うとメッセージボックスが表示されない不具合が生じます。7個ぐらい添付すると表示されなくなるときもあれば、2,3個めで表示されないときもあります。軽いファイルでやってもすぐに表示されなくなることから、添付するファイルの重さは関係ないように思われます。表示されなくなる原因と解決策を教えてください。宜しくお願い致します。
以下がプログラムです。
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Windows.Forms;namespace OutlookAddIn1
{
public partial class ThisAddIn
{
Outlook.Inspectors inspectors;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector +=
new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.AttachmentAdd += MailItem_AttachmentAdd;
}
}
}private void MailItem_AttachmentAdd(Outlook.Attachment Attachment)
{
MessageBox.Show("aaaa");
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
//注: Outlook はこのイベントを発行しなくなりました。Outlook が
}
#region VSTO で生成されたコード
/// <summary>
/// デザイナーのサポートに必要なメソッドです。
/// このメソッドの内容をコード エディターで変更しないでください。
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}#endregion
}
}
回答
-
自分の手元はOutlook2016なのですが、同じコードで同じ問題が再現しました。いくつか試してみての推測なのですが、Inspectors_NewInspector メソッド内のローカル変数であるmailItem がGCされてしまって、次回以降AttachmentAdd イベントが発生しないのでイベントハンドラも呼ばれなくなるのが原因ではないかと思われます。GCされたらメッセージボックスが表示されなくなるのは、GC.Collectメソッドを追加すると確認できます。
private void MailItem_AttachmentAdd(Outlook.Attachment attachment) { MessageBox.Show("aaaa"); GC.Collect(); }
このようにすると、1回目の添付ファイル追加ではメッセージボックスは表示されるが、2回目以降は表示されないようになりました。GCが原因だと考えると、表示されなくなる回数がその時によって違うという挙動も理解できます。
これを防ぐ方法ですが、mailItem がGCされないように適当に変数を保持しておけばよさそうですが、単純にThisAddInクラスのフィールドにすると、複数の新しいメッセージを作成するウィンドウを立ち上げた場合に最新のウィンドウ以外は結局GC対象になってしまいます。そこでかなりトリッキーですが、mailItem 変数をAttachmentAdd イベントハンドラのラムダ式にキャプチャーさせる方法があります。これだとGCされても、また複数のウィンドウを立ち上げても手元では常にメッセージボックスが表示されました。
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector) { var mailItem = Inspector.CurrentItem as Outlook.MailItem; if (mailItem != null) { if (mailItem.EntryID == null) { mailItem.AttachmentAdd += attachment => { //ラムダ式内でmaiItemを参照させることでキャプチャーして、GCされないようにさせている MessageBox.Show($"{mailItem.Subject} に添付ファイル {attachment.FileName} を追加 "); //GCしてもイベントハンドラがされるかテスト GC.Collect(); }; } } }
ただ、この方法はラムダ式内のメソッドを変更したときにmailItemへの参照がなくなってしまうと意味がなくなってしまいます。より明示的に変数を保持させたい場合はInspector.Close イベントハンドラを追加し、そこでmailItemを参照してnullを代入する処理を記述することにより、mailItemをGCさせない方法はどうでしょうか?
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector) { var mailItem = Inspector.CurrentItem as Outlook.MailItem; if (mailItem != null) { if (mailItem.EntryID == null) { mailItem.AttachmentAdd += attachment => { MessageBox.Show($"添付ファイル {attachment.FileName} を追加 "); GC.Collect(); }; } }
//Interfaceの明示的実装によりCloseメソッドとCloseイベントハンドラの両方が定義されているので、キャストしてイベントハンドラを追加 ((Outlook.InspectorEvents_10_Event)Inspector).Close += () => { mailItem = null; MessageBox.Show($"{Inspector.Caption} を閉じます"); }; }
参考になれば幸いです。
- 回答の候補に設定 星 睦美 2016年11月21日 0:28
- 回答としてマーク 栗下 望Microsoft employee, Moderator 2016年11月22日 1:31
すべての返信
-
自分の手元はOutlook2016なのですが、同じコードで同じ問題が再現しました。いくつか試してみての推測なのですが、Inspectors_NewInspector メソッド内のローカル変数であるmailItem がGCされてしまって、次回以降AttachmentAdd イベントが発生しないのでイベントハンドラも呼ばれなくなるのが原因ではないかと思われます。GCされたらメッセージボックスが表示されなくなるのは、GC.Collectメソッドを追加すると確認できます。
private void MailItem_AttachmentAdd(Outlook.Attachment attachment) { MessageBox.Show("aaaa"); GC.Collect(); }
このようにすると、1回目の添付ファイル追加ではメッセージボックスは表示されるが、2回目以降は表示されないようになりました。GCが原因だと考えると、表示されなくなる回数がその時によって違うという挙動も理解できます。
これを防ぐ方法ですが、mailItem がGCされないように適当に変数を保持しておけばよさそうですが、単純にThisAddInクラスのフィールドにすると、複数の新しいメッセージを作成するウィンドウを立ち上げた場合に最新のウィンドウ以外は結局GC対象になってしまいます。そこでかなりトリッキーですが、mailItem 変数をAttachmentAdd イベントハンドラのラムダ式にキャプチャーさせる方法があります。これだとGCされても、また複数のウィンドウを立ち上げても手元では常にメッセージボックスが表示されました。
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector) { var mailItem = Inspector.CurrentItem as Outlook.MailItem; if (mailItem != null) { if (mailItem.EntryID == null) { mailItem.AttachmentAdd += attachment => { //ラムダ式内でmaiItemを参照させることでキャプチャーして、GCされないようにさせている MessageBox.Show($"{mailItem.Subject} に添付ファイル {attachment.FileName} を追加 "); //GCしてもイベントハンドラがされるかテスト GC.Collect(); }; } } }
ただ、この方法はラムダ式内のメソッドを変更したときにmailItemへの参照がなくなってしまうと意味がなくなってしまいます。より明示的に変数を保持させたい場合はInspector.Close イベントハンドラを追加し、そこでmailItemを参照してnullを代入する処理を記述することにより、mailItemをGCさせない方法はどうでしょうか?
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector) { var mailItem = Inspector.CurrentItem as Outlook.MailItem; if (mailItem != null) { if (mailItem.EntryID == null) { mailItem.AttachmentAdd += attachment => { MessageBox.Show($"添付ファイル {attachment.FileName} を追加 "); GC.Collect(); }; } }
//Interfaceの明示的実装によりCloseメソッドとCloseイベントハンドラの両方が定義されているので、キャストしてイベントハンドラを追加 ((Outlook.InspectorEvents_10_Event)Inspector).Close += () => { mailItem = null; MessageBox.Show($"{Inspector.Caption} を閉じます"); }; }
参考になれば幸いです。
- 回答の候補に設定 星 睦美 2016年11月21日 0:28
- 回答としてマーク 栗下 望Microsoft employee, Moderator 2016年11月22日 1:31