mirror of
https://github.com/soukoku/ntwain.git
synced 2025-09-19 10:08:00 +08:00
Fixed stuck in Invoke.
This commit is contained in:
98
NTwain/MessageLoop.cs
Normal file
98
NTwain/MessageLoop.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using NTwain.Triplets;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
|
namespace NTwain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a message loop for old TWAIN to post or new TWAIN to synchronize callbacks.
|
||||||
|
/// </summary>
|
||||||
|
class MessageLoop
|
||||||
|
{
|
||||||
|
// mostly wraps around a dispatcher?
|
||||||
|
static MessageLoop _instance = new MessageLoop();
|
||||||
|
public static MessageLoop Instance { get { return _instance; } }
|
||||||
|
|
||||||
|
Dispatcher _dispatcher;
|
||||||
|
bool _started;
|
||||||
|
HwndSource _dummyWindow;
|
||||||
|
|
||||||
|
private MessageLoop() { }
|
||||||
|
public void EnsureStarted()
|
||||||
|
{
|
||||||
|
if (!_started)
|
||||||
|
{
|
||||||
|
var loopThread = new Thread(new ThreadStart(() =>
|
||||||
|
{
|
||||||
|
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||||
|
if (Dsm.IsWin)
|
||||||
|
{
|
||||||
|
// start a windows msg loop for old twain to post msgs
|
||||||
|
// the style values are purely guesses here with
|
||||||
|
// CS_NOCLOSE, WS_DISABLED, and WS_EX_NOACTIVATE
|
||||||
|
_dummyWindow = new HwndSource(0x0200, 0x8000000, 0x8000000, 0, 0, "NTWAIN_LOOPER", IntPtr.Zero);
|
||||||
|
}
|
||||||
|
Dispatcher.Run();
|
||||||
|
}));
|
||||||
|
loopThread.IsBackground = true;
|
||||||
|
loopThread.SetApartmentState(ApartmentState.STA);
|
||||||
|
loopThread.Start();
|
||||||
|
_started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr LoopHandle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _dummyWindow == null ? IntPtr.Zero : _dummyWindow.Handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginInvoke(Action action)
|
||||||
|
{
|
||||||
|
if (_dispatcher != null)
|
||||||
|
{
|
||||||
|
_dispatcher.BeginInvoke(DispatcherPriority.Normal, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invoke(Action action)
|
||||||
|
{
|
||||||
|
if (_dispatcher != null)
|
||||||
|
{
|
||||||
|
if (_dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//_dispatcher.Invoke(DispatcherPriority.Normal, action);
|
||||||
|
|
||||||
|
var man = new ManualResetEvent(false);
|
||||||
|
_dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
man.Set();
|
||||||
|
}));
|
||||||
|
man.WaitOne();
|
||||||
|
man.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddHook(HwndSourceHook hook)
|
||||||
|
{
|
||||||
|
if (_dummyWindow != null) { _dummyWindow.AddHook(hook); }
|
||||||
|
}
|
||||||
|
public void RemoveHook(HwndSourceHook hook)
|
||||||
|
{
|
||||||
|
if (_dummyWindow != null) { _dummyWindow.RemoveHook(hook); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -212,27 +212,31 @@ namespace NTwain
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public ReturnCode OpenManager()
|
public ReturnCode OpenManager()
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
MessageLoop.Instance.Invoke(() =>
|
||||||
var rc = DGControl.Parent.OpenDsm(MessageLoop.Instance.LoopHandle);
|
|
||||||
if (rc == ReturnCode.Success)
|
|
||||||
{
|
{
|
||||||
// if twain2 then get memory management functions
|
Debug.WriteLine(string.Format("Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
|
||||||
if ((_appId.DataFunctionalities & DataFunctionalities.Dsm2) == DataFunctionalities.Dsm2)
|
|
||||||
|
rc = DGControl.Parent.OpenDsm(MessageLoop.Instance.LoopHandle);
|
||||||
|
if (rc == ReturnCode.Success)
|
||||||
{
|
{
|
||||||
TWEntryPoint entry;
|
// if twain2 then get memory management functions
|
||||||
rc = DGControl.EntryPoint.Get(out entry);
|
if ((_appId.DataFunctionalities & DataFunctionalities.Dsm2) == DataFunctionalities.Dsm2)
|
||||||
if (rc == ReturnCode.Success)
|
|
||||||
{
|
{
|
||||||
MemoryManager.Instance.UpdateEntryPoint(entry);
|
TWEntryPoint entry;
|
||||||
Debug.WriteLine("Using TWAIN2 memory functions.");
|
rc = DGControl.EntryPoint.Get(out entry);
|
||||||
}
|
if (rc == ReturnCode.Success)
|
||||||
else
|
{
|
||||||
{
|
MemoryManager.Instance.UpdateEntryPoint(entry);
|
||||||
CloseManager();
|
Debug.WriteLine("Using TWAIN2 memory functions.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CloseManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,9 +246,13 @@ namespace NTwain
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public ReturnCode CloseManager()
|
public ReturnCode CloseManager()
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
MessageLoop.Instance.Invoke(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine(string.Format("Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
|
||||||
|
|
||||||
var rc = DGControl.Parent.CloseDsm(MessageLoop.Instance.LoopHandle);
|
rc = DGControl.Parent.CloseDsm(MessageLoop.Instance.LoopHandle);
|
||||||
|
});
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,15 +268,16 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(sourceProductName)) { throw new ArgumentException("Source name is required.", "sourceProductName"); }
|
if (string.IsNullOrEmpty(sourceProductName)) { throw new ArgumentException("Source name is required.", "sourceProductName"); }
|
||||||
|
|
||||||
Debug.WriteLine(string.Format("Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
MessageLoop.Instance.Invoke(() =>
|
||||||
var source = new TWIdentity();
|
|
||||||
source.ProductName = sourceProductName;
|
|
||||||
|
|
||||||
var rc = DGControl.Identity.OpenDS(source);
|
|
||||||
if (rc == ReturnCode.Success)
|
|
||||||
{
|
{
|
||||||
}
|
Debug.WriteLine(string.Format("Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
|
||||||
|
|
||||||
|
var source = new TWIdentity();
|
||||||
|
source.ProductName = sourceProductName;
|
||||||
|
|
||||||
|
rc = DGControl.Identity.OpenDS(source);
|
||||||
|
});
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,15 +290,19 @@ namespace NTwain
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public ReturnCode CloseSource()
|
public ReturnCode CloseSource()
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
MessageLoop.Instance.Invoke(() =>
|
||||||
var rc = DGControl.Identity.CloseDS();
|
|
||||||
if (rc == ReturnCode.Success)
|
|
||||||
{
|
{
|
||||||
MessageLoop.Instance.RemoveHook(HandleWndProcMessage);
|
Debug.WriteLine(string.Format("Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
|
||||||
_callbackObj = null;
|
|
||||||
SupportedCaps = null;
|
rc = DGControl.Identity.CloseDS();
|
||||||
}
|
if (rc == ReturnCode.Success)
|
||||||
|
{
|
||||||
|
MessageLoop.Instance.RemoveHook(HandleWndProcMessage);
|
||||||
|
_callbackObj = null;
|
||||||
|
SupportedCaps = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,12 +316,12 @@ namespace NTwain
|
|||||||
/// <exception cref="ArgumentNullException">context</exception>
|
/// <exception cref="ArgumentNullException">context</exception>
|
||||||
public ReturnCode EnableSource(SourceEnableMode mode, bool modal, IntPtr windowHandle)
|
public ReturnCode EnableSource(SourceEnableMode mode, bool modal, IntPtr windowHandle)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
|
|
||||||
|
|
||||||
ReturnCode rc = ReturnCode.Success;
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
|
||||||
MessageLoop.Instance.Invoke(() =>
|
MessageLoop.Instance.Invoke(() =>
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine(string.Format("Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
|
||||||
|
|
||||||
// app v2.2 or higher uses callback2
|
// app v2.2 or higher uses callback2
|
||||||
if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
|
if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
|
||||||
{
|
{
|
||||||
@@ -363,12 +376,12 @@ namespace NTwain
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected ReturnCode DisableSource()
|
protected ReturnCode DisableSource()
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
|
|
||||||
|
|
||||||
ReturnCode rc = ReturnCode.Success;
|
ReturnCode rc = ReturnCode.Success;
|
||||||
|
|
||||||
MessageLoop.Instance.Invoke(() =>
|
MessageLoop.Instance.Invoke(() =>
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine(string.Format("Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
|
||||||
|
|
||||||
rc = DGControl.UserInterface.DisableDS(_twui);
|
rc = DGControl.UserInterface.DisableDS(_twui);
|
||||||
if (rc == ReturnCode.Success)
|
if (rc == ReturnCode.Success)
|
||||||
{
|
{
|
||||||
@@ -386,48 +399,46 @@ namespace NTwain
|
|||||||
public void ForceStepDown(int targetState)
|
public void ForceStepDown(int targetState)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: ForceStepDown.", Thread.CurrentThread.ManagedThreadId));
|
Debug.WriteLine(string.Format("Thread {0}: ForceStepDown.", Thread.CurrentThread.ManagedThreadId));
|
||||||
MessageLoop.Instance.Invoke(() =>
|
|
||||||
|
bool origFlag = EnforceState;
|
||||||
|
EnforceState = false;
|
||||||
|
|
||||||
|
// From the twain spec
|
||||||
|
// Stepping Back Down the States
|
||||||
|
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
|
||||||
|
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
|
||||||
|
// DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS → state 5 to 4
|
||||||
|
// DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS → state 4 to 3
|
||||||
|
// Ignore the status returns from the calls prior to the one yielding the desired state. For instance, if a
|
||||||
|
// call during scanning returns TWCC_SEQERROR and the desire is to return to state 5, then use the
|
||||||
|
// following commands.
|
||||||
|
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
|
||||||
|
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
|
||||||
|
// Being sure to confirm that DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET returned
|
||||||
|
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
|
||||||
|
// be ignored.
|
||||||
|
|
||||||
|
if (targetState < 7)
|
||||||
{
|
{
|
||||||
bool origFlag = EnforceState;
|
DGControl.PendingXfers.EndXfer(new TWPendingXfers());
|
||||||
EnforceState = false;
|
}
|
||||||
|
if (targetState < 6)
|
||||||
// From the twain spec
|
{
|
||||||
// Stepping Back Down the States
|
DGControl.PendingXfers.Reset(new TWPendingXfers());
|
||||||
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
|
}
|
||||||
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
|
if (targetState < 5)
|
||||||
// DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS → state 5 to 4
|
{
|
||||||
// DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS → state 4 to 3
|
DisableSource();
|
||||||
// Ignore the status returns from the calls prior to the one yielding the desired state. For instance, if a
|
}
|
||||||
// call during scanning returns TWCC_SEQERROR and the desire is to return to state 5, then use the
|
if (targetState < 4)
|
||||||
// following commands.
|
{
|
||||||
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
|
CloseSource();
|
||||||
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
|
}
|
||||||
// Being sure to confirm that DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET returned
|
if (targetState < 3)
|
||||||
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
|
{
|
||||||
// be ignored.
|
CloseManager();
|
||||||
|
}
|
||||||
if (targetState < 7)
|
EnforceState = origFlag;
|
||||||
{
|
|
||||||
DGControl.PendingXfers.EndXfer(new TWPendingXfers());
|
|
||||||
}
|
|
||||||
if (targetState < 6)
|
|
||||||
{
|
|
||||||
DGControl.PendingXfers.Reset(new TWPendingXfers());
|
|
||||||
}
|
|
||||||
if (targetState < 5)
|
|
||||||
{
|
|
||||||
DisableSource();
|
|
||||||
}
|
|
||||||
if (targetState < 4)
|
|
||||||
{
|
|
||||||
CloseSource();
|
|
||||||
}
|
|
||||||
if (targetState < 3)
|
|
||||||
{
|
|
||||||
CloseManager();
|
|
||||||
}
|
|
||||||
EnforceState = origFlag;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -609,10 +620,7 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
Debug.WriteLine(string.Format("Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
|
Debug.WriteLine(string.Format("Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
|
||||||
|
|
||||||
MessageLoop.Instance.BeginInvoke(() =>
|
HandleSourceMsg(evt.TWMessage);
|
||||||
{
|
|
||||||
HandleSourceMsg(evt.TWMessage);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@@ -50,7 +50,17 @@ namespace Tester.WPF
|
|||||||
{
|
{
|
||||||
Messenger.Default.Register<DialogMessage>(this, msg =>
|
Messenger.Default.Register<DialogMessage>(this, msg =>
|
||||||
{
|
{
|
||||||
ModernMessageBox.Show(this, msg.Content, msg.Caption, msg.Button, msg.Icon, msg.DefaultResult);
|
if (Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
ModernMessageBox.Show(this, msg.Content, msg.Caption, msg.Button, msg.Icon, msg.DefaultResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
ModernMessageBox.Show(this, msg.Content, msg.Caption, msg.Button, msg.Icon, msg.DefaultResult);
|
||||||
|
}));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user