我正在尝试按照本指南为Visual Studio 2015编写Visual Studio Visualizer:
https://msdn.microsoft.com/en-us/library/ms164759.aspx
但是我想使用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 = -2146234347消息=卸载appdomain时出错。 (来自HRESULT的异常:0x80131015)Source = mscorlib StackTrace: 在System.AppDomain.Unload(AppDomain域) 在Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.ManagedShim.Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.IManagedViewerHost.CreateViewer(IntPtr hwnd,Object hostServicesParam,IPropertyProxyEESide proxy) 在Microsoft.VisualStudio.DebuggerVisualizers.VisualizerDevelopmentHost.EEProxyImpl.ShowVisualizer(IntPtr parentWindow) 在Microsoft.VisualStudio.DebuggerVisualizers.VisualizerDevelopmentHost.ShowVisualizer() 在C:\ git \ Visualizer \ Visualizer \ Class1.cs中的Visualizer.DebuggerSide.TestShowVisualizer(Object thingToVisualize):第20行 at C:\ git \ Visualizer \ Visualizer.Debug \ Program.cs中的Visualizer.Debug.Program.Main(String [] args):第10行 在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,String [] args) 在System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String [] args) 在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback回调,对象状态,布尔值 preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean preserveSyncCtx) 在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,对象状态) 在System.Threading.ThreadHelper.ThreadStart()InnerException:
认为错误可能是由于WPF代码试图返回到Visual Studio代码,我尝试在一个完全独立的AppDomain中启动WPF窗口但是当我尝试卸载该AppDomain时,我得到了同样的错误。
MainWindow代码:
namespace Visualizer
{
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
答案 0 :(得分:0)
嗯,这太令人讨厌了!
我在启用触摸屏的计算机上,因此WPF注册了Stylus Input。当WPF窗口卸载自身并且AppDomain
尝试卸载时,Stylus Input线程将继续运行并阻止AppDomain卸载。 (https://connect.microsoft.com/VisualStudio/feedback/details/798279/stylus-input-thread-prevents-appdomain-from-unloading)
我最初尝试使用单独的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);
}
MainWindow有以下代码:
public MainWindow()
{
InitializeComponent();
}
protected override void OnClosing(CancelEventArgs e)
{
// Without this AppDomain unloading will fail and hate you forever...
Dispatcher.InvokeShutdown();
base.OnClosing(e);
}
这适用于从控制台应用程序进行调试,但似乎Visual Studio不允许您在Visualizers中调用CrossAppDomainDelegates
,并且我得到了某种形式的序列化异常。
显而易见的下一步,因为我对Stylus支持毫不在意,所以完全删除它。这里给出了代码 - https://msdn.microsoft.com/en-us/library/dd901337(v=vs.90).aspx
这使得代码位于Visualizer
:
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();
}
}
}
MainWindow
:
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 });
}
}
}
}
事实证明这两个步骤都是过度的,我只需要在Dispatcher.InvokeShutdown()
事件处理程序中保持对OnClosing
的调用并正常调用主窗口:
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);
}