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 mCommand; private readonly Queue 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(); mMessage = new Queue(); // 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()); } } }