我有一个具有此功能的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();
});
答案 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。