C#VSTO添加到任务序列中

C#VSTO添加到任务序列中,c#,outlook,vsto,C#,Outlook,Vsto,在VSTO Outlook外接程序中,单击按钮可触发两种方法:第一种方法对MailItem对象执行简单操作并快速运行,第二种方法执行其他需要更多计算时间的任务。我希望第二个在“后台”运行,以便MailItem对象上的操作能够快速响应。现在我还不知道该怎么做,只有在完成这两种方法后,才能在Outlook中看到对MailItem对象的操作的效果 public void ButtonAction(Office.IRibbonControl control) { bool processed =

在VSTO Outlook外接程序中,单击按钮可触发两种方法:第一种方法对
MailItem对象执行简单操作并快速运行,第二种方法执行其他需要更多计算时间的任务。我希望第二个在“后台”运行,以便
MailItem对象上的操作能够快速响应。现在我还不知道该怎么做,只有在完成这两种方法后,才能在Outlook中看到对
MailItem对象的操作的效果

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对象涉及封送处理,简而言之,这需要时间

对于您的任务,您需要开发使用
BackgroundWorker
类的解决方案。有两个在主线程上工作的事件,它们是:

  • 进展改变
  • RunWorkerCompleted
您的
OtherTasks
方法应该使用其中一个来处理后台任务的结果

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

//强制BackgroundWorker在VSTA_主线程上运行ProgressChanged和RunWorkerCompleted事件。这很重要,
//因为此线程管理Word COM对象。
System.Threading.SynchronizationContext.SetSynchronizationContext(新WindowsFormsSynchronizationContext());
BackgroundWorker工人=新的BackgroundWorker();
worker.WorkerReportsProgress=true;
worker.DoWork+=委托(对象发送方,DoWorkEventArgs e)
{
//不要使用COM对象
工人进度报告(0);
};
worker.ProgressChanged+=委托(对象发送方,ProgressChangedEventArgs e)
{
//在主线程上执行任务(在COM对象上)
};
worker.RunWorkerCompleted+=委托(对象发送方,RunWorkerCompletedEventArgs e)
{
};
worker.RunWorkerAsync();

自Outlook 2016起,Outlook检测到除Outlook主线程外的其他线程的访问权限时,将立即引发异常。这实际上只适用于COM加载项,因为在进程外(外部)应用程序中,所有调用都将被封送到Outlook主线程,因此应用程序中的多线程处理变得毫无意义


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

检索到的值。完全按照预期尝试从VSTO addinWorks中的不同线程访问COM对象可能不是一个好主意!我刚刚用Outlook 2016尝试了tinamou的解决方案(使用
BackgroundWorker
类),没有出现任何异常。也许我必须明确指出,在后台调用的方法(
OtherTasks
在我的问题中)没有使用
Globals.ThisAddIn.Application
来访问
MailItem对象
,而是从“外部”应用程序调用的
win32com.client.Dispatch(“Outlook.Application”).GetNamespace(“MAPI”)
但这是外部应用程序使用的Python,而不是COM插件,对吗?是的:COM插件调用第一个方法直接访问
MailItem对象
,在后台调用第二个方法,该方法使用
win32com.client.Dispatch(“Outlook.Application”).GetNamespace运行Python脚本的可执行版本‌​(“MAPI”)
访问
MailItem对象
。来自该可执行文件的调用将由COM系统封送到Outlook主线程。