mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-12 21:10:42 +00:00
a35c88e577
* If there's no replay context we can still use the live connection to copy and delete captures remotely. Try to use that whenever possible and warn the user when it's not possible (i.e if the program has been closed and there's no replay context, we have no way to access the files anymore). * If the user tries to open a remote log without a replay context, prompt them either to swithc to a replay context on that host or to save the log locally.
934 lines
33 KiB
C#
934 lines
33 KiB
C#
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2016 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Threading;
|
|
using WeifenLuo.WinFormsUI.Docking;
|
|
using renderdocui.Code;
|
|
using renderdoc;
|
|
using System.IO;
|
|
using System.Drawing.Imaging;
|
|
using System.Drawing.Drawing2D;
|
|
|
|
using Process = System.Diagnostics.Process;
|
|
|
|
namespace renderdocui.Windows
|
|
{
|
|
public partial class LiveCapture : DockContent
|
|
{
|
|
MainWindow m_Main = null;
|
|
ImageList thumbs = null;
|
|
|
|
Core m_Core = null;
|
|
|
|
Thread m_ConnectThread = null;
|
|
bool m_TriggerCapture = false;
|
|
bool m_QueueCapture = false;
|
|
int m_CaptureNumFrames = 1;
|
|
int m_CaptureFrameNum = 0;
|
|
int m_CaptureCounter = 0;
|
|
bool m_Disconnect = false;
|
|
TargetControl m_Connection = null;
|
|
|
|
uint m_CopyLogID = uint.MaxValue;
|
|
string m_CopyLogLocalPath = "";
|
|
List<uint> m_DeleteLogs = new List<uint>();
|
|
|
|
bool m_IgnoreThreadClosed = false;
|
|
|
|
string m_Host;
|
|
UInt32 m_RemoteIdent;
|
|
|
|
class CaptureLog
|
|
{
|
|
public uint remoteID;
|
|
public string exe;
|
|
public string api;
|
|
public DateTime timestamp;
|
|
|
|
public bool saved;
|
|
public bool opened;
|
|
|
|
public string path;
|
|
public bool local;
|
|
};
|
|
|
|
class ChildProcess
|
|
{
|
|
public int PID;
|
|
public uint ident;
|
|
public string name;
|
|
public bool added = false;
|
|
};
|
|
|
|
List<ChildProcess> m_Children = new List<ChildProcess>();
|
|
|
|
public LiveCapture(Core core, string host, UInt32 remoteIdent, MainWindow main)
|
|
{
|
|
InitializeComponent();
|
|
|
|
if (SystemInformation.HighContrast)
|
|
toolStrip1.Renderer = new ToolStripSystemRenderer();
|
|
|
|
m_Core = core;
|
|
m_Main = main;
|
|
|
|
this.DoubleBuffered = true;
|
|
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
|
|
|
|
Icon = global::renderdocui.Properties.Resources.icon;
|
|
|
|
m_Connection = null;
|
|
m_Host = host;
|
|
m_RemoteIdent = remoteIdent;
|
|
|
|
childProcessLabel.Visible = false;
|
|
childProcesses.Visible = false;
|
|
|
|
m_ConnectThread = null;
|
|
|
|
Text = (m_Host.Length > 0 ? (m_Host + " - ") : "") + "Connecting...";
|
|
connectionStatus.Text = "Connecting...";
|
|
connectionIcon.Image = global::renderdocui.Properties.Resources.hourglass;
|
|
|
|
thumbs = new ImageList();
|
|
thumbs.ColorDepth = ColorDepth.Depth24Bit;
|
|
|
|
thumbs.ImageSize = new Size(256, 144);
|
|
captures.TileSize = new Size(400, 160);
|
|
captures.LargeImageList = thumbs;
|
|
|
|
captures.Columns.AddRange(new ColumnHeader[] { new ColumnHeader(), new ColumnHeader(), new ColumnHeader() });
|
|
}
|
|
|
|
public void QueueCapture(int frameNum)
|
|
{
|
|
m_CaptureFrameNum = frameNum;
|
|
m_QueueCapture = true;
|
|
}
|
|
|
|
public string Hostname
|
|
{
|
|
get
|
|
{
|
|
return m_Host;
|
|
}
|
|
}
|
|
|
|
private void LiveCapture_Shown(object sender, EventArgs e)
|
|
{
|
|
m_ConnectThread = Helpers.NewThread(new ThreadStart(ConnectionThreadEntry));
|
|
m_ConnectThread.Start();
|
|
}
|
|
|
|
private void ConnectionThreadEntry()
|
|
{
|
|
try
|
|
{
|
|
string username = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
|
|
|
|
m_Connection = StaticExports.CreateTargetControl(m_Host, m_RemoteIdent, username, true);
|
|
|
|
if (m_Connection.Connected)
|
|
{
|
|
string api = "...";
|
|
if (m_Connection.API.Length > 0) api = m_Connection.API;
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
if (m_Connection.PID == 0)
|
|
{
|
|
connectionStatus.Text = String.Format("Connection established to {0} ({1})", m_Connection.Target, api);
|
|
Text = String.Format("{0} ({1})", m_Connection.Target, api);
|
|
}
|
|
else
|
|
{
|
|
connectionStatus.Text = String.Format("Connection established to {0} [PID {1}] ({2})",
|
|
m_Connection.Target, m_Connection.PID, api);
|
|
Text = String.Format("{0} [PID {1}] ({2})", m_Connection.Target, m_Connection.PID, api);
|
|
}
|
|
connectionIcon.Image = global::renderdocui.Properties.Resources.connect;
|
|
});
|
|
}
|
|
else
|
|
{
|
|
throw new ReplayCreateException(ReplayCreateStatus.NetworkIOFailed);
|
|
}
|
|
|
|
while (m_Connection.Connected)
|
|
{
|
|
m_Connection.ReceiveMessage();
|
|
|
|
if (m_TriggerCapture)
|
|
{
|
|
m_Connection.TriggerCapture((uint)m_CaptureNumFrames);
|
|
m_TriggerCapture = false;
|
|
}
|
|
|
|
if (m_QueueCapture)
|
|
{
|
|
m_Connection.QueueCapture((uint)m_CaptureFrameNum);
|
|
m_QueueCapture = false;
|
|
m_CaptureFrameNum = 0;
|
|
}
|
|
|
|
if (m_CopyLogLocalPath != "")
|
|
{
|
|
m_Connection.CopyCapture(m_CopyLogID, m_CopyLogLocalPath);
|
|
m_CopyLogLocalPath = "";
|
|
m_CopyLogID = uint.MaxValue;
|
|
}
|
|
|
|
List<uint> dels = new List<uint>();
|
|
lock (m_DeleteLogs)
|
|
{
|
|
dels.AddRange(m_DeleteLogs);
|
|
m_DeleteLogs.Clear();
|
|
}
|
|
|
|
foreach(var del in dels)
|
|
m_Connection.DeleteCapture(del);
|
|
|
|
if (m_Disconnect)
|
|
{
|
|
m_Connection.Shutdown();
|
|
m_Connection = null;
|
|
return;
|
|
}
|
|
|
|
if (m_Connection.InfoUpdated)
|
|
{
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
connectionStatus.Text = String.Format("Connection established to {0} ({1})", m_Connection.Target, m_Connection.API);
|
|
Text = String.Format("{0} ({1})", m_Connection.Target, m_Connection.API);
|
|
connectionIcon.Image = global::renderdocui.Properties.Resources.connect;
|
|
});
|
|
|
|
m_Connection.InfoUpdated = false;
|
|
}
|
|
|
|
if (m_Connection.CaptureExists)
|
|
{
|
|
uint capID = m_Connection.CaptureFile.ID;
|
|
DateTime timestamp = new DateTime(1970, 1, 1, 0, 0, 0);
|
|
timestamp = timestamp.AddSeconds(m_Connection.CaptureFile.timestamp).ToLocalTime();
|
|
byte[] thumb = m_Connection.CaptureFile.thumbnail;
|
|
string path = m_Connection.CaptureFile.path;
|
|
bool local = m_Connection.CaptureFile.local;
|
|
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
CaptureAdded(capID, m_Connection.Target, m_Connection.API, thumb, timestamp, path, local);
|
|
});
|
|
m_Connection.CaptureExists = false;
|
|
}
|
|
|
|
if (m_Connection.CaptureCopied)
|
|
{
|
|
uint capID = m_Connection.CaptureFile.ID;
|
|
string path = m_Connection.CaptureFile.path;
|
|
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
CaptureCopied(capID, path);
|
|
});
|
|
|
|
m_Connection.CaptureCopied = false;
|
|
}
|
|
|
|
if (m_Connection.ChildAdded)
|
|
{
|
|
if (m_Connection.NewChild.PID != 0)
|
|
{
|
|
try
|
|
{
|
|
ChildProcess c = new ChildProcess();
|
|
c.PID = (int)m_Connection.NewChild.PID;
|
|
c.ident = m_Connection.NewChild.ident;
|
|
c.name = Process.GetProcessById((int)m_Connection.NewChild.PID).ProcessName;
|
|
|
|
lock (m_Children)
|
|
{
|
|
m_Children.Add(c);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// process expired/doesn't exist anymore
|
|
}
|
|
}
|
|
|
|
m_Connection.ChildAdded = false;
|
|
}
|
|
}
|
|
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
connectionStatus.Text = "Connection closed";
|
|
connectionIcon.Image = global::renderdocui.Properties.Resources.disconnect;
|
|
|
|
numFrames.Enabled = captureDelay.Enabled = captureFrame.Enabled =
|
|
triggerCapture.Enabled = queueCap.Enabled = false;
|
|
|
|
ConnectionClosed();
|
|
});
|
|
}
|
|
catch (ReplayCreateException)
|
|
{
|
|
this.BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
Text = (m_Host.Length > 0 ? (m_Host + " - ") : "") + "Connection failed";
|
|
connectionStatus.Text = "Connection failed";
|
|
connectionIcon.Image = global::renderdocui.Properties.Resources.delete;
|
|
|
|
ConnectionClosed();
|
|
});
|
|
}
|
|
}
|
|
|
|
Image MakeThumb(Size s, Stream st)
|
|
{
|
|
Bitmap thumb = new Bitmap(s.Width, s.Height, PixelFormat.Format32bppArgb);
|
|
|
|
Graphics g = Graphics.FromImage(thumb);
|
|
|
|
g.Clear(Color.Transparent);
|
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
|
|
if (st != null)
|
|
{
|
|
try
|
|
{
|
|
using (var im = Image.FromStream(st))
|
|
{
|
|
float x = 0, y = 0;
|
|
float width = 0, height = 0;
|
|
|
|
float srcaspect = (float)im.Width / (float)im.Height;
|
|
float dstaspect = (float)s.Width / (float)s.Height;
|
|
|
|
if (srcaspect > dstaspect)
|
|
{
|
|
width = s.Width;
|
|
height = width / srcaspect;
|
|
|
|
y = (s.Height - height) / 2;
|
|
}
|
|
else
|
|
{
|
|
height = s.Height;
|
|
width = height * srcaspect;
|
|
|
|
x = (s.Width - width) / 2;
|
|
}
|
|
|
|
g.DrawImage(im, x, y, width, height);
|
|
}
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// swallow - invalid thumbnail just allow it to be transparent
|
|
}
|
|
}
|
|
|
|
g.Dispose();
|
|
|
|
return thumb;
|
|
}
|
|
|
|
private void captures_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
|
|
{
|
|
deleteMenu.Enabled = (captures.SelectedItems.Count > 0);
|
|
saveMenu.Enabled = saveThisCaptureToolStripMenuItem.Enabled =
|
|
openMenu.Enabled = openThisCaptureToolStripMenuItem.Enabled =
|
|
(captures.SelectedItems.Count == 1);
|
|
|
|
if(captures.SelectedItems.Count == 1)
|
|
newInstanceToolStripMenuItem.Enabled = (captures.SelectedItems[0].Tag as CaptureLog).local;
|
|
}
|
|
|
|
private void captures_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (captures.SelectedItems.Count == 1)
|
|
OpenCapture(captures.SelectedItems[0].Tag as CaptureLog);
|
|
}
|
|
|
|
private void captures_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right && captures.SelectedItems.Count > 0)
|
|
rightclickContext.Show(captures.PointToScreen(e.Location));
|
|
}
|
|
|
|
private void OpenCapture(CaptureLog log)
|
|
{
|
|
log.opened = true;
|
|
|
|
if (!log.local &&
|
|
(m_Core.Renderer.Remote == null ||
|
|
m_Core.Renderer.Remote.Hostname != m_Host ||
|
|
!m_Core.Renderer.Remote.Connected)
|
|
)
|
|
{
|
|
MessageBox.Show(
|
|
String.Format("This capture is on remote host {0} and there is no active replay context on that host.\n" +
|
|
"You can either save the log locally, or switch to a replay context on {0}.\n\n", m_Host),
|
|
"No active replay context", MessageBoxButtons.OK);
|
|
return;
|
|
}
|
|
|
|
m_Main.LoadLogfile(log.path, !log.saved, log.local);
|
|
}
|
|
|
|
private bool SaveCapture(CaptureLog log)
|
|
{
|
|
string path = m_Main.GetSavePath();
|
|
|
|
// we copy the temp log to the desired path, but the log item remains referring to the temp path.
|
|
// This ensures that if the user deletes the saved path we can still open or re-save it.
|
|
if (path.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
if (log.local)
|
|
{
|
|
File.Copy(log.path, path, true);
|
|
}
|
|
else if (m_Connection.Connected)
|
|
{
|
|
// if we have a current live connection, prefer using it
|
|
m_CopyLogLocalPath = path;
|
|
m_CopyLogID = log.remoteID;
|
|
}
|
|
else
|
|
{
|
|
if (m_Core.Renderer.Remote == null ||
|
|
m_Core.Renderer.Remote.Hostname != m_Host ||
|
|
!m_Core.Renderer.Remote.Connected)
|
|
{
|
|
MessageBox.Show(
|
|
String.Format("This capture is on remote host {0} and there is no active replay context on that host.\n" +
|
|
"Without an active replay context the capture cannot be saved, try switching to a replay context on {0}.\n\n", m_Host),
|
|
"No active replay context", MessageBoxButtons.OK);
|
|
return false;
|
|
}
|
|
|
|
m_Core.Renderer.CopyCaptureFromRemote(log.path, path, this);
|
|
m_Core.Renderer.DeleteCapture(log.path, false);
|
|
}
|
|
|
|
log.saved = true;
|
|
log.path = path;
|
|
m_Core.Config.AddRecentFile(m_Core.Config.RecentLogFiles, path, 10);
|
|
m_Main.PopulateRecentFiles();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
MessageBox.Show("Couldn't save to " + path + Environment.NewLine + ex.ToString(), "Cannot save",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool CheckAllowClose()
|
|
{
|
|
m_IgnoreThreadClosed = true;
|
|
|
|
bool suppressRemoteWarning = false;
|
|
|
|
foreach (ListViewItem i in captures.Items)
|
|
{
|
|
var log = i.Tag as CaptureLog;
|
|
|
|
if (log.saved) continue;
|
|
|
|
captures.SelectedItems.Clear();
|
|
Activate();
|
|
captures.Focus();
|
|
i.Selected = true;
|
|
|
|
DialogResult res = DialogResult.No;
|
|
|
|
if (!suppressRemoteWarning)
|
|
{
|
|
res = MessageBox.Show(String.Format("Save this logfile from {0} at {1}?", log.exe, log.timestamp.ToString("T")),
|
|
"Unsaved log", MessageBoxButtons.YesNoCancel);
|
|
}
|
|
|
|
if (res == DialogResult.Cancel)
|
|
{
|
|
m_IgnoreThreadClosed = false;
|
|
return false;
|
|
}
|
|
|
|
// we either have to save or delete the log. Make sure that if it's remote that we are able
|
|
// to by having an active connection or replay context on that host.
|
|
if (suppressRemoteWarning == false && !m_Connection.Connected &&
|
|
(m_Core.Renderer.Remote == null ||
|
|
m_Core.Renderer.Remote.Hostname != m_Host ||
|
|
!m_Core.Renderer.Remote.Connected)
|
|
)
|
|
{
|
|
DialogResult res2 = MessageBox.Show(
|
|
String.Format("This capture is on remote host {0} and there is no active replay context on that host.\n", m_Host) +
|
|
"Without an active replay context the capture cannot be " + (res == DialogResult.Yes ? "saved.\n\n" : "deleted.\n\n") +
|
|
"Would you like to continue and discard this capture and any others, to be left in the temporary folder on the remote machine?",
|
|
"No active replay context", MessageBoxButtons.YesNoCancel);
|
|
|
|
if (res2 == DialogResult.Yes)
|
|
{
|
|
suppressRemoteWarning = true;
|
|
res = DialogResult.No;
|
|
}
|
|
else
|
|
{
|
|
m_IgnoreThreadClosed = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (res == DialogResult.Yes)
|
|
{
|
|
bool success = SaveCapture(log);
|
|
|
|
if (!success)
|
|
{
|
|
m_IgnoreThreadClosed = false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_IgnoreThreadClosed = false;
|
|
return true;
|
|
}
|
|
|
|
public void CleanItems()
|
|
{
|
|
foreach (ListViewItem i in captures.Items)
|
|
{
|
|
var log = i.Tag as CaptureLog;
|
|
|
|
if (!log.saved)
|
|
{
|
|
try
|
|
{
|
|
if (log.path == m_Core.LogFileName)
|
|
{
|
|
m_Main.OwnTemporaryLog = true;
|
|
}
|
|
else
|
|
{
|
|
// if connected, prefer using the live connection
|
|
if (m_Connection.Connected && !log.local)
|
|
{
|
|
lock (m_DeleteLogs)
|
|
{
|
|
m_DeleteLogs.Add(log.remoteID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Core.Renderer.DeleteCapture(log.path, log.local);
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
// couldn't delete log - deleted from under us?
|
|
}
|
|
}
|
|
}
|
|
captures.Items.Clear();
|
|
}
|
|
|
|
private void LiveCapture_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (CheckAllowClose() == false)
|
|
{
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
CleanItems();
|
|
KillThread();
|
|
}
|
|
|
|
private void KillThread()
|
|
{
|
|
if (m_ConnectThread.ThreadState != ThreadState.Aborted &&
|
|
m_ConnectThread.ThreadState != ThreadState.Stopped)
|
|
{
|
|
m_Disconnect = true;
|
|
m_ConnectThread.Join();
|
|
}
|
|
}
|
|
|
|
private void LiveCapture_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
m_Main.LiveCaptureClosed(this);
|
|
|
|
captures.LargeImageList.Images.Clear();
|
|
thumbs.Dispose();
|
|
|
|
CleanItems();
|
|
KillThread();
|
|
}
|
|
|
|
private bool CheckAllowDelete()
|
|
{
|
|
bool needcheck = false;
|
|
|
|
bool multiple = captures.SelectedItems.Count > 1;
|
|
|
|
foreach (ListViewItem i in captures.SelectedItems)
|
|
needcheck |= !(i.Tag as CaptureLog).saved;
|
|
|
|
if (!needcheck || captures.SelectedItems.Count == 0)
|
|
return true;
|
|
|
|
Focus();
|
|
|
|
DialogResult res = MessageBox.Show(String.Format("Are you sure you wish to delete {0}?", multiple ? "these logs" : "this log") +
|
|
"\nAny log currently opened will be closed.",
|
|
multiple ? "Unsaved logs" : "Unsaved log", MessageBoxButtons.YesNoCancel);
|
|
|
|
if (res == DialogResult.Cancel || res == DialogResult.No)
|
|
return false;
|
|
|
|
if (res == DialogResult.Yes)
|
|
return true;
|
|
|
|
return true;
|
|
}
|
|
|
|
private void DeleteCaptureUnprompted(ListViewItem item)
|
|
{
|
|
var log = item.Tag as CaptureLog;
|
|
|
|
if (!log.saved)
|
|
{
|
|
if (log.path == m_Core.LogFileName)
|
|
{
|
|
m_Main.OwnTemporaryLog = true;
|
|
m_Main.CloseLogfile();
|
|
}
|
|
|
|
try
|
|
{
|
|
// if connected, prefer using the live connection
|
|
if (m_Connection.Connected && !log.local)
|
|
{
|
|
lock (m_DeleteLogs)
|
|
{
|
|
m_DeleteLogs.Add(log.remoteID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Core.Renderer.DeleteCapture(log.path, log.local);
|
|
}
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
// couldn't delete log - deleted from under us?
|
|
}
|
|
}
|
|
|
|
captures.Items.Remove(item);
|
|
}
|
|
|
|
private void openCapture_Click(object sender, EventArgs e)
|
|
{
|
|
if (captures.SelectedItems.Count == 1)
|
|
OpenCapture(captures.SelectedItems[0].Tag as CaptureLog);
|
|
}
|
|
|
|
private void openNewWindow_Click(object sender, EventArgs e)
|
|
{
|
|
if (captures.SelectedItems.Count == 1)
|
|
{
|
|
var log = captures.SelectedItems[0].Tag as CaptureLog;
|
|
|
|
var temppath = m_Core.TempLogFilename(log.exe);
|
|
|
|
if (!log.local)
|
|
{
|
|
MessageBox.Show("Can't open log in new instance with remote server in use", "Cannot open new instance",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
File.Copy(log.path, temppath);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
MessageBox.Show("Couldn't save log to temporary location" + Environment.NewLine + ex.ToString(), "Cannot save temporary log",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
var process = new System.Diagnostics.Process();
|
|
process.StartInfo = new System.Diagnostics.ProcessStartInfo(Application.ExecutablePath, String.Format("--tempfile \"{0}\"", temppath));
|
|
process.Start();
|
|
}
|
|
}
|
|
|
|
private void saveCapture_Click(object sender, EventArgs e)
|
|
{
|
|
if (captures.SelectedItems.Count == 1)
|
|
SaveCapture(captures.SelectedItems[0].Tag as CaptureLog);
|
|
}
|
|
|
|
private void deleteCapture_Click(object sender, EventArgs e)
|
|
{
|
|
bool allow = CheckAllowDelete();
|
|
|
|
if (!allow) return;
|
|
|
|
foreach (ListViewItem i in captures.SelectedItems)
|
|
DeleteCaptureUnprompted(i);
|
|
}
|
|
|
|
private void captures_KeyUp(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Delete)
|
|
{
|
|
deleteCapture_Click(sender, null);
|
|
}
|
|
}
|
|
|
|
private void CaptureCopied(uint ID, string localPath)
|
|
{
|
|
foreach (ListViewItem item in captures.Items)
|
|
{
|
|
var log = item.Tag as CaptureLog;
|
|
|
|
if (log != null && log.remoteID == ID)
|
|
{
|
|
log.local = true;
|
|
log.path = localPath;
|
|
item.SubItems[0].Text = log.exe;
|
|
item.SubItems[0].Font = new Font(item.SubItems[0].Font, FontStyle.Regular);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CaptureAdded(uint ID, string executable, string api, byte[] thumbnail, DateTime timestamp, string path, bool local)
|
|
{
|
|
if (thumbnail == null || thumbnail.Length == 0)
|
|
{
|
|
using (Image t = MakeThumb(thumbs.ImageSize, null))
|
|
{
|
|
thumbs.Images.Add(t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (var ms = new MemoryStream(thumbnail))
|
|
using (Image t = MakeThumb(thumbs.ImageSize, ms))
|
|
{
|
|
thumbs.Images.Add(t);
|
|
}
|
|
}
|
|
|
|
CaptureLog log = new CaptureLog();
|
|
log.remoteID = ID;
|
|
log.exe = executable;
|
|
log.api = api;
|
|
log.timestamp = timestamp;
|
|
log.saved = false;
|
|
log.path = path;
|
|
log.local = local;
|
|
|
|
string title = log.exe;
|
|
if (!local)
|
|
title += " (Remote)";
|
|
|
|
var item = new ListViewItem(new string[] { title, log.api, log.timestamp.ToString() }, thumbs.Images.Count - 1);
|
|
item.Tag = log;
|
|
if(!local)
|
|
item.SubItems[0].Font = new Font(item.SubItems[0].Font, FontStyle.Italic);
|
|
|
|
captures.Items.Add(item);
|
|
}
|
|
|
|
private void ConnectionClosed()
|
|
{
|
|
if (m_IgnoreThreadClosed) return;
|
|
|
|
if (captures.Items.Count <= 1)
|
|
{
|
|
if (captures.Items.Count == 1)
|
|
{
|
|
var log = captures.Items[0].Tag as CaptureLog;
|
|
|
|
// only auto-open a non-local log if we are successfully connected
|
|
// to this machine as a remote context
|
|
if (!log.local)
|
|
{
|
|
if (m_Core.Renderer.Remote == null ||
|
|
m_Host != m_Core.Renderer.Remote.Hostname ||
|
|
!m_Core.Renderer.Remote.Connected)
|
|
return;
|
|
}
|
|
|
|
if (log.opened)
|
|
return;
|
|
|
|
OpenCapture(log);
|
|
if (!log.saved)
|
|
{
|
|
log.saved = true;
|
|
m_Main.OwnTemporaryLog = true;
|
|
}
|
|
}
|
|
|
|
// auto-close and load log if we got a capture. If we
|
|
// don't haveany captures but DO have child processes,
|
|
// then don't close just yet.
|
|
if(captures.Items.Count == 1 || m_Children.Count == 0)
|
|
Close();
|
|
|
|
// if we have no captures and only one child, close and
|
|
// open up a connection to it (similar to behaviour with
|
|
// only one capture
|
|
if (captures.Items.Count == 0 && m_Children.Count == 1)
|
|
{
|
|
uint ident = m_Children[0].ident;
|
|
var live = new LiveCapture(m_Core, m_Host, ident, m_Main);
|
|
m_Main.ShowLiveCapture(live);
|
|
Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void triggerCapture_Click(object sender, EventArgs e)
|
|
{
|
|
m_CaptureNumFrames = (int)numFrames.Value;
|
|
if (captureDelay.Value == 0)
|
|
{
|
|
m_TriggerCapture = true;
|
|
}
|
|
else
|
|
{
|
|
m_CaptureCounter = (int)captureDelay.Value;
|
|
captureCountdown.Enabled = true;
|
|
triggerCapture.Enabled = false;
|
|
triggerCapture.Text = String.Format("Triggering in {0}s", m_CaptureCounter);
|
|
}
|
|
}
|
|
|
|
private void queueCap_Click(object sender, EventArgs e)
|
|
{
|
|
m_CaptureFrameNum = (int)captureFrame.Value;
|
|
m_QueueCapture = true;
|
|
}
|
|
|
|
private void captureCountdown_Tick(object sender, EventArgs e)
|
|
{
|
|
m_CaptureCounter--;
|
|
|
|
if (m_CaptureCounter == 0)
|
|
{
|
|
m_TriggerCapture = true;
|
|
captureCountdown.Enabled = false;
|
|
triggerCapture.Enabled = true;
|
|
triggerCapture.Text = "Trigger Capture";
|
|
}
|
|
else
|
|
{
|
|
triggerCapture.Text = String.Format("Triggering in {0}s", m_CaptureCounter);
|
|
}
|
|
}
|
|
|
|
private void childUpdateTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
// remove any stale processes
|
|
for (int i = 0; i < m_Children.Count; i++)
|
|
{
|
|
try
|
|
{
|
|
// if this throws an exception the process no longer exists so we'll remove it
|
|
Process.GetProcessById(m_Children[i].PID);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (m_Children[i].added)
|
|
childProcesses.Items.RemoveByKey(m_Children[i].PID.ToString());
|
|
|
|
// process expired/doesn't exist anymore
|
|
m_Children.RemoveAt(i);
|
|
|
|
// don't increment i, check the next element at i (if we weren't at the end
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_Children.Count; i++)
|
|
{
|
|
if (!m_Children[i].added)
|
|
{
|
|
string text = String.Format("{0} [PID {1}]", m_Children[i].name, m_Children[i].PID);
|
|
|
|
m_Children[i].added = true;
|
|
childProcesses.Items.Add(m_Children[i].PID.ToString(), text, 0).Tag = m_Children[i].ident;
|
|
}
|
|
}
|
|
|
|
if (m_Children.Count > 0)
|
|
{
|
|
childProcessLabel.Visible = childProcesses.Visible = true;
|
|
}
|
|
else
|
|
{
|
|
childProcessLabel.Visible = childProcesses.Visible = false;
|
|
}
|
|
}
|
|
|
|
private void childProcesses_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (childProcesses.SelectedItems.Count == 1 && childProcesses.SelectedItems[0].Tag is uint)
|
|
{
|
|
uint ident = (uint)childProcesses.SelectedItems[0].Tag;
|
|
var live = new LiveCapture(m_Core, m_Host, ident, m_Main);
|
|
m_Main.ShowLiveCapture(live);
|
|
}
|
|
}
|
|
}
|
|
}
|