From e10a03db1f82f3d084a47b9305d288a3cc13db02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Qui=C3=B1ones?= Date: Wed, 6 Sep 2023 02:41:50 +0200 Subject: [PATCH] Fixed exception when unexpected disposed RtMidiCallback Keep reference to midi event callback in RtMidiIn interop to prevent GC bumbling off with the handler --- RtMidi.Net/InteropServices/RtMidiIn.cs | 8 ++++++++ WorkerTest/Program.cs | 2 +- WorkerTest/Worker.cs | 20 +++++++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/RtMidi.Net/InteropServices/RtMidiIn.cs b/RtMidi.Net/InteropServices/RtMidiIn.cs index c372988..da30a16 100644 --- a/RtMidi.Net/InteropServices/RtMidiIn.cs +++ b/RtMidi.Net/InteropServices/RtMidiIn.cs @@ -16,6 +16,12 @@ namespace RtMidi.Net.InteropServices; /// internal class RtMidiIn : RtMidiBase { +#pragma warning disable IDE0052 + // ReSharper disable once NotAccessedField.Local + // Used to prevent garbage collection of the delegate + private RtMidiCallback? _midiCallback; +#pragma warning restore IDE0052 + /// /// Default constructor /// @@ -67,6 +73,7 @@ public override MidiApi GetCurrentApi() /// passed to the callback function whenever it is called. public void SetCallback(RtMidiCallback callback, byte[]? userData) { + _midiCallback = callback; RtMidiInterop.rtmidi_in_set_callback(RtMidiPtr, callback, userData); } @@ -126,6 +133,7 @@ public void IgnoreTypes(bool midiSysex = true, bool midiTime = true, bool midiSe /// protected override void ReleaseUnmanagedResources() { + _midiCallback = null; RtMidiInterop.rtmidi_in_free(RtMidiPtr); } } \ No newline at end of file diff --git a/WorkerTest/Program.cs b/WorkerTest/Program.cs index 9c5353c..36183a3 100644 --- a/WorkerTest/Program.cs +++ b/WorkerTest/Program.cs @@ -1,6 +1,6 @@ using WorkerTest; -IHost host = Host.CreateDefaultBuilder(args) +var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService(); diff --git a/WorkerTest/Worker.cs b/WorkerTest/Worker.cs index cc9da5c..d9c2319 100644 --- a/WorkerTest/Worker.cs +++ b/WorkerTest/Worker.cs @@ -9,6 +9,7 @@ public class Worker : BackgroundService { private readonly ILogger _logger; private MidiInputClient? _midiInputClient; + private const bool UseEventHandler = true; //Change to test /// /// Constructor @@ -29,12 +30,14 @@ public override async Task StartAsync(CancellationToken cancellationToken) } if (devices.Any()) { - var devicePort = 1u; //TODO Change device to test + var devicePort = 1u; //Change device to test var device = MidiManager.GetDeviceInfo(devicePort, MidiDeviceType.Input); _midiInputClient = new MidiInputClient(device); - //TODO The event throws an exception after a while, i'm not sure why - //_midiInputClient.OnMessageReceived += MidiClient_OnMessageReceived; - //_midiInputClient.ActivateMessageReceivedEvent(); + if (UseEventHandler) + { + _midiInputClient.OnMessageReceived += MidiClient_OnMessageReceived; + _midiInputClient.ActivateMessageReceivedEvent(); + } _midiInputClient.Open(); } else @@ -50,10 +53,13 @@ public override async Task StartAsync(CancellationToken cancellationToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Executing"); - while (!stoppingToken.IsCancellationRequested) + if (!UseEventHandler) { - var (message, _) = await _midiInputClient!.GetMessageAsync(stoppingToken); - OnMessageReceived(message); + while (!stoppingToken.IsCancellationRequested) + { + var (message, _) = await _midiInputClient!.GetMessageAsync(stoppingToken); + OnMessageReceived(message); + } } }