在线程中调用互操作Excel函数时的CastError?

时间:2012-08-10 20:14:10

标签: c# excel excel-interop

我有一个具有此功能的excel插件类:

public void testRibbon()
{
     Excel.Workbook workbook = this.Application.ActiveWorkbook;      
     Excel.Worksheet activeSheet = workbook.Sheets[1];
     Excel.Range range = activeSheet.get_Range(JOB_ID_FIELD + HEADER_ROW, TOTAL_STATUS_PERCENTAGE_KEY_FIELD + (10 + 1).ToString());
     range.get_Range("C4").Value = "test Ribbon complete";
}

在功能区中我添加了一个按钮,按下该按钮将在一个线程中调用testRibbon:

private void process_Click(object sender, RibbonControlEventArgs e)
{
    Thread processThread = new Thread(delegate(){
        Globals.ExcelAddin.testRibbon();
    });
    processThread.Start();
} 

这会导致施法错误:

  

无法将“System .__ ComObject”类型的COM对象强制转换为接口   输入“Microsoft.Office.Interop.Excel._Workbook”。此操作失败   因为QueryInterface调用COM组件的接口   因为IID'{000208DA-0000-0000-C000-000000000046}'失败了   以下错误:加载类型库/ DLL时出错。 (例外   HRESULT:0x80029C4A(TYPE_E_CANTLOADLIBRARY))。

如果我不使用新线程,则不会发生强制转换错误。

编辑:尝试使用任务工厂,同样的错误:

var task2 = Task.Factory.StartNew(
() =>
{
     Globals.BobStats.testRibbon();
});

3 个答案:

答案 0 :(得分:2)

在Excel中使用多个线程没有任何意义,因为Excel的COM接口基本上是单线程的(请参阅此article)。加载项中可能存在进一步的限制。

但我不得不说我真的不明白错误信息试图说的是什么。

答案 1 :(得分:1)

我认为目前,您正在通过COMObject连接到接口类型'Workbook'...它将自动创建一个新的非托管线程,并且当您启动()时将与您的线程崩溃。具体来说,当你创建线程对象时,它会将一些数据结构保存到共享内存中,并且没有使用Start(),因此此时线程没有执行

答案 2 :(得分:1)

当您使用工作线程中的Excel接口方法时,方法调用需要封送。换句话说,需要从创建Application对象的线程进行调用。与在.NET gui应用程序中使用Control.Invoke()或Dispatcher.Invoke()相同的想法。

为了完成这项工作,COM需要知道该方法的参数是什么,以便它可以正确地复制它们。这种信息需要相当于.NET中的Reflection。这需要元数据,描述COM方法的元数据存储在类型库中。 Excel的类型库作为资源存储在Excel.exe中。

找到类型库是这里出了什么问题。此信息存储在注册表中,并且由于某种原因它在您的计算机上已损坏。受到重击的最可能的关键是HKLM\SOFTWARE\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}\1.7\0\win32,尽管它取决于Office的确切版本。您可以从SysInternals的ProcMon实用程序获得更多信息,您将看到您的程序正在搜索密钥。

这种事故很少仅限于一把钥匙。您需要重新恢复计算机并重新安装Office。

哦,请记住,代码实际上并不在工作线程上运行。这需要在该工作线程上创建Application对象,并在启动之前调用SetApartmentState()使其成为STA。