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.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;
|
||||
|
@@ -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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
@@ -57,17 +57,14 @@ namespace NTwain
|
||||
if (_state != value)
|
||||
{
|
||||
_state = value;
|
||||
if (StateChanged != null)
|
||||
{
|
||||
_uiThreadMarshaller.Invoke(() =>
|
||||
_uiThreadMarshaller.Send(obj =>
|
||||
{
|
||||
try
|
||||
{
|
||||
StateChanged.Invoke(this, value);
|
||||
((TwainAppSession)obj!).StateChanged?.Invoke(this, value);
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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.Invoke(() =>
|
||||
_uiThreadMarshaller.Send(obj =>
|
||||
{
|
||||
try
|
||||
{
|
||||
TransferError?.Invoke(this, new TransferErrorEventArgs(ex));
|
||||
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?
|
||||
|
@@ -23,7 +23,7 @@ namespace NTwain
|
||||
/// <param name="exeFilePath"></param>
|
||||
/// <param name="appLanguage"></param>
|
||||
/// <param name="appCountry"></param>
|
||||
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
|
||||
/// <param name="appInfo"></param>
|
||||
/// <param name="appLanguage"></param>
|
||||
/// <param name="appCountry"></param>
|
||||
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
|
||||
/// <param name="appLanguage"></param>
|
||||
/// <param name="appCountry"></param>
|
||||
/// <param name="supportedTypes"></param>
|
||||
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<MSG> _bgPendingMsgs = new();
|
||||
private readonly IThreadMarshaller _uiThreadMarshaller;
|
||||
private readonly SynchronizationContext _uiThreadMarshaller;
|
||||
bool _closeDsRequested;
|
||||
bool _inTransfer;
|
||||
readonly AutoResetEvent _xferReady = new(false);
|
||||
|
Reference in New Issue
Block a user