Made it possible to create event args externally for testing.

This commit is contained in:
soukoku 2014-09-12 07:16:10 -04:00
parent 81710b7e99
commit 0cd02ac16e
10 changed files with 245 additions and 161 deletions

View File

@ -1730,12 +1730,13 @@ namespace NTwain.Data
public string ProductName { get { return _productName; } set { value.VerifyLengthUnder(TwainConst.String32 - 1); _productName = value; } }
/// <summary>
/// Creates a <see cref="TWIdentity"/> from assembly values.
/// Creates a <see cref="TWIdentity" /> from assembly values.
/// </summary>
/// <param name="supportedGroups">The supported groups.</param>
/// <param name="assembly">The assembly.</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">assembly</exception>
/// <exception cref="System.ArgumentNullException">assembly</exception>
/// <exception cref="System.ArgumentException"></exception>
public static TWIdentity CreateFromAssembly(DataGroups supportedGroups, Assembly assembly)
{
if (assembly == null) { throw new ArgumentNullException("assembly"); }
@ -1755,10 +1756,16 @@ namespace NTwain.Data
/// <param name="productName">Name of the product.</param>
/// <param name="productDescription">The product description.</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">version</exception>
/// <exception cref="System.ArgumentNullException">assembly</exception>
/// <exception cref="System.ArgumentException"></exception>
public static TWIdentity Create(DataGroups supportedGroups, Version version,
string manufacturer, string productFamily, string productName, string productDescription)
{
if ((supportedGroups & DataGroups.Image) == 0 &&
(supportedGroups & DataGroups.Audio) == 0)
{
throw new ArgumentException(Resources.BadDataGroupsForAppId);
}
if (version == null) { throw new ArgumentNullException("version"); }
return new TWIdentity

View File

@ -8,6 +8,37 @@ namespace NTwain
/// </summary>
public class DataTransferredEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="DataTransferredEventArgs"/> class.
/// </summary>
/// <param name="nativeData">The native data.</param>
/// <param name="imageInfo">The image information.</param>
public DataTransferredEventArgs(IntPtr nativeData, TWImageInfo imageInfo)
{
NativeData = nativeData;
ImageInfo = imageInfo;
}
/// <summary>
/// Initializes a new instance of the <see cref="DataTransferredEventArgs"/> class.
/// </summary>
/// <param name="fileDataPath">The file data path.</param>
/// <param name="imageInfo">The image information.</param>
public DataTransferredEventArgs(string fileDataPath, TWImageInfo imageInfo)
{
FileDataPath = fileDataPath;
ImageInfo = imageInfo;
}
/// <summary>
/// Initializes a new instance of the <see cref="DataTransferredEventArgs"/> class.
/// </summary>
/// <param name="memoryData">The memory data.</param>
/// <param name="imageInfo">The image information.</param>
public DataTransferredEventArgs(byte[] memoryData, TWImageInfo imageInfo)
{
MemoryData = memoryData;
ImageInfo = imageInfo;
}
/// <summary>
/// Gets pointer to the complete data if the transfer was native.
/// The data will be freed once the event handler ends
@ -16,7 +47,7 @@ namespace NTwain
/// This pointer is already locked for the duration of this event.
/// </summary>
/// <value>The data pointer.</value>
public IntPtr NativeData { get; internal set; }
public IntPtr NativeData { get; private set; }
/// <summary>
/// Gets the file path to the complete data if the transfer was file or memory-file.
@ -24,7 +55,7 @@ namespace NTwain
/// <value>
/// The file path.
/// </value>
public string FileDataPath { get; internal set; }
public string FileDataPath { get; private set; }
/// <summary>
/// Gets the raw memory data if the transfer was memory.
@ -35,7 +66,7 @@ namespace NTwain
/// The memory data.
/// </value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public byte[] MemoryData { get; internal set; }
public byte[] MemoryData { get; private set; }
/// <summary>
/// Gets the final image information if applicable.
@ -43,7 +74,7 @@ namespace NTwain
/// <value>
/// The final image information.
/// </value>
public TWImageInfo ImageInfo { get; internal set; }
public TWImageInfo ImageInfo { get; private set; }
///// <summary>
///// Gets the extended image information if applicable.

View File

@ -39,14 +39,7 @@ namespace NTwain.Internals
}
// ask consumer for xfer details
var preXferArgs = new TransferReadyEventArgs
{
AudioInfo = audInfo,
PendingImageInfo = imgInfo,
PendingTransferCount = pending.Count,
EndOfJob = pending.EndOfJob == 0
};
var preXferArgs = new TransferReadyEventArgs(pending.Count, pending.EndOfJob == 0, imgInfo, audInfo); ;
session.SafeSyncableRaiseEvent(preXferArgs);
#endregion
@ -144,16 +137,16 @@ namespace NTwain.Internals
lockedPtr = Platform.MemoryManager.Lock(dataPtr);
}
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs { NativeData = lockedPtr });
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs(lockedPtr, null));
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(ex));
}
finally
{
@ -184,11 +177,11 @@ namespace NTwain.Internals
var xrc = session.DGAudio.AudioFileXfer.Get();
if (xrc == ReturnCode.XferDone)
{
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs { FileDataPath = filePath });
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs(filePath, null));
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
@ -214,12 +207,12 @@ namespace NTwain.Internals
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(ex));
}
finally
{
@ -254,7 +247,7 @@ namespace NTwain.Internals
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
@ -317,13 +310,13 @@ namespace NTwain.Internals
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(ex));
}
finally
{
@ -396,12 +389,12 @@ namespace NTwain.Internals
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.CurrentSource.GetStatus() });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(xrc, session.CurrentSource.GetStatus()));
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs(ex));
}
finally
{
@ -425,6 +418,7 @@ namespace NTwain.Internals
static void DoImageXferredEventRoutine(ITwainSessionInternal session, IntPtr dataPtr, byte[] dataArray, string filePath)
{
DataTransferredEventArgs args = null;
TWImageInfo imgInfo;
//TWExtImageInfo extInfo = null;
//if (session.CurrentSource.SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
@ -438,14 +432,19 @@ namespace NTwain.Internals
{
imgInfo = null;
}
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs
if (dataPtr != IntPtr.Zero)
{
NativeData = dataPtr,
MemoryData = dataArray,
FileDataPath = filePath,
ImageInfo = imgInfo,
//ExImageInfo = extInfo
});
args = new DataTransferredEventArgs(dataPtr, imgInfo);
}
else if (dataArray != null)
{
args = new DataTransferredEventArgs(dataArray, imgInfo);
}
else
{
args = new DataTransferredEventArgs(filePath, imgInfo);
}
session.SafeSyncableRaiseEvent(args);
//if (extInfo != null) { extInfo.Dispose(); }
}

View File

@ -113,7 +113,6 @@ namespace NTwain
//{
System.Windows.Forms.Application.RemoveMessageFilter(this);
_filter = null;
Handle = IntPtr.Zero;
//});
}
@ -191,9 +190,9 @@ namespace NTwain
if (_hooker != null)
{
_hooker.RemoveHook(FilterMessage);
_hooker.Dispose();
// cannot really dispose _hook or the window will also dispose
//_hooker.Dispose();
_hooker = null;
Handle = IntPtr.Zero;
}
}
}

View File

@ -8,10 +8,10 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace NTwain.Properties
{
namespace NTwain.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@ -60,6 +60,15 @@ namespace NTwain.Properties
}
}
/// <summary>
/// Looks up a localized string similar to Data group for application must contain either Image or Audio..
/// </summary>
internal static string BadDataGroupsForAppId {
get {
return ResourceManager.GetString("BadDataGroupsForAppId", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid value type for {0}..
/// </summary>

View File

@ -112,11 +112,14 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BadDataGroupsForAppId" xml:space="preserve">
<value>Data group for application must contain either Image or Audio.</value>
</data>
<data name="BadValueType" xml:space="preserve">
<value>Invalid value type for {0}.</value>
</data>

View File

@ -8,13 +8,33 @@ namespace NTwain
/// </summary>
public class TransferErrorEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="TransferErrorEventArgs"/> class.
/// </summary>
/// <param name="error">The error.</param>
public TransferErrorEventArgs(Exception error)
{
Exception = error;
}
/// <summary>
/// Initializes a new instance of the <see cref="TransferErrorEventArgs"/> class.
/// </summary>
/// <param name="code">The code.</param>
/// <param name="status">The status.</param>
public TransferErrorEventArgs(ReturnCode code, TWStatus status)
{
ReturnCode = code;
SourceStatus = status;
}
/// <summary>
/// Gets the return code from the transfer.
/// </summary>
/// <value>
/// The return code.
/// </value>
public ReturnCode ReturnCode { get; internal set; }
public ReturnCode ReturnCode { get; private set; }
/// <summary>
/// Gets the source status.
@ -22,7 +42,7 @@ namespace NTwain
/// <value>
/// The source status.
/// </value>
public TWStatus SourceStatus { get; internal set; }
public TWStatus SourceStatus { get; private set; }
/// <summary>
/// Gets the exception if the error is from some exception.
@ -30,6 +50,6 @@ namespace NTwain
/// <value>
/// The exception.
/// </value>
public Exception Exception { get; internal set; }
public Exception Exception { get; private set; }
}
}

View File

@ -8,6 +8,21 @@ namespace NTwain
/// </summary>
public class TransferReadyEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="TransferReadyEventArgs"/> class.
/// </summary>
/// <param name="pendingCount">The pending count.</param>
/// <param name="endOfJob">if set to <c>true</c> [end of job].</param>
/// <param name="imageInfo">The image information.</param>
/// <param name="audioInfo">The audio information.</param>
public TransferReadyEventArgs(int pendingCount, bool endOfJob, TWImageInfo imageInfo, TWAudioInfo audioInfo)
{
PendingTransferCount = pendingCount;
EndOfJob = endOfJob;
PendingImageInfo = imageInfo;
AudioInfo = audioInfo;
}
/// <summary>
/// Gets or sets a value indicating whether the current transfer should be canceled
/// and continue next transfer if there are more data.
@ -25,14 +40,14 @@ namespace NTwain
/// Gets a value indicating whether current transfer signifies an end of job in TWAIN world.
/// </summary>
/// <value><c>true</c> if transfer is end of job; otherwise, <c>false</c>.</value>
public bool EndOfJob { get; internal set; }
public bool EndOfJob { get; private set; }
/// <summary>
/// Gets the known pending transfer count. This may not be appilicable
/// for certain scanning modes.
/// </summary>
/// <value>The pending count.</value>
public int PendingTransferCount { get; internal set; }
public int PendingTransferCount { get; private set; }
/// <summary>
/// Gets the tentative image information for the current transfer if applicable.
@ -41,7 +56,7 @@ namespace NTwain
/// <value>
/// The image info.
/// </value>
public TWImageInfo PendingImageInfo { get; internal set; }
public TWImageInfo PendingImageInfo { get; private set; }
/// <summary>
/// Gets the audio information for the current transfer if applicable.
@ -49,7 +64,7 @@ namespace NTwain
/// <value>
/// The audio information.
/// </value>
public TWAudioInfo AudioInfo { get; internal set; }
public TWAudioInfo AudioInfo { get; private set; }
}
}

View File

@ -18,7 +18,7 @@ namespace NTwain
/// <summary>
/// Basic class for interfacing with TWAIN. You should only have one of this per application process.
/// </summary>
public partial class TwainSession : ITwainSessionInternal, IWinMessageFilter
public partial class TwainSession
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession"/> class.
@ -557,114 +557,5 @@ namespace NTwain
protected virtual void OnTransferError(TransferErrorEventArgs e) { }
#endregion
#region handle twain ds message
#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)
{
// transform it into a pointer for twain
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);
var evt = new TWEvent();
evt.pEvent = msgPtr;
if (handled = (((ITwainSessionInternal)this).DGControl.Event.ProcessEvent(evt) == ReturnCode.DSEvent))
{
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
HandleSourceMsg(evt.TWMessage);
}
}
finally
{
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)
{
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CallbackHandler at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, msg));
// spec says we must handle this on the thread that enabled the DS.
// by using the internal dispatcher this will be the case.
_msgLoopHook.BeginInvoke(() =>
{
HandleSourceMsg(msg);
});
return ReturnCode.Success;
}
return ReturnCode.Failure;
}
// final method that handles msg from the source, whether it's from wndproc or callbacks
void HandleSourceMsg(Message msg)
{
switch (msg)
{
case Message.XferReady:
if (State < 6)
{
State = 6;
}
TransferLogic.DoTransferRoutine(this);
break;
case Message.DeviceEvent:
TWDeviceEvent de;
var rc = ((ITwainSessionInternal)this).DGControl.DeviceEvent.Get(out de);
if (rc == ReturnCode.Success)
{
SafeSyncableRaiseOnEvent(OnDeviceEvent, DeviceEvent, new DeviceEventArgs(de));
}
break;
case Message.CloseDSReq:
case Message.CloseDSOK:
Debug.WriteLine("Got msg " + msg);
// even though it says closeDS it's really disable.
// dsok is sent if source is enabled with uionly
// some sources send this at other states so do a step down
if (State > 5)
{
ForceStepDown(4);
}
else if (State == 5)
{
// needs this state check since some source sends this more than once
((ITwainSessionInternal)this).DisableSource();
}
break;
}
}
#endregion
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
@ -13,7 +14,7 @@ namespace NTwain
{
// for internal pieces since the main twain session file is getting too long
partial class TwainSession
partial class TwainSession : ITwainSessionInternal, IWinMessageFilter
{
#region ITwainSessionInternal Members
@ -196,5 +197,114 @@ namespace NTwain
#endregion
#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)
{
// transform it into a pointer for twain
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);
var evt = new TWEvent();
evt.pEvent = msgPtr;
if (handled = (((ITwainSessionInternal)this).DGControl.Event.ProcessEvent(evt) == ReturnCode.DSEvent))
{
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
HandleSourceMsg(evt.TWMessage);
}
}
finally
{
if (msgPtr != IntPtr.Zero) { Marshal.FreeHGlobal(msgPtr); }
}
}
return handled;
}
#endregion
#region handle twain ds message
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)
{
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CallbackHandler at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, msg));
// spec says we must handle this on the thread that enabled the DS.
// by using the internal dispatcher this will be the case.
_msgLoopHook.BeginInvoke(() =>
{
HandleSourceMsg(msg);
});
return ReturnCode.Success;
}
return ReturnCode.Failure;
}
// final method that handles msg from the source, whether it's from wndproc or callbacks
void HandleSourceMsg(Message msg)
{
switch (msg)
{
case Message.XferReady:
if (State < 6)
{
State = 6;
}
TransferLogic.DoTransferRoutine(this);
break;
case Message.DeviceEvent:
TWDeviceEvent de;
var rc = ((ITwainSessionInternal)this).DGControl.DeviceEvent.Get(out de);
if (rc == ReturnCode.Success)
{
SafeSyncableRaiseOnEvent(OnDeviceEvent, DeviceEvent, new DeviceEventArgs(de));
}
break;
case Message.CloseDSReq:
case Message.CloseDSOK:
Debug.WriteLine("Got msg " + msg);
// even though it says closeDS it's really disable.
// dsok is sent if source is enabled with uionly
// some sources send this at other states so do a step down
if (State > 5)
{
ForceStepDown(4);
}
else if (State == 5)
{
// needs this state check since some source sends this more than once
((ITwainSessionInternal)this).DisableSource();
}
break;
}
}
#endregion
}
}