mirror of
https://github.com/soukoku/ntwain.git
synced 2025-11-24 08:47:06 +08:00
Added UI thread sync placeholder obj & methods.
This commit is contained in:
@@ -44,7 +44,7 @@ namespace NTwain
|
||||
public ReturnCode ShowUI(IntPtr windowHandle, bool modal = false)
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
Session.Invoke(() =>
|
||||
Session.InternalInvoke(() =>
|
||||
{
|
||||
var ui = new TW_USERINTERFACE
|
||||
{
|
||||
@@ -72,7 +72,7 @@ namespace NTwain
|
||||
public ReturnCode Enable(bool showUI, IntPtr windowHandle, bool modal = false)
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
Session.Invoke(() =>
|
||||
Session.InternalInvoke(() =>
|
||||
{
|
||||
var ui = new TW_USERINTERFACE
|
||||
{
|
||||
|
||||
@@ -25,6 +25,10 @@ namespace NTwain.Threading
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: do something
|
||||
}
|
||||
finally
|
||||
{
|
||||
waiter?.Set();
|
||||
|
||||
22
src/NTwain/Threading/IThreadContext.cs
Normal file
22
src/NTwain/Threading/IThreadContext.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace NTwain.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for running some work on an associated thread.
|
||||
/// </summary>
|
||||
interface IThreadContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs the action synchronously on the associated thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
void Invoke(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the action asynchronously on the associated thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
void BeginInvoke(Action action);
|
||||
}
|
||||
}
|
||||
95
src/NTwain/Threading/UIThreadContext.cs
Normal file
95
src/NTwain/Threading/UIThreadContext.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NTwain.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="IThreadContext"/> using <see cref="SynchronizationContext"/>
|
||||
/// for running actions on an UI thread.
|
||||
/// </summary>
|
||||
class UIThreadContext : IThreadContext
|
||||
{
|
||||
private readonly SynchronizationContext context;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UIThreadContext"/> using
|
||||
/// the <see cref="SynchronizationContext.Current"/>.
|
||||
/// </summary>
|
||||
public UIThreadContext() : this(SynchronizationContext.Current)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UIThreadContext"/> using
|
||||
/// the specified <see cref="SynchronizationContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public UIThreadContext(SynchronizationContext context)
|
||||
{
|
||||
this.context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the action asynchronously on the <see cref="SynchronizationContext"/> thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void BeginInvoke(Action action)
|
||||
{
|
||||
if (action == null) return;
|
||||
|
||||
context.Post(o =>
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: do something
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the action synchronously on the <see cref="SynchronizationContext"/> thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void Invoke(Action action)
|
||||
{
|
||||
if (action == null) return;
|
||||
|
||||
Exception error = null;
|
||||
context.Send(o =>
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
}, null);
|
||||
|
||||
if (error != null) { Rethrow(error); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rethrows the specified excetion while keeping stack trace.
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
static void Rethrow(Exception ex)
|
||||
{
|
||||
typeof(Exception).GetMethod("PrepForRemoting",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(ex, new object[0]);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
@@ -15,7 +16,7 @@ namespace NTwain.Threading
|
||||
/// <summary>
|
||||
/// Provides an internal message pump on Windows using a background thread.
|
||||
/// </summary>
|
||||
class WinMsgLoop
|
||||
class WinMsgLoop : IThreadContext
|
||||
{
|
||||
static ushort classAtom;
|
||||
static IntPtr hInstance;
|
||||
@@ -143,10 +144,6 @@ namespace NTwain.Threading
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
UnsafeNativeMethods.PostQuitMessage(0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// clear queue
|
||||
@@ -163,7 +160,7 @@ namespace NTwain.Threading
|
||||
loopThread.Start();
|
||||
startWaiter.Wait();
|
||||
|
||||
if (startErr != null) throw startErr;
|
||||
if (startErr != null) Rethrow(startErr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,10 +190,7 @@ namespace NTwain.Threading
|
||||
return loopThread == Thread.CurrentThread || loopThread == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the action synchronously on the internal message pump thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
|
||||
public void Invoke(Action action)
|
||||
{
|
||||
if (IsSameThread())
|
||||
@@ -216,10 +210,7 @@ namespace NTwain.Threading
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the action asynchronously on the internal message pump thread.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
|
||||
public void BeginInvoke(Action action)
|
||||
{
|
||||
if (hWnd == IntPtr.Zero) action();
|
||||
@@ -231,6 +222,17 @@ namespace NTwain.Threading
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Rethrows the specified excetion while keeping stack trace.
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
static void Rethrow(Exception ex)
|
||||
{
|
||||
typeof(Exception).GetMethod("PrepForRemoting",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(ex, new object[0]);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal static class UnsafeNativeMethods
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NTwain
|
||||
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
||||
{
|
||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
||||
BeginInvoke(() =>
|
||||
InternalBeginInvoke(() =>
|
||||
{
|
||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: in BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
||||
HandleSourceMsg(msg);
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace NTwain
|
||||
{
|
||||
internal readonly TwainConfig Config;
|
||||
|
||||
private IntPtr _hWnd;
|
||||
// cache generated twain sources so if you get same source from same session it'll return the same object
|
||||
readonly Dictionary<string, DataSource> _ownedSources = new Dictionary<string, DataSource>();
|
||||
// need to keep delegate around to prevent GC
|
||||
@@ -28,6 +27,9 @@ namespace NTwain
|
||||
// for windows only
|
||||
readonly WinMsgLoop _winMsgLoop;
|
||||
|
||||
private IntPtr _hWnd;
|
||||
private IThreadContext _extSyncContext;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="TwainSession"/>.
|
||||
@@ -49,27 +51,53 @@ namespace NTwain
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously invokes an action on the internal thread if possible.
|
||||
/// Sets the optional synchronization context.
|
||||
/// Because most TWAIN-related things are happening on a different thread,
|
||||
/// this allows events to be raised on the thread associated with this context and
|
||||
/// may be useful if you want to handle them in the UI thread.
|
||||
/// </summary>
|
||||
/// <param name="context">Usually you want to use <see cref="SynchronizationContext.Current"/> while on the UI thread.</param>
|
||||
public void SetSynchronizationContext(SynchronizationContext context)
|
||||
{
|
||||
_extSyncContext = new UIThreadContext(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously invokes an action on the external user thread if possible.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public void Invoke(Action action)
|
||||
void ExternalInvoke(Action action)
|
||||
{
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
if (_extSyncContext != null) _extSyncContext.Invoke(action);
|
||||
action();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously invokes an action on the external user thread if possible.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
void ExternalBeginInvoke(Action action)
|
||||
{
|
||||
if (_extSyncContext != null) _extSyncContext.BeginInvoke(action);
|
||||
action();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously invokes an action on the internal TWAIN thread if possible.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
internal void InternalInvoke(Action action)
|
||||
{
|
||||
if (_winMsgLoop != null) _winMsgLoop.Invoke(action);
|
||||
else action();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously invokes an action on the internal thread if possible.
|
||||
/// Asynchronously invokes an action on the internal TWAIN thread if possible.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public void BeginInvoke(Action action)
|
||||
void InternalBeginInvoke(Action action)
|
||||
{
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
|
||||
if (_winMsgLoop != null) _winMsgLoop.BeginInvoke(action);
|
||||
else action();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user