Cosmos/source/Cosmos.VS.Windows/CosmosWindowsPackage.cs
2017-04-17 09:02:54 -05:00

229 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using Cosmos.Debug.Common;
using Cosmos.Debug.DebugConnectors;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace Cosmos.VS.Windows
{
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideToolWindow(typeof(AssemblyTW))]
[ProvideToolWindow(typeof(RegistersTW))]
[ProvideToolWindow(typeof(StackTW))]
[ProvideToolWindow(typeof(InternalTW))]
[ProvideToolWindow(typeof(ConsoleTW))]
[Guid(Guids.PackageGuidString)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
public sealed class CosmosWindowsPackage: Package
{
private readonly Queue<ushort> mCommand;
private readonly Queue<byte[]> mMessage;
private readonly System.Timers.Timer mTimer = new System.Timers.Timer(100);
private PipeServer mPipeDown;
private PipeServer mConsoleDown;
public StateStorer StateStorer { get; }
private readonly Type[] mAllChannelWindowTypes = { typeof(ConsoleTW) };
public CosmosWindowsPackage()
{
StateStorer = new StateStorer();
mCommand = new Queue<ushort>();
mMessage = new Queue<byte[]>();
// There are a lot of threading issues in VSIP, and the WPF dispatchers do not work.
// So instead we use a stack and a timer to poll it for data.
mTimer.AutoReset = true;
mTimer.Elapsed += ProcessMessage;
mTimer.Start();
mPipeDown = new PipeServer(Pipes.DownName);
mPipeDown.DataPacketReceived += PipeThread_DataPacketReceived;
mPipeDown.Start();
}
protected override void Initialize()
{
base.Initialize();
var xDTE = (EnvDTE80.DTE2)GetGlobalService(typeof(EnvDTE.DTE));
var xPane = xDTE.ToolWindows.OutputWindow.OutputWindowPanes;
Global.OutputPane = xPane.Add("Cosmos");
Global.OutputPane.OutputString("Debugger windows loaded.\r\n");
CosmosMenuCmdSet.Initialize(this);
}
void ProcessMessage(object sender, EventArgs e)
{
ushort xCmd;
byte[] xMsg;
while (true)
{
lock (mCommand)
{
if (mCommand.Count == 0)
{
break;
}
xCmd = mCommand.Dequeue();
xMsg = mMessage.Dequeue();
}
if (xCmd <= 127)
{
// debug channel
switch (xCmd)
{
case Debugger2Windows.Noop:
break;
case Debugger2Windows.Stack:
UpdateWindow(typeof(StackTW), "STACK", xMsg);
break;
case Debugger2Windows.Frame:
UpdateWindow(typeof(StackTW), "FRAME", xMsg);
break;
case Debugger2Windows.Registers:
UpdateWindow(typeof(RegistersTW), null, xMsg);
break;
case Debugger2Windows.Quit:
break;
case Debugger2Windows.AssemblySource:
UpdateWindow(typeof(AssemblyTW), null, xMsg);
break;
case Debugger2Windows.PongVSIP:
UpdateWindow(typeof(InternalTW), null, Encoding.UTF8.GetBytes("Pong from VSIP"));
break;
case Debugger2Windows.PongDebugStub:
UpdateWindow(typeof(InternalTW), null, Encoding.UTF8.GetBytes("Pong from DebugStub"));
break;
case Debugger2Windows.OutputPane:
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
(Action)delegate
{
Global.OutputPane.OutputString(Encoding.UTF8.GetString(xMsg));
}
);
break;
case Debugger2Windows.OutputClear:
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
(Action)delegate ()
{
Global.OutputPane.Clear();
StateStorer.ClearState();
});
break;
}
}
else
{
UpdateChannelWindows(xCmd, xMsg);
}
}
}
void PipeThread_DataPacketReceived(ushort aCmd, byte[] aMsg)
{
lock (mCommand)
{
mCommand.Enqueue(aCmd);
mMessage.Enqueue(aMsg);
}
}
public ToolWindowPane2 FindWindow(Type aWindowType)
{
// Get the instance number 0 of this tool window.
// Our windows are single instance so this instance will be the only one.
// The last flag is set to true so that if the tool window does not exists it will be created.
var xWindow = FindToolWindow(aWindowType, 0, true);
if (xWindow?.Frame == null)
{
throw new NotSupportedException("Failed to create the Cosmos tool window.");
}
return xWindow as ToolWindowPane2;
}
public ToolWindowPaneChannel FindChannelWindow(Type aWindowType)
{
// Get the instance number 0 of this tool window.
// Our windows are single instance so this instance will be the only one.
// The last flag is set to true so that if the tool window does not exists it will be created.
var xWindow = FindToolWindow(aWindowType, 0, true);
if (xWindow?.Frame == null)
{
throw new NotSupportedException("Failed to create the Cosmos tool window.");
}
return xWindow as ToolWindowPaneChannel;
}
public void UpdateChannelWindows(ushort aChannelAndCommand, byte[] aData)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
(Action)delegate ()
{
foreach (var xType in mAllChannelWindowTypes)
{
var xWindow = FindChannelWindow(xType);
xWindow.UserControl.Package = this;
xWindow.UserControl.HandleChannelMessage(aChannelAndCommand, aData);
}
}
);
}
public void UpdateWindow(Type aWindowType, string aTag, byte[] aData)
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
(Action)delegate ()
{
var xWindow = FindWindow(aWindowType);
xWindow.UserControl.Package = this;
xWindow.UserControl.Update(aTag, aData);
}
);
}
public void StoreAllStates()
{
var cWindow = FindWindow(typeof(StackTW));
byte[] aData = cWindow.UserControl.GetCurrentState();
StateStorer.StoreState("StackTW", aData == null ? null : (byte[])aData.Clone());
cWindow = FindWindow(typeof(RegistersTW));
aData = cWindow.UserControl.GetCurrentState();
StateStorer.StoreState("RegistersTW", aData == null ? null : (byte[])aData.Clone());
}
public void RestoreAllStates()
{
var cWindow = FindWindow(typeof(StackTW));
byte[] aData = StateStorer.RetrieveState(StateStorer.CurrLineId, "StackTW");
cWindow.UserControl.SetCurrentState(aData == null ? null : (byte[])aData.Clone());
cWindow = FindWindow(typeof(RegistersTW));
aData = StateStorer.RetrieveState(StateStorer.CurrLineId, "RegistersTW");
cWindow.UserControl.SetCurrentState(aData == null ? null : (byte[])aData.Clone());
}
}
}