NUnit对 VSTO加载项的COM对象进行测试:HRESULT: 0x80004002

编程语言 2026-07-09

我正在为Excel构建一个加载项,并一直在用NUnit进行测试。这在测试通过COM与 Excel交互的功能时出现了问题。来自Microsoft.Office.Interop/Core对象的必要调用导致 InvalidCastException HRESULT: 0x80004002。据我所知,这通常源于并发问题,需要在STA线程中运行COM调用。我在尽我所知的一切努力把所有操作都放在同一个线程里,但仍然会抛出。

我的策略是创建一个用于测试的Excel应用程序实例,并用它来测试我的代码

using NUnit.Framework;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Excel;

[TestFixture]
[SingleThreaded]
internal class MyTest
{
    Application _app;
    Workbook _wb;
    SomeTypedCOMObject _comThingy;

    [SetUp]
    [STAThread]
    public void SetUp()
    {
        _app = new Application
        {
            Visible = false,
            DisplayAlters = false,
        };
        _wb = _app.Workbooks.Add();
        var obj = _wb.SomeProblematicCall // returns System.__ComObject
        _comThingy = obj as SomeTypedCOMObject; // works fine
    }
}

当我尝试在主项目中对同一个COM对象进行转换时,问题就出现了。

public class MyMainProjectClass
{
    public SomTypedCOMObject ComThingy { get; set; }

    public void MyMainProjectClass( Workbook wb )
    {
        ComThingy = wb.SomeProblematicCall // throws HRESULT 0x80004002
    }
}

我找到了 这个问题 里介绍的让NUnit在单线程中运行的方法。我已经尝试了它所提到的所有建议

[assembly: Apartment(ApartmentState.STA)]
[assembly: LevelOfParallelism(0)]
[assembly: NonParallelizable]

我是在Visual Studio的 Test Explorer中运行,而不是命令行,因此无法使用它所使用的 --Agents=1 参数。尽管我确实尝试通过一个.runsettings文件来实现。

<RunSettings>
    <NUnit>
        <NumberOfTestWorkers>1</NumberOfTestWorkers>
    </NUnit>
</RunSettings>

没有运气。我觉得这里还有别的原因在起作用,是我没理解清楚的。当抛出异常时,调用 Thread.CurrentThread.ApartmentState 返回 STA。所以看起来似乎不是并发问题。如果有关系,导致问题的类型是 Microsoft.Office.Core.DocumentProperties,调用是 Microsoft.Office.Interop.Workbook.CustomDocumentProperties。我正在使用的NuGet包是:

NUnit 4.5.1

我已将主项目中的相同程序集引用包含到:

Microsoft.Office.Interop.Excel 15.0.0.0

Microsoft.Office.Core 15.0.0.0

解决方案

*编辑:这并不是一个解决方案。***

我不知道为什么这会起作用,但确实如此。编辑:它“起作用”其实是因为它并没有起作用。

public MyMainProjectClass
{
    public SomeTypedCOMObject ComThingy { get; set; }
    public MyMainProjectClass( Workbook wb )
    {
        ComThingy = wb.SomeProblematicCall as SomeTypedCOMObject
    }
}

你本以为问题中的代码会隐式执行相同的转换,但实际发生的情况不同。有没有更懂行的人愿意来指点一二?

编辑:以下代码确实会抛出异常

public MyMainProjectClass
{
    public SomeTypedCOMObject ComThingy { get; set; }
    public MyMainProjectClass( Workbook wb )
    {
        ComThingy = ( SomeTypedCOMObject ) wb.SomeProblematicCall
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章