Skip to content

Commit 363c43f

Browse files
committed
Implemented Ctrl+C functionality for cancelling spawned child processes
1 parent c11c0f4 commit 363c43f

File tree

4 files changed

+92
-28
lines changed

4 files changed

+92
-28
lines changed

Background-Terminal/Background-Terminal.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<PackageReference Include="CoreMeter" Version="1.0.2" />
2121
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.10" />
2222
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
23+
<PackageReference Include="System.Management" Version="4.7.0" />
2324
</ItemGroup>
2425

2526
<ItemGroup>

Background-Terminal/MainWindow.xaml.cs

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics;
77
using System.IO;
88
using System.Linq;
9+
using System.Management;
910
using System.Text;
1011
using System.Threading.Tasks;
1112
using System.Windows;
@@ -35,10 +36,12 @@ public partial class MainWindow : Window
3536

3637
private BackgroundTerminalSettings _settings;
3738

38-
private Process _cmdProcess;
39+
private Process _process;
3940

4041
private ObservableCollection<string> _terminalData = new ObservableCollection<string>();
4142

43+
private int _cmdProcessId;
44+
4245
private bool _terminalWindowActive = false;
4346

4447
private bool _awaitingKey1 = false;
@@ -52,7 +55,7 @@ public MainWindow()
5255
InitializeComponent();
5356

5457
// Create TerminalWindow
55-
_terminalWindow = new TerminalWindow(SendCommand);
58+
_terminalWindow = new TerminalWindow(SendCommand, KillChildren);
5659
_terminalWindow.Show();
5760

5861
// Apply changes in terminal data to TerminalWindow
@@ -136,34 +139,51 @@ private void ApplySettingsToTerminalWindow()
136139
_terminalWindow.UpdateTerminalDataTextBoxMargin();
137140
}
138141

142+
private List<Process> GetProcessChildren()
143+
{
144+
List<Process> children = new List<Process>();
145+
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(String.Format("Select * From Win32_Process Where ParentProcessID={0}", _process.Id));
146+
147+
foreach (ManagementObject managementObject in managementObjectSearcher.Get())
148+
{
149+
children.Add(Process.GetProcessById(Convert.ToInt32(managementObject["ProcessID"])));
150+
}
151+
152+
return children;
153+
}
154+
139155
#region Terminal Data Handlers
140156
private async Task<int> RunTerminalProcessAsync()
141157
{
142158
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
143159

144-
_cmdProcess = new Process();
160+
_process = new Process();
145161

146-
_cmdProcess.StartInfo.FileName = "cmd.exe";
147-
_cmdProcess.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
148-
_cmdProcess.StartInfo.UseShellExecute = false;
149-
_cmdProcess.StartInfo.CreateNoWindow = true;
150-
_cmdProcess.StartInfo.RedirectStandardInput = true;
151-
_cmdProcess.StartInfo.RedirectStandardOutput = true;
152-
_cmdProcess.StartInfo.RedirectStandardError = true;
162+
_process.StartInfo.FileName = "cmd.exe";
163+
_process.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
164+
_process.StartInfo.UseShellExecute = false;
165+
_process.StartInfo.CreateNoWindow = true;
166+
_process.StartInfo.RedirectStandardInput = true;
167+
_process.StartInfo.RedirectStandardOutput = true;
168+
_process.StartInfo.RedirectStandardError = true;
153169

154-
_cmdProcess.EnableRaisingEvents = true;
155-
_cmdProcess.OutputDataReceived += CMD_OutputDataReceived;
156-
_cmdProcess.ErrorDataReceived += CMD_ErrorDataReceived;
170+
_process.EnableRaisingEvents = true;
171+
_process.OutputDataReceived += CMD_OutputDataReceived;
172+
_process.ErrorDataReceived += CMD_ErrorDataReceived;
157173

158-
_cmdProcess.Exited += new EventHandler((sender, args) =>
174+
_process.Exited += new EventHandler((sender, args) =>
159175
{
160-
taskCompletionSource.SetResult(_cmdProcess.ExitCode);
161-
_cmdProcess.Dispose();
176+
taskCompletionSource.SetResult(_process.ExitCode);
177+
_process.Dispose();
162178
});
163179

164-
_cmdProcess.Start();
165-
_cmdProcess.BeginOutputReadLine();
166-
_cmdProcess.BeginErrorReadLine();
180+
_process.Start();
181+
_process.BeginOutputReadLine();
182+
_process.BeginErrorReadLine();
183+
184+
List<Process> children = GetProcessChildren();
185+
if (children.Count > 0)
186+
_cmdProcessId = children[0].Id;
167187

168188
return await taskCompletionSource.Task;
169189
}
@@ -178,10 +198,25 @@ private void CMD_ErrorDataReceived(object sender, DataReceivedEventArgs e)
178198
_terminalData.Add(e.Data);
179199
}
180200

181-
private void SendCommand(string command)
201+
private void SendCommand(string command, bool output = true)
182202
{
183-
_terminalData.Add(command);
184-
_cmdProcess.StandardInput.WriteLine(command);
203+
if (output)
204+
_terminalData.Add(command);
205+
206+
_process.StandardInput.WriteLine(command);
207+
}
208+
209+
private void KillChildren()
210+
{
211+
List<Process> children = GetProcessChildren();
212+
213+
foreach (Process child in children)
214+
{
215+
if (!child.Id.Equals(_cmdProcessId))
216+
{
217+
child.Kill();
218+
}
219+
}
185220
}
186221
#endregion
187222

Background-Terminal/TerminalWindow.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
BorderThickness="0"
3030
Focusable="True"
3131
FontFamily="Consolas"
32-
PreviewKeyDown="InputTextBox_PreviewKeyDown" />
32+
PreviewKeyDown="InputTextBox_PreviewKeyDown"
33+
PreviewKeyUp="InputTextBox_PreviewKeyUp" />
3334
</Grid>
3435
</Window>

Background-Terminal/TerminalWindow.xaml.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@ namespace Background_Terminal
1414
{
1515
public partial class TerminalWindow : Window
1616
{
17-
public delegate void SendCommandProc(string command);
17+
public delegate void SendCommandProc(string command, bool output = false);
1818
private SendCommandProc SendCommand;
1919

20-
public TerminalWindow(SendCommandProc sendCommand)
20+
public delegate void KillChildrenProc();
21+
private KillChildrenProc KillChildren;
22+
23+
bool _ctrlDown = false;
24+
25+
public TerminalWindow(SendCommandProc sendCommand, KillChildrenProc killChildren)
2126
{
2227
InitializeComponent();
2328

2429
SendCommand = sendCommand;
30+
KillChildren = killChildren;
2531
}
2632

2733
public void UpdateTerminalDataTextBoxMargin() // I do what I want
@@ -30,13 +36,26 @@ public TerminalWindow(SendCommandProc sendCommand)
3036
}
3137

3238
#region Event Handlers
33-
public void TerminalDataTextBox_TextChanged(object sender, TextChangedEventArgs e)
39+
private void TerminalDataTextBox_TextChanged(object sender, TextChangedEventArgs e)
3440
{
3541
TerminalData_TextBox.ScrollToEnd();
3642
}
3743

38-
public void InputTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
44+
private void InputTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
3945
{
46+
// Cancel current command
47+
if (e.Key.Equals(Key.C) && _ctrlDown)
48+
{
49+
KillChildren();
50+
51+
e.Handled = true;
52+
}
53+
54+
if (e.Key.Equals(Key.LeftCtrl))
55+
{
56+
_ctrlDown = true;
57+
}
58+
4059
if (e.Key.Equals(Key.Return) || e.Key.Equals(Key.Enter))
4160
{
4261
SendCommand(Input_TextBox.Text);
@@ -45,7 +64,15 @@ public void InputTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
4564
}
4665
}
4766

48-
public void TerminalWindow_Loaded(object sender, EventArgs e)
67+
private void InputTextBox_PreviewKeyUp(object sender, KeyEventArgs e)
68+
{
69+
if (e.Key.Equals(Key.LeftCtrl))
70+
{
71+
_ctrlDown = false;
72+
}
73+
}
74+
75+
private void TerminalWindow_Loaded(object sender, EventArgs e)
4976
{
5077
UpdateTerminalDataTextBoxMargin();
5178
}

0 commit comments

Comments
 (0)