Skip to content

Commit

Permalink
Improved code, added ability to kill explorer process with specified …
Browse files Browse the repository at this point in the history
…delay.
  • Loading branch information
Lamonin committed Mar 26, 2024
1 parent 6f1f3fd commit f62f7ec
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 72 deletions.
4 changes: 3 additions & 1 deletion MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@
</Grid>
<Grid Margin="8,8,8,0" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="ProcessListBox" d:ItemsSource="{d:SampleData ItemCount=5}" SelectionChanged="ProcessListBox_SelectionChanged"/>
<StackPanel Grid.Column="1" Margin="8,0,0,0">
<Button x:Name="AddProcessButton" Content="Add" Height="24" Click="Add_Button_Click"/>
<Button x:Name="EditProcessButton" Content="Edit" Margin="0,8,0,0" Height="24" Click="Edit_Button_Click"/>
<Button x:Name="RemoveProcessButton" Content="Remove" Margin="0,8,0,0" Height="24" Click="Remove_Button_Click"/>
<Label x:Name="DelayLabel" Content="Delay (sec.)" Margin="-5,4,0,-2" ToolTip="If the process is found, explorer will be closed not immediately, but after the specified delay in seconds"/>
<TextBox x:Name="ProcessDelayTextBox" Text="{Binding ElementName=ProcessListBox, Path=SelectedItem.Delay}"/>
</StackPanel>
</Grid>
<Label x:Name="DateTimeLabel" Content="13.11.2023 Time: 10:26" Grid.Row="0" Margin="8,0,8,0"/>
Expand Down
191 changes: 122 additions & 69 deletions MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,54 @@
using System.IO;
using System.Windows.Documents;
using System.Windows.Media;
using System.Threading.Tasks;
using System.Threading;

using Timer = System.Timers.Timer;
using System.Collections.ObjectModel;

namespace XboxExplorerKiller
{
public class SaveData
public class ProcessData
{
public ObservableCollection<ProcessInfo> Processes { get; set; } = new ObservableCollection<ProcessInfo>();
}

public class ProcessInfo
{
public string[]? Processes { get; set; }
public string Name { get; set; }
public int Delay { get; set; }
}

public partial class MainWindow : Window
{
const string saveFileName = "XboxExplorerKiller.json";
const string saveFileName = "XboxExplorerKillerData.json";

private Run? killerStatusRun;
private Run? explorerStatusRun;
private CancellationTokenSource cancelationTokenSource;

private bool isProcessKilled;
private bool isProcessSelected;
private bool isExplorerKilled;
private string killedProcessName;
private Timer timeTimer;
private Timer killerTimer;
HashSet<string> processNamesForKill;
private readonly Timer timeTimer;
private readonly Timer killerTimer;
private HashSet<string> processNamesForKill;

private ProcessData processData;

public MainWindow()
{
InitializeComponent();

timeTimer = new Timer(10000);
timeTimer.Elapsed += TimeTimer_Elapsed;
timeTimer.Start();
processData = new ProcessData();
processNamesForKill = new HashSet<string>();

cancelationTokenSource = new CancellationTokenSource();

timeTimer = new Timer(1000);
timeTimer.Elapsed += DateTimeTimer_Elapsed;
timeTimer.Start();

killerTimer = new Timer(1000);
killerTimer.Elapsed += KillerTimer_Elapsed;
Expand All @@ -48,23 +66,29 @@ public MainWindow()

private void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadData();

ProcessListBox.ItemsSource = processData.Processes;
ProcessListBox.DisplayMemberPath = "Name";

EditProcessButton.IsEnabled = false;
RemoveProcessButton.IsEnabled = false;
DelayLabel.IsEnabled = false;
ProcessDelayTextBox.IsEnabled = false;

LoadData();

killedProcessName = "";
DateTimeLabel.Content = DateTime.Now;
DateTimeLabel.Content = DateTime.Now.ToString("g");

killerStatusRun = new Run("Stopped") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold };
var tb = new TextBlock();
tb.Inlines.Add("Killer status: ");
killerStatusRun = new Run("Stopped") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold };
tb.Inlines.Add(killerStatusRun);
KillerStatusLabel.Content = tb;

explorerStatusRun = IsExplorerRunning()
? new Run("Running") { Foreground = Brushes.Green, FontWeight = FontWeights.Bold }
: new Run("Killed") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold };
tb = new TextBlock();
tb.Inlines.Add("Explorer status: ");
explorerStatusRun = new Run("Running") { Foreground = Brushes.Green, FontWeight = FontWeights.Bold };
tb.Inlines.Add(explorerStatusRun);
ExplorerStatusLabel.Content = tb;
}
Expand All @@ -74,60 +98,69 @@ private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEven
{
killerTimer.Stop();

SaveData();

if (isExplorerKilled)
{
const string message_title = "Restart explorer.exe process?";
const string message_body = "If you do not restart the Explorer process now, " +
"then you will have to do it through the Task Manager, or in some other way. " +
"Do you want to restart the Explorer process?";

if (ConfirmDialog.Open(this, message_title, message_body, "Yes", "No") == true)
{
RestartExplorer();
}

cancelationTokenSource.Cancel(true);
}
}

private void TimeTimer_Elapsed(object? sender, ElapsedEventArgs e)
private void DateTimeTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
Dispatcher.Invoke(() =>
{
DateTimeLabel.Content = DateTime.Now;
DateTimeLabel.Content = DateTime.Now.ToString("g");
});
}

private void KillerTimer_Elapsed(object sender, ElapsedEventArgs e)
private void KillerTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
if (killedProcessName != "")
if (!isProcessKilled)
{
if (Process.GetProcessesByName(killedProcessName).Length == 0)
if (IsAnyProcessRunning(out var process))
{
RestartExplorer();
killedProcessName = "";
isProcessKilled = true;
var findedProcess = processData.Processes.First(p => p.Name == process.ProcessName);
KillExplorerAndWaitUntilProcessExit(process, findedProcess.Delay);
}
}
else
{
Process[] processlist = Process.GetProcesses();
}

foreach (var process in processlist)
{
if (processNamesForKill.Contains(process.ProcessName))
{
KillExplorer();
killedProcessName = process.ProcessName;
break;
}
}
private async void KillExplorerAndWaitUntilProcessExit(Process process, int delay)
{
cancelationTokenSource = new CancellationTokenSource();
try
{
await Task.Delay(delay * 1000, cancelationTokenSource.Token);
KillExplorer();
await process.WaitForExitAsync(cancelationTokenSource.Token);
RestartExplorer();
}
catch (OperationCanceledException)
{
}
finally
{
isProcessKilled = false;
}
}

private void SaveData()
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, saveFileName);
var save = new SaveData() { Processes = ProcessListBox.Items.Cast<string>().ToArray() };
var options = new JsonSerializerOptions { WriteIndented = true };
File.WriteAllText(path, JsonSerializer.Serialize(save, options));
File.WriteAllText(path, JsonSerializer.Serialize(processData, options));
}

private void LoadData()
Expand All @@ -139,15 +172,13 @@ private void LoadData()
return;
}

var jsonString = File.ReadAllText(saveFileName);
SaveData savedData = JsonSerializer.Deserialize<SaveData>(jsonString);
foreach (var process in savedData.Processes)
{
ProcessListBox.Items.Add(process);
}
var savedData = JsonSerializer.Deserialize<ProcessData>(File.ReadAllText(saveFileName));
if (savedData == null || savedData.Processes == null) return;

processData = savedData;
}

private void CallCmdCommand(string command)
private static void CallCmdCommand(string command)
{
Process process = new Process
{
Expand All @@ -164,73 +195,88 @@ private void CallCmdCommand(string command)
process.WaitForExit();
}

private bool IsAnyProcessRunning(out Process runnedProcess)
{
Process[] processList = Process.GetProcesses();

foreach (var process in processList)
{
if (processNamesForKill.Contains(process.ProcessName))
{
runnedProcess = process;
return true;
}
}
runnedProcess = null;
return false;
}

private bool IsExplorerRunning()
{
Process[] processlist = Process.GetProcesses();
foreach (var process in processlist)
{
if (process.ProcessName == "explorer")
{
return true;
}
}
return false;
}

private void KillExplorer()
{
CallCmdCommand("taskkill /f /im explorer.exe");
Dispatcher.Invoke(() =>
{
explorerStatusRun.Text = "Killed";
explorerStatusRun.Foreground = Brushes.Red;
});
CallCmdCommand("taskkill /f /im explorer.exe");
isExplorerKilled = true;
}

private void RestartExplorer()
{
CallCmdCommand("start %windir%\\explorer.exe");
Dispatcher.Invoke(() =>
{
explorerStatusRun.Text = "Running";
explorerStatusRun.Foreground = Brushes.Green;
});
CallCmdCommand("start %windir%\\explorer.exe");
isExplorerKilled = false;
}

private string GetListBoxSelectedValue(ListBox listBox)
{
int idx = listBox.SelectedIndex;
return idx != -1 ? listBox.Items[listBox.SelectedIndex].ToString() : "";
}

private void ReplaceListBoxSelectedValue(ListBox listBox, string newValue)
{
int idx = listBox.SelectedIndex;
listBox.Items.RemoveAt(idx);
listBox.Items.Insert(idx, newValue);
}


private void ProcessListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
isProcessSelected = ProcessListBox.SelectedIndex != -1;
EditProcessButton.IsEnabled = isProcessSelected;
RemoveProcessButton.IsEnabled = isProcessSelected;
DelayLabel.IsEnabled = isProcessSelected;
ProcessDelayTextBox.IsEnabled = isProcessSelected;
}

private void Add_Button_Click(object sender, RoutedEventArgs e)
{
if (InputDialog.Open(this, "Add process", "Enter process name:", "Example Process Name") == true)
{
ProcessListBox.Items.Add(InputDialog.UserInput);
SaveData();
processData.Processes.Add(new ProcessInfo { Name = InputDialog.UserInput, Delay = 0 });
}
}

private void Edit_Button_Click(object sender, RoutedEventArgs e)
{
if (InputDialog.Open(this, "Edit process name", "Enter new process name:", GetListBoxSelectedValue(ProcessListBox)) == true)
if (InputDialog.Open(this, "Edit process name", "Enter new process name:", ((ProcessInfo) ProcessListBox.SelectedValue).Name) == true)
{
ReplaceListBoxSelectedValue(ProcessListBox, InputDialog.UserInput);
SaveData();
var p = processData.Processes[ProcessListBox.SelectedIndex];
processData.Processes[ProcessListBox.SelectedIndex] = new ProcessInfo { Name = InputDialog.UserInput, Delay = p.Delay };
}
}

private void Remove_Button_Click(object sender, RoutedEventArgs e)
{
if (ConfirmDialog.Open(this, "Remove process from list", "Are you sure?") == true)
{
ProcessListBox.Items.RemoveAt(ProcessListBox.SelectedIndex);
SaveData();
processData.Processes.RemoveAt(ProcessListBox.SelectedIndex);
}
}

Expand All @@ -240,10 +286,14 @@ private void Stop_Button_Click(object sender, RoutedEventArgs e)
AddProcessButton.IsEnabled = true;
EditProcessButton.IsEnabled = isProcessSelected;
RemoveProcessButton.IsEnabled = isProcessSelected;
DelayLabel.IsEnabled = isProcessSelected;
ProcessDelayTextBox.IsEnabled = isProcessSelected;

killerStatusRun.Text = "Stopped";
killerStatusRun.Foreground = Brushes.Red;
killerTimer.Stop();

cancelationTokenSource.Cancel(true);
}

private void Start_Button_Click(object sender, RoutedEventArgs e)
Expand All @@ -252,8 +302,10 @@ private void Start_Button_Click(object sender, RoutedEventArgs e)
AddProcessButton.IsEnabled = false;
EditProcessButton.IsEnabled = false;
RemoveProcessButton.IsEnabled = false;
DelayLabel.IsEnabled = false;
ProcessDelayTextBox.IsEnabled = false;

processNamesForKill = new HashSet<string>(ProcessListBox.Items.Cast<string>().ToList());
processNamesForKill = processData.Processes.Select(p => p.Name).ToHashSet();
killerStatusRun.Text = "Working";
killerStatusRun.Foreground = Brushes.Green;
killerTimer.Start();
Expand All @@ -267,6 +319,7 @@ private void Kill_Button_Click(object sender, RoutedEventArgs e)

private void Restart_Button_Click(object sender, RoutedEventArgs e)
{
cancelationTokenSource.Cancel(true);
RestartExplorer();
Focus();
}
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Xbox Explorer Killer

<p align="center">
<img src="https://github.com/Lamonin/XboxExplorerKiller/assets/48371419/f59fc2b7-0b7b-44d0-b19b-38c3b813cdc0" alt="Xbox Explorer Killer Image"/>
<img src="https://github.com/Lamonin/XboxExplorerKiller/assets/48371419/2ba3559a-8eb8-4a58-9ccb-87191656c624" alt="Xbox Explorer Killer Image"/>
</p>

**Xbox Explorer Killer** - is a simple program that automatically kills the explorer process in Windows when you launch a game from Xbox Game Pass, and starts the explorer process over again when you close the game.
Expand All @@ -16,7 +16,7 @@ Just start the program, add the names of the processes you need (you can find th
![image](https://github.com/Lamonin/XboxExplorerKiller/assets/48371419/252eeb44-646a-4c9c-851f-85fe28c8fb62)

***
***

**Xbox Explorer Killer** - это простая программа, которая автоматически завершает процесс проводника в Windows при запуске игры из Xbox Game Pass и запускает проводник заново при закрытии игры.
## Для чего это нужно?
Если вы играете в игры Game Pass, используя чужой аккаунт (так называемый sharing-аккаунт), то наверняка замечали, что некоторые игры периодически закрываются без ошибок. Иногда это происходит сразу после запуска игры, а иногда только через полчаса. Проблема решается закрытием процесса проводника, и данная программа предназначена для того, чтобы делать это за вас автоматически.
Expand Down

0 comments on commit f62f7ec

Please sign in to comment.