C#VSTO加载项任务序列

时间:2017-07-29 12:09:47

标签: c# outlook vsto

在Outlook的VSTO加载项中,单击按钮可触发2种方法:第一种方法在MailItem Object上执行简单操作并快速运行,第二种方法执行需要更多计算时间的其他几项任务。我希望第二个在"背景"以便MailItem Object上的操作能够快速响应。现在我无法弄清楚如何做到这一点,只有在完成2个方法后,才能在Outlook中看到MailItem Object上的操作效果。

public void ButtonAction(Office.IRibbonControl control)
{
    bool processed = ActionsOnMailItem();
    string output = OtherTasks(processed);
}

public static bool ActionsOnMailItem()
{
    Outlook.Selection selected = olApplication.ActiveExplorer().Selection;
    bool isEmailProcessed = false;
    try
    {
        foreach (Outlook.MailItem mailItem in selected)
        {
            mailItem.SaveAs(saveItemPath, Outlook.OlSaveAsType.olMSG);
        }
        isEmailProcessed = true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
        isEmailProcessed = false;
    }
    return isEmailProcessed;
}

public static string OtherTasks(bool isEmailProcessed)
{
    if (isEmailProcessed)
    {
        // Perform several tasks requiring computing time
        ...
    }
}

我已尝试使用async方法,但没有成功(没有死锁,但第一种方法也没有快速响应)。在我深入研究之前,我想知道这是否是正确的道路,或者是否有更简单的方法来做到这一点。

2 个答案:

答案 0 :(得分:3)

从Outlook 2016开始,只要Outlook在主Outlook线程以外的线程上检测到访问权限,就会引发异常。这实际上只适用于COM插件,因为在进程外(外部)应用程序中,无论如何所有调用都将被编组到主Outlook线程中,因此应用程序中的多线程变得没有实际意义。

您可以在辅助线程上使用扩展MAPI(C ++或Delphi) - 读取主线程上的Namespace.MAPIOBJECT属性(它返回IMAPISession对象)并将其存储在变量中。在辅助线程上,调用MAPIInitialize并使用主线程中的IMAPISesion - 与OOM对象不同,它可以在多个线程中使用。对于C ++或Delphi以外的语言,您可以使用Redemption及其RDO对象系列 - 它是扩展MAPI的包装器,可以在辅助线程上使用。在辅助线程上创建RDOSession的实例,并将其MAPIOBJECT属性设置为从Namespace.MAPIOBJECT在主线程上检索的值。

答案 1 :(得分:1)

首先,你必须要知道从后台线程访问COM对象涉及编组,简而言之,这需要时间。 Further reading...

对于您的任务,您需要开发使用BackgroundWorker类的解决方案。 BackgroundWorker有两个适用于MainThread的事件:

  • ProgressChanged
  • RunWorkerCompleted

您的OtherTasks方法应使用其中一个来处理后台任务的结果。

对于VSTO,在使用BackgroundWorker类之前使用WindowsFormsSynchronizationContext也很重要 例如:

// Forces BackgroundWorker to run ProgressChanged and RunWorkerCompleted events on VSTA_Main thread. It's important,
// because this thread manages Word COM objects. 
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;

worker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
    // do stuff not using COM object
    worker.ReportProgress(0);
};
worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
{
    // do stuff on main thread (on COM objects)
};
worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{

};

worker.RunWorkerAsync();