diff --git a/samples/ConsoleSample/Program.cs b/samples/ConsoleSample/Program.cs index ba5e6b1..b555ab5 100644 --- a/samples/ConsoleSample/Program.cs +++ b/samples/ConsoleSample/Program.cs @@ -141,6 +141,5 @@ internal class Program { Console.WriteLine($"BUMMER! No twain data #{++xferCount} on thread {Environment.CurrentManagedThreadId}."); } - e.Dispose(); } } diff --git a/samples/KodakTester/MainForm.cs b/samples/KodakTester/MainForm.cs index 0df0413..45b85a3 100644 --- a/samples/KodakTester/MainForm.cs +++ b/samples/KodakTester/MainForm.cs @@ -98,7 +98,6 @@ public partial class MainForm : Form var path = info.FileName.ToString(); LogIt($"Received {e.ImageInfo.PixelType} {info.Format} in {e.ImageInfo.Compression} compressed file {path}"); } - e.Dispose(); LogIt(""); } diff --git a/samples/WinForm32/Form1.cs b/samples/WinForm32/Form1.cs index 80773f8..3bbc683 100644 --- a/samples/WinForm32/Form1.cs +++ b/samples/WinForm32/Form1.cs @@ -131,20 +131,22 @@ public partial class Form1 : Form if (_useThreadForImag) { // bad thread example but whatev. should use a dedicated thread of some sort for real + var data = e.TakeDataOwnership(); Task.Run(() => { - HandleTransferredData(e); + HandleTransferredData(data); + data?.Dispose(); }); } else { - HandleTransferredData(e); + HandleTransferredData(e.Data); } } - private void HandleTransferredData(TransferredEventArgs e) + private void HandleTransferredData(BufferedData? data) { - if (e.Data != null) + if (data != null) { try { @@ -153,7 +155,7 @@ public partial class Form1 : Form if (_useSystemDrawing) { - using (var img = Image.FromStream(e.Data.AsStream())) + using (var img = Image.FromStream(data.AsStream())) { if (img.PixelFormat == System.Drawing.Imaging.PixelFormat.Format1bppIndexed || img.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed) @@ -174,7 +176,7 @@ public partial class Form1 : Form } else { - using (var img = new ImageMagick.MagickImage(e.Data.AsSpan())) + using (var img = new ImageMagick.MagickImage(data.AsSpan())) { var format = ImageMagick.MagickFormat.Png; if (img.ColorType == ImageMagick.ColorType.Palette) @@ -198,7 +200,7 @@ public partial class Form1 : Form catch { } finally { - e.Dispose(); + data.Dispose(); } } } diff --git a/samples/WinUI3/MainWindow.xaml.cs b/samples/WinUI3/MainWindow.xaml.cs index d3473d6..52fc586 100644 --- a/samples/WinUI3/MainWindow.xaml.cs +++ b/samples/WinUI3/MainWindow.xaml.cs @@ -110,10 +110,6 @@ public sealed partial class MainWindow : Window } } catch { } - finally - { - e.Dispose(); - } } private void Session_SourceDisabled(TwainAppSession sender, TWIdentityWrapper e) diff --git a/src/NTwain/Events/TransferredEventArgs.cs b/src/NTwain/Events/TransferredEventArgs.cs index fe9f607..e8cf4b6 100644 --- a/src/NTwain/Events/TransferredEventArgs.cs +++ b/src/NTwain/Events/TransferredEventArgs.cs @@ -34,11 +34,14 @@ public class TransferredEventArgs : EventArgs, IDisposable /// public bool IsImage { get; } - private readonly BufferedData? _data; + private BufferedData? _data; + private bool _dataOwnershipTransferred; + /// - /// The complete file data if memory was involved in the transfer. - /// IMPORTANT: Content of this array will not be valid once - /// this event arg has been disposed. + /// Gets the transferred data. + /// IMPORTANT: This data is only valid during the event handler execution. + /// If you need to keep the data after the event handler returns, call first. + /// Otherwise, the data will be automatically disposed when the event completes. /// public BufferedData? Data => _data; @@ -73,8 +76,51 @@ public class TransferredEventArgs : EventArgs, IDisposable return _twain.WrapInSTS(DGImage.ExtImageInfo.Get(_twain.AppIdentity, _twain.CurrentSource, ref container)); } + /// + /// Transfers ownership of the in-memory data to the caller, preventing automatic disposal. + /// Use this when you need to process the data asynchronously or keep it beyond the event handler scope. + /// After calling this, you MUST manually dispose the returned BufferedData when finished. + /// + /// + /// The buffered data with transferred ownership, or null if no data exists or ownership was already transferred. + /// + /// + /// + /// private async void OnTransferred(TwainAppSession sender, TransferredEventArgs e) + /// { + /// var data = e.TakeDataOwnership(); // Take ownership + /// if (data != null) + /// { + /// try + /// { + /// await ProcessDataAsync(data); // Can use beyond event handler + /// } + /// finally + /// { + /// data.Dispose(); // Must dispose when done + /// } + /// } + /// } + /// + /// + public BufferedData? TakeDataOwnership() + { + if (_dataOwnershipTransferred || _data == null) + return null; + + _dataOwnershipTransferred = true; + var data = _data; + _data = null; + return data; + } + public void Dispose() { - _data?.Dispose(); + // Only dispose if ownership wasn't transferred + if (!_dataOwnershipTransferred && _data != null) + { + _data.Dispose(); + _data = null; + } } } \ No newline at end of file diff --git a/src/NTwain/TransferLoopThread.Logic.cs b/src/NTwain/TransferLoopThread.Logic.cs index 5370f08..57d0a88 100644 --- a/src/NTwain/TransferLoopThread.Logic.cs +++ b/src/NTwain/TransferLoopThread.Logic.cs @@ -224,10 +224,7 @@ partial class TransferLoopThread var args = new TransferredEventArgs(info, data); _twain.RaiseTransferred(args); } - catch - { - data.Dispose(); - } + catch { } } } @@ -314,10 +311,7 @@ partial class TransferLoopThread var args = new TransferredEventArgs(_twain, info, null, data); _twain.RaiseTransferred(args); } - catch - { - data.Dispose(); - } + catch { } } diff --git a/src/NTwain/TwainAppSession.TwainState.cs b/src/NTwain/TwainAppSession.TwainState.cs index 5841ca2..6504090 100644 --- a/src/NTwain/TwainAppSession.TwainState.cs +++ b/src/NTwain/TwainAppSession.TwainState.cs @@ -66,7 +66,7 @@ partial class TwainAppSession internal void RaiseTransferred(TransferredEventArgs args) { // no wait here, app can handle at its leisure - RaiseEvent(Transferred, args); + RaiseEvent(Transferred, args, static e => e.Dispose()); } @@ -139,17 +139,24 @@ partial class TwainAppSession /// The type of event arguments. /// The event handler to invoke. /// The event arguments. - protected void RaiseEvent(EventHandler? eventHandler, TEventArgs eventArgs) + /// + protected void RaiseEvent(EventHandler? eventHandler, TEventArgs eventArgs, + Action? completeCallback = null) { if (eventHandler == null) return; if (AppThreadContext != null) { - AppThreadContext.Post(_ => eventHandler(this, eventArgs), null); + AppThreadContext.Post(_ => + { + eventHandler(this, eventArgs); + completeCallback?.Invoke(eventArgs); + }, null); } else { eventHandler(this, eventArgs); + completeCallback?.Invoke(eventArgs); } }