From 0cd02ac16ee7e0e8cb06ced28633f9c0a3653752 Mon Sep 17 00:00:00 2001 From: soukoku Date: Fri, 12 Sep 2014 07:16:10 -0400 Subject: [PATCH] Made it possible to create event args externally for testing. --- NTwain/Data/TwainTypesExtended.cs | 13 ++- NTwain/DataTransferredEventArgs.cs | 39 ++++++++- NTwain/Internals/TransferLogic.cs | 53 ++++++----- NTwain/MessageLoopHooks.cs | 5 +- NTwain/Properties/Resources.Designer.cs | 17 +++- NTwain/Properties/Resources.resx | 7 +- NTwain/TransferErrorEventArgs.cs | 26 +++++- NTwain/TransferReadyEventArgs.cs | 23 ++++- NTwain/TwainSession.cs | 111 +---------------------- NTwain/TwainSessionInternal.cs | 112 +++++++++++++++++++++++- 10 files changed, 245 insertions(+), 161 deletions(-) diff --git a/NTwain/Data/TwainTypesExtended.cs b/NTwain/Data/TwainTypesExtended.cs index e8b7389..1bb56da 100644 --- a/NTwain/Data/TwainTypesExtended.cs +++ b/NTwain/Data/TwainTypesExtended.cs @@ -1730,12 +1730,13 @@ namespace NTwain.Data public string ProductName { get { return _productName; } set { value.VerifyLengthUnder(TwainConst.String32 - 1); _productName = value; } } /// - /// Creates a from assembly values. + /// Creates a from assembly values. /// /// The supported groups. /// The assembly. /// - /// assembly + /// assembly + /// public static TWIdentity CreateFromAssembly(DataGroups supportedGroups, Assembly assembly) { if (assembly == null) { throw new ArgumentNullException("assembly"); } @@ -1755,10 +1756,16 @@ namespace NTwain.Data /// Name of the product. /// The product description. /// - /// version + /// assembly + /// 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 diff --git a/NTwain/DataTransferredEventArgs.cs b/NTwain/DataTransferredEventArgs.cs index a6c3724..c447081 100644 --- a/NTwain/DataTransferredEventArgs.cs +++ b/NTwain/DataTransferredEventArgs.cs @@ -8,6 +8,37 @@ namespace NTwain /// public class DataTransferredEventArgs : EventArgs { + /// + /// Initializes a new instance of the class. + /// + /// The native data. + /// The image information. + public DataTransferredEventArgs(IntPtr nativeData, TWImageInfo imageInfo) + { + NativeData = nativeData; + ImageInfo = imageInfo; + } + /// + /// Initializes a new instance of the class. + /// + /// The file data path. + /// The image information. + public DataTransferredEventArgs(string fileDataPath, TWImageInfo imageInfo) + { + FileDataPath = fileDataPath; + ImageInfo = imageInfo; + } + /// + /// Initializes a new instance of the class. + /// + /// The memory data. + /// The image information. + public DataTransferredEventArgs(byte[] memoryData, TWImageInfo imageInfo) + { + MemoryData = memoryData; + ImageInfo = imageInfo; + } + /// /// 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. /// /// The data pointer. - public IntPtr NativeData { get; internal set; } + public IntPtr NativeData { get; private set; } /// /// Gets the file path to the complete data if the transfer was file or memory-file. @@ -24,7 +55,7 @@ namespace NTwain /// /// The file path. /// - public string FileDataPath { get; internal set; } + public string FileDataPath { get; private set; } /// /// Gets the raw memory data if the transfer was memory. @@ -35,7 +66,7 @@ namespace NTwain /// The memory data. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public byte[] MemoryData { get; internal set; } + public byte[] MemoryData { get; private set; } /// /// Gets the final image information if applicable. @@ -43,7 +74,7 @@ namespace NTwain /// /// The final image information. /// - public TWImageInfo ImageInfo { get; internal set; } + public TWImageInfo ImageInfo { get; private set; } ///// ///// Gets the extended image information if applicable. diff --git a/NTwain/Internals/TransferLogic.cs b/NTwain/Internals/TransferLogic.cs index 8ce7b62..562885c 100644 --- a/NTwain/Internals/TransferLogic.cs +++ b/NTwain/Internals/TransferLogic.cs @@ -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(); } } diff --git a/NTwain/MessageLoopHooks.cs b/NTwain/MessageLoopHooks.cs index ee2bfe7..1e4a477 100644 --- a/NTwain/MessageLoopHooks.cs +++ b/NTwain/MessageLoopHooks.cs @@ -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; } } } diff --git a/NTwain/Properties/Resources.Designer.cs b/NTwain/Properties/Resources.Designer.cs index 7e7473b..5636576 100644 --- a/NTwain/Properties/Resources.Designer.cs +++ b/NTwain/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace NTwain.Properties -{ - - +namespace NTwain.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -60,6 +60,15 @@ namespace NTwain.Properties } } + /// + /// Looks up a localized string similar to Data group for application must contain either Image or Audio.. + /// + internal static string BadDataGroupsForAppId { + get { + return ResourceManager.GetString("BadDataGroupsForAppId", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid value type for {0}.. /// diff --git a/NTwain/Properties/Resources.resx b/NTwain/Properties/Resources.resx index 2f92336..a8b8d9f 100644 --- a/NTwain/Properties/Resources.resx +++ b/NTwain/Properties/Resources.resx @@ -112,11 +112,14 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Data group for application must contain either Image or Audio. + Invalid value type for {0}. diff --git a/NTwain/TransferErrorEventArgs.cs b/NTwain/TransferErrorEventArgs.cs index 583e940..2ad4434 100644 --- a/NTwain/TransferErrorEventArgs.cs +++ b/NTwain/TransferErrorEventArgs.cs @@ -8,13 +8,33 @@ namespace NTwain /// public class TransferErrorEventArgs : EventArgs { + /// + /// Initializes a new instance of the class. + /// + /// The error. + public TransferErrorEventArgs(Exception error) + { + Exception = error; + } + + /// + /// Initializes a new instance of the class. + /// + /// The code. + /// The status. + public TransferErrorEventArgs(ReturnCode code, TWStatus status) + { + ReturnCode = code; + SourceStatus = status; + } + /// /// Gets the return code from the transfer. /// /// /// The return code. /// - public ReturnCode ReturnCode { get; internal set; } + public ReturnCode ReturnCode { get; private set; } /// /// Gets the source status. @@ -22,7 +42,7 @@ namespace NTwain /// /// The source status. /// - public TWStatus SourceStatus { get; internal set; } + public TWStatus SourceStatus { get; private set; } /// /// Gets the exception if the error is from some exception. @@ -30,6 +50,6 @@ namespace NTwain /// /// The exception. /// - public Exception Exception { get; internal set; } + public Exception Exception { get; private set; } } } diff --git a/NTwain/TransferReadyEventArgs.cs b/NTwain/TransferReadyEventArgs.cs index 1293fc4..6de97f4 100644 --- a/NTwain/TransferReadyEventArgs.cs +++ b/NTwain/TransferReadyEventArgs.cs @@ -8,6 +8,21 @@ namespace NTwain /// public class TransferReadyEventArgs : EventArgs { + /// + /// Initializes a new instance of the class. + /// + /// The pending count. + /// if set to true [end of job]. + /// The image information. + /// The audio information. + public TransferReadyEventArgs(int pendingCount, bool endOfJob, TWImageInfo imageInfo, TWAudioInfo audioInfo) + { + PendingTransferCount = pendingCount; + EndOfJob = endOfJob; + PendingImageInfo = imageInfo; + AudioInfo = audioInfo; + } + /// /// 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. /// /// true if transfer is end of job; otherwise, false. - public bool EndOfJob { get; internal set; } + public bool EndOfJob { get; private set; } /// /// Gets the known pending transfer count. This may not be appilicable /// for certain scanning modes. /// /// The pending count. - public int PendingTransferCount { get; internal set; } + public int PendingTransferCount { get; private set; } /// /// Gets the tentative image information for the current transfer if applicable. @@ -41,7 +56,7 @@ namespace NTwain /// /// The image info. /// - public TWImageInfo PendingImageInfo { get; internal set; } + public TWImageInfo PendingImageInfo { get; private set; } /// /// Gets the audio information for the current transfer if applicable. @@ -49,7 +64,7 @@ namespace NTwain /// /// The audio information. /// - public TWAudioInfo AudioInfo { get; internal set; } + public TWAudioInfo AudioInfo { get; private set; } } } diff --git a/NTwain/TwainSession.cs b/NTwain/TwainSession.cs index f3061c2..2782993 100644 --- a/NTwain/TwainSession.cs +++ b/NTwain/TwainSession.cs @@ -18,7 +18,7 @@ namespace NTwain /// /// Basic class for interfacing with TWAIN. You should only have one of this per application process. /// - public partial class TwainSession : ITwainSessionInternal, IWinMessageFilter + public partial class TwainSession { /// /// Initializes a new instance of the class. @@ -557,114 +557,5 @@ namespace NTwain protected virtual void OnTransferError(TransferErrorEventArgs e) { } #endregion - - #region handle twain ds message - - - #region IWinMessageFilter Members - - /// - /// Checks and handle the message if it's a TWAIN message. - /// - /// The window handle. - /// The message. - /// The w parameter. - /// The l parameter. - /// - /// true if handled internally. - /// - 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 } } diff --git a/NTwain/TwainSessionInternal.cs b/NTwain/TwainSessionInternal.cs index 46115f9..5e3a4b2 100644 --- a/NTwain/TwainSessionInternal.cs +++ b/NTwain/TwainSessionInternal.cs @@ -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 + + /// + /// Checks and handle the message if it's a TWAIN message. + /// + /// The window handle. + /// The message. + /// The w parameter. + /// The l parameter. + /// + /// true if handled internally. + /// + 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 + } }