WPF退出时出现COM异常

13
在执行以下两个测试用例后,控制台打印了一个COM执行。我做错了什么?
如果我只运行任意一个测试用例或同时运行这两个测试用例,异常信息会被写入控制台一次。这让我怀疑有一些AppDomain级别的资源我没有清理干净。
我已经尝试了NUnit和MSTest,两个环境中表现一致。(实际上,我不确定在MSTest中同时运行这两个测试用例是否会导致单个异常信息还是两个异常信息。)
异常信息:
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
at System.Windows.Input.TextServicesContext.StopTransitoryExtension()
at System.Windows.Input.TextServicesContext.Uninitialize(Boolean appDomainShutdown)
at System.Windows.Input.TextServicesContext.TextServicesContextShutDownListener.OnShutDown(Object target)
at MS.Internal.ShutDownListener.HandleShutDown(Object sender, EventArgs e)

测试代码:

using NUnit.Framework;

namespace TaskdockSidebarTests.Client
{
    [TestFixture, RequiresSTA]
    public class ElementHostRCWError
    {
        [Test]
        public void WinForms()
        {
            var form = new System.Windows.Forms.Form();
            var elementHost = new System.Windows.Forms.Integration.ElementHost();
            form.Controls.Add(elementHost);

            // If the form is not shown, the exception is not printed.
            form.Show();

            // These lines are optional. The exception is printed with or without
            form.Close();
            form.Controls.Remove(elementHost);
            elementHost.Dispose();
            form.Dispose();
        }

        [Test]
        public void WPF()
        {
            var window = new Window();

            // If the window is not shown, the exception is not printed.
            window.Show();

            window.Close();
        }
    }
}

也许这个链接可以帮助你:http://social.msdn.microsoft.com/forums/en-US/vststest/thread/e53fdc45-23f3-4aee-aad9-f63769f2c638/。 - parapura rajkumar
不幸的是,由于WPF要求STA,我无法使用MTA。在SetUp中创建表单和元素主机似乎也行不通。唉。 - Patrick Linskey
如果我没记错的话,这个异常不会导致单元测试失败,对吧?在测试我的WPF控件时,我也遇到了同样的异常,但我选择忽略它.. ;) - Bubblewrap
它不会导致失败,但当你有几十个UI集成测试时,它会产生大量的日志输出。 - Patrick Linskey
2个回答

20

再次查看我的代码,下面这行可能有助于WPF测试,就在最后。

Dispatcher.CurrentDispatcher.InvokeShutdown();

太棒了!成功了。谢谢!现在我只需要想办法将其融入我的测试架构中。 - Patrick Linskey
令人失望的是,一旦一个调度程序绑定到一个线程上(即 Dispatcher.CurrentDispatcher),就不能再将另一个调度程序与该线程关联。一旦调度程序被关闭,就无法重新启动它。因此,虽然这解决了我的问题,但我遗憾地不能在基本测试类的TearDown方法中直接调用InvokeShutdown()。 - Patrick Linskey
尝试在每个单元测试中启动一个新的STA线程,在该新线程中执行测试,并使用Thread.Join()等待该线程完成。 - Bubblewrap
1
我试图避免那种侵入性。不过,[RequiresThread] 正好做到了这一点。我已经在类上(在夹具级别)使用 [RequiresThread] 并在 [TestFixtureTearDown] 中调用 InvokeShutdown() 使其正常工作。 - Patrick Linskey
1
非常感谢你的那一个代码行,它节省了我很多时间。我已经至少花费了3个小时调试异常来源和原因。它让我终于能够扩展xUnit+Unity清理过程以正确地处理AppDomain!不幸的是,似乎PresentationFramework代码中确实缺少try-catch。 - quetzalcoatl

1

你可能无法对WindowForm类进行单元测试。WinForms应用程序和WPF应用程序都有一个Application类,用于启动底层管道(消息泵等)。我敢打赌这是避免异常的关键。

你没有在那里这样做,也许不能这样做。

我曾经读过的每一篇单元测试建议都是重构,使得Form类和Window类不执行任何需要进行单元测试的操作(例如WPF中的M-V-VM模式)。这可能与无法显示UI有关。

还有其他测试UI的方法。此答案讨论了单元测试UI。


3
实际上,测试运行良好——只是我的日志文件里有很多垃圾信息。关于UI测试和纯业务逻辑测试的比较——我的观点是,我越接近测试用户实际接触的东西,晚上睡得就越安心。 - Patrick Linskey
我认为Joel和Patrick都是正确的,所以两位都加一分。虽然我同意Joel的设计建议,但有时您必须自动化一些控件/窗口,因为某些旧代码需要它们,尤其是这些代码脆弱而不属于您的责任范畴。 - quetzalcoatl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接