Skip to content

Commit 1cf162c

Browse files
committed
PlayState: Improve process handling to prevent potential crashes
- Prevent crashes when accessing MainWindowHandle on exited processes - Add error handling and fallback logic in IsWindowInForeground, BringToForeground, and GetProcessByWindowHandle - Improve logging for debugging process/window state and failures
1 parent bfeb2f3 commit 1cf162c

File tree

1 file changed

+135
-18
lines changed

1 file changed

+135
-18
lines changed

source/Generic/PlayState/Models/PlayStateData.cs

Lines changed: 135 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,29 @@ public StateActions SuspendProcesses()
175175
{
176176
foreach (var gameProcess in GameProcesses)
177177
{
178-
if (gameProcess is null || gameProcess.Process.Handle == null || gameProcess.Process.Handle == IntPtr.Zero)
178+
if (gameProcess?.Process is null)
179179
{
180180
continue;
181181
}
182182

183-
Ntdll.NtSuspendProcess(gameProcess.Process.Handle);
183+
try
184+
{
185+
if (!gameProcess.Process.HasExited)
186+
{
187+
var handle = gameProcess.Process.Handle;
188+
Ntdll.NtSuspendProcess(handle);
189+
}
190+
}
191+
catch (InvalidOperationException ex)
192+
{
193+
_logger.Error(ex, "Error on process suspend");
194+
continue;
195+
}
196+
catch (Exception ex)
197+
{
198+
_logger.Error(ex, "Error on process suspend");
199+
continue;
200+
}
184201
}
185202

186203
IsSuspended = true;
@@ -192,12 +209,29 @@ public StateActions ResumeProcesses()
192209
{
193210
foreach (var gameProcess in GameProcesses)
194211
{
195-
if (gameProcess is null || gameProcess.Process.Handle == null || gameProcess.Process.Handle == IntPtr.Zero)
212+
if (gameProcess?.Process is null)
196213
{
197214
continue;
198215
}
199216

200-
Ntdll.NtResumeProcess(gameProcess.Process.Handle);
217+
try
218+
{
219+
if (!gameProcess.Process.HasExited)
220+
{
221+
var handle = gameProcess.Process.Handle;
222+
Ntdll.NtResumeProcess(handle);
223+
}
224+
}
225+
catch (InvalidOperationException ex)
226+
{
227+
_logger.Error(ex, "Error on process resume");
228+
continue;
229+
}
230+
catch (Exception ex)
231+
{
232+
_logger.Error(ex, "Error on process resume");
233+
continue;
234+
}
201235
}
202236

203237
IsSuspended = false;
@@ -235,36 +269,90 @@ public ProcessItem GetProcessByWindowHandle(IntPtr handle)
235269
return null;
236270
}
237271

238-
var process = GameProcesses.FirstOrDefault(x => x.Process?.MainWindowHandle == handle);
239-
return process;
272+
foreach (var processItem in GameProcesses)
273+
{
274+
if (processItem?.Process is null)
275+
{
276+
continue;
277+
}
278+
279+
try
280+
{
281+
if (!processItem.Process.HasExited &&
282+
processItem.Process.MainWindowHandle == handle)
283+
{
284+
return processItem;
285+
}
286+
}
287+
catch (InvalidOperationException ex)
288+
{
289+
_logger.Warn(ex, $"GetProcessByWindowHandle: Failed to access process {processItem.Process?.Id}.");
290+
continue;
291+
}
292+
}
293+
294+
return null;
240295
}
241296

242297
public void BringToForeground()
243298
{
244-
if (!GameProcesses.HasItems() || IsSuspended)
299+
if (!GameProcesses.HasItems())
245300
{
301+
_logger.Debug("BringToForeground: No game processes.");
302+
return;
303+
}
304+
305+
if (IsSuspended)
306+
{
307+
_logger.Debug("BringToForeground: Skipped because game is suspended.");
246308
return;
247309
}
248310

249311
if (IsWindowInForeground())
250312
{
313+
_logger.Debug("BringToForeground: Already in foreground.");
251314
return;
252315
}
253316

254-
var processItem = GameProcesses?.FirstOrDefault(x => x.Process.MainWindowHandle != null && x.Process.MainWindowHandle != IntPtr.Zero);
255-
if (processItem is null)
317+
IntPtr? windowHandle = null;
318+
foreach (var processItem in GameProcesses)
319+
{
320+
if (processItem?.Process is null)
321+
{
322+
continue;
323+
}
324+
325+
try
326+
{
327+
if (!processItem.Process.HasExited)
328+
{
329+
var handle = processItem.Process.MainWindowHandle;
330+
if (handle != IntPtr.Zero)
331+
{
332+
windowHandle = handle;
333+
break;
334+
}
335+
}
336+
}
337+
catch (InvalidOperationException ex)
338+
{
339+
_logger.Warn(ex, $"BringToForeground: Process {processItem.Process?.Id} may have exited.");
340+
}
341+
}
342+
343+
if (windowHandle == null)
256344
{
345+
_logger.Warn($"BringToForeground: No valid window handle found for game {Game.Name}.");
257346
return;
258347
}
259348

260-
var windowHandle = processItem.Process.MainWindowHandle;
261349
try
262350
{
263-
WindowsHelper.RestoreAndFocusWindow(windowHandle);
351+
WindowsHelper.RestoreAndFocusWindow(windowHandle.Value);
264352
}
265353
catch (Exception e)
266354
{
267-
_logger.Error(e, $"Error while restoring game window of game {Game.Name}, {windowHandle}");
355+
_logger.Error(e, $"BringToForeground: Error while restoring game window {windowHandle} for game {Game.Name}.");
268356
}
269357
}
270358

@@ -300,9 +388,29 @@ public bool IsWindowInForeground()
300388
}
301389

302390
var foregroundWindowHandle = WindowsHelper.GetForegroundWindowHandle();
303-
var isInForeground = GameProcesses?
304-
.Any(x => x.Process.MainWindowHandle == foregroundWindowHandle) == true;
305-
return isInForeground;
391+
foreach (var processItem in GameProcesses)
392+
{
393+
if (processItem?.Process is null)
394+
{
395+
continue;
396+
}
397+
398+
try
399+
{
400+
if (!processItem.Process.HasExited &&
401+
processItem.Process.MainWindowHandle == foregroundWindowHandle)
402+
{
403+
return true;
404+
}
405+
}
406+
catch (InvalidOperationException ex)
407+
{
408+
_logger.Error(ex, "IsWindowInForeground: Failed to check process. It may have exited.");
409+
continue;
410+
}
411+
}
412+
413+
return false;
306414
}
307415

308416
public bool IsWindowMinimized()
@@ -317,14 +425,23 @@ private List<IntPtr> GetWindowHandles()
317425
var windowHandles = new List<IntPtr>();
318426
foreach (var gameProcess in GameProcesses)
319427
{
320-
if (gameProcess is null || gameProcess.Process.Handle == null || gameProcess.Process.Handle == IntPtr.Zero)
428+
if (gameProcess?.Process is null)
321429
{
322430
continue;
323431
}
324432

325-
if (gameProcess.Process.MainWindowHandle != IntPtr.Zero)
433+
try
326434
{
327-
windowHandles.AddMissing(gameProcess.Process.MainWindowHandle);
435+
if (!gameProcess.Process.HasExited && gameProcess.Process.MainWindowHandle != IntPtr.Zero)
436+
{
437+
windowHandles.AddMissing(gameProcess.Process.MainWindowHandle);
438+
}
439+
}
440+
catch (InvalidOperationException e)
441+
{
442+
// Process has likely exited between checks
443+
_logger.Error(e, $"Error during while obtaining Handles of {nameof(GameProcesses)}");
444+
continue;
328445
}
329446
}
330447

0 commit comments

Comments
 (0)