From 9cd20f3e627c14a3a28b6e7651e1e8a722c13e3a Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Mon, 10 Apr 2023 08:02:38 -0400 Subject: [PATCH] Changed to use SynchronizationContext instead of the DIY threadmarshaller. --- samples/WinForm32/Form1.cs | 3 +- src/NTwain/ThreadMarshaller.Windows.cs | 58 ------------------------ src/NTwain/ThreadMarshaller.cs | 46 ------------------- src/NTwain/TwainAppSession.Callbacks.cs | 13 +++--- src/NTwain/TwainAppSession.PropEvents.cs | 15 +++--- src/NTwain/TwainAppSession.Windows.cs | 6 +-- src/NTwain/TwainAppSession.Xfers.cs | 37 +++++++-------- src/NTwain/TwainAppSession.cs | 8 ++-- 8 files changed, 39 insertions(+), 147 deletions(-) delete mode 100644 src/NTwain/ThreadMarshaller.Windows.cs delete mode 100644 src/NTwain/ThreadMarshaller.cs diff --git a/samples/WinForm32/Form1.cs b/samples/WinForm32/Form1.cs index 1a7a3dc..a656e5f 100644 --- a/samples/WinForm32/Form1.cs +++ b/samples/WinForm32/Form1.cs @@ -11,6 +11,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Versioning; +using System.Threading; using System.Windows.Forms; namespace WinFormSample @@ -28,7 +29,7 @@ namespace WinFormSample TWPlatform.PreferLegacyDSM = false; - twain = new TwainAppSession(new WinformMarshaller(this), Assembly.GetExecutingAssembly().Location); + twain = new TwainAppSession(SynchronizationContext.Current!, Assembly.GetExecutingAssembly().Location); twain.StateChanged += Twain_StateChanged; twain.DefaultSourceChanged += Twain_DefaultSourceChanged; twain.CurrentSourceChanged += Twain_CurrentSourceChanged; diff --git a/src/NTwain/ThreadMarshaller.Windows.cs b/src/NTwain/ThreadMarshaller.Windows.cs deleted file mode 100644 index aa1d0e7..0000000 --- a/src/NTwain/ThreadMarshaller.Windows.cs +++ /dev/null @@ -1,58 +0,0 @@ -#if WINDOWS || NETFRAMEWORK -using System; -using System.Windows.Forms; -using System.Windows.Threading; - -namespace NTwain -{ - /// - /// An that can be used - /// to integrate with - /// an existing Winforms app. - /// - public class WinformMarshaller : IThreadMarshaller - { - private readonly Control control; - - public WinformMarshaller(System.Windows.Forms.Control control) - { - this.control = control; - } - - public void BeginInvoke(Delegate work, params object[] args) - { - control.BeginInvoke(work, args); - } - - public object? Invoke(Delegate work, params object[] args) - { - return control.Invoke(work, args); - } - } - - /// - /// An that can be used - /// to integrate with - /// an existing WPF app. - /// - public class WpfMarshaller : IThreadMarshaller - { - private readonly Dispatcher dispatcher; - - public WpfMarshaller(Dispatcher dispatcher) - { - this.dispatcher = dispatcher; - } - - public void BeginInvoke(Delegate work, params object[] args) - { - dispatcher.BeginInvoke(work, args); - } - - public object? Invoke(Delegate work, params object[] args) - { - return dispatcher.Invoke(work, args); - } - } -} -#endif diff --git a/src/NTwain/ThreadMarshaller.cs b/src/NTwain/ThreadMarshaller.cs deleted file mode 100644 index 2a1328b..0000000 --- a/src/NTwain/ThreadMarshaller.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -namespace NTwain -{ - /// - /// Allows work to be marshalled to a different (usually UI) thread as necessary. - /// - public interface IThreadMarshaller - { - /// - /// Starts work asynchronously and returns immediately. - /// - /// - /// - void BeginInvoke(Delegate work, params object[] args); - - /// - /// Starts work synchronously until it returns. - /// - /// - /// - /// - object? Invoke(Delegate work, params object[] args); - } - - /// - /// No marshalling occurs. Invokes happen right in place synchronously. - /// - public class InPlaceMarshaller : IThreadMarshaller - { - /// - /// No async invocation here. - /// - /// - /// - public void BeginInvoke(Delegate work, params object[] args) - { - work.DynamicInvoke(args); - } - - public object? Invoke(Delegate work, params object[] args) - { - return work.DynamicInvoke(args); - } - } -} diff --git a/src/NTwain/TwainAppSession.Callbacks.cs b/src/NTwain/TwainAppSession.Callbacks.cs index ddbca66..bb93bcb 100644 --- a/src/NTwain/TwainAppSession.Callbacks.cs +++ b/src/NTwain/TwainAppSession.Callbacks.cs @@ -112,23 +112,24 @@ namespace NTwain if (!_inTransfer) { // this should be done on ui thread (or same one that enabled the ds) - _uiThreadMarshaller.BeginInvoke(() => + _uiThreadMarshaller.Post(obj => { - DisableSource(); - }); + ((TwainAppSession)obj!).DisableSource(); + }, this); } break; case MSG.DEVICEEVENT: if (DeviceEvent != null && DGControl.DeviceEvent.Get(ref _appIdentity, ref _currentDS, out TW_DEVICEEVENT de) == TWRC.SUCCESS) { - _uiThreadMarshaller.BeginInvoke(() => + _uiThreadMarshaller.Post(obj => { try { - DeviceEvent.Invoke(this, de); + var twain = (TwainAppSession)obj!; + twain.DeviceEvent!.Invoke(twain, de); } catch { } - }); + }, this); } break; } diff --git a/src/NTwain/TwainAppSession.PropEvents.cs b/src/NTwain/TwainAppSession.PropEvents.cs index 746cd54..f095d5e 100644 --- a/src/NTwain/TwainAppSession.PropEvents.cs +++ b/src/NTwain/TwainAppSession.PropEvents.cs @@ -57,17 +57,14 @@ namespace NTwain if (_state != value) { _state = value; - if (StateChanged != null) + _uiThreadMarshaller.Send(obj => { - _uiThreadMarshaller.Invoke(() => + try { - try - { - StateChanged.Invoke(this, value); - } - catch { } - }); - } + ((TwainAppSession)obj!).StateChanged?.Invoke(this, value); + } + catch { } + }, this); } } } diff --git a/src/NTwain/TwainAppSession.Windows.cs b/src/NTwain/TwainAppSession.Windows.cs index 806f572..f818c36 100644 --- a/src/NTwain/TwainAppSession.Windows.cs +++ b/src/NTwain/TwainAppSession.Windows.cs @@ -60,16 +60,16 @@ namespace NTwain bool IMessageFilter.PreFilterMessage(ref Message m) { - return CheckIfTwainMessage(m.HWnd, m.Msg, m.WParam, m.LParam); + return WndProc(m.HWnd, m.Msg, m.WParam, m.LParam); } IntPtr WpfHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { - handled = CheckIfTwainMessage(hwnd, msg, wParam, lParam); + handled = WndProc(hwnd, msg, wParam, lParam); return IntPtr.Zero; } - private bool CheckIfTwainMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) + private bool WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) { // this handles the message from a typical WndProc message loop and checks if it's for the TWAIN source. bool handled = false; diff --git a/src/NTwain/TwainAppSession.Xfers.cs b/src/NTwain/TwainAppSession.Xfers.cs index 6916f82..601b4cf 100644 --- a/src/NTwain/TwainAppSession.Xfers.cs +++ b/src/NTwain/TwainAppSession.Xfers.cs @@ -74,14 +74,15 @@ namespace NTwain var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ); if (TransferReady != null) { - _uiThreadMarshaller.Invoke(() => + _uiThreadMarshaller.Send(_ => { try { + TransferReady.Invoke(this, readyArgs); } catch { } // don't let consumer kill the loop if they have exception - }); + }, null); } if (readyArgs.Cancel == CancelType.EndNow || _closeDsRequested) @@ -154,17 +155,15 @@ namespace NTwain } catch (Exception ex) { - if (TransferError != null) + _uiThreadMarshaller.Send(obj => { - _uiThreadMarshaller.Invoke(() => + try { - try - { - TransferError?.Invoke(this, new TransferErrorEventArgs(ex)); - } - catch { } - }); - } + var twain = ((TwainAppSession)obj!); + twain.TransferError?.Invoke(twain, new TransferErrorEventArgs(ex)); + } + catch { } + }, this); } } } while (sts.RC == TWRC.SUCCESS && pending.Count != 0); @@ -173,10 +172,10 @@ namespace NTwain HandleXferCode(sts); if (State >= STATE.S5) { - _uiThreadMarshaller.Invoke(() => + _uiThreadMarshaller.Send(obj => { - DisableSource(); - }); + ((TwainAppSession)obj!).DisableSource(); + }, this); } _inTransfer = false; } @@ -191,13 +190,11 @@ namespace NTwain break; case TWRC.CANCEL: // might eventually have option to cancel this or all like transfer ready - if (TransferCanceled != null) + _uiThreadMarshaller.Send(obj => { - _uiThreadMarshaller.Invoke(() => - { - TransferCanceled.Invoke(this, new TransferCanceledEventArgs()); - }); - }; + var twain = ((TwainAppSession)obj!); + twain.TransferCanceled?.Invoke(twain, new TransferCanceledEventArgs()); + }, this); TW_PENDINGXFERS pending = default; DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending); // todo: also reset? diff --git a/src/NTwain/TwainAppSession.cs b/src/NTwain/TwainAppSession.cs index fd23307..121756d 100644 --- a/src/NTwain/TwainAppSession.cs +++ b/src/NTwain/TwainAppSession.cs @@ -23,7 +23,7 @@ namespace NTwain /// /// /// - public TwainAppSession(IThreadMarshaller uiThreadMarshaller, + public TwainAppSession(SynchronizationContext uiThreadMarshaller, string exeFilePath, TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) : this(uiThreadMarshaller, FileVersionInfo.GetVersionInfo(exeFilePath), appLanguage, appCountry) @@ -35,7 +35,7 @@ namespace NTwain /// /// /// - public TwainAppSession(IThreadMarshaller uiThreadMarshaller, + public TwainAppSession(SynchronizationContext uiThreadMarshaller, FileVersionInfo appInfo, TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) : this(uiThreadMarshaller, @@ -57,7 +57,7 @@ namespace NTwain /// /// /// - public TwainAppSession(IThreadMarshaller uiThreadMarshaller, + public TwainAppSession(SynchronizationContext uiThreadMarshaller, string companyName, string productFamily, string productName, Version productVersion, string productDescription = "", TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA, @@ -104,7 +104,7 @@ namespace NTwain #endif // test threads a bit //readonly BlockingCollection _bgPendingMsgs = new(); - private readonly IThreadMarshaller _uiThreadMarshaller; + private readonly SynchronizationContext _uiThreadMarshaller; bool _closeDsRequested; bool _inTransfer; readonly AutoResetEvent _xferReady = new(false);