C# VSTO 加载项任务序列

C# VSTO add-in task sequence

在适用于 Outlook 的 VSTO 加载项中,单击按钮会触发 2 种方法:第一种方法在 MailItem Object 和 运行 秒内快速执行简单操作,第二种方法执行其他几个需要更多计算时间的任务。我想在 "background" 中将第二个发送到 运行,以便 MailItem Object 上的操作快速响应。现在我不知道该怎么做,只有在这两种方法完成后,MailItem Object 上的操作效果才会在 Outlook 中可见。

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 方法但没有成功(没有死锁,但第一种方法也没有快速响应)。在我深入研究之前,我想知道这是否是正确的路径,或者是否有更直接的方法来做到这一点。

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

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

  • 进度已更改
  • RunWorkerCompleted

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

对于 VSTO,在使用 BackgroundWorker class 之前使用 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();

从 Outlook 2016 开始,只要 Outlook 检测到对 Outlook 主线程以外的线程的访问,就会引发异常。这仅适用于 COM 插件,因为在进程外(外部)应用程序中,所有调用无论如何都将编组到主 Outlook 线程,因此应用程序中的多线程成为一个有争议的问题。

您可以在辅助线程上使用扩展 MAPI(C++ 或 Delphi)- 阅读主线程上的 Namespace.MAPIOBJECT 属性(它 returns IMAPISession 对象)并将其存储在变量中。在辅助线程上,调用 MAPIInitialize 并从主线程使用 IMAPISesion - 与 OOM 对象不同,它可以从多个线程使用。对于 C++ 或 Delphi 以外的语言,您可以在辅助线程上使用 Redemption (I am its author) and its RDO family of objects - it is a wrapper around Extended MAPI and can be used on secondary threads. Create an instance of the RDOSession 并将其 MAPIOBJECT 属性 设置为在主线程上从 Namespace.MAPIOBJECT.[= 检索到的值16=]