C# 可视化工具:类型为';的未处理异常;System.CannotUnloadAppDomainException';发生在mscorlib.dll中

C# 可视化工具:类型为';的未处理异常;System.CannotUnloadAppDomainException';发生在mscorlib.dll中,c#,wpf,visual-studio-2015,C#,Wpf,Visual Studio 2015,我正试图按照以下指南为Visual Studio 2015编写Visual Studio可视化工具: 但是我想使用WPF而不是WinForms 对于可视化工具,我有以下代码: using Microsoft.VisualStudio.DebuggerVisualizers; [assembly: System.Diagnostics.DebuggerVisualizer(typeof(Visualizer.DebuggerSide), typeof(VisualizerObjectSource

我正试图按照以下指南为Visual Studio 2015编写Visual Studio可视化工具:

但是我想使用WPF而不是WinForms

对于可视化工具,我有以下代码:

using Microsoft.VisualStudio.DebuggerVisualizers;
[assembly: System.Diagnostics.DebuggerVisualizer(typeof(Visualizer.DebuggerSide), typeof(VisualizerObjectSource),
    Target = typeof(string), Description = "Visualizer")]
namespace Visualizer
{
    public class DebuggerSide : DialogDebuggerVisualizer
    {
        protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            var window = new MainWindow();
            window.ShowDialog();
        }

        public static void TestShowVisualizer(object thingToVisualize)
        {
            var visualizerHost = new VisualizerDevelopmentHost(thingToVisualize, typeof(DebuggerSide));
            visualizerHost.ShowVisualizer();
        }
    }
}
然后我从控制台应用程序调用它进行如下测试:

namespace Visualizer.Debug
{
    using System;
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            var data = "test";
            DebuggerSide.TestShowVisualizer(data);
        }
    }
}
代码运行良好,窗口在
window.ShowDialog
点启动。关闭窗口后,代码从
Show
返回,并在
visualizerHost.ShowVisualizer()处抛出异常

例外情况是:

System.CannotUnloadAppDomainException未处理
HResult=-21462347 Message=卸载appdomain时出错。 (来自HRESULT:0x801315的异常)源=mscorlib StackTrace: 位于System.AppDomain.Unload(AppDomain域) 位于Microsoft.VisualStudio.DebuggerVisualizers.DebugViewersIM.ManagedShim.Microsoft.VisualStudio.DebuggerVisualizers.DebugViewersIM.IManagedViewerHost.CreateViewer(IntPtr hwnd,对象主机服务SPARAM,IPropertyProxyesIDE代理) 在Microsoft.VisualStudio.DebuggerVisualizers.VisualizerDevelopmentHost.EEProxyImpl.ShowVisualizer(IntPtr 父窗口) 在Microsoft.VisualStudio.DebuggerVisualizers.VisualizerDevelopmentHost.ShowVisualizer()上 在C:\git\Visualizer\Visualizer\Class1.cs中的Visualizer.DebuggerSide.TestShowVisualizer(对象ThingToVisualizer)中:第20行 在C:\git\Visualizer\Visualizer.Debug\Program.cs中的Visualizer.Debug.Program.Main(字符串[]args)处:第10行 位于System.AppDomain.\u nExecuteAssembly(RuntimeAssembly程序集,字符串[]args) 位于System.AppDomain.ExecuteAssembly(字符串汇编文件、证据汇编安全性、字符串[]args) 在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()上 位于System.Threading.ThreadHelper.ThreadStart\u上下文(对象状态) 位于System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback回调、对象状态、布尔值 (同步CTX) 在System.Threading.ExecutionContext.Run(ExecutionContext ExecutionContext,ContextCallback回调,对象状态,布尔值 (同步CTX) 在System.Threading.ExecutionContext.Run(ExecutionContext ExecutionContext,ContextCallback回调,对象状态) 位于System.Threading.ThreadHelper.ThreadStart()的内部异常:

考虑到错误可能是由于WPF代码试图返回Visual Studio代码,我尝试在一个完全独立的AppDomain中启动WPF窗口,但当我尝试卸载该AppDomain时,我得到了相同的错误

主窗口的代码:

namespace Visualizer
{
    using System.Windows;
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

嗯,那真是令人讨厌

我在一台支持触摸屏的计算机上,因此WPF为手写笔输入注册。当WPF窗口自行卸载并且
AppDomain
尝试卸载时,手写笔输入线程将继续运行并阻止AppDomain卸载。()

我最初尝试使用一个单独的AppDomain来解决这个问题,并确保在关闭主窗口时调用了
Dispatcher.InvokeShutdown()
,正如链接文章中建议的那样。在我的可视化工具中,
Show
方法变成:

protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
    var domain = AppDomain.CreateDomain("My Friendly Domain");

    CrossAppDomainDelegate action = () =>
    {
        var window = new MainWindow();

        window.ShowDialog();
    };

    domain.DoCallBack(action);

    AppDomain.Unload(domain);
}
主窗口有以下代码:

public MainWindow()
{
    InitializeComponent();
}

protected override void OnClosing(CancelEventArgs e)
{
    // Without this AppDomain unloading will fail and hate you forever...
    Dispatcher.InvokeShutdown();

    base.OnClosing(e);
}
这适用于从控制台应用程序进行调试,但是VisualStudio似乎不允许您在Visualizers中调用
CrossAppDomainDelegates
,并且我得到了某种形式的序列化异常

答复 显而易见的下一步,因为我根本不在乎手写笔的支持是完全删除它。此处给出了这方面的代码-

这使得
可视化工具中的代码

using Microsoft.VisualStudio.DebuggerVisualizers;

[assembly: System.Diagnostics.DebuggerVisualizer(
    typeof(Visualizer.DebuggerSide),
    typeof(VisualizerObjectSource),
    Target = typeof(string),
    Description = "Visualizer")]
namespace Visualizer
{
    public class DebuggerSide : DialogDebuggerVisualizer
    {
        protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            var window = new MainWindow();

            window.ShowDialog();
        }

        public static void TestShowVisualizer(object thingToVisualize)
        {
            var visualizerHost = new VisualizerDevelopmentHost(thingToVisualize, typeof(DebuggerSide));
            visualizerHost.ShowVisualizer();
        }
    }
}
主窗口

namespace Visualizer
{
    using System.ComponentModel;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Input;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DisableWPFTabletSupport();

            InitializeComponent();
        }

        public static void DisableWPFTabletSupport()
        {
            // Get a collection of the tablet devices for this window.  
            var devices = Tablet.TabletDevices;

            if (devices.Count == 0)
            {
                return;
            }

            var inputManagerType = typeof(InputManager);

            var stylusLogic = inputManagerType.InvokeMember("StylusLogic",
                        BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                        null, InputManager.Current, null);

            if (stylusLogic == null)
            {
                return;
            }

            var stylusLogicType = stylusLogic.GetType();

            while (devices.Count > 0)
            {
                // Remove the first tablet device in the devices collection.
                stylusLogicType.InvokeMember("OnTabletRemoved",
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                        null, stylusLogic, new object[] { (uint)0 });
            }            
        }
    }
}
更简单的答案 事实证明,这两个步骤都有些过分,实际上我只需要在
OnClosing
事件处理程序中保留对
Dispatcher.InvokeShutdown()的调用,并正常调用主窗口:

protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
    var window = new MainWindow();

    window.ShowDialog();
}
和主窗口:

protected override void OnClosing(CancelEventArgs e)
{
    Dispatcher.InvokeShutdown();

    base.OnClosing(e);
}