NUnit对 VSTO加载项的COM对象进行测试:HRESULT: 0x80004002
我正在为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
}
}