在线程中调用Excel函数时CastError?

我有一个Excel插件类有这个function:

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"; } 

在function区中,我添加了一个button,当按下它时,会在一个线程中调用testRibbon:

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

这会导致一个转换错误:

无法将“System .__ ComObject”types的COM对象转换为接口types“Microsoft.Office.Interop.Excel._Workbook”。 此操作失败,因为IIC“{000208DA-0000-0000-C000-000000000046}”接口的COM组件上的QueryInterface调用失败,原因如下错误:加载types库/ DLL时出错。 (来自HRESULT的exception:0x80029C4A(TYPE_E_CANTLOADLIBRARY))。

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

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

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

在Excel中使用多个线程是没有意义的,因为Excel的COM接口基本上是单线程的(参见本文 )。 插件中可能会有进一步的限制。

然而,我不得不说,我不明白错误信息是怎么说的。

我认为目前,你正在通过COMObject连接到接口types“工作簿”…它会自动创build一个新的非托pipe线程,当你启动()时,你的线程会崩溃。 具体来说,当你创build线程对象的时候,它会把一些数据结构保存到共享内存中,并且不使用Start(),所以此时线程没有执行

当您使用工作线程的Excel接口方法时,方法调用需要编组 。 换句话说,调用需要从创buildApplication对象的线程中进行。 在.NET GUI应用程序中使用Control.Invoke()或Dispatcher.Invoke()的想法。

为了做到这一点,COM需要知道这个方法的参数是什么,所以它可以正确地复制它们。 这是需要在.NET中等效于Reflection的那种信息。 这需要元数据,描述COM方法的元数据存储在types库中。 Excel的types库作为资源存储在Excel.exe中。

findtypes库是什么问题在这里。 此信息存储在registry中,出于某种原因在您的计算机上已损坏。 受到HKLM\SOFTWARE\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}\1.7\0\win32的最可能的关键是HKLM\SOFTWARE\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}\1.7\0\win32尽pipe它取决于Office的确切版本。 您可以从SysInternals的ProcMon实用程序中获得更多的信息,您将看到您的程序正在search关键字。

这种事故很less局限于一个关键。 您需要再次使机器健康并重新安装Office。

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