2017-07-29 24 views
1

在VSTO添加用于Outlook单击按钮火灾2种方法:第一种执行上MailItem Object简单的操作和运行快,第二个执行需要更多的计算时间等多项任务。我希望第二个在“后台”运行,以便MailItem Object上的操作能够快速响应。现在我无法弄清楚如何做到这一点,只有在完成2种方法后,MailItem Object上的操作才能在Outlook中看到。C#VSTO附加任务序列

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方法,但都没有成功(没有僵局但是对于第一种方法没有快速反应既不)尝试。在深入研究之前,我想知道这是否是正确的道路,或者是否有更直接的方法来实现。

+1

尝试从VSTO插件内的不同线程访问COM对象可能不是一个好主意 – MickyD

回答

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(); 
+1

完全按照预期工作! –

3

如Outlook 2016,一个异常会尽快Outlook检测上比其他线程访问募集主要的Outlook线程。这仅适用于COM外接程序,因为在外部应用程序(外部)应用程序中,所有调用都会被整理到主Outlook线程中,因此您的应用程序中的多线程成为一个有争议的问题。

您可以在辅助线程上使用Extended MAPI(C++或Delphi) - 读取主线程上的Namespace.MAPIOBJECT属性(它返回IMAPISession对象)并将其存储在变量中。在辅助线程,调用MAPIInitialize从主线程中使用的IMAPISesion - 不像OOM对象,它可以从多个线程中使用。对于除C++或Delphi等语言,你可以使用Redemption及其RDO家庭的对象 - 这是一个包装周围扩展MAPI,可以在二级线程使用。辅助线程上创建RDOSession的一个实例,其MAPIOBJECT属性设置为从Namespace.MAPIOBJECT主线程上检索到的值。

+0

我刚刚在Outlook 2016中使用过tinamou的解决方案(使用'BackgroundWorker'类),并且不会引发异常。也许我必须精确地说,在后台调用的方法(在我的问题中为'OtherTasks')不使用'Globals.ThisAddIn.Application'来访问'MailItem对象',而是'win32com.client.Dispatch(“Outlook.Application”)。GetNamespace (“MAPI”)从一个“外部”应用程序调用(如你所说)。 –

+0

但是,这是从外部应用程序使用Python,而不是COM插件,对不对? –

+0

是的:COM插件调用直接访问'MailItem对象'的第一个方法,第二个方法使用'win32com.client.Dispatch(“Outlook.Application”)运行可执行版本的Python脚本。GetNamespace(“ MAPI“)'来访问'MailItem对象'。 –