diff --git a/src/SimConnect.NET/Events/SimSystemEventReceivedEventArgs.cs b/src/SimConnect.NET/Events/SimSystemEventReceivedEventArgs.cs
new file mode 100644
index 0000000..1b92f3d
--- /dev/null
+++ b/src/SimConnect.NET/Events/SimSystemEventReceivedEventArgs.cs
@@ -0,0 +1,28 @@
+//
+// Copyright (c) BARS. All rights reserved.
+//
+
+namespace SimConnect.NET.Events
+{
+ ///
+ /// Provides data for an event that is raised when a Simconnect system event is raised.
+ ///
+ ///
+ /// Initializes a new instance of the class with the specified event identifier and.
+ /// associated data.
+ ///
+ /// The unique identifier for the system event.
+ /// The data associated with the system event.
+ public class SimSystemEventReceivedEventArgs(uint eventId, uint data) : EventArgs
+ {
+ ///
+ /// Gets the unique identifier for the event.
+ ///
+ public uint EventId { get; } = eventId;
+
+ ///
+ /// Gets the data associated with the event.
+ ///
+ public uint Data { get; } = data;
+ }
+}
diff --git a/src/SimConnect.NET/SimConnectClient.cs b/src/SimConnect.NET/SimConnectClient.cs
index c5b6d3f..2516dc6 100644
--- a/src/SimConnect.NET/SimConnectClient.cs
+++ b/src/SimConnect.NET/SimConnectClient.cs
@@ -2,7 +2,6 @@
// Copyright (c) BARS. All rights reserved.
//
-using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -70,6 +69,11 @@ public SimConnectClient(string applicationName = "SimConnect.NET Client")
///
public event EventHandler? RawMessageReceived;
+ ///
+ /// Occurs when a subscribed event is fired.
+ ///
+ public event EventHandler? SystemEventReceived;
+
///
/// Gets a value indicating whether the client is connected to SimConnect.
///
@@ -345,6 +349,40 @@ public async Task DisconnectAsync()
}
}
+ ///
+ /// Subscribes to a specific simulator system event.
+ ///
+ /// The name of the system event (e.g., "SimStart", "4Sec", "Crashed").
+ /// A user-defined ID to identify this subscription.
+ /// Cancellation token for the operation.
+ /// A task representing the event.
+ /// Thrown when a sim connection wasn't found.
+ /// Thrown when the event wasn't subscribed.
+ public async Task SubscribeToEventAsync(string systemEventName, uint systemEventId, CancellationToken cancellationToken = default)
+ {
+ ObjectDisposedException.ThrowIf(this.disposed, nameof(SimConnectClient));
+
+ if (!this.isConnected)
+ {
+ throw new InvalidOperationException("Not connected to SimConnect.");
+ }
+
+ await Task.Run(
+ () =>
+ {
+ var result = SimConnectNative.SimConnect_SubscribeToSystemEvent(
+ this.simConnectHandle,
+ systemEventId,
+ systemEventName);
+
+ if (result != (int)SimConnectError.None)
+ {
+ throw new SimConnectException($"Failed to subscribe to event {systemEventName}: {(SimConnectError)result}", (SimConnectError)result);
+ }
+ },
+ cancellationToken).ConfigureAwait(false);
+ }
+
///
/// Processes the next SimConnect message.
///
@@ -419,6 +457,9 @@ public async Task ProcessNextMessageAsync(CancellationToken cancellationTo
case SimConnectRecvId.VorList:
case SimConnectRecvId.NdbList:
break;
+ case SimConnectRecvId.Event:
+ this.ProcessSystemEvent(ppData);
+ break;
default:
this.simVarManager?.ProcessReceivedData(ppData, pcbData);
break;
@@ -543,6 +584,29 @@ private void ProcessOpen(IntPtr ppData)
}
}
+ ///
+ /// Processes a system event message from SimConnect.
+ ///
+ /// Pointer to the received Event data.
+ private void ProcessSystemEvent(IntPtr ppData)
+ {
+ try
+ {
+ var recvEvent = Marshal.PtrToStructure(ppData);
+
+ this.SystemEventReceived?.Invoke(this, new SimSystemEventReceivedEventArgs(recvEvent.EventId, recvEvent.Data));
+
+ if (SimConnectLogger.IsLevelEnabled(SimConnectLogger.LogLevel.Debug))
+ {
+ SimConnectLogger.Debug($"System Event Received: ID={recvEvent.EventId} Data={recvEvent.Data}");
+ }
+ }
+ catch (Exception ex) when (!ExceptionHelper.IsCritical(ex))
+ {
+ SimConnectLogger.Error("Error processing system event", ex);
+ }
+ }
+
///
/// Starts the background message processing loop.
///
diff --git a/tests/SimConnect.NET.Tests.Net8/Tests/SystemEventSubscriptionTests.cs b/tests/SimConnect.NET.Tests.Net8/Tests/SystemEventSubscriptionTests.cs
new file mode 100644
index 0000000..f124d37
--- /dev/null
+++ b/tests/SimConnect.NET.Tests.Net8/Tests/SystemEventSubscriptionTests.cs
@@ -0,0 +1,70 @@
+//
+// Copyright (c) BARS. All rights reserved.
+//
+
+namespace SimConnect.NET.Tests.Net8.Tests
+{
+ internal class SystemEventSubscriptionTests : ISimConnectTest
+ {
+ public string Name => "SystemEventSubscription";
+
+ public string Description => "Tests system event subscription";
+
+ public string Category => "System Event";
+
+ public async Task RunAsync(SimConnectClient client, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ if (!client.IsConnected)
+ {
+ Console.WriteLine(" ❌ Client should already be connected");
+ return false;
+ }
+
+ Console.WriteLine(" ✅ Connection status verified");
+
+ using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ cts.CancelAfter(TimeSpan.FromSeconds(15));
+
+ bool testEventReceived = false;
+ client.SystemEventReceived += (sender, e) =>
+ {
+ switch (e.EventId)
+ {
+ case 100:
+ Console.WriteLine("4 seconds has passed!");
+ testEventReceived = true;
+ break;
+ }
+ };
+
+ await client.SubscribeToEventAsync("4sec", 100, cts.Token);
+
+ Console.WriteLine("Listening for events...");
+
+ while (!testEventReceived && !cts.Token.IsCancellationRequested)
+ {
+ await Task.Delay(500, cts.Token);
+ }
+ if (!testEventReceived)
+ {
+ Console.WriteLine(" ❌ Did not receive expected system event");
+ return false;
+ }
+ Console.WriteLine(" ✅ Received expected system event");
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine(" ❌ Connection test timed out");
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($" ❌ Connection test failed: {ex.Message}");
+ return false;
+ }
+ }
+ }
+}
diff --git a/tests/SimConnect.NET.Tests.Net8/Tests/TestRunner.cs b/tests/SimConnect.NET.Tests.Net8/Tests/TestRunner.cs
index 7f2c83d..512f3aa 100644
--- a/tests/SimConnect.NET.Tests.Net8/Tests/TestRunner.cs
+++ b/tests/SimConnect.NET.Tests.Net8/Tests/TestRunner.cs
@@ -31,6 +31,7 @@ public TestRunner()
new InputEventTests(),
new InputEventValueTests(),
new PerformanceTests(),
+ new SystemEventSubscriptionTests(),
};
}
@@ -143,6 +144,9 @@ private static TestOptions ParseArguments(string[] args)
case "--verbose":
options.Verbose = true;
break;
+ case "--test-events":
+ options.Categories.Add("System Event");
+ break;
}
}