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=]
在适用于 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=]