在不同的线程中使用Excel COM对象

时间:2018-07-13 16:11:55

标签: c# multithreading com

我读了很多东西,我想我了解发生了什么,但我不知道如何解决。

我有一个c#wpf应用程序。用户单击一个按钮,然后我启动另一个线程来做很多工作。其中一部分是打开excel,创建一个excel文件,然后对excel工作表进行很多设置。

完成该部分后,用户将使用该应用程序,并且有一些按钮可以进一步与excel文件进​​行交互,例如添加更多内容。

我的问题是当线程完成时,我将该COM对象丢失到excel文件中。不知道如何解决此问题。 这是我的代码(后来绑定到excel)

..我在excel对象上包含一个公共静态类

public static class MyGlobals
{
    public static string ExcelprogID = "Excel.Application";
    public static dynamic xlWorkBook;
    public static dynamic xlApp;  
}

..然后用户单击一个按钮,然后我启动另一个线程

private void btnSearch_Click(object sender, RoutedEventArgs e)
{
    string dialogValues = "...";//replaced code for simplicity
    Thread t = new Thread(() => workerthread(dialogValues));
    t.SetApartmentState(ApartmentState.STA);
    t.Start();
}

..然后在新线程中我做了很多,但是其中一件事是创建excel对象并设置excel表格。

private void workerthread(string e)
{
    MyGlobals.xlApp = Activator.CreateInstance(Type.GetTypeFromProgID(MyGlobals.ExcelprogID));
    MyGlobals.xlWorkBook = MyGlobals.xlApp.Workbooks.Add(MyGlobals.misValue);
    MyGlobals.xlApp.Visible = true; 
    //more code here to do stuff with excel.
}

...这是我的问题发生的地方。现在返回主UI线程,用户可以单击按钮来操纵excel文件中的内容。但是我得到这个错误

private void btnPlaceBM_Click(object sender, RoutedEventArgs e)
{
    //Excel.Worksheet BM;
    dynamic BM;
    try
    {
        BM = MyGlobals.xlWorkBook.Sheets["CADD Basemaps"];
    }
    catch
    {
        MessageBox.Show("Project Index Excel file was closed! Can not place any data to excel file!");
        return;
    }
}

这是我得到的错误

  

System.Runtime.InteropServices.InvalidComObjectException被捕获:“无法使用与其基础RCW分离的COM对象。”

有人可以给我一些帮助吗..我能以某种方式找回那个excel对象吗?

1 个答案:

答案 0 :(得分:0)

好的评论让我开始思考。我试图查看如何在线程结束之前从工作线程获取COM对象。更改代码太困难了,因此在启动工作线程之前,UI线程会创建COM对象,因为在工作线程中途才能创建COM对象。然后它就撞到了我。当我到达创建COM对象的工作线程的那一部分时,就将其分派到UI线程。现在,这可能不是最佳解决方案,但它可以工作。 这是如何在辅助线程中执行此操作的代码段。

private void workerthread(string e)
{
    //code removed for simplicity..

    //Create COM object for excel app & excel workbook in Main thread to prevent object reference loss
    App.Current.Dispatcher.Invoke((Action)delegate
    {
        MyGlobals.xlApp = Activator.CreateInstance(Type.GetTypeFromProgID(MyGlobals.ExcelprogID));
        MyGlobals.xlWorkBook = MyGlobals.xlApp.Workbooks.Add(MyGlobals.misValue);
    });
    MyGlobals.xlApp.Visible = true; 
    //more code here to do stuff with excel.
}