mirror of
https://github.com/soukoku/ntwain.git
synced 2025-11-08 10:34:47 +08:00
Road to v1 begins!
This commit is contained in:
43
NTwain/Internals/Extensions.cs
Normal file
43
NTwain/Internals/Extensions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using NTwain.Data;
|
||||
using NTwain.Properties;
|
||||
using System;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies the string length is under the maximum length
|
||||
/// and throws <see cref="ArgumentException"/> if violated.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="maxLength">The maximum length.</param>
|
||||
public static void VerifyLengthUnder(this string value, int maxLength)
|
||||
{
|
||||
if (value != null && value.Length > maxLength)
|
||||
throw new ArgumentException(Resources.MaxStringLengthExceeded);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the session is within the specified state range (inclusive). Throws
|
||||
/// <see cref="TwainStateException" /> if violated.
|
||||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <param name="allowedMinimum">The allowed minimum.</param>
|
||||
/// <param name="allowedMaximum">The allowed maximum.</param>
|
||||
/// <param name="group">The triplet data group.</param>
|
||||
/// <param name="dataArgumentType">The triplet data argument type.</param>
|
||||
/// <param name="message">The triplet message.</param>
|
||||
/// <exception cref="TwainStateException"></exception>
|
||||
public static void VerifyState(this ITwainStateInternal session, int allowedMinimum, int allowedMaximum, DataGroups group, DataArgumentType dataArgumentType, Message message)
|
||||
{
|
||||
if (session.EnforceState && (session.State < allowedMinimum || session.State > allowedMaximum))
|
||||
{
|
||||
throw new TwainStateException(session.State, allowedMinimum, allowedMaximum, group, dataArgumentType, message,
|
||||
string.Format("TWAIN state {0} does not match required range {1}-{2} for operation {3}-{4}-{5}.",
|
||||
session.State, allowedMinimum, allowedMaximum, group, dataArgumentType, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
NTwain/Internals/ICommittable.cs
Normal file
9
NTwain/Internals/ICommittable.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
interface ICommittable : IDisposable
|
||||
{
|
||||
void Commit();
|
||||
}
|
||||
}
|
||||
41
NTwain/Internals/ITwainStateInternal.cs
Normal file
41
NTwain/Internals/ITwainStateInternal.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using NTwain.Data;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal interface for state management.
|
||||
/// </summary>
|
||||
interface ITwainStateInternal : ITwainState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the app id used for the session.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
TWIdentity AppId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if state value is enforced; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool EnforceState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the state right away.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state.</param>
|
||||
/// <param name="notifyChange">if set to <c>true</c> to notify change.</param>
|
||||
void ChangeState(int newState, bool notifyChange);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pending state changer and tentatively changes the session state to the specified value.
|
||||
/// Value will only stick if committed.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state.</param>
|
||||
/// <returns></returns>
|
||||
ICommittable GetPendingStateChanger(int newState);
|
||||
|
||||
void ChangeSourceId(TWIdentity sourceId);
|
||||
}
|
||||
}
|
||||
102
NTwain/Internals/MessageLoop.cs
Normal file
102
NTwain/Internals/MessageLoop.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using NTwain.Properties;
|
||||
using NTwain.Triplets;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
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
|
||||
{
|
||||
static MessageLoop _instance = new MessageLoop();
|
||||
public static MessageLoop Instance { get { return _instance; } }
|
||||
|
||||
Dispatcher _dispatcher;
|
||||
WindowsHook _hook;
|
||||
private MessageLoop() { }
|
||||
|
||||
public void EnsureStarted(WindowsHook.WndProcHook hook)
|
||||
{
|
||||
if (_dispatcher == null)
|
||||
{
|
||||
// using this terrible hack so the new thread will start running before this function returns
|
||||
using (var hack = new WrappedManualResetEvent())
|
||||
{
|
||||
var loopThread = new Thread(new ThreadStart(() =>
|
||||
{
|
||||
Debug.WriteLine("NTwain message loop is starting.");
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
if (!Platform.IsOnMono)
|
||||
{
|
||||
_hook = new WindowsHook(hook);
|
||||
}
|
||||
hack.Set();
|
||||
Dispatcher.Run();
|
||||
// if for whatever reason it ever gets here make everything uninitialized
|
||||
_dispatcher = null;
|
||||
if (_hook != null)
|
||||
{
|
||||
_hook.Dispose();
|
||||
_hook = null;
|
||||
}
|
||||
}));
|
||||
loopThread.IsBackground = true;
|
||||
loopThread.SetApartmentState(ApartmentState.STA);
|
||||
loopThread.Start();
|
||||
hack.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr LoopHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hook == null ? IntPtr.Zero : _hook.Handle;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginInvoke(Action action)
|
||||
{
|
||||
if (_dispatcher == null) { throw new InvalidOperationException(Resources.MsgLoopUnavailble); }
|
||||
|
||||
_dispatcher.BeginInvoke(DispatcherPriority.Normal, action);
|
||||
}
|
||||
|
||||
public void Invoke(Action action)
|
||||
{
|
||||
if (_dispatcher == null) { throw new InvalidOperationException(Resources.MsgLoopUnavailble); }
|
||||
|
||||
if (_dispatcher.CheckAccess())
|
||||
{
|
||||
action();
|
||||
}
|
||||
else if (Platform.IsOnMono)
|
||||
{
|
||||
using (var man = new WrappedManualResetEvent())
|
||||
{
|
||||
_dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
man.Set();
|
||||
}
|
||||
}));
|
||||
man.Wait();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcher.Invoke(DispatcherPriority.Normal, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
NTwain/Internals/NativeMethods.cs
Normal file
27
NTwain/Internals/NativeMethods.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
static class NativeMethods
|
||||
{
|
||||
// should be unsafe native methods?
|
||||
|
||||
#region mem stuff for twain 1.x
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalAlloc")]
|
||||
internal static extern IntPtr WinGlobalAlloc(uint uFlags, UIntPtr dwBytes);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalFree")]
|
||||
internal static extern IntPtr WinGlobalFree(IntPtr hMem);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalLock")]
|
||||
internal static extern IntPtr WinGlobalLock(IntPtr handle);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalUnlock")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool WinGlobalUnlock(IntPtr handle);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
42
NTwain/Internals/TentativeStateCommitable.cs
Normal file
42
NTwain/Internals/TentativeStateCommitable.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
class TentativeStateCommitable : ICommittable
|
||||
{
|
||||
bool _commit;
|
||||
ITwainStateInternal _session;
|
||||
int _origState;
|
||||
int _newState;
|
||||
public TentativeStateCommitable(ITwainStateInternal session, int newState)
|
||||
{
|
||||
_session = session;
|
||||
_origState = session.State;
|
||||
_newState = newState;
|
||||
_session.ChangeState(newState, false);
|
||||
}
|
||||
|
||||
#region ICommitable Members
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
if (_session.State == _newState)
|
||||
{
|
||||
_session.ChangeState(_newState, true);
|
||||
}
|
||||
_commit = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_commit && _session.State == _newState)
|
||||
{
|
||||
_session.ChangeState(_origState, false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
NTwain/Internals/WinMemoryManager.cs
Normal file
39
NTwain/Internals/WinMemoryManager.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using NTwain.Data;
|
||||
using NTwain.Internals;
|
||||
using System;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for managing memory on data exchanged with twain sources using old win32 methods.
|
||||
/// This should only be used after the DSM has been opened.
|
||||
/// </summary>
|
||||
class WinMemoryManager : IMemoryManager
|
||||
{
|
||||
public IntPtr Allocate(uint size)
|
||||
{
|
||||
IntPtr retVal = NativeMethods.WinGlobalAlloc(0x0040, new UIntPtr(size));
|
||||
|
||||
if (retVal == IntPtr.Zero)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void Free(IntPtr handle)
|
||||
{
|
||||
NativeMethods.WinGlobalFree(handle);
|
||||
}
|
||||
|
||||
public IntPtr Lock(IntPtr handle)
|
||||
{
|
||||
return NativeMethods.WinGlobalLock(handle);
|
||||
}
|
||||
|
||||
public void Unlock(IntPtr handle)
|
||||
{
|
||||
NativeMethods.WinGlobalUnlock(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
NTwain/Internals/WindowsHook.cs
Normal file
94
NTwain/Internals/WindowsHook.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Abstracts out wnd proc hook on Windows from MessageLoop class.
|
||||
/// This allows things to not depend on PresentationCore.dll at runtime on mono.
|
||||
/// Not that everything works yet in mono but it's something.
|
||||
/// </summary>
|
||||
class WindowsHook : IDisposable
|
||||
{
|
||||
IDisposable _win;
|
||||
WndProcHook _hook;
|
||||
|
||||
public WindowsHook(WndProcHook hook)
|
||||
{
|
||||
// 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
|
||||
HwndSource win = null;
|
||||
try
|
||||
{
|
||||
win = new HwndSource(0x0200, 0x8000000, 0x8000000, 0, 0, "NTWAIN_LOOPER", IntPtr.Zero);
|
||||
Handle = win.Handle;
|
||||
win.AddHook(WndProc);
|
||||
_win = win;
|
||||
_hook = hook;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (win != null) { win.Dispose(); }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var winmsg = new MESSAGE(hwnd, msg, wParam, lParam);
|
||||
_hook(ref winmsg, ref handled);
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (_win != null)
|
||||
{
|
||||
((HwndSource)_win).RemoveHook(WndProc);
|
||||
_win.Dispose();
|
||||
_win = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
57
NTwain/Internals/WrappedManualResetEvent.cs
Normal file
57
NTwain/Internals/WrappedManualResetEvent.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace NTwain.Internals
|
||||
{
|
||||
|
||||
// just a test
|
||||
|
||||
class WrappedManualResetEvent : IDisposable
|
||||
{
|
||||
#if NET4
|
||||
ManualResetEventSlim _slim;
|
||||
#else
|
||||
ManualResetEvent _mre;
|
||||
#endif
|
||||
|
||||
public WrappedManualResetEvent()
|
||||
{
|
||||
#if NET4
|
||||
_slim = new ManualResetEventSlim();
|
||||
#else
|
||||
_mre = new ManualResetEvent(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
#if NET4
|
||||
_slim.Wait();
|
||||
#else
|
||||
_mre.WaitOne();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Set()
|
||||
{
|
||||
#if NET4
|
||||
_slim.Set();
|
||||
#else
|
||||
_mre.Set();
|
||||
#endif
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if NET4
|
||||
_slim.Dispose();
|
||||
#else
|
||||
_mre.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user