1

我有一个具有此功能的 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”。此操作失败,因为 IID 为“{000208DA-0000-0000-C000-000000000046}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:加载类型库/DLL 时出错。(来自 HRESULT 的异常:0x80029C4A (TYPE_E_CANTLOADLIBRARY))。

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

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

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

3 回答 3

2

在 Excel 中使用多线程毫无意义,因为 Excel 的 COM 接口基本上是单线程的(请参阅本文)。加载项中可能有进一步的限制。

但是我不得不说我真的不明白错误消息想说什么。

于 2012-08-10T20:47:56.350 回答
1

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

于 2012-08-10T20:30:25.777 回答
1

当您从工作线程中使用 Excel 接口方法时,需要对方法调用进行封送处理。换句话说,需要从创建应用程序对象的线程进行调用。与在 .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。

于 2012-08-10T21:42:36.690 回答