Office Outlook VSTOを使ってパブリックフォルダアイテムの変更通知アドイン開発(C#)
Outlookのパブリックフォルダを使って日報を投稿する集団にいます。
投稿者以外の人がその日報にコメントを付けるために、その投稿アイテムを編集保存します。折角コメントをつけたのに投稿者に通知が飛ばないので見逃してしまう可能性があるので、通知してくれるようなOutlookアドインをがんばって書いてみました。
やりたいこと
パブリックフォルダに投稿アイテム(プロパティ:PostItem)を投稿する、
or 他人の投稿アイテムにコメントする
↓
自分が一度でも編集したことのある投稿アイテムの一意IDを記録
↓
記録した投稿アイテムを他人が編集(コメントを付けたら)したら、それを未読アイテムとしてリストアップ
↓
リストからクリックすると投稿アイテムにアクセスできるようにする
Visual Studioのテンプレートを使う
OutlookVSTOアドインのためのテンプレートを使いました。すると、ThisAddIn.csとRibbon1.csが現れて、Ribbon1.cs[デザイン]に新規のボタンを作成します。
日報に他人からのコメントが追加されてないかどうか、気が向いたら確認したいときにリストを開くことができますね。
ボタンを実際のOutlookで表示するには、Outlookの開発タブを有効にする一手間もあります。
フォームの追加で、ボタンを押すと飛び出るリストボックスを作成します。
コード
・ThisAddIn.cs
using System;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Outlook;
using System.Windows.Forms;
using System.IO;
namespace OutlookAddIn1_CheckPostItemUseMemoCSV
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.Inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspctor_Event); //イベントハンドラ:書き込み
((Outlook.ApplicationEvents_11_Event)Application).Quit += new Outlook.ApplicationEvents_11_QuitEventHandler(ThisAddIn_Quit); //イベントハンドラ:Outlookを閉じるとき
void ThisAddIn_Quit() //閉じるとき
{
var OneDrivePath = Environment.GetEnvironmentVariable("OneDrive");
var fileName = OneDrivePath + @"\CheckPostLog_ItemIDData.txt"; //メモファイル作成しログファイルにする
FileInfo myFile = new FileInfo(fileName); //ログファイルを取得
using (StreamWriter writer = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS"))) //trueは追加
{
myFile.Attributes &= ~FileAttributes.Hidden; //隠しファイルを読み書き可の表示ファイルにする
}
string[] lines = File.ReadAllLines(fileName); //ファイルを行ごとに読む
int isDeleteOld = 0;
for (int jcount = 0; jcount < lines.Length; jcount++) //Logファイルの全て行に対して検索
{
string[] values = lines[jcount].Split(','); //行をカンマで区切り
if (values[0].CompareTo(DateTime.Now.AddDays(-100).ToString("yyyy/MM/dd_HH:mm:ss")) == -1) //現在から100日前だったらm削除
{
lines[jcount] = null;
isDeleteOld = 1;
}
}
if(isDeleteOld ==1) //削除すべきものがあったら
{
using (var swriter = new FileStream(fileName, FileMode.Open))
{
swriter.SetLength(0); //txtファイルの全ての文字をクリア
}
using (StreamWriter swriter = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS"))) //tureは追加
{
for (int icount = 0; icount < lines.Length; icount++)
{
if (lines[icount] == null)
{
continue; //ループ中の次へ行く
}
else
{
swriter.WriteLine(lines[icount]);
}
}
}
}
myFile.Attributes |= FileAttributes.Hidden; //隠しファイルにする
}//voidQuit
}//Startup
private void WriteLineFileAsync_Item(string itemIDs)
{
var OneDrivePath = Environment.GetEnvironmentVariable("OneDrive");
var fileName = OneDrivePath + @"\CheckPostLog_ItemIDData.txt";
FileInfo myFile = new FileInfo(fileName);
using (StreamWriter sw = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS"))) //tureは追加
{
myFile.Attributes &= ~FileAttributes.Hidden;
await sw.WriteLineAsync(itemIDs);
}
myFile.Attributes |= FileAttributes.Hidden; //隠しファイルにする
}
private void ReadAndReplaceLineFileAsync_Item(string itemIDs)
{
string[] itemID = itemIDs.Split(',');
var OneDrivePath = Environment.GetEnvironmentVariable("OneDrive");
var fileName = OneDrivePath + @"\CheckPostLog_ItemIDData.txt";
FileInfo myFile = new FileInfo(fileName);
int isReplace = 0;
using (StreamWriter writer = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS"))) //ファイルを読み書き可で開く
{
myFile.Attributes &= ~FileAttributes.Hidden;
}
string[] lines = File.ReadAllLines(fileName); //行ごと読み込み
for (int jcount = 0; jcount < lines.Length; jcount++) //全部の行について
{
string[] values = lines[jcount].Split(',');
if (String.Compare(itemID[1], values[1]) == 0) //被りの行があれば削除
{
values[0] = itemID[0];
lines[jcount] = values[0] +","+ values[1] + "," + values[2] ; //該当の行を置き換える
isReplace = 1;
break; //forを強制終了
}
}
if (isReplace == 1)
{
using (var swriter = new FileStream(fileName, FileMode.Open))
{
swriter.SetLength(0);
}
using (StreamWriter swriter = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS")))
{
for (int icount = 0; icount < lines.Length; icount++)
{
swriter.WriteLine(lines[icount]);
}
}
}
else if (isReplace == 0)
{
WriteLineFileAsync_Item(itemIDs);
}
myFile.Attributes |= FileAttributes.Hidden; //隠しファイルに戻す
}//ReadAndReplaceLineFileAsync_Item
private void Inspctor_Event(Outlook.Inspector Inspector)
{
var OneDrivePath = Environment.GetEnvironmentVariable("OneDrive");
var fileName = OneDrivePath + @"\CheckPostLog_ItemIDData.txt";
FileInfo myFile = new FileInfo(fileName);
try
{
Outlook.Folder ItemparentFolder = Inspector.CurrentItem.Parent as Outlook.Folder;
MAPIFolder PsotObjectFolder =
Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders); //パブリックフォルダ
string storeid = PsotObjectFolder.StoreID;
if (ItemparentFolder == null) //定期的な予定はItemparentFolderがnullになるので例外になる。
{
return;//void Inspctor_Event()を強制終了、抜ける。
}
if (ItemparentFolder.StoreID == storeid) //パブリックフォルダ内限定でLogを取る
{
if (Inspector.CurrentItem is PostItem explorerPostItem) //PostItemの場合とMailItemの場合を分ける
{
Outlook.PostItem postitem = (Inspector.CurrentItem as Outlook.PostItem);
string entryid = postitem.EntryID;
explorerPostItem.Write += new Outlook.ItemEvents_10_WriteEventHandler(currentItem_AfterWrite); //これはIf( PostItemかどうか )の中にいれる
void currentItem_AfterWrite(ref bool Cancel)
{
if (entryid == null) //新規投稿。新しく投稿の時は、IDがnullになるので、条件分岐で対処をする
{
explorerPostItem.Save(); //保存して閉じてから一意EntryIDを与える
string entryid_new = explorerPostItem.EntryID;
DateTime datetime = DateTime.Now;
var PostitemIDs = datetime.ToString("yyyy/MM/dd_HH:mm:ss") + "," + entryid_new + "," + storeid;
WriteLineFileAsync_Item(PostitemIDs); //Logに記録する
explorerPostItem.UnRead = false; //既読にする
return; //void Inspctor_Event()を強制終了、抜ける。
}
else if(entryid != null) //保存時間だけ上書きの場合
{
DateTime datetime = DateTime.Now;
var PostitemIDs = datetime.ToString("yyyy/MM/dd_HH:mm:ss") + "," + entryid + "," + storeid;
ReadAndReplaceLineFileAsync_Item(PostitemIDs); //時間だけ上書きする
}
explorerPostItem.Write -= new Outlook.ItemEvents_10_WriteEventHandler(currentItem_AfterWrite); //上書きが繰り返さないようにイベントハンドラを一つ削除
}//void currentItem_AfterWrite
}//If PostItem
if (Inspector.CurrentItem is MailItem explorerMailItem) //Mailitemも同様に
{
Outlook.MailItem mailitem = (Inspector.CurrentItem as Outlook.MailItem);
string entryid = mailitem.EntryID;
explorerMailItem.Write += new Outlook.ItemEvents_10_WriteEventHandler(currentItem_AfterWrite);
void currentItem_AfterWrite(ref bool Cancel)
{
if (entryid == null) //新規投稿
{
explorerMailItem.Save();
string entryid_new = explorerMailItem.EntryID;
DateTime datetime = DateTime.Now;
var MailitemIDs = datetime.ToString("yyyy/MM/dd_HH:mm:ss") + "," + entryid_new + "," + storeid;
WriteLineFileAsync_Item(MailitemIDs);
explorerMailItem.UnRead = false;
return;
}
else
{
DateTime datetime = DateTime.Now;
var MailitemIDs = datetime.ToString("yyyy/MM/dd_HH:mm:ss") + "," + entryid + "," + storeid;
ReadAndReplaceLineFileAsync_Item(MailitemIDs);
}
explorerMailItem.Write -= new Outlook.ItemEvents_10_WriteEventHandler(currentItem_AfterWrite); //上書きが繰り返さないようにイベントハンドラを一つ削除
}//void currentItem_AfterWrite
}//If MailItem
}//If In PublicFolder
}//try
catch (System.Exception ex)
{
MessageBox.Show("Bug Occurred. Or not online. If Bug, please call: suzukitomoyu@rohto.co.jp");
}
}//void Inspctor_Event
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
//注: Outlook はこのイベントを発行しなくなりました。Outlook が
// を Outlook のシャットダウン時に実行する必要があります。https://go.microsoft.com/fwlink/?LinkId=506785 をご覧ください
}
#region VSTO で生成されたコード
/// <summary>
/// デザイナーのサポートに必要なメソッドです。
/// このメソッドの内容をコード エディターで変更しないでください。
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}//public partial class
}//namespace
・Ribbon1.cs
using Microsoft.Office.Tools.Ribbon;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OutlookAddIn1_CheckPostItemUseMemoCSV
{
public partial class Ribbon1
{
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
}
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Form1 f = new Form1(); //ボタンをクリックするとフォーム表示
f.Show();
}
}
}
・Form1.cs
using System;
using System.Text;
using System.Windows.Forms;
using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Outlook;
using System.IO;
namespace OutlookAddIn1_CheckPostItemUseMemoCSV
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); //初期化
var OneDrivePath = Environment.GetEnvironmentVariable("OneDrive");
var fileName = OneDrivePath + @"\CheckPostLog_ItemIDData.txt";
FileInfo myFile = new FileInfo(fileName); //ログファイルを取得
using (StreamWriter writer = new StreamWriter(fileName, true, Encoding.GetEncoding("Shift_JIS"))) //falseならば上書き
{
myFile.Attributes &= ~FileAttributes.Hidden; //隠しファイルを読み書き可の表示ファイルにする
}
string[] lines = File.ReadAllLines(fileName); //ファイルを行ごとに読む
myFile.Attributes |= FileAttributes.Hidden; //隠しファイルにする
Outlook.NameSpace ns =
Globals.ThisAddIn.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders).Session;
foreach (string line in lines)
{
string[] values = line.Split(','); //一行をカンマで区切り
object item; //object型宣言
try
{
item = ns.GetItemFromID(values[1], values[2]);
if (item is Outlook.PostItem)
{
Outlook.PostItem postitemi = ns.GetItemFromID(values[1], values[2]);
if (postitemi.UnRead)
{
string title = postitemi.SenderName + " " + postitemi.Subject + " " + postitemi.Body;
listBox1.Items.Add(title);
listBox1.Items.Add(item);
}
}
if (item is Outlook.MailItem)
{
Outlook.MailItem mailitemi = ns.GetItemFromID(values[1], values[2]);
if (mailitemi.UnRead)
{
string title = mailitemi.SenderName + " " + mailitemi.Subject + " " + mailitemi.Body;
listBox1.Items.Add(title);
listBox1.Items.Add(item);
}
}
}//try
catch
{
item = null;
continue;
}
}//foreach
}//Form1
private void Form1_Load(object sender, EventArgs e)
{
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
ListBox listbox = (ListBox)sender;
if (listbox.SelectedItem is PostItem explorerPostItem)
{
explorerPostItem.Display(false);
}
if (listbox.SelectedItem is MailItem explorerMailItem)
{
explorerMailItem.Display(false);
}
}
private void Form1_Load_1(object sender, EventArgs e)
{
}
}//public partial class
}//namespcae
追記
パブリックフォルダは場合によって、投稿アイテム以外にメールアイテムも投稿されることあるみたいなので、どっちも処理しないといけませんでした。
今回はボタンを押すと、他人によって編集された投稿アイテムがリストアップされる様式ですが、メールを飛ばして即通知するのも便利そうですね。
例外処理がまだ甘いので、要改善です。