插件编程





3.00/5 (3投票s)
Add-in - 插件编程以及 Outlook 插件的区域、视图、规则和报表示例。
引言
我发现在互联网上关于 Outlook 插件编程的信息不够充分,因此我决定写这篇文章。我搜索了很多网站,也阅读了一些关于插件编程的书籍。在我的一些专业工作之后,我为新的插件程序员找到了一些关键的解决方案,现在我在这里分享。
本文识别了一些与以下内容相关的关键细节
目录
- 区域
- 事件处理程序
- 报告
- 任务、邮件和约会管理
- 操作系统和版本检测
- 视图
- 规则
启动
插件的启动伴随着一些事件处理程序,因为如果程序员想根据 office 程序中的某些条件执行某些操作,它会向程序员提供一个事件,程序员可以处理该事件并根据自己的目标使用它。事件处理程序在启动时如下开始
private void InternalStartup()
{
    this.Startup += new System.EventHandler(ThisAddIn_Startup);
    this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
在此过程之后,程序员可以使用下一步
public static Outlook.Inspectors inspectors;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    inspectors.NewInspector += 
      new Microsoft.Office.Interop.Outlook.
      InspectorsEvents_NewInspectorEventHandler(inspectors_NewInspector);
    _instance = this;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
    //..
}
逻辑
将按钮插入工具栏或上下文菜单,捕获某些屏幕以根据需要更改其算法并满足全部要求是插件程序中的主要因素。因此,我们可以从之前在启动事件中处理的 Inspector 开始编写所有这些阶段。Current Item 在事件触发时为我们提供了实质性元素。在这种情况下,我已处理了 Appointment Item,如下所示。我将使用 timer 对象在用户进入的当前窗口中绘制图形。在此操作之后,我将调用 ReportInspector 函数进行检查,如果存在任何报告,我将将其作为邮件发送给当前用户。
void inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
    _manuelCancel = false;
    Object anitem = (Outlook.AppointmentItem)Inspector.CurrentItem;
    if (anitem != null)
    {
        _aitm = (Outlook.AppointmentItem)anitem;
        if (_aitm.Body == null)
        {
            _tm.Interval = 10;
            _tm.Tick += new EventHandler(tm_Tick);
            _timerSwitch = true;
            _tm.Start();
        }
    }
    _isLiiveMessageShowing = IsLiveMeetingShowing(DateTime.Now, Inspector);
    ReportInspector(DateTime.Now, Inspector);
}
当我们处理了新的约会窗口后,就可以利用它的内容,通过一些表格和图形 COM 对象来制作新的特殊设计。在这里,内容是 Document 类。
void tm_Tick(object sender, EventArgs e)
{
    if (_timerSwitch)
    {
        try
        {
            _timerSwitch = false;
            _tm.Stop();
            if (((inspectors[1].WordEditor as 
                  Microsoft.Office.Interop.Word.DocumentClass)
                  ).InlineShapes.Count < 1)
            {
                object ranger = (inspectors[1].WordEditor as 
                  Microsoft.Office.Interop.Word.DocumentClass
                  ).Application.Selection.Range;
                if (((inspectors[1].WordEditor as 
                      Microsoft.Office.Interop.Word.DocumentClass)).Tables.Count == 0)
                    ((inspectors[1].WordEditor as 
                      Microsoft.Office.Interop.Word.DocumentClass)).Tables.Add(
                      range, 2, 2, ref defaultBehavior, ref autoFitBehavior);
                Microsoft.Office.Interop.Word.Cell vertical1 = 
                  ((inspectors[1].WordEditor as 
                    Microsoft.Office.Interop.Word.DocumentClass)
                    ).Tables[1].Columns[1].Cells[1];
                Microsoft.Office.Interop.Word.Column clm1 = 
                  ((inspectors[1].WordEditor as 
                    Microsoft.Office.Interop.Word.DocumentClass)).Tables[1].Columns[1];
                clm1.Width = 40;
                area1.Range.Font.Name = "Calibri";
                merger3.Merge(vertical1);
                vertical1.Range.Font.Size = 22;
                vertical1.Range.Font.Name = "Calibri";
                vertical1.Height = 200;
                vertical1.Range.Bold = 12;
                vertical1.Range.Text = "";
                vertical1.Range.Orientation = 
                  Microsoft.Office.Interop.Word.WdTextOrientation.wdTextOrientationUpward;
                vertical1.VerticalAlignment = 
                  Microsoft.Office.Interop.Word.WdCellVerticalAlignment.wdCellAlignVerticalCenter;
                vertical1.Range.Shading.BackgroundPatternColor = 
                  Microsoft.Office.Interop.Word.WdColor.wdColorBrightGreen;
                vertical1.Range.FitTextWidth = 0;
                vertical1.Range.Font.Color = Microsoft.Office.Interop.Word.WdColor.wdColorWhite;
                area1.Range.Borders.OutsideLineStyle = 
                       Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleThinThickLargeGap;
                cellHorizontal1.Range.Borders.OutsideLineStyle = 
                       Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleThinThickLargeGap;
                cellHorizontal1.Range.Borders[
                       Microsoft.Office.Interop.Word.WdBorderType.wdBorderTop].Color = 
                       Microsoft.Office.Interop.Word.WdColor.wdColorGray05;
                //...
                vertical1.Borders[Microsoft.Office.Interop.Word.WdBorderType.wdBorderBottom].Color = 
                        Microsoft.Office.Interop.Word.WdColor.wdColorGray05;
                Outlook.AppointmentItem app = inspectors[1].CurrentItem as Outlook.AppointmentItem;
                ((Outlook.ItemEvents_10_Event)app).Close += 
                     new ItemEvents_10_CloseEventHandler(ThisAddIn_Close);
                ((Outlook.ItemEvents_10_Event)app).Send += 
                     new ItemEvents_10_SendEventHandler(ThisAddIn_Send);
            }
        }
        catch (System.Exception ex)
        {
            Outlook.AppointmentItem outlookAppointmentItem = 
                    (Outlook.AppointmentItem)inspectors[2].CurrentItem;
            outlookAppointmentItem.Close(OlInspectorClose.olDiscard);
            outlookAppointmentItem.Delete();
        }
    }
}
关闭和发送事件处理程序在用户交互式发送或关闭此项时提供了一些特殊的行为。在我关闭和发送此项时,我使用了许多控制代码。在关闭事件中,我通过算法识别了用户按下了哪个按钮,因为 Close 事件在用户在用户界面中更改的每种情况下都有效。
public void ThisAddIn_Send(ref bool Cancel)
{
    if (!Cancel && IsLiveMeeting((inspectors[1].CurrentItem as 
         Outlook.AppointmentItem).RequiredAttendees, 
         (inspectors[1].CurrentItem as Outlook.AppointmentItem).Location))
    {
        WarningLiveMeeting wlm = new WarningLiveMeeting();
        wlm.StartPosition = FormStartPosition.CenterScreen;
        if (wlm.ShowDialog() == DialogResult.OK)
        {
            if (wlm._sellection)
            {
                Cancel = true;
                return;
            }
        }
    }
    (((inspectors[1].WordEditor as Microsoft.Office.Interop.Word.DocumentClass)
      ).Content as Outlook.MailItem).BodyFormat = OlBodyFormat.olFormatUnspecified;
}
报表和功能
我们可以以某种方式获得定期的统计报表。我们也可以从 Exchange Server 获取一些报表。我将向您展示一些通过此场景获得的个人报表。GetMonthlyReport 函数获取默认的 MAPI 文件夹,以确定用户在上个月释放了多少会议,花了多少时间。
public List<Outlook.AppointmentItem> GetMonthlyReport()
{
    counter = 0;
    Outlook.MAPIFolder mpiFolder = 
       Application.GetNamespace("MAPI").GetDefaultFolder(
       OlDefaultFolders.olFolderCalendar);
    var appItems = new List<Outlook.AppointmentItem>();
           
    foreach (object obj in mpiFolder.Items)
    {
        counter++;
        AppointmentItem appointment = obj as AppointmentItem;
        if (appointment != null)
        {
            if (appointment.Start.Month == DateTime.Now.Month -1)
            {
                if (appointment.RequiredAttendees == null || 
                    appointment.RequiredAttendees.Equals(string.Empty))
                {
                    _meetingCountOwner++;
                }
                if (appointment.MeetingStatus == OlMeetingStatus.olMeeting ||
                    appointment.MeetingStatus == OlMeetingStatus.olMeetingReceived ||
                    appointment.RequiredAttendees == null ||
                    appointment.RequiredAttendees.Equals(string.Empty))
                {
                    _meetingCount++;
                    _totalMeetingTime += appointment.End - appointment.Start;
                }
                if (appointment.Subject.Contains("Live Meeting") ||
                    appointment.Subject.Contains("LiveMeeting") ||
                    appointment.Subject.Contains("livemeeting") ||
                    appointment.Subject.Contains("live meeting"))
                      _livmeetingCount++;
            }
        }
    }
    return appItems;
}
这种情况也正在寻找他的实时会议和已接受的会议。我们也可以在其他文件夹中使用此例程,检查它们周期性的背景。例如,如下获取特色的约会、任务和邮件。
public List<Outlook.AppointmentItem> GetFeaturedAppointments()
{
    Outlook.MAPIFolder mpiFolder = 
      Application.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
    var appItems = new List<Outlook.AppointmentItem>();
    foreach (object obj in mpiFolder.Items)
    {
        AppointmentItem appointment = obj as AppointmentItem;
        if (appointment != null)
        {
            if (appointment.Start > DateTime.Now - new TimeSpan(0, 30, 0))
                appItems.Add(appointment);
        }
    }
    return appItems;
}
public List<Outlook.TaskItem> GetFeaturedTasks()
{
    Outlook.MAPIFolder mpiFolder = 
       Application.GetNamespace("MAPI").GetDefaultFolder(
       OlDefaultFolders.olFolderTasks);
    var taskItems = new List<Outlook.TaskItem>();
    foreach (object obj in mpiFolder.Items)
    {
        TaskItem task = obj as TaskItem; 
        if (task != null)
        {
            taskItems.Add(task);
        }
    }
    return taskItems;
}
public List<Outlook.MailItem> GetFeaturedMails()
{
    Outlook.MAPIFolder mpiFolder = 
      Application.GetNamespace("MAPI").GetDefaultFolder(
      OlDefaultFolders.olFolderSentMail);
    var mailItems = new List<Outlook.MailItem>();
    foreach (object obj in mpiFolder.Items)
    {
        MailItem mail = obj as MailItem;
        if (mail != null)
        {
            mailItems.Add(mail);
        }
    }
    return mailItems;
}
版本检测
操作系统检测超出了本文的范围,但我想向您展示如何检测此插件的版本,以及如何根据其他操作系统和 office 的其他版本来编写插件。我将在下面展示操作系统检测例程。
private string FindVersion()
{
    System.OperatingSystem osInfo = System.Environment.OSVersion;
    string operatingSystem = "Unknown";
    switch (osInfo.Platform)
    {
        case System.PlatformID.Win32Windows:
            // Current OS is Windows - can be Windows95, 98 or ME
            switch (osInfo.Version.Minor)
            {
                case 0:
                    operatingSystem = "Windows 95";
                    break;
                case 10:
                    operatingSystem = "Windows 98";
                    break;
                case 90:
                    operatingSystem = "Windows Me";
                    break;
            }
            break;
         case System.PlatformID.Win32NT:
            // Current OS is Windows NT, 2000 or XP
            switch (osInfo.Version.Major)
            {
                case 3:
                    operatingSystem = "Windows NT 3.51";
                    break;
                case 4:
                    operatingSystem = "Windows NT 4.0";
                    break;
                case 5:
                    if (osInfo.Version.Minor == 0)
                        operatingSystem = "Windows 2000";
                    else
                        operatingSystem = "XP";
                    break;
                case 6:
                    operatingSystem = "Win7";
                    break;
            }
            break;
    }
    return operatingSystem;
}
当我们获得了系统知识后,我们就可以根据其属性编写任何机会。无论如何,这里真正重要的点是我准备好的版本检测,如下所示。
string version = ((Microsoft.Office.Interop.Outlook.ApplicationClass)(
  ((Microsoft.Office.Interop.Outlook.InspectorClass)(_inspector)).Application)).Version;
int preVer = Convert.ToInt32(version.Substring(0, 2));
当 preVer 指示 12 时,office 版本为 2007;当 preVer 指示 14 时,office 版本为 2007。
我从这项工作中了解到,Microsoft Office 2007 SP1 对 Office 2007 对象模型的关闭和保存事件支持进行了修复。即使 Office 2010 还没有提供此功能。
视图
视图提供 Outlook 中的自定义屏幕和操作管理。用户可以从工具菜单进行设置,但我想用 C# 以编程方式准备此功能。
private void CreateCustomAksiyonViewIfNotExist()
{
    Application.ActiveExplorer().CurrentFolder.CustomViewsOnly = true;
    Outlook.View lastView = Application.ActiveExplorer().CurrentFolder.CurrentView;
    Outlook.View newView = null;
    try
    {
        bool isthere = false;
        Application.ActiveExplorer().NavigationPane.CurrentModule = 
             Application.ActiveExplorer().NavigationPane.Modules[4];
        foreach (Outlook.View view in Application.ActiveExplorer().CurrentFolder.Views)
        {
            if (view.Name.Equals("Actions"))
                isthere = true;
        }
        if (!isthere)
        {
            newView = Application.ActiveExplorer().CurrentFolder.Views.Add("Actions",
            Outlook.OlViewType.olTableView,
            Outlook.OlViewSaveOption.olViewSaveOptionThisFolderEveryone);
            newView.Filter = "\"urn:schemas:httpmail:subject\" LIKE '% Actions %'";
            newView.Save();
            newView.Apply();
        }
    }
    catch (System.ArgumentException ex)
    { 
        MessageBox.Show(ex.Message);
        return;
    }
    Application.ActiveExplorer().NavigationPane.CurrentModule = 
                Application.ActiveExplorer().NavigationPane.Modules[1];
}
规则
规则看起来像用户自定义的视图,但规则仅依赖于操作和在执行操作时产生的内容。
void CreateActionRule()
{
    Rules ruless = this.Application.Session.DefaultStore.GetRules();
    for (int k = 1; k < ruless.Count; k++)
    {
        if (ruless[k].Name.Equals("ActionAndDecissionRule"))
            return;
    }
    Outlook.MAPIFolder inbox = this.Application.Session.GetDefaultFolder(
            Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
    inbox.Folders.Add("Aksiyon&Kararlar", 
       Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
    Outlook.MAPIFolder targetfolder = inbox.Folders["Aksiyon&Kararlar"];
    Rules rules = this.Application.Session.DefaultStore.GetRules();
    Outlook.Rule rule = rules.Create("ActionAndDecissionRule",
    Microsoft.Office.Interop.Outlook.OlRuleType.olRuleReceive);
    Outlook.TextRuleCondition sub = rule.Conditions.Body;
    sub.Enabled = true;
    sub.Text = new string[] { "ToplantisiKararlari-iyitoplantiKontrol" };
    Outlook.MoveOrCopyRuleAction movecopy = rule.Actions.MoveToFolder;
    movecopy.Enabled = true; 
    movecopy.Folder = targetfolder; 
    rule.Execute(true, inbox, false,
    Outlook.OlRuleExecuteOption.olRuleExecuteUnreadMessages);
    rules.Save(false);
}
结论
总而言之,我研究了许多文档,但没有找到关于这个主题的足够信息,也无法在该领域获得免费书籍。我确信这些信息对任何想编写插件的人都会有用。
附录
- WPF 到插件:http://msdn.microsoft.com/en-us/library/bb410039.aspx
- 插件区域表单:http://www.csharpnedir.com/articles/read/?id=945
- 提醒:http://ru2nuts.livejournal.com/77689.html
- 报表:http://blogs.msdn.com/coding4fun/archive/2006/11/20/1111248.aspx
- 报表 2:http://msdn.microsoft.com/en-us/library/dd492013.aspx
- 附件:http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/186ddb96-8780-4f92-8f0f-ab6698985220
- 属性访问器:http://us.generation-nt.com/get-formatted-text-appointmentitem-body-help-75158652.html
- InstallShield 自定义操作:http://geekswithblogs.net/Gaurav/archive/2006/05/30/80082.aspx
- Clickone 安装:http://msdn.microsoft.com/en-us/library/bb821233.aspx
- 创建自定义视图:http://msdn.microsoft.com/en-us/library/aa155658(office.10).aspx
- 自定义视图类型:http://msdn.microsoft.com/en-us/library/aa204771(office.11).aspx
- Outlook 2007 重要信息:http://msdn.microsoft.com/en-us/library/bb226714(office.12).aspx
- Outlook 文件夹和 Exchange 连接:http://www.c-sharpcorner.com/UploadFile/rambab/OutlookIntegration10282006032802AM/OutlookIntegration.aspx
- Exchange API 用法:http://msdn.microsoft.com/en-us/library/dd637749(EXCHG.80).aspx
- 规则:http://msdn.microsoft.com/en-us/library/bb226714(office.12).aspx

