mirror of
https://github.com/soukoku/ntwain.git
synced 2025-09-19 10:08:00 +08:00
Changed to use SynchronizationContext instead of the DIY threadmarshaller.
This commit is contained in:
@@ -11,6 +11,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace WinFormSample
|
namespace WinFormSample
|
||||||
@@ -28,7 +29,7 @@ namespace WinFormSample
|
|||||||
|
|
||||||
TWPlatform.PreferLegacyDSM = false;
|
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.StateChanged += Twain_StateChanged;
|
||||||
twain.DefaultSourceChanged += Twain_DefaultSourceChanged;
|
twain.DefaultSourceChanged += Twain_DefaultSourceChanged;
|
||||||
twain.CurrentSourceChanged += Twain_CurrentSourceChanged;
|
twain.CurrentSourceChanged += Twain_CurrentSourceChanged;
|
||||||
|
@@ -1,58 +0,0 @@
|
|||||||
#if WINDOWS || NETFRAMEWORK
|
|
||||||
using System;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Windows.Threading;
|
|
||||||
|
|
||||||
namespace NTwain
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IThreadMarshaller"/> that can be used
|
|
||||||
/// to integrate <see cref="TwainAppSession"/> with
|
|
||||||
/// an existing Winforms app.
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IThreadMarshaller"/> that can be used
|
|
||||||
/// to integrate <see cref="TwainAppSession"/> with
|
|
||||||
/// an existing WPF app.
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
@@ -1,46 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace NTwain
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Allows work to be marshalled to a different (usually UI) thread as necessary.
|
|
||||||
/// </summary>
|
|
||||||
public interface IThreadMarshaller
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Starts work asynchronously and returns immediately.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="work"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
void BeginInvoke(Delegate work, params object[] args);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts work synchronously until it returns.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="work"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
object? Invoke(Delegate work, params object[] args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// No marshalling occurs. Invokes happen right in place synchronously.
|
|
||||||
/// </summary>
|
|
||||||
public class InPlaceMarshaller : IThreadMarshaller
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No async invocation here.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="work"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void BeginInvoke(Delegate work, params object[] args)
|
|
||||||
{
|
|
||||||
work.DynamicInvoke(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? Invoke(Delegate work, params object[] args)
|
|
||||||
{
|
|
||||||
return work.DynamicInvoke(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -112,23 +112,24 @@ namespace NTwain
|
|||||||
if (!_inTransfer)
|
if (!_inTransfer)
|
||||||
{
|
{
|
||||||
// this should be done on ui thread (or same one that enabled the ds)
|
// 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;
|
break;
|
||||||
case MSG.DEVICEEVENT:
|
case MSG.DEVICEEVENT:
|
||||||
if (DeviceEvent != null && DGControl.DeviceEvent.Get(ref _appIdentity, ref _currentDS, out TW_DEVICEEVENT de) == TWRC.SUCCESS)
|
if (DeviceEvent != null && DGControl.DeviceEvent.Get(ref _appIdentity, ref _currentDS, out TW_DEVICEEVENT de) == TWRC.SUCCESS)
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.BeginInvoke(() =>
|
_uiThreadMarshaller.Post(obj =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeviceEvent.Invoke(this, de);
|
var twain = (TwainAppSession)obj!;
|
||||||
|
twain.DeviceEvent!.Invoke(twain, de);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
});
|
}, this);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -57,17 +57,14 @@ namespace NTwain
|
|||||||
if (_state != value)
|
if (_state != value)
|
||||||
{
|
{
|
||||||
_state = value;
|
_state = value;
|
||||||
if (StateChanged != null)
|
_uiThreadMarshaller.Send(obj =>
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.Invoke(() =>
|
try
|
||||||
{
|
{
|
||||||
try
|
((TwainAppSession)obj!).StateChanged?.Invoke(this, value);
|
||||||
{
|
}
|
||||||
StateChanged.Invoke(this, value);
|
catch { }
|
||||||
}
|
}, this);
|
||||||
catch { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,16 +60,16 @@ namespace NTwain
|
|||||||
|
|
||||||
bool IMessageFilter.PreFilterMessage(ref Message m)
|
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)
|
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;
|
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.
|
// this handles the message from a typical WndProc message loop and checks if it's for the TWAIN source.
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
@@ -74,14 +74,15 @@ namespace NTwain
|
|||||||
var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ);
|
var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ);
|
||||||
if (TransferReady != null)
|
if (TransferReady != null)
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.Invoke(() =>
|
_uiThreadMarshaller.Send(_ =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
TransferReady.Invoke(this, readyArgs);
|
TransferReady.Invoke(this, readyArgs);
|
||||||
}
|
}
|
||||||
catch { } // don't let consumer kill the loop if they have exception
|
catch { } // don't let consumer kill the loop if they have exception
|
||||||
});
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readyArgs.Cancel == CancelType.EndNow || _closeDsRequested)
|
if (readyArgs.Cancel == CancelType.EndNow || _closeDsRequested)
|
||||||
@@ -154,17 +155,15 @@ namespace NTwain
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (TransferError != null)
|
_uiThreadMarshaller.Send(obj =>
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.Invoke(() =>
|
try
|
||||||
{
|
{
|
||||||
try
|
var twain = ((TwainAppSession)obj!);
|
||||||
{
|
twain.TransferError?.Invoke(twain, new TransferErrorEventArgs(ex));
|
||||||
TransferError?.Invoke(this, new TransferErrorEventArgs(ex));
|
}
|
||||||
}
|
catch { }
|
||||||
catch { }
|
}, this);
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (sts.RC == TWRC.SUCCESS && pending.Count != 0);
|
} while (sts.RC == TWRC.SUCCESS && pending.Count != 0);
|
||||||
@@ -173,10 +172,10 @@ namespace NTwain
|
|||||||
HandleXferCode(sts);
|
HandleXferCode(sts);
|
||||||
if (State >= STATE.S5)
|
if (State >= STATE.S5)
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.Invoke(() =>
|
_uiThreadMarshaller.Send(obj =>
|
||||||
{
|
{
|
||||||
DisableSource();
|
((TwainAppSession)obj!).DisableSource();
|
||||||
});
|
}, this);
|
||||||
}
|
}
|
||||||
_inTransfer = false;
|
_inTransfer = false;
|
||||||
}
|
}
|
||||||
@@ -191,13 +190,11 @@ namespace NTwain
|
|||||||
break;
|
break;
|
||||||
case TWRC.CANCEL:
|
case TWRC.CANCEL:
|
||||||
// might eventually have option to cancel this or all like transfer ready
|
// might eventually have option to cancel this or all like transfer ready
|
||||||
if (TransferCanceled != null)
|
_uiThreadMarshaller.Send(obj =>
|
||||||
{
|
{
|
||||||
_uiThreadMarshaller.Invoke(() =>
|
var twain = ((TwainAppSession)obj!);
|
||||||
{
|
twain.TransferCanceled?.Invoke(twain, new TransferCanceledEventArgs());
|
||||||
TransferCanceled.Invoke(this, new TransferCanceledEventArgs());
|
}, this);
|
||||||
});
|
|
||||||
};
|
|
||||||
TW_PENDINGXFERS pending = default;
|
TW_PENDINGXFERS pending = default;
|
||||||
DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending);
|
DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending);
|
||||||
// todo: also reset?
|
// todo: also reset?
|
||||||
|
@@ -23,7 +23,7 @@ namespace NTwain
|
|||||||
/// <param name="exeFilePath"></param>
|
/// <param name="exeFilePath"></param>
|
||||||
/// <param name="appLanguage"></param>
|
/// <param name="appLanguage"></param>
|
||||||
/// <param name="appCountry"></param>
|
/// <param name="appCountry"></param>
|
||||||
public TwainAppSession(IThreadMarshaller uiThreadMarshaller,
|
public TwainAppSession(SynchronizationContext uiThreadMarshaller,
|
||||||
string exeFilePath,
|
string exeFilePath,
|
||||||
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) :
|
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) :
|
||||||
this(uiThreadMarshaller, FileVersionInfo.GetVersionInfo(exeFilePath), appLanguage, appCountry)
|
this(uiThreadMarshaller, FileVersionInfo.GetVersionInfo(exeFilePath), appLanguage, appCountry)
|
||||||
@@ -35,7 +35,7 @@ namespace NTwain
|
|||||||
/// <param name="appInfo"></param>
|
/// <param name="appInfo"></param>
|
||||||
/// <param name="appLanguage"></param>
|
/// <param name="appLanguage"></param>
|
||||||
/// <param name="appCountry"></param>
|
/// <param name="appCountry"></param>
|
||||||
public TwainAppSession(IThreadMarshaller uiThreadMarshaller,
|
public TwainAppSession(SynchronizationContext uiThreadMarshaller,
|
||||||
FileVersionInfo appInfo,
|
FileVersionInfo appInfo,
|
||||||
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) :
|
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA) :
|
||||||
this(uiThreadMarshaller,
|
this(uiThreadMarshaller,
|
||||||
@@ -57,7 +57,7 @@ namespace NTwain
|
|||||||
/// <param name="appLanguage"></param>
|
/// <param name="appLanguage"></param>
|
||||||
/// <param name="appCountry"></param>
|
/// <param name="appCountry"></param>
|
||||||
/// <param name="supportedTypes"></param>
|
/// <param name="supportedTypes"></param>
|
||||||
public TwainAppSession(IThreadMarshaller uiThreadMarshaller,
|
public TwainAppSession(SynchronizationContext uiThreadMarshaller,
|
||||||
string companyName, string productFamily, string productName,
|
string companyName, string productFamily, string productName,
|
||||||
Version productVersion, string productDescription = "",
|
Version productVersion, string productDescription = "",
|
||||||
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA,
|
TWLG appLanguage = TWLG.ENGLISH_USA, TWCY appCountry = TWCY.USA,
|
||||||
@@ -104,7 +104,7 @@ namespace NTwain
|
|||||||
#endif
|
#endif
|
||||||
// test threads a bit
|
// test threads a bit
|
||||||
//readonly BlockingCollection<MSG> _bgPendingMsgs = new();
|
//readonly BlockingCollection<MSG> _bgPendingMsgs = new();
|
||||||
private readonly IThreadMarshaller _uiThreadMarshaller;
|
private readonly SynchronizationContext _uiThreadMarshaller;
|
||||||
bool _closeDsRequested;
|
bool _closeDsRequested;
|
||||||
bool _inTransfer;
|
bool _inTransfer;
|
||||||
readonly AutoResetEvent _xferReady = new(false);
|
readonly AutoResetEvent _xferReady = new(false);
|
||||||
|
Reference in New Issue
Block a user