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)
|
public ReturnCode ShowUI(IntPtr windowHandle, bool modal = false)
|
||||||
{
|
{
|
||||||
var rc = ReturnCode.Failure;
|
var rc = ReturnCode.Failure;
|
||||||
Session.Invoke(() =>
|
Session.InternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var ui = new TW_USERINTERFACE
|
var ui = new TW_USERINTERFACE
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@ namespace NTwain
|
|||||||
public ReturnCode Enable(bool showUI, IntPtr windowHandle, bool modal = false)
|
public ReturnCode Enable(bool showUI, IntPtr windowHandle, bool modal = false)
|
||||||
{
|
{
|
||||||
var rc = ReturnCode.Failure;
|
var rc = ReturnCode.Failure;
|
||||||
Session.Invoke(() =>
|
Session.InternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var ui = new TW_USERINTERFACE
|
var ui = new TW_USERINTERFACE
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ namespace NTwain.Threading
|
|||||||
{
|
{
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// TODO: do something
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
waiter?.Set();
|
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.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -15,7 +16,7 @@ namespace NTwain.Threading
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an internal message pump on Windows using a background thread.
|
/// Provides an internal message pump on Windows using a background thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class WinMsgLoop
|
class WinMsgLoop : IThreadContext
|
||||||
{
|
{
|
||||||
static ushort classAtom;
|
static ushort classAtom;
|
||||||
static IntPtr hInstance;
|
static IntPtr hInstance;
|
||||||
@@ -143,10 +144,6 @@ namespace NTwain.Threading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
|
||||||
UnsafeNativeMethods.PostQuitMessage(0);
|
|
||||||
}
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// clear queue
|
// clear queue
|
||||||
@@ -163,7 +160,7 @@ namespace NTwain.Threading
|
|||||||
loopThread.Start();
|
loopThread.Start();
|
||||||
startWaiter.Wait();
|
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;
|
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)
|
public void Invoke(Action action)
|
||||||
{
|
{
|
||||||
if (IsSameThread())
|
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)
|
public void BeginInvoke(Action action)
|
||||||
{
|
{
|
||||||
if (hWnd == IntPtr.Zero) 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]
|
[SuppressUnmanagedCodeSecurity]
|
||||||
internal static class UnsafeNativeMethods
|
internal static class UnsafeNativeMethods
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace NTwain
|
|||||||
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {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})");
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: in BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
||||||
HandleSourceMsg(msg);
|
HandleSourceMsg(msg);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace NTwain
|
namespace NTwain
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
internal readonly TwainConfig Config;
|
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
|
// 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>();
|
readonly Dictionary<string, DataSource> _ownedSources = new Dictionary<string, DataSource>();
|
||||||
// need to keep delegate around to prevent GC
|
// need to keep delegate around to prevent GC
|
||||||
@@ -28,6 +27,9 @@ namespace NTwain
|
|||||||
// for windows only
|
// for windows only
|
||||||
readonly WinMsgLoop _winMsgLoop;
|
readonly WinMsgLoop _winMsgLoop;
|
||||||
|
|
||||||
|
private IntPtr _hWnd;
|
||||||
|
private IThreadContext _extSyncContext;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new <see cref="TwainSession"/>.
|
/// Constructs a new <see cref="TwainSession"/>.
|
||||||
@@ -49,27 +51,53 @@ namespace NTwain
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="action"></param>
|
/// <param name="action"></param>
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
void ExternalInvoke(Action action)
|
||||||
public void Invoke(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);
|
if (_winMsgLoop != null) _winMsgLoop.Invoke(action);
|
||||||
else action();
|
else action();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously invokes an action on the internal thread if possible.
|
/// Asynchronously invokes an action on the internal TWAIN thread if possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="action"></param>
|
/// <param name="action"></param>
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
void InternalBeginInvoke(Action action)
|
||||||
public void BeginInvoke(Action action)
|
|
||||||
{
|
{
|
||||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
|
||||||
|
|
||||||
if (_winMsgLoop != null) _winMsgLoop.BeginInvoke(action);
|
if (_winMsgLoop != null) _winMsgLoop.BeginInvoke(action);
|
||||||
else action();
|
else action();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user