//------------------------------------------------------------------------------
//
// 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);
}
}
}
}