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);