diff --git a/GameShuffler.sln b/GameShuffler.sln
new file mode 100644
index 0000000..5d87823
--- /dev/null
+++ b/GameShuffler.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33213.308
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameShuffler", "GameShuffler\GameShuffler.csproj", "{3B963663-2FDD-4E59-BB36-1F1AA1C5FE4C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3B963663-2FDD-4E59-BB36-1F1AA1C5FE4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B963663-2FDD-4E59-BB36-1F1AA1C5FE4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B963663-2FDD-4E59-BB36-1F1AA1C5FE4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B963663-2FDD-4E59-BB36-1F1AA1C5FE4C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CDDFD207-731D-46AF-B821-D5261AFD2D9F}
+ EndGlobalSection
+EndGlobal
diff --git a/GameShuffler/Form1.Designer.cs b/GameShuffler/Form1.Designer.cs
new file mode 100644
index 0000000..8426e5f
--- /dev/null
+++ b/GameShuffler/Form1.Designer.cs
@@ -0,0 +1,201 @@
+using System.Runtime.InteropServices;
+
+namespace GameShuffler
+{
+ partial class Form1
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+
+ if (disposing)
+ {
+ UnregisterHotKey(this.Handle, RemoveGameKeyId);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.runningProcessesSelectionList = new System.Windows.Forms.CheckedListBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.startButton = new System.Windows.Forms.Button();
+ this.label3 = new System.Windows.Forms.Label();
+ this.minTimeTextBox = new System.Windows.Forms.TextBox();
+ this.maxTimeTextBox = new System.Windows.Forms.TextBox();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.label6 = new System.Windows.Forms.Label();
+ this.refreshButton = new System.Windows.Forms.Button();
+ this.stopButton = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // runningProcessesSelectionList
+ //
+ this.runningProcessesSelectionList.FormattingEnabled = true;
+ this.runningProcessesSelectionList.Location = new System.Drawing.Point(40, 40);
+ this.runningProcessesSelectionList.Name = "runningProcessesSelectionList";
+ this.runningProcessesSelectionList.Size = new System.Drawing.Size(720, 340);
+ this.runningProcessesSelectionList.TabIndex = 0;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(40, 9);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(200, 25);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "Select games to shuffle:";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(40, 502);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(426, 25);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "Press PageDown to remove a game from the shuffle";
+ //
+ // startButton
+ //
+ this.startButton.Location = new System.Drawing.Point(472, 497);
+ this.startButton.Name = "startButton";
+ this.startButton.Size = new System.Drawing.Size(133, 34);
+ this.startButton.TabIndex = 3;
+ this.startButton.Text = "Start Shuffling";
+ this.startButton.UseVisualStyleBackColor = true;
+ this.startButton.Click += new System.EventHandler(this.StartButton_Clicked);
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(40, 389);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(263, 25);
+ this.label3.TabIndex = 4;
+ this.label3.Text = "Minimum time before shuffling:";
+ //
+ // minTimeTextBox
+ //
+ this.minTimeTextBox.Location = new System.Drawing.Point(309, 386);
+ this.minTimeTextBox.Name = "minTimeTextBox";
+ this.minTimeTextBox.Size = new System.Drawing.Size(150, 31);
+ this.minTimeTextBox.TabIndex = 5;
+ this.minTimeTextBox.Text = "60";
+ //
+ // maxTimeTextBox
+ //
+ this.maxTimeTextBox.Location = new System.Drawing.Point(309, 423);
+ this.maxTimeTextBox.Name = "maxTimeTextBox";
+ this.maxTimeTextBox.Size = new System.Drawing.Size(150, 31);
+ this.maxTimeTextBox.TabIndex = 6;
+ this.maxTimeTextBox.Text = "120";
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(40, 426);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(266, 25);
+ this.label4.TabIndex = 7;
+ this.label4.Text = "Maximum time before shuffling:";
+ //
+ // label5
+ //
+ this.label5.AutoSize = true;
+ this.label5.Location = new System.Drawing.Point(465, 389);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(77, 25);
+ this.label5.TabIndex = 8;
+ this.label5.Text = "seconds";
+ //
+ // label6
+ //
+ this.label6.AutoSize = true;
+ this.label6.Location = new System.Drawing.Point(465, 426);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(77, 25);
+ this.label6.TabIndex = 9;
+ this.label6.Text = "seconds";
+ //
+ // refreshButton
+ //
+ this.refreshButton.Location = new System.Drawing.Point(648, 4);
+ this.refreshButton.Name = "refreshButton";
+ this.refreshButton.Size = new System.Drawing.Size(112, 34);
+ this.refreshButton.TabIndex = 10;
+ this.refreshButton.Text = "Refresh";
+ this.refreshButton.UseVisualStyleBackColor = true;
+ this.refreshButton.Click += new System.EventHandler(this.Refresh_Clicked);
+ //
+ // stopButton
+ //
+ this.stopButton.Enabled = false;
+ this.stopButton.Location = new System.Drawing.Point(611, 497);
+ this.stopButton.Name = "stopButton";
+ this.stopButton.Size = new System.Drawing.Size(149, 34);
+ this.stopButton.TabIndex = 11;
+ this.stopButton.Text = "Stop Shuffling";
+ this.stopButton.UseVisualStyleBackColor = true;
+ this.stopButton.Click += new System.EventHandler(this.StopButton_Clicked);
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 593);
+ this.Controls.Add(this.stopButton);
+ this.Controls.Add(this.refreshButton);
+ this.Controls.Add(this.label6);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.maxTimeTextBox);
+ this.Controls.Add(this.minTimeTextBox);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.startButton);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.runningProcessesSelectionList);
+ this.Name = "Form1";
+ this.Text = "Game Shuffler";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private CheckedListBox runningProcessesSelectionList;
+ private Label label1;
+ private Label label2;
+ private Button startButton;
+ private Label label3;
+ private TextBox minTimeTextBox;
+ private TextBox maxTimeTextBox;
+ private Label label4;
+ private Label label5;
+ private Label label6;
+ private Button refreshButton;
+ private Button stopButton;
+ }
+}
\ No newline at end of file
diff --git a/GameShuffler/Form1.cs b/GameShuffler/Form1.cs
new file mode 100644
index 0000000..2e1f4c2
--- /dev/null
+++ b/GameShuffler/Form1.cs
@@ -0,0 +1,230 @@
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace GameShuffler
+{
+ public partial class Form1 : Form
+ {
+ [Flags]
+ public enum ThreadAccess : int
+ {
+ TERMINATE = (0x0001),
+ SUSPEND_RESUME = (0x0002),
+ GET_CONTEXT = (0x0008),
+ SET_CONTEXT = (0x0010),
+ SET_INFORMATION = (0x0020),
+ QUERY_INFORMATION = (0x0040),
+ SET_THREAD_TOKEN = (0x0080),
+ IMPERSONATE = (0x0100),
+ DIRECT_IMPERSONATION = (0x0200)
+ }
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
+ [DllImport("kernel32.dll")]
+ static extern uint SuspendThread(IntPtr hThread);
+ [DllImport("kernel32.dll")]
+ static extern int ResumeThread(IntPtr hThread);
+ [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
+ static extern bool CloseHandle(IntPtr handle);
+
+ [DllImport("user32.dll")]
+ static extern bool SetForegroundWindow(IntPtr hWnd);
+ [DllImport("User32.dll")]
+ public static extern bool ShowWindow(IntPtr handle, int nCmdShow);
+
+ [DllImport("user32.dll")]
+ public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
+ [DllImport("user32.dll")]
+ public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ int RemoveGameKeyId = 1;
+
+ int RemoveGameKey = (int)Keys.PageDown;
+
+ Thread? shuffleThread = null;
+ bool stopShuffle = false;
+
+ int minShuffleTime = 0;
+ int maxShuffleTime = 0;
+ List gamesToShuffle = new List();
+
+ Process? currentGame = null;
+
+ Random rand = new Random();
+
+ public Form1()
+ {
+ InitializeComponent();
+ }
+
+ private void Refresh_Clicked(object sender, EventArgs e)
+ {
+ var allProcesses = Process.GetProcesses();
+ runningProcessesSelectionList.SelectedItems.Clear();
+ runningProcessesSelectionList.Items.Clear();
+ runningProcessesSelectionList.Items.AddRange(
+ allProcesses.Where(process => !string.IsNullOrEmpty(process.MainWindowTitle))
+ .Select(process => $"{process.MainWindowTitle}\t{process.Id}")
+ .ToArray());
+ }
+
+ private void StartButton_Clicked(object sender, EventArgs e)
+ {
+ refreshButton.Enabled = false;
+ runningProcessesSelectionList.Enabled = false;
+ startButton.Enabled = false;
+ minTimeTextBox.Enabled = false;
+ maxTimeTextBox.Enabled = false;
+ stopButton.Enabled = true;
+ if (!int.TryParse(minTimeTextBox.Text, out minShuffleTime) || minShuffleTime < 0)
+ {
+ MessageBox.Show("Minimum shuffle time must be a positive number");
+ }
+
+ if (!int.TryParse(maxTimeTextBox.Text, out maxShuffleTime) || maxShuffleTime < minShuffleTime)
+ {
+ MessageBox.Show("Maximum shuffle time must be a positive number greater than minimum shuffle time");
+ }
+
+ if (!RegisterHotKey(this.Handle, RemoveGameKeyId, 0x0000, RemoveGameKey))
+ {
+ MessageBox.Show("Failed to initialize shuffle start key");
+ }
+
+ foreach (var processString in runningProcessesSelectionList.CheckedItems)
+ {
+ var process = Process.GetProcessById(int.Parse(((string)processString).Split("\t")[1]));
+ gamesToShuffle.Add(process);
+ }
+
+ shuffleThread = new Thread(ShuffleLoop);
+ shuffleThread.Start();
+ }
+
+ private void StopButton_Clicked(object sender, EventArgs e)
+ {
+ refreshButton.Enabled = true;
+ runningProcessesSelectionList.Items.Clear();
+ runningProcessesSelectionList.Enabled = true;
+ startButton.Enabled = true;
+ minTimeTextBox.Enabled = true;
+ maxTimeTextBox.Enabled = true;
+ stopButton.Enabled = false;
+
+ if (shuffleThread != null)
+ {
+ stopShuffle = true;
+ }
+
+ UnregisterHotKey(this.Handle, RemoveGameKeyId);
+ }
+
+ private void ShuffleLoop()
+ {
+ foreach (var process in gamesToShuffle)
+ {
+ SuspendProcess(process);
+ }
+
+ while (gamesToShuffle.Any() && !stopShuffle)
+ {
+ if (currentGame != null)
+ {
+ ShowWindow(currentGame.MainWindowHandle, 0);
+ SuspendProcess(currentGame);
+ }
+
+ StartNewGame();
+
+ Thread.Sleep(rand.Next(minShuffleTime, maxShuffleTime)*1000);
+ }
+
+ foreach (var process in gamesToShuffle)
+ {
+ if (process != currentGame)
+ {
+ ResumeProcess(process);
+ }
+ }
+
+ stopShuffle = false;
+ }
+
+ private void StartNewGame()
+ {
+ var currentGameIndex = currentGame == null ? 0 : gamesToShuffle.IndexOf(currentGame);
+ var newGameIndex = currentGameIndex;
+ while (newGameIndex == currentGameIndex)
+ {
+ newGameIndex = rand.Next(gamesToShuffle.Count);
+ }
+ var newGame = gamesToShuffle[newGameIndex];
+ ResumeProcess(newGame);
+ ShowWindow(newGame.MainWindowHandle, 3);
+ SetForegroundWindow(newGame.MainWindowHandle);
+ currentGame = newGame;
+ }
+
+ private static void SuspendProcess(Process process)
+ {
+ foreach (ProcessThread pT in process.Threads)
+ {
+ IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
+
+ if (pOpenThread == IntPtr.Zero)
+ {
+ continue;
+ }
+
+ SuspendThread(pOpenThread);
+
+ CloseHandle(pOpenThread);
+ }
+ }
+
+ public static void ResumeProcess(Process process)
+ {
+ if (process.ProcessName == string.Empty)
+ return;
+
+ foreach (ProcessThread pT in process.Threads)
+ {
+ IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
+
+ if (pOpenThread == IntPtr.Zero)
+ {
+ continue;
+ }
+
+ var suspendCount = 0;
+ do
+ {
+ suspendCount = ResumeThread(pOpenThread);
+ } while (suspendCount > 0);
+
+ CloseHandle(pOpenThread);
+ }
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == 0x0312)
+ {
+ int id = (int)m.WParam;
+
+ if (id == RemoveGameKeyId && currentGame != null)
+ {
+ gamesToShuffle.Remove(currentGame);
+ currentGame.Kill();
+ if (gamesToShuffle.Any())
+ {
+ StartNewGame();
+ }
+ }
+ }
+
+ base.WndProc(ref m);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GameShuffler/Form1.resx b/GameShuffler/Form1.resx
new file mode 100644
index 0000000..f298a7b
--- /dev/null
+++ b/GameShuffler/Form1.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/GameShuffler/GameShuffler.csproj b/GameShuffler/GameShuffler.csproj
new file mode 100644
index 0000000..da05a80
--- /dev/null
+++ b/GameShuffler/GameShuffler.csproj
@@ -0,0 +1,16 @@
+
+
+
+ WinExe
+ net6.0-windows
+ enable
+ true
+ enable
+ true
+ true
+ win-x64
+ true
+ true
+
+
+
\ No newline at end of file
diff --git a/GameShuffler/Program.cs b/GameShuffler/Program.cs
new file mode 100644
index 0000000..4eada52
--- /dev/null
+++ b/GameShuffler/Program.cs
@@ -0,0 +1,17 @@
+namespace GameShuffler
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ // To customize application configuration such as set high DPI settings or default font,
+ // see https://aka.ms/applicationconfiguration.
+ ApplicationConfiguration.Initialize();
+ Application.Run(new Form1());
+ }
+ }
+}
\ No newline at end of file