mirror of
https://github.com/soukoku/ntwain.git
synced 2025-04-29 19:43:19 +08:00
Added first naive dispatcher loop without win message pump.
This commit is contained in:
parent
47ae473316
commit
9d7b1154f5
28
src/NTwain/Internals/ExceptionExtensions.cs
Normal file
28
src/NTwain/Internals/ExceptionExtensions.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
static class ExceptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Rethrows the specified excetion while keeping stack trace
|
||||
/// if not null.
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
public static void TryRethrow(this Exception ex)
|
||||
{
|
||||
if (ex != null)
|
||||
{
|
||||
typeof(Exception).GetMethod("PrepForRemoting",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(ex, new object[0]);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ namespace NTwain.Threading
|
||||
private ManualResetEventSlim waiter;
|
||||
private Action action;
|
||||
|
||||
public Exception Error { get; private set; }
|
||||
|
||||
public ActionItem(Action action)
|
||||
{
|
||||
this.action = action;
|
||||
@ -27,7 +29,7 @@ namespace NTwain.Threading
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: do something
|
||||
Error = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
110
src/NTwain/Threading/DispatcherLoop.cs
Normal file
110
src/NTwain/Threading/DispatcherLoop.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using NTwain.Data.Win32;
|
||||
using NTwain.Internals;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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;
|
||||
using System.Threading;
|
||||
|
||||
namespace NTwain.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an internal message pump on non-Windows using a background thread.
|
||||
/// </summary>
|
||||
class DispatcherLoop : IThreadContext
|
||||
{
|
||||
readonly TwainSession session;
|
||||
BlockingCollection<ActionItem> actionQueue;
|
||||
CancellationTokenSource stopToken;
|
||||
|
||||
Thread loopThread;
|
||||
|
||||
public DispatcherLoop(TwainSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (loopThread != null) return;
|
||||
|
||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(WinMsgLoop)}.{nameof(Start)}()");
|
||||
|
||||
actionQueue = new BlockingCollection<ActionItem>();
|
||||
stopToken = new CancellationTokenSource();
|
||||
|
||||
// startWaiter ensures thread is running before this method returns
|
||||
using (var startWaiter = new ManualResetEventSlim())
|
||||
{
|
||||
loopThread = new Thread(new ThreadStart(() =>
|
||||
{
|
||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: starting msg pump...");
|
||||
|
||||
startWaiter.Set();
|
||||
|
||||
try
|
||||
{
|
||||
while (actionQueue.TryTake(out ActionItem work, -1, stopToken.Token))
|
||||
{
|
||||
work.DoAction();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
finally
|
||||
{
|
||||
// clear queue
|
||||
actionQueue.Dispose();
|
||||
|
||||
loopThread = null;
|
||||
}
|
||||
}));
|
||||
loopThread.IsBackground = true;
|
||||
loopThread.TrySetApartmentState(ApartmentState.STA);
|
||||
loopThread.Start();
|
||||
startWaiter.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!stopToken.IsCancellationRequested) stopToken.Cancel();
|
||||
}
|
||||
|
||||
bool IsSameThread()
|
||||
{
|
||||
return loopThread == Thread.CurrentThread || loopThread == null;
|
||||
}
|
||||
|
||||
|
||||
public void Invoke(Action action)
|
||||
{
|
||||
if (IsSameThread())
|
||||
{
|
||||
// ok
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
// queue up work
|
||||
using (var waiter = new ManualResetEventSlim())
|
||||
{
|
||||
var work = new ActionItem(waiter, action);
|
||||
actionQueue.TryAdd(work);
|
||||
waiter.Wait();
|
||||
work.Error.TryRethrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginInvoke(Action action)
|
||||
{
|
||||
actionQueue.TryAdd(new ActionItem(action));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using NTwain.Internals;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -68,18 +69,7 @@ namespace NTwain.Threading
|
||||
}
|
||||
}, 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;
|
||||
error.TryRethrow();
|
||||
}
|
||||
|
||||
void IThreadContext.Start()
|
||||
|
@ -1,4 +1,5 @@
|
||||
using NTwain.Data.Win32;
|
||||
using NTwain.Internals;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@ -160,7 +161,7 @@ namespace NTwain.Threading
|
||||
loopThread.Start();
|
||||
startWaiter.Wait();
|
||||
|
||||
if (startErr != null) Rethrow(startErr);
|
||||
startErr.TryRethrow();
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,9 +204,11 @@ namespace NTwain.Threading
|
||||
// queue up work
|
||||
using (var waiter = new ManualResetEventSlim())
|
||||
{
|
||||
actionQueue.Enqueue(new ActionItem(waiter, action));
|
||||
var work = new ActionItem(waiter, action);
|
||||
actionQueue.Enqueue(work);
|
||||
UnsafeNativeMethods.PostMessageW(hWnd, dequeMsg, IntPtr.Zero, IntPtr.Zero);
|
||||
waiter.Wait();
|
||||
work.Error.TryRethrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,17 +225,6 @@ 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
|
||||
{
|
||||
|
@ -44,6 +44,8 @@ namespace NTwain
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
_internalContext = new DispatcherLoop(this);
|
||||
break;
|
||||
default:
|
||||
_internalContext = new WinMsgLoop(this);
|
||||
_callback32Delegate = new Callback32(Handle32BitCallback);
|
||||
|
Loading…
Reference in New Issue
Block a user