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