Added most hooks except xfer logic and winform/wpf versions.

This commit is contained in:
soukoku 2014-04-05 19:39:41 -04:00
parent 94f2720de4
commit 0064db2c2d
6 changed files with 228 additions and 24 deletions

View File

@ -27,11 +27,4 @@ namespace NTwain
/// </summary>
DGImage DGImage { get; }
}
/// <summary>
/// Internal interface for operations use.
/// </summary>
interface ITwainOperationInternal : ITwainOperation
{
}
}

View File

@ -76,6 +76,8 @@
<Compile Include="Triplets\PInvokes.64bit.cs" />
<Compile Include="TwainSessionExtensions.cs" />
<Compile Include="TwainSessionBase.cs" />
<Compile Include="TwainSessionWPF.cs" />
<Compile Include="TwainSessionWinform.cs" />
<Compile Include="TwainStateException.cs" />
<Compile Include="Data\Types.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -715,14 +715,16 @@ namespace NTwain
try
{
// no need to lock for marshal alloc
msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
Marshal.StructureToPtr(winmsg, msgPtr, false);
return HandleLoopMsgEvent(ref msgPtr);
}
finally
{
if (msgPtr != IntPtr.Zero)
MemoryManager.Instance.Free(msgPtr);
Marshal.FreeHGlobal(msgPtr);
//MemoryManager.Instance.Free(msgPtr);
}
}
return false;
@ -754,14 +756,16 @@ namespace NTwain
try
{
// no need to lock for marshal alloc
msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
Marshal.StructureToPtr(winmsg, msgPtr, false);
handled = HandleLoopMsgEvent(ref msgPtr);
}
finally
{
if (msgPtr != IntPtr.Zero)
MemoryManager.Instance.Free(msgPtr);
Marshal.FreeHGlobal(msgPtr);
//MemoryManager.Instance.Free(msgPtr);
}
}
return IntPtr.Zero;

View File

@ -9,13 +9,14 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Interop;
namespace NTwain
{
/// <summary>
/// Base working class for interfacing with TWAIN.
/// Base class for interfacing with TWAIN.
/// </summary>
public class TwainSessionBase : ITwainStateInternal, ITwainOperationInternal
public class TwainSessionBase : ITwainStateInternal, ITwainOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession" /> class.
@ -276,7 +277,7 @@ namespace NTwain
// app v2.2 or higher uses callback2
if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
{
var cb = new TWCallback2(CallbackHandler);
var cb = new TWCallback2(HandleCallback);
var rc2 = DGControl.Callback2.RegisterCallback(cb);
if (rc2 == ReturnCode.Success)
@ -287,7 +288,7 @@ namespace NTwain
}
else
{
var cb = new TWCallback(CallbackHandler);
var cb = new TWCallback(HandleCallback);
var rc2 = DGControl.Callback.RegisterCallback(cb);
@ -384,36 +385,143 @@ namespace NTwain
#region custom events and overridables
/// <summary>
/// Occurs when <see cref="State"/> has changed.
/// </summary>
public event EventHandler StateChanged;
/// <summary>
/// Occurs when <see cref="SourceId"/> has changed.
/// </summary>
public event EventHandler SourceChanged;
/// <summary>
/// Occurs when source has been disabled (back to state 4).
/// </summary>
public event EventHandler SourceDisabled;
/// <summary>
/// Occurs when the source has generated an event.
/// </summary>
public event EventHandler<DeviceEventArgs> DeviceEvent;
/// <summary>
/// Occurs when a data transfer is ready.
/// </summary>
public event EventHandler<TransferReadyEventArgs> TransferReady;
/// <summary>
/// Occurs when data has been transferred.
/// </summary>
public event EventHandler<DataTransferredEventArgs> DataTransferred;
/// <summary>
/// Called when <see cref="State"/> changed.
/// </summary>
protected virtual void OnStateChanged() { }
protected virtual void OnStateChanged()
{
var hand = StateChanged;
if (hand != null)
{
hand(this, EventArgs.Empty);
}
}
/// <summary>
/// Called when <see cref="SourceId"/> changed.
/// </summary>
protected virtual void OnSourceIdChanged() { }
protected virtual void OnSourceChanged()
{
var hand = SourceChanged;
if (hand != null)
{
hand(this, EventArgs.Empty);
}
}
/// <summary>
/// Called when source has been disabled (back to state 4).
/// </summary>
protected virtual void OnSourceDisabled() { }
protected virtual void OnSourceDisabled()
{
var hand = SourceDisabled;
if (hand != null)
{
hand(this, EventArgs.Empty);
}
}
/// <summary>
/// Raises the <see cref="E:DeviceEvent" /> event.
/// </summary>
/// <param name="e">The <see cref="DeviceEventArgs"/> instance containing the event data.</param>
protected virtual void OnDeviceEvent(DeviceEventArgs e)
{
var hand = DeviceEvent;
if (hand != null)
{
hand(this, e);
}
}
/// <summary>
/// Raises the <see cref="E:TransferReady" /> event.
/// </summary>
/// <param name="e">The <see cref="TransferReadyEventArgs"/> instance containing the event data.</param>
protected virtual void OnTransferReady(TransferReadyEventArgs e)
{
var hand = TransferReady;
if (hand != null)
{
hand(this, e);
}
}
/// <summary>
/// Raises the <see cref="E:DataTransferred" /> event.
/// </summary>
/// <param name="e">The <see cref="DataTransferredEventArgs"/> instance containing the event data.</param>
protected virtual void OnDataTransferred(DataTransferredEventArgs e)
{
var hand = DataTransferred;
if (hand != null)
{
hand(this, e);
}
}
#endregion
#region real TWAIN logic during xfer
void HandleSourceMsg(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
/// <summary>
/// Handles the message from a typical WndProc message loop and check if it's from the TWAIN source.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>True if handled by TWAIN.</returns>
protected bool HandleWndProcMessage(ref MSG message)
{
if (msg != Message.Null)
if (State >= 4) // technically we should only handle on state >= 5 but there might be missed msgs if we wait until state changes after enabling ds
{
Debug.WriteLine(string.Format("Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}.", Thread.CurrentThread.ManagedThreadId, State, dg, dat, msg));
// transform it into a pointer for twain
IntPtr msgPtr = IntPtr.Zero;
try
{
// no need to do another lock call when using marshal alloc
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(message));
Marshal.StructureToPtr(message, msgPtr, false);
TWEvent evt = new TWEvent();
evt.pEvent = msgPtr;
var rc = DGControl.Event.ProcessEvent(evt);
// TODO: not sure what to DAT to pass
HandleSourceMsg(null, null, DataGroups.Control, DataArgumentType.Null, evt.TWMessage, IntPtr.Zero);
return rc == ReturnCode.DSEvent;
}
finally
{
if (msgPtr != IntPtr.Zero) { Marshal.FreeHGlobal(msgPtr); }
}
}
return false;
}
ReturnCode CallbackHandler(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
ReturnCode HandleCallback(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
{
if (origin != null && SourceId != null && origin.Id == SourceId.Id)
{
@ -444,6 +552,16 @@ namespace NTwain
return ReturnCode.Failure;
}
// final method that handles stuff from the source, whether it's from wndproc or callbacks
void HandleSourceMsg(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
{
if (msg != Message.Null)
{
Debug.WriteLine(string.Format("Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}.", Thread.CurrentThread.ManagedThreadId, State, dg, dat, msg));
}
}
#endregion
}

48
NTwain/TwainSessionWPF.cs Normal file
View File

@ -0,0 +1,48 @@
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Windows.Forms;
using System.Windows.Interop;
namespace NTwain
{
/// <summary>
/// A customized TWAIN session for use in WPF environment.
/// Use this by using <see cref="PreFilterMessage"/> method as the target of <see cref="HwndSource.AddHook"/> delegate.
/// </summary>
public class TwainSessionWPF : TwainSessionBase
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSessionWPF" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TwainSessionWPF(TWIdentity appId) : base(appId) { }
/// <summary>
/// Message loop processor for WPF.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="msg">The message ID.</param>
/// <param name="wParam">The message's wParam value.</param>
/// <param name="lParam">The message's lParam value.</param>
/// <param name="handled">A value that indicates whether the message was handled. Set the value to true if the message was handled; otherwise, false.</param>
/// <returns></returns>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public IntPtr PreFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
MSG winmsg = default(MSG);
winmsg.hwnd = hwnd;
winmsg.lParam = lParam;
winmsg.message = msg;
winmsg.wParam = wParam;
handled = base.HandleWndProcMessage(ref winmsg);
return IntPtr.Zero;
}
}
}

View File

@ -0,0 +1,39 @@
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Interop;
namespace NTwain
{
/// <summary>
/// A customized TWAIN session for use in winform environment.
/// Use this by adding this as an <see cref="IMessageFilter "/> via <see cref="Application.AddMessageFilter"/>.
/// </summary>
public class TwainSessionWinform : TwainSessionBase, IMessageFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSessionWinform" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TwainSessionWinform(TWIdentity appId) : base(appId) { }
#region IMessageFilter Members
bool IMessageFilter.PreFilterMessage(ref Message m)
{
MSG winmsg = default(MSG);
winmsg.hwnd = m.HWnd;
winmsg.lParam = m.LParam;
winmsg.message = m.Msg;
winmsg.wParam = m.WParam;
return HandleWndProcMessage(ref winmsg);
}
#endregion
}
}