mirror of
https://github.com/soukoku/ntwain.git
synced 2025-04-05 20:59:23 +08:00
Merged in optional-internal-loop (pull request #2)
Update to make internal message loop optional
This commit is contained in:
commit
91c7e0f83e
@ -39,7 +39,9 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -76,11 +78,17 @@
|
||||
<Compile Include="..\NTwain\Internals\ICommittable.cs">
|
||||
<Link>Internals\ICommittable.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Internals\InternalMessageLoopHook.cs">
|
||||
<Link>Internals\InternalMessageLoopHook.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Internals\ITwainSessionInternal.cs">
|
||||
<Link>Internals\ITwainSessionInternal.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Internals\MessageLoop.cs">
|
||||
<Link>Internals\MessageLoop.cs</Link>
|
||||
<Compile Include="..\NTwain\Internals\IWinMessageFilter.cs">
|
||||
<Link>Internals\IWinMessageFilter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Internals\MESSAGE.cs">
|
||||
<Link>Internals\MESSAGE.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Internals\TentativeStateCommitable.cs">
|
||||
<Link>Internals\TentativeStateCommitable.cs</Link>
|
||||
@ -103,6 +111,9 @@
|
||||
<Compile Include="..\NTwain\ITwainSession.cs">
|
||||
<Link>ITwainSession.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\MessageLoopHooks.cs">
|
||||
<Link>MessageLoopHooks.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NTwain\Platform.cs">
|
||||
<Link>Platform.cs</Link>
|
||||
</Compile>
|
||||
|
@ -44,13 +44,24 @@ namespace NTwain
|
||||
/// <returns></returns>
|
||||
TwainSource ShowSourceSelector();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Opens the data source manager. This must be the first method used
|
||||
/// before using other TWAIN functions. Calls to this must be followed by <see cref="Close"/> when done with a TWAIN session.
|
||||
/// before using other TWAIN functions. Calls to this must be followed by
|
||||
/// <see cref="Close" /> when done with a TWAIN session.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
ReturnCode Open();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the data source manager. This must be the first method used
|
||||
/// before using other TWAIN functions. Calls to this must be followed by
|
||||
/// <see cref="Close" /> when done with a TWAIN session.
|
||||
/// </summary>
|
||||
/// <param name="messageLoopHook">The message loop hook.</param>
|
||||
/// <returns></returns>
|
||||
ReturnCode Open(MessageLoopHook messageLoopHook);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the data source manager.
|
||||
/// </summary>
|
||||
|
@ -14,6 +14,8 @@ namespace NTwain.Internals
|
||||
/// <returns></returns>
|
||||
TWIdentity AppId { get; }
|
||||
|
||||
MessageLoopHook MessageLoopHook { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
|
||||
/// </summary>
|
||||
|
26
NTwain/Internals/IWinMessageFilter.cs
Normal file
26
NTwain/Internals/IWinMessageFilter.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for checking whether messages from WndProc is a TWAIN message and is handled
|
||||
/// internally.
|
||||
/// </summary>
|
||||
interface IWinMessageFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks and handle the message if it's a TWAIN message.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The window handle.</param>
|
||||
/// <param name="msg">The message.</param>
|
||||
/// <param name="wParam">The w parameter.</param>
|
||||
/// <param name="lParam">The l parameter.</param>
|
||||
/// <returns>true if handled internally.</returns>
|
||||
bool IsTwainMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,29 +1,31 @@
|
||||
using NTwain.Properties;
|
||||
using NTwain.Triplets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a message loop for old TWAIN to post or new TWAIN to synchronize callbacks.
|
||||
/// </summary>
|
||||
class MessageLoop
|
||||
sealed class InternalMessageLoopHook : MessageLoopHook
|
||||
{
|
||||
static MessageLoop _instance = new MessageLoop();
|
||||
public static MessageLoop Instance { get { return _instance; } }
|
||||
|
||||
Dispatcher _dispatcher;
|
||||
WindowsHook _hook;
|
||||
private MessageLoop() { }
|
||||
|
||||
public void EnsureStarted(WindowsHook.WndProcHook hook)
|
||||
internal override void Stop()
|
||||
{
|
||||
if (_dispatcher != null)
|
||||
{
|
||||
_dispatcher.InvokeShutdown();
|
||||
}
|
||||
}
|
||||
internal override void Start(IWinMessageFilter filter)
|
||||
{
|
||||
if (_dispatcher == null)
|
||||
{
|
||||
// using this terrible hack so the new thread will start running before this function returns
|
||||
// using this hack so the new thread will start running before this function returns
|
||||
using (var hack = new WrappedManualResetEvent())
|
||||
{
|
||||
var loopThread = new Thread(new ThreadStart(() =>
|
||||
@ -32,11 +34,12 @@ namespace NTwain.Internals
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
if (!Platform.IsOnMono)
|
||||
{
|
||||
_hook = new WindowsHook(hook);
|
||||
_hook = new WindowsHook(filter);
|
||||
Handle = _hook.Handle;
|
||||
}
|
||||
hack.Set();
|
||||
Dispatcher.Run();
|
||||
// if for whatever reason it ever gets here make everything uninitialized
|
||||
// if dispatcher shutsdown we'll get here so make everything uninitialized
|
||||
_dispatcher = null;
|
||||
if (_hook != null)
|
||||
{
|
||||
@ -52,22 +55,14 @@ namespace NTwain.Internals
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr LoopHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hook == null ? IntPtr.Zero : _hook.Handle;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginInvoke(Action action)
|
||||
internal override void BeginInvoke(Action action)
|
||||
{
|
||||
if (_dispatcher == null) { throw new InvalidOperationException(Resources.MsgLoopUnavailble); }
|
||||
|
||||
_dispatcher.BeginInvoke(DispatcherPriority.Normal, action);
|
||||
}
|
||||
|
||||
public void Invoke(Action action)
|
||||
internal override void Invoke(Action action)
|
||||
{
|
||||
if (_dispatcher == null) { throw new InvalidOperationException(Resources.MsgLoopUnavailble); }
|
||||
|
36
NTwain/Internals/MESSAGE.cs
Normal file
36
NTwain/Internals/MESSAGE.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The MSG structure in Windows for TWAIN use.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MESSAGE
|
||||
{
|
||||
public MESSAGE(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
_hwnd = hwnd;
|
||||
_message = (uint)message;
|
||||
_wParam = wParam;
|
||||
_lParam = lParam;
|
||||
_time = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
}
|
||||
|
||||
IntPtr _hwnd;
|
||||
uint _message;
|
||||
IntPtr _wParam;
|
||||
IntPtr _lParam;
|
||||
uint _time;
|
||||
int _x;
|
||||
int _y;
|
||||
}
|
||||
}
|
@ -13,10 +13,12 @@ namespace NTwain.Internals
|
||||
class WindowsHook : IDisposable
|
||||
{
|
||||
IDisposable _win;
|
||||
WndProcHook _hook;
|
||||
IWinMessageFilter _filter;
|
||||
|
||||
public WindowsHook(WndProcHook hook)
|
||||
public WindowsHook(IWinMessageFilter filter)
|
||||
{
|
||||
_filter = filter;
|
||||
|
||||
// hook into 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
|
||||
@ -27,7 +29,6 @@ namespace NTwain.Internals
|
||||
Handle = win.Handle;
|
||||
win.AddHook(WndProc);
|
||||
_win = win;
|
||||
_hook = hook;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -36,15 +37,12 @@ namespace NTwain.Internals
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void WndProcHook(ref MESSAGE winMsg, ref bool handled);
|
||||
|
||||
|
||||
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (_hook != null)
|
||||
if (_filter != null)
|
||||
{
|
||||
var winmsg = new MESSAGE(hwnd, msg, wParam, lParam);
|
||||
_hook(ref winmsg, ref handled);
|
||||
handled = _filter.IsTwainMessage(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
@ -52,32 +50,6 @@ namespace NTwain.Internals
|
||||
public IntPtr Handle { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The MSG structure in Windows for TWAIN use.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MESSAGE
|
||||
{
|
||||
public MESSAGE(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
_hwnd = hwnd;
|
||||
_message = (uint)message;
|
||||
_wParam = wParam;
|
||||
_lParam = lParam;
|
||||
_time = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
}
|
||||
|
||||
IntPtr _hwnd;
|
||||
uint _message;
|
||||
IntPtr _wParam;
|
||||
IntPtr _lParam;
|
||||
uint _time;
|
||||
int _x;
|
||||
int _y;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
|
189
NTwain/MessageLoopHooks.cs
Normal file
189
NTwain/MessageLoopHooks.cs
Normal file
@ -0,0 +1,189 @@
|
||||
using NTwain.Internals;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for TWAIN to hook into windows message loops.
|
||||
/// </summary>
|
||||
public abstract class MessageLoopHook
|
||||
{
|
||||
internal IntPtr Handle { get; set; }
|
||||
internal SynchronizationContext SyncContext { get; set; }
|
||||
|
||||
internal abstract void Start(IWinMessageFilter filter);
|
||||
internal abstract void Stop();
|
||||
|
||||
internal virtual void BeginInvoke(Action action)
|
||||
{
|
||||
if (SyncContext == null)
|
||||
{
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncContext.Post(o =>
|
||||
{
|
||||
action();
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
internal virtual void Invoke(Action action)
|
||||
{
|
||||
if (SyncContext == null)
|
||||
{
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncContext.Send(o =>
|
||||
{
|
||||
action();
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ThrowInvalidOp()
|
||||
{
|
||||
throw new InvalidOperationException(InvalidMessage);
|
||||
}
|
||||
|
||||
internal virtual string InvalidMessage { get { return string.Empty; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="MessageLoopHook"/> for use in winform applications.
|
||||
/// </summary>
|
||||
public sealed class WindowsFormsMessageLoopHook : MessageLoopHook, System.Windows.Forms.IMessageFilter
|
||||
{
|
||||
IWinMessageFilter _filter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowsFormsMessageLoopHook"/> class.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle to the app window.</param>
|
||||
/// <exception cref="System.ArgumentException">A valid window handle is required.</exception>
|
||||
public WindowsFormsMessageLoopHook(IntPtr hwnd)
|
||||
{
|
||||
if (hwnd == IntPtr.Zero) { throw new ArgumentException("A valid window handle is required."); }
|
||||
|
||||
if (!System.Windows.Forms.Application.MessageLoop)
|
||||
{
|
||||
ThrowInvalidOp();
|
||||
}
|
||||
var sync = SynchronizationContext.Current;
|
||||
if (sync == null)
|
||||
{
|
||||
ThrowInvalidOp();
|
||||
}
|
||||
Handle = hwnd;
|
||||
SyncContext = sync;
|
||||
}
|
||||
internal override string InvalidMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return "This can only be created on the Windows Forms UI thread.";
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Start(IWinMessageFilter filter)
|
||||
{
|
||||
//Invoke(() =>
|
||||
//{
|
||||
_filter = filter;
|
||||
System.Windows.Forms.Application.AddMessageFilter(this);
|
||||
//});
|
||||
}
|
||||
|
||||
internal override void Stop()
|
||||
{
|
||||
//Invoke(() =>
|
||||
//{
|
||||
System.Windows.Forms.Application.RemoveMessageFilter(this);
|
||||
_filter = null;
|
||||
//});
|
||||
}
|
||||
|
||||
|
||||
#region IMessageFilter Members
|
||||
|
||||
bool System.Windows.Forms.IMessageFilter.PreFilterMessage(ref System.Windows.Forms.Message m)
|
||||
{
|
||||
if (_filter != null)
|
||||
{
|
||||
return _filter.IsTwainMessage(m.HWnd, m.Msg, m.WParam, m.LParam);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="MessageLoopHook"/> for use in WPF applications.
|
||||
/// </summary>
|
||||
public sealed class WpfMessageLoopHook : MessageLoopHook
|
||||
{
|
||||
HwndSource _hooker;
|
||||
IWinMessageFilter _filter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WpfMessageLoopHook"/> class.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle to the app window.</param>
|
||||
/// <exception cref="System.ArgumentException">A valid window handle is required.</exception>
|
||||
public WpfMessageLoopHook(IntPtr hwnd)
|
||||
{
|
||||
if (hwnd == IntPtr.Zero) { throw new ArgumentException("A valid window handle is required."); }
|
||||
|
||||
if (System.Windows.Application.Current == null ||
|
||||
!System.Windows.Application.Current.Dispatcher.CheckAccess())
|
||||
{
|
||||
ThrowInvalidOp();
|
||||
}
|
||||
var sync = SynchronizationContext.Current;
|
||||
if (sync == null)
|
||||
{
|
||||
ThrowInvalidOp();
|
||||
}
|
||||
Handle = hwnd;
|
||||
SyncContext = sync;
|
||||
}
|
||||
internal override string InvalidMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return "This can only be created on the WPF UI thread.";
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Start(IWinMessageFilter filter)
|
||||
{
|
||||
_filter = filter;
|
||||
_hooker = HwndSource.FromHwnd(Handle);
|
||||
_hooker.AddHook(FilterMessage);
|
||||
}
|
||||
|
||||
private IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (_filter != null)
|
||||
{
|
||||
handled = _filter.IsTwainMessage(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal override void Stop()
|
||||
{
|
||||
if (_hooker != null)
|
||||
{
|
||||
_hooker.RemoveHook(FilterMessage);
|
||||
_hooker.Dispose();
|
||||
_hooker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,10 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -60,14 +63,17 @@
|
||||
<Compile Include="DataTransferredEventArgs.cs" />
|
||||
<Compile Include="IMemoryManager.cs" />
|
||||
<Compile Include="Internals\ICommittable.cs" />
|
||||
<Compile Include="Internals\InternalMessageLoopHook.cs" />
|
||||
<Compile Include="Internals\ITwainSessionInternal.cs" />
|
||||
<Compile Include="Internals\MESSAGE.cs" />
|
||||
<Compile Include="Internals\TransferLogic.cs" />
|
||||
<Compile Include="Internals\WindowsHook.cs" />
|
||||
<Compile Include="Internals\WrappedManualResetEvent.cs" />
|
||||
<Compile Include="ITwainSession.cs" />
|
||||
<Compile Include="Internals\WinMemoryManager.cs" />
|
||||
<Compile Include="Internals\MessageLoop.cs" />
|
||||
<Compile Include="Internals\UnsafeNativeMethods.cs" />
|
||||
<Compile Include="Internals\IWinMessageFilter.cs" />
|
||||
<Compile Include="MessageLoopHooks.cs" />
|
||||
<Compile Include="Platform.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
|
@ -12,8 +12,8 @@ namespace NTwain
|
||||
class _NTwainVersionInfo
|
||||
{
|
||||
// keep this same in majors releases
|
||||
public const string Release = "1.0.0.0";
|
||||
public const string Release = "2.0.0.0";
|
||||
// change this for each nuget release
|
||||
public const string Build = "1.0.2";
|
||||
public const string Build = "2.0.0";
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace NTwain
|
||||
/// <summary>
|
||||
/// Basic class for interfacing with TWAIN. You should only have one of this per application process.
|
||||
/// </summary>
|
||||
public class TwainSession : ITwainSessionInternal
|
||||
public class TwainSession : ITwainSessionInternal, IWinMessageFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TwainSession"/> class.
|
||||
@ -41,8 +41,6 @@ namespace NTwain
|
||||
_appId = appId;
|
||||
((ITwainSessionInternal)this).ChangeState(1, false);
|
||||
EnforceState = true;
|
||||
|
||||
MessageLoop.Instance.EnsureStarted(HandleWndProcMessage);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
|
||||
@ -50,6 +48,7 @@ namespace NTwain
|
||||
TWIdentity _appId;
|
||||
TWUserInterface _twui;
|
||||
|
||||
|
||||
static readonly Dictionary<string, TwainSource> __ownedSources = new Dictionary<string, TwainSource>();
|
||||
|
||||
internal static TwainSource GetSourceInstance(ITwainSessionInternal session, TWIdentity sourceId)
|
||||
@ -62,12 +61,9 @@ namespace NTwain
|
||||
return __ownedSources[key] = new TwainSource(session, sourceId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the optional synchronization context.
|
||||
/// This allows events to be raised on the thread
|
||||
/// associated with the context.
|
||||
/// Gets or sets the optional synchronization context when not specifying a <see cref="MessageLoopHook"/> on <see cref="Open"/>.
|
||||
/// This allows events to be raised on the thread associated with the context. This is experimental is not recommended for use.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The synchronization context.
|
||||
@ -77,6 +73,9 @@ namespace NTwain
|
||||
|
||||
#region ITwainSessionInternal Members
|
||||
|
||||
MessageLoopHook _msgLoopHook;
|
||||
MessageLoopHook ITwainSessionInternal.MessageLoopHook { get { return _msgLoopHook; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the app id used for the session.
|
||||
/// </summary>
|
||||
@ -126,6 +125,46 @@ namespace NTwain
|
||||
SafeSyncableRaiseOnEvent(OnTransferReady, TransferReady, e);
|
||||
}
|
||||
|
||||
DGAudio _dgAudio;
|
||||
DGAudio ITwainSessionInternal.DGAudio
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgAudio == null) { _dgAudio = new DGAudio(this); }
|
||||
return _dgAudio;
|
||||
}
|
||||
}
|
||||
|
||||
DGControl _dgControl;
|
||||
DGControl ITwainSessionInternal.DGControl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgControl == null) { _dgControl = new DGControl(this); }
|
||||
return _dgControl;
|
||||
}
|
||||
}
|
||||
|
||||
DGImage _dgImage;
|
||||
DGImage ITwainSessionInternal.DGImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgImage == null) { _dgImage = new DGImage(this); }
|
||||
return _dgImage;
|
||||
}
|
||||
}
|
||||
|
||||
DGCustom _dgCustom;
|
||||
DGCustom ITwainSessionInternal.DGCustom
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgCustom == null) { _dgCustom = new DGCustom(this); }
|
||||
return _dgCustom;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ITwainSession Members
|
||||
@ -202,66 +241,37 @@ namespace NTwain
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ITwainOperation Members
|
||||
|
||||
DGAudio _dgAudio;
|
||||
/// <summary>
|
||||
/// Gets the triplet operations defined for audio data group.
|
||||
/// Opens the data source manager. This must be the first method used
|
||||
/// before using other TWAIN functions. Calls to this must be followed by
|
||||
/// <see cref="Close" /> when done with a TWAIN session.
|
||||
/// </summary>
|
||||
DGAudio ITwainSessionInternal.DGAudio
|
||||
/// <returns></returns
|
||||
public ReturnCode Open()
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgAudio == null) { _dgAudio = new DGAudio(this); }
|
||||
return _dgAudio;
|
||||
}
|
||||
}
|
||||
|
||||
DGControl _dgControl;
|
||||
DGControl ITwainSessionInternal.DGControl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgControl == null) { _dgControl = new DGControl(this); }
|
||||
return _dgControl;
|
||||
}
|
||||
}
|
||||
|
||||
DGImage _dgImage;
|
||||
DGImage ITwainSessionInternal.DGImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgImage == null) { _dgImage = new DGImage(this); }
|
||||
return _dgImage;
|
||||
}
|
||||
}
|
||||
|
||||
DGCustom _dgCustom;
|
||||
DGCustom ITwainSessionInternal.DGCustom
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dgCustom == null) { _dgCustom = new DGCustom(this); }
|
||||
return _dgCustom;
|
||||
}
|
||||
return Open(new InternalMessageLoopHook());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the data source manager. This must be the first method used
|
||||
/// before using other TWAIN functions. Calls to this must be followed by <see cref="Close"/> when done with a TWAIN session.
|
||||
/// before using other TWAIN functions. Calls to this must be followed by
|
||||
/// <see cref="Close" /> when done with a TWAIN session.
|
||||
/// </summary>
|
||||
/// <param name="messageLoopHook">The message loop hook.</param>
|
||||
/// <returns></returns>
|
||||
public ReturnCode Open()
|
||||
/// <exception cref="System.ArgumentNullException">messageLoopHook</exception>
|
||||
public ReturnCode Open(MessageLoopHook messageLoopHook)
|
||||
{
|
||||
if (messageLoopHook == null) { throw new ArgumentNullException("messageLoopHook"); }
|
||||
|
||||
_msgLoopHook = messageLoopHook;
|
||||
_msgLoopHook.Start(this);
|
||||
var rc = ReturnCode.Failure;
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_msgLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
rc = ((ITwainSessionInternal)this).DGControl.Parent.OpenDsm(MessageLoop.Instance.LoopHandle);
|
||||
rc = ((ITwainSessionInternal)this).DGControl.Parent.OpenDsm(_msgLoopHook.Handle);
|
||||
if (rc == ReturnCode.Success)
|
||||
{
|
||||
// if twain2 then get memory management functions
|
||||
@ -291,14 +301,15 @@ namespace NTwain
|
||||
public ReturnCode Close()
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_msgLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
rc = ((ITwainSessionInternal)this).DGControl.Parent.CloseDsm(MessageLoop.Instance.LoopHandle);
|
||||
rc = ((ITwainSessionInternal)this).DGControl.Parent.CloseDsm(_msgLoopHook.Handle);
|
||||
if (rc == ReturnCode.Success)
|
||||
{
|
||||
Platform.MemoryManager = null;
|
||||
_msgLoopHook.Stop();
|
||||
}
|
||||
});
|
||||
return rc;
|
||||
@ -399,7 +410,7 @@ namespace NTwain
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_msgLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
@ -454,7 +465,7 @@ namespace NTwain
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_msgLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
@ -495,7 +506,7 @@ namespace NTwain
|
||||
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
|
||||
// be ignored.
|
||||
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_msgLoopHook.Invoke(() =>
|
||||
{
|
||||
if (targetState < 7)
|
||||
{
|
||||
@ -668,8 +679,22 @@ namespace NTwain
|
||||
|
||||
#region handle twain ds message
|
||||
|
||||
void HandleWndProcMessage(ref WindowsHook.MESSAGE winMsg, ref bool handled)
|
||||
|
||||
#region IWinMessageFilter Members
|
||||
|
||||
/// <summary>
|
||||
/// Checks and handle the message if it's a TWAIN message.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The window handle.</param>
|
||||
/// <param name="msg">The message.</param>
|
||||
/// <param name="wParam">The w parameter.</param>
|
||||
/// <param name="lParam">The l parameter.</param>
|
||||
/// <returns>
|
||||
/// true if handled internally.
|
||||
/// </returns>
|
||||
public bool IsTwainMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
bool handled = false;
|
||||
// this handles the message from a typical WndProc message loop and check if it's from the TWAIN source.
|
||||
if (_state >= 5)
|
||||
{
|
||||
@ -677,6 +702,8 @@ namespace NTwain
|
||||
IntPtr msgPtr = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
var winMsg = new NTwain.Internals.MESSAGE(hwnd, msg, wParam, lParam);
|
||||
|
||||
// no need to do another lock call when using marshal alloc
|
||||
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winMsg));
|
||||
Marshal.StructureToPtr(winMsg, msgPtr, false);
|
||||
@ -695,8 +722,11 @@ namespace NTwain
|
||||
if (msgPtr != IntPtr.Zero) { Marshal.FreeHGlobal(msgPtr); }
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
ReturnCode HandleCallback(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
||||
{
|
||||
if (origin != null && CurrentSource != null && origin.Id == CurrentSource.Identity.Id && _state >= 5)
|
||||
@ -705,7 +735,7 @@ namespace NTwain
|
||||
// spec says we must handle this on the thread that enabled the DS.
|
||||
// by using the internal dispatcher this will be the case.
|
||||
|
||||
MessageLoop.Instance.BeginInvoke(() =>
|
||||
_msgLoopHook.BeginInvoke(() =>
|
||||
{
|
||||
HandleSourceMsg(msg);
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ namespace NTwain
|
||||
public ReturnCode Open()
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_session.MessageLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
@ -48,7 +48,7 @@ namespace NTwain
|
||||
public ReturnCode Close()
|
||||
{
|
||||
var rc = ReturnCode.Failure;
|
||||
MessageLoop.Instance.Invoke(() =>
|
||||
_session.MessageLoopHook.Invoke(() =>
|
||||
{
|
||||
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
|
||||
|
||||
|
@ -8,7 +8,7 @@ This project has these features/goals:
|
||||
|
||||
* Targets latest TWAIN version (2.3 at this writing)
|
||||
* Supports all the TWAIN functions in the spec
|
||||
* Hosts an internal message loop so there's no need to hook into application UI thread
|
||||
* Optionally hosts an internal message loop so there's no need to hook into application UI thread
|
||||
|
||||
The solution contains tester projects in winform, wpf, and even (gasp!) console.
|
||||
A nuget package is also [available here](https://www.nuget.org/packages/ntwain)
|
||||
@ -97,8 +97,9 @@ At the moment this lib does not provide ways to parse transferred image data and
|
||||
consumers to do the conversion themselves. The winform project contains one such
|
||||
example for handling DIB image in native transfer using the CommonWin32 lib.
|
||||
|
||||
Because it hosts its own message thread, the events will be raised from another thread.
|
||||
If you would like things marshalled to a UI thread then set the SynchronizationContext property
|
||||
If you call session.Open() without passing a message loop hook argument, it will run an
|
||||
internal message loop behind the scenes. When this happens the events will be raised from another thread.
|
||||
If you would like things marshalled to a UI thread then set the experimental SynchronizationContext property
|
||||
to the one from the UI thread.
|
||||
|
||||
```
|
||||
|
@ -65,7 +65,11 @@ namespace Tester.WPF
|
||||
{
|
||||
base.OnSourceInitialized(e);
|
||||
|
||||
var rc = _twainVM.Open();
|
||||
// use this for internal msg loop
|
||||
//var rc = _twainVM.Open();
|
||||
// use this to hook into current app loop
|
||||
var rc = _twainVM.Open(new WpfMessageLoopHook(new WindowInteropHelper(this).Handle));
|
||||
|
||||
if (rc == ReturnCode.Success)
|
||||
{
|
||||
SrcList.ItemsSource = _twainVM.GetSources().Select(s => new DSVM { DS = s });
|
||||
|
@ -78,7 +78,7 @@ namespace Tester.WPF
|
||||
Format = wantFormat,
|
||||
FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.tif")
|
||||
};
|
||||
var rc = this.DGControl.SetupFileXfer.Set(fileSetup);
|
||||
var rc = this.CurrentSource.DGControl.SetupFileXfer.Set(fileSetup);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,17 +3,17 @@ using System.Windows.Forms;
|
||||
|
||||
namespace Tester.Winform
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new TestForm());
|
||||
}
|
||||
}
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new TestForm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,10 @@ namespace Tester.Winform
|
||||
}
|
||||
if (_twain.State < 3)
|
||||
{
|
||||
// use this for internal msg loop
|
||||
_twain.Open();
|
||||
// use this to hook into current app loop
|
||||
//_twain.Open(new WinformMessageLoopHook(this.Handle));
|
||||
}
|
||||
|
||||
if (_twain.State >= 3)
|
||||
|
Loading…
Reference in New Issue
Block a user