Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

COM exceptions on exit with WPF

Tags:

c#

wpf

com

rcw

After execution both of the following test cases, a COM execution is printed to the console. What am I doing wrong?

If I run either test singly, or if I run both tests together, the exception is written to the console exactly once. This makes me suspect that there's some sort of a per-AppDomain resource that I'm not cleaning up.

I have tried the tests with NUnit and with MSTest, with the same behavior in both environments. (Actually, I'm not sure if running both tests in MSTest results in a single exception printout or two.)

Exception:

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)

Test code:

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();
        }
    }
}
like image 558
Patrick Linskey Avatar asked Jun 03 '11 21:06

Patrick Linskey


2 Answers

Looking at my own code again, the following line might help for the WPF test, right at the end.

Dispatcher.CurrentDispatcher.InvokeShutdown();
like image 166
Bubblewrap Avatar answered Oct 23 '22 02:10

Bubblewrap


You probably can't unit test the Window and Form classes at all. Both WinForms applications and WPF applications have an Application class used to start the underlying plumbing (message pumps and whatnot). I bet it's the key to avoiding that exception.

You're not doing that there and may not be able to.

Every recommendation for unit testing I've ever read is that you refactor so that the Form classes and Window classes don't do anything you need to unit test (like the M-V-VM pattern in WPF). Could have something to do with not being able to show the UI.

There are other ways to test a UI. This answer discusses unit testing UI.

like image 24
Joel B Fant Avatar answered Oct 23 '22 02:10

Joel B Fant