# How SharpEmu Integrates with Its GUI for Game Launching and Logging

> Discover how SharpEmu integrates its GUI with the emulator core using EmulatorProcess to launch games and stream real-time log output for enhanced game launching and logging.

- Repository: [Berk/sharpemu](https://github.com/par274/sharpemu)
- Tags: internals
- Published: 2026-07-16

---

**SharpEmu separates its user interface from the emulator core by using the `EmulatorProcess` class to spawn the CLI as a managed child process, capturing stdout and stderr through anonymous pipes, and streaming formatted log output back to the GUI console in real time.**

The open-source PlayStation emulator SharpEmu (par274/sharpemu) implements a clean separation between its Avalonia-based GUI and the emulation engine. This article examines how SharpEmu GUI integration works for game launching and logging, detailing the specific classes and system calls used to manage process lifecycles and capture console output across Windows and Unix-like platforms.

## Architecture Overview

SharpEmu’s GUI layer resides in the `SharpEmu.GUI` namespace and relies on two primary components to handle game execution. The `MainWindow` class defined in [`SharpEmu.GUI/MainWindow.axaml.cs`](https://github.com/par274/sharpemu/blob/main/SharpEmu.GUI/MainWindow.axaml.cs) orchestrates user interactions and console display, while [`SharpEmu.GUI/EmulatorProcess.cs`](https://github.com/par274/sharpemu/blob/main/SharpEmu.GUI/EmulatorProcess.cs) contains the low-level process management logic. This design allows the GUI to remain responsive while the emulator runs as a separate process, preventing UI freezes during intensive emulation tasks.

## Game Launch Sequence

When a user clicks the Play button, the GUI initiates a structured sequence to start the emulator core with the correct parameters.

### Initiating the Launch

The process begins in [`MainWindow.axaml.cs`](https://github.com/par274/sharpemu/blob/main/MainWindow.axaml.cs) where the launcher logic collects the selected ISO path and optional flags. It constructs an `IReadOnlyList<string>` of arguments and instantiates the `EmulatorProcess` class to handle the actual execution. If the emulator binary is missing, the GUI immediately logs an error using `AppendConsoleLine()` with the `ErrorLineBrush` styling:

```csharp
AppendConsoleLine(Localization.Instance.Get("Launch.ExeNotFound"), ErrorLineBrush);

```

### Process Creation

The `EmulatorProcess.Start()` method accepts the executable path, argument list, and working directory. It then branches into platform-specific implementations to ensure reliable process control and output capture.

## Cross-Platform Process Management

SharpEmu handles process creation differently on Windows versus non-Windows platforms to leverage OS-specific features while maintaining functional parity.

### Windows Implementation

On Windows, the `StartWindows()` method performs several critical setup steps:

- **Environment Configuration**: Sets `SHARPEMU_DISABLE_MITIGATION_RELAUNCH=1` to prevent the CLI from attempting to relaunch with security mitigations that would break the pipe connection.
- **Pipe Creation**: Establishes anonymous pipes for stdout and stderr to enable non-blocking reads.
- **Job Object Assignment**: Creates a Windows job object with the `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` flag, ensuring that if the GUI process terminates unexpectedly, the entire emulator process tree is automatically destroyed.
- **Process Creation**: Uses `CreateProcessW` to spawn the child with the prepared pipes and environment.

### Non-Windows Fallback

For Linux and macOS, the `StartFallback()` method utilizes .NET’s `ProcessStartInfo` with `RedirectStandardOutput` and `RedirectStandardError` set to true. It initiates asynchronous read operations on both streams to capture output without blocking the main thread.

## Real-Time Log Integration

Once the process starts, the GUI captures, formats, and displays log messages through an event-driven pipeline.

### Output Event Handling

Both platform implementations hook into the `OutputReceived` event. When the `EmulatorProcess` receives a line from the child process, it invokes this event with the text content and an error flag. The `MainWindow` subscribes to this event and routes messages to the console panel:

```csharp
private void LaunchGame(string exePath, string[] args, string workingDir)
{
    var proc = new EmulatorProcess();
    proc.OutputReceived += (line, isError) =>
        AppendConsoleLine(line, isError ? ErrorLineBrush : InfoLineBrush);
    proc.Exited += exitCode =>
        AppendConsoleLine($"Emulator exited with code {exitCode}", InfoLineBrush);

    proc.Start(exePath, args, workingDir);
}

```

### Console Display and Formatting

The `AppendConsoleLine()` method in [`MainWindow.axaml.cs`](https://github.com/par274/sharpemu/blob/main/MainWindow.axaml.cs) creates a `TextBlock` with the appropriate foreground brush and appends it to the console panel. It then auto-scrolls to the newest entry:

```csharp
private void AppendConsoleLine(string text, IBrush brush)
{
    var line = new TextBlock { Text = text, Foreground = brush };
    ConsolePanel.Children.Add(line);
    ConsoleScrollViewer.ScrollToEnd();
}

```

## Process Lifecycle and Cleanup

Proper termination handling ensures that the emulator does not outlive the GUI or consume resources after a session ends.

### Exit Monitoring

The `EmulatorProcess` class spawns a dedicated watcher thread via `StartExitWatcherThread()`. On Windows, this thread blocks on `WaitForSingleObject` for the child process handle. When the emulator exits, `OnExited(exitCode)` fires the `Exited` event, allowing the GUI to display a final status line and re-enable the Play button:

```csharp
AppendConsoleLine(Localization.Instance.Format("Launch.ProcessExited", exitCode, meaning), brush);

```

### Graceful Shutdown

When the user closes the application window or clicks Stop, the GUI calls `EmulatorProcess.Stop()`. On Windows, this terminates the job object, killing the entire process tree instantly. On other platforms, it disposes of the `Process` object and kills the process if necessary. The `MainWindow` overrides `OnClosed` to ensure cleanup always occurs:

```csharp
protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);
    _emulatorProcess?.Stop();
}

```

## Summary

- SharpEmu uses a dedicated `EmulatorProcess` class in [`SharpEmu.GUI/EmulatorProcess.cs`](https://github.com/par274/sharpemu/blob/main/SharpEmu.GUI/EmulatorProcess.cs) to isolate the CLI emulator from the GUI, preventing UI blocking during emulation.
- Windows-specific features include job objects with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` and the `SHARPEMU_DISABLE_MITIGATION_RELAUNCH` environment variable to maintain pipe connectivity.
- Log output flows through anonymous pipes (Windows) or redirected standard streams (non-Windows) to the `OutputReceived` event, then to `AppendConsoleLine()` in [`MainWindow.axaml.cs`](https://github.com/par274/sharpemu/blob/main/MainWindow.axaml.cs) for colored console display.
- A background watcher thread monitors process termination via `WaitForSingleObject` or process exit events, ensuring the GUI updates immediately when emulation ends.
- Cleanup logic in `OnClosed` and `Stop()` guarantees that child processes cannot outlive the GUI application.

## Frequently Asked Questions

### How does SharpEmu prevent zombie processes when the GUI closes?

SharpEmu prevents zombie processes by assigning the emulator to a Windows job object with the `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` flag. When the GUI process terminates, Windows automatically terminates the entire job object tree. On non-Windows platforms, the `Stop()` method explicitly kills the process during window closure.

### Why does SharpEmu disable mitigation relaunch on Windows?

The GUI sets the environment variable `SHARPEMU_DISABLE_MITIGATION_RELAUNCH=1` before spawning the process to prevent the CLI from relaunching itself with CET/CFG mitigations. A relaunch would detach the process from the GUI's pipes, breaking real-time log capture and preventing proper process management via the job object.

### How does the GUI distinguish between error and info log lines?

The `OutputReceived` event includes a boolean flag indicating whether the line originated from stderr. The `MainWindow` uses this flag to select the appropriate brush (`ErrorLineBrush` or `InfoLineBrush`) when calling `AppendConsoleLine()`, color-coding errors in red while displaying standard output in neutral colors.

### Can SharpEmu run on Linux or macOS?

Yes, SharpEmu supports non-Windows platforms through the `StartFallback()` method, which uses standard .NET `Process` APIs with `RedirectStandardOutput` and `RedirectStandardError`. While this path lacks Windows-specific job object features, it maintains equivalent functionality for log capture and process termination.