mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
229 lines
9.3 KiB
C#
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());
|
|
}
|
|
}
|
|
}
|