//------------------------------------------------------------------------------ // // Copyright (c) Company. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.ComponentModel.Design; using System.Globalization; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System.Linq; using System.IO; using System.Diagnostics; using Tango.Transport; using Tango.Transport.Adapters; using Tango.Transport.Transporters; using System.IO.Compression; using Tango.Core.IO; using Tango.PMR.IO; using Google.Protobuf; using EnvDTE; using System.Collections.Generic; namespace Tango.BuildExtensions { /// /// Command handler /// internal sealed class RemoteDebugCommand : VSIXBase { private DebuggerEvents _dteDebuggerEvents; private ITransporter transporter; private bool _attached; private string processId; private OleMenuCommand _menuCommand; private IList _projects; /// /// Command ID. /// public const int CommandId = 4129; /// /// Command menu group (command set GUID). /// public static readonly Guid CommandSet = new Guid("c03a7b01-8109-4ec5-8f90-858bed027e5d"); /// /// VS Package that provides this command, not null. /// private readonly Package package; /// /// Initializes a new instance of the class. /// Adds our command handlers for menu (commands must exist in the command table file) /// /// Owner package, not null. private RemoteDebugCommand(Package package) : base(package) { if (package == null) { throw new ArgumentNullException("package"); } _dteDebuggerEvents = DTE.Events.DebuggerEvents; _dteDebuggerEvents.OnEnterDesignMode += _dteDebuggerEvents_OnEnterDesignMode; DTE.Events.SolutionEvents.Opened += () => { _projects = GetSolutionProjects().ToList(); }; this.package = package; OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; if (commandService != null) { var menuCommandID = new CommandID(CommandSet, CommandId); _menuCommand = new OleMenuCommand(this.MenuItemCallback, menuCommandID); _menuCommand.Visible = false; _menuCommand.BeforeQueryStatus += _menuCommand_BeforeQueryStatus; commandService.AddCommand(_menuCommand); } } private void _menuCommand_BeforeQueryStatus(object sender, EventArgs e) { (sender as OleMenuCommand).Visible = false; if (DTE.Debugger.CurrentMode == dbgDebugMode.dbgDesignMode && DTE.Solution != null && DTE.Solution.IsOpen) { String projectName = DTE.Solution.Properties.Item("StartupProject").Value.ToStringSafe(); if (projectName != null) { var project = _projects.SingleOrDefault(x => x.Name == projectName); String path = GetProjectOutputFilePath(project); if (Path.GetExtension(path.ToLower()) == ".exe") { (sender as OleMenuCommand).Visible = true; } } } } #pragma warning disable VSTHRD100 // Avoid async void methods private async void _dteDebuggerEvents_OnEnterDesignMode(dbgEventReason Reason) #pragma warning restore VSTHRD100 // Avoid async void methods { if (_attached) { if (Reason == dbgEventReason.dbgEventReasonStopDebugging) { _attached = false; if (transporter != null) { OpenVSProgress("Tango Remote Runner", "Closing remote process...", true); await transporter.SendRequest(new KillProcessRequest() { ProcessID = processId, }); await transporter.Disconnect(); transporter = null; await System.Threading.Tasks.Task.Delay(2000); CloseVSProgress(); } } } } /// /// Gets the instance of the command. /// public static RemoteDebugCommand Instance { get; private set; } /// /// Gets the service provider from the owner package. /// private IServiceProvider ServiceProvider { get { return this.package; } } /// /// Initializes the singleton instance of the command. /// /// Owner package, not null. public static void Initialize(Package package) { Instance = new RemoteDebugCommand(package); } // %ifnot% $toolWindow$ /// /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// /// Event sender. /// Event args. private void MenuItemCallback(object sender, EventArgs e) { RunRemote(); } private void RunRemote() { _attached = false; try { String projectName = DTE.Solution.Properties.Item("StartupProject").Value.ToString(); RemoteDebugForm dlg = new RemoteDebugForm(projectName); if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { #pragma warning disable VSTHRD105 // Avoid method overloads that assume TaskScheduler.Current System.Threading.Tasks.Task.Factory.StartNew(async () => #pragma warning restore VSTHRD105 // Avoid method overloads that assume TaskScheduler.Current { try { OpenVSProgress("Tango Remote Runner", "Connecting to " + dlg.SelectedRemoteRunner.HostName + "...", true); ITransportAdapter adapter = new TcpTransportAdapter(dlg.SelectedRemoteRunner.Address, dlg.SelectedRemoteRunner.Port); transporter = new BasicTransporter(adapter); await transporter.Connect(); SetVSProgress("Building " + projectName + "..."); var project = GetSolutionProjects().SingleOrDefault(x => x.Name == projectName); String filePath = GetProjectOutputFilePath(project); String folder = Path.GetDirectoryName(filePath); String fileName = Path.GetFileName(filePath); DTE.Solution.SolutionBuild.BuildProject("Debug", project.FullName, true); var tempFolder = TemporaryManager.Default.CreateFolder(); var zipFile = tempFolder.CreateFile(".zip"); await zipFile.DeleteAsync(); SetVSProgress("Packaging project..."); ZipFile.CreateFromDirectory(folder, zipFile.Path); System.IO.FileInfo zipFileInfo = new System.IO.FileInfo(zipFile); CloseVSProgress(); OpenVSProgress("Tango Remote Runner", "Uploading project...", false); var upResponse = await transporter.SendRequest(new FileUploadRequest() { Path = zipFile.FileName, Length = zipFileInfo.Length, }); var uploadResponse = upResponse.Message; int currentFilePosition = 0; do { using (FileStream fs = new FileStream(zipFileInfo.FullName, FileMode.Open)) { fs.Position = currentFilePosition; FileChunkUploadRequest chunkRequest = new FileChunkUploadRequest(); chunkRequest.UploadID = uploadResponse.UploadID; long remaining = zipFileInfo.Length - currentFilePosition; byte[] buffer = new byte[uploadResponse.MaxChunkLength < remaining ? uploadResponse.MaxChunkLength : remaining]; await fs.ReadAsync(buffer, 0, buffer.Length); chunkRequest.Buffer = ByteString.CopyFrom(buffer); await transporter.SendRequest(chunkRequest); currentFilePosition += buffer.Length; SetVSProgress("Uploading project...", "", currentFilePosition, (int)zipFileInfo.Length); } } while (currentFilePosition < zipFileInfo.Length); Wait(1000); CloseVSProgress(); OpenVSProgress("Tango Remote Runner", "Executing remote process...", true); var response = await transporter.SendRequest(new ExecuteProcessRequest() { UploadID = uploadResponse.UploadID, FileName = fileName, }, TimeSpan.FromSeconds(30)); processId = response.Message.ProcessID; SetVSProgress("Attaching Debugger..."); var debugger = DTE.Debugger as EnvDTE80.Debugger2; var transport = debugger.Transports.Item("Remote"); var process = debugger.GetProcesses(transport, dlg.SelectedRemoteRunner.Address).Item(fileName) as EnvDTE80.Process2; process.Attach(); _attached = true; CloseVSProgress(); } catch (Exception ex) { CloseVSProgress(); ShowMessage(ex.Message); } }); } } catch (Exception ex) { CloseVSProgress(); ShowMessage(ex.Message); } } } }