2014-04-06 04:48:44 +08:00
using NTwain.Data ;
2014-04-21 04:57:38 +08:00
using NTwain.Internals ;
2014-04-17 19:11:39 +08:00
using NTwain.Properties ;
2014-04-06 04:48:44 +08:00
using NTwain.Triplets ;
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
2014-04-06 06:33:21 +08:00
using System.Diagnostics ;
2014-04-21 06:42:51 +08:00
using System.Globalization ;
2014-04-06 09:54:08 +08:00
using System.IO ;
2014-05-31 19:13:18 +08:00
using System.Linq ;
2014-05-20 08:48:21 +08:00
using System.Reflection ;
2014-04-06 06:33:21 +08:00
using System.Runtime.InteropServices ;
using System.Threading ;
2014-04-06 04:48:44 +08:00
namespace NTwain
{
2014-04-06 06:33:21 +08:00
/// <summary>
2014-04-20 20:10:20 +08:00
/// Basic class for interfacing with TWAIN. You should only have one of this per application process.
2014-04-06 06:33:21 +08:00
/// </summary>
2014-09-17 06:33:35 +08:00
public partial class TwainSession
2014-04-06 04:48:44 +08:00
{
2014-05-20 08:48:21 +08:00
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession"/> class.
/// </summary>
/// <param name="supportedGroups">The supported groups.</param>
public TwainSession ( DataGroups supportedGroups )
: this ( TWIdentity . CreateFromAssembly ( supportedGroups , Assembly . GetEntryAssembly ( ) ) )
{
}
2014-04-06 04:48:44 +08:00
/// <summary>
2014-04-17 19:11:39 +08:00
/// Initializes a new instance of the <see cref="TwainSession" /> class.
2014-04-06 04:48:44 +08:00
/// </summary>
2014-05-01 07:38:46 +08:00
/// <param name="appId">The app id that represents calling application.</param>
2014-04-06 04:48:44 +08:00
/// <exception cref="System.ArgumentNullException"></exception>
2014-04-06 09:54:08 +08:00
public TwainSession ( TWIdentity appId )
2014-04-06 04:48:44 +08:00
{
if ( appId = = null ) { throw new ArgumentNullException ( "appId" ) ; }
2014-04-21 04:57:38 +08:00
2014-04-06 06:33:21 +08:00
_appId = appId ;
2014-09-15 19:24:13 +08:00
_ownedSources = new Dictionary < string , DataSource > ( ) ;
2015-01-08 10:34:56 +08:00
if ( PlatformInfo . Current . IsSupported )
{
( ( ITwainSessionInternal ) this ) . ChangeState ( 2 , false ) ;
}
2014-08-31 21:01:38 +08:00
#if DEBUG
// defaults to false on release since it's only useful during dev
2014-04-06 04:48:44 +08:00
EnforceState = true ;
2014-08-31 21:01:38 +08:00
#endif
2014-04-06 04:48:44 +08:00
}
2014-04-21 06:42:51 +08:00
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
2014-04-06 06:33:21 +08:00
object _callbackObj ; // kept around so it doesn't get gc'ed
2014-04-21 06:42:51 +08:00
TWIdentity _appId ;
2014-04-06 06:33:21 +08:00
TWUserInterface _twui ;
2014-05-25 20:45:52 +08:00
2014-09-12 10:24:27 +08:00
// cache generated twain sources so if you get same source from one session it'll return the same object
2014-09-15 19:24:13 +08:00
readonly Dictionary < string , DataSource > _ownedSources ;
2014-05-24 06:41:18 +08:00
2014-09-15 19:24:13 +08:00
DataSource GetSourceInstance ( ITwainSessionInternal session , TWIdentity sourceId )
2014-05-24 06:41:18 +08:00
{
2014-09-15 19:24:13 +08:00
DataSource source = null ;
2014-09-12 10:24:27 +08:00
Debug . WriteLine ( "Source id = " + sourceId . Id ) ;
var key = string . Format ( CultureInfo . InvariantCulture , "{0}|{1}|{2}|{3}" , sourceId . Id , sourceId . Manufacturer , sourceId . ProductFamily , sourceId . ProductName ) ;
2014-09-03 07:10:35 +08:00
if ( _ownedSources . ContainsKey ( key ) )
2014-05-24 06:41:18 +08:00
{
2014-09-03 07:10:35 +08:00
source = _ownedSources [ key ] ;
2014-05-24 06:41:18 +08:00
}
2014-08-14 19:34:55 +08:00
else
{
2014-09-15 19:24:13 +08:00
_ownedSources [ key ] = source = new DataSource ( session , sourceId ) ;
2014-08-14 19:34:55 +08:00
}
return source ;
2014-05-24 06:41:18 +08:00
}
2014-09-17 06:33:35 +08:00
2014-09-12 10:24:27 +08:00
#region ITwainSession Members
2014-05-24 06:41:18 +08:00
2014-04-06 06:33:21 +08:00
2014-04-06 04:48:44 +08:00
/// <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>
public bool EnforceState { get ; set ; }
2014-09-12 10:24:27 +08:00
/// <summary>
2014-09-15 19:24:13 +08:00
/// [Experimental] Gets or sets the optional synchronization context when not specifying a <see cref="MessageLoopHook"/> on <see cref="Open()"/>.
2014-09-12 10:24:27 +08:00
/// 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.
/// </value>
public SynchronizationContext SynchronizationContext { get ; set ; }
2014-04-06 04:48:44 +08:00
/// <summary>
2014-05-20 08:48:21 +08:00
/// Gets the currently open source.
2014-04-06 04:48:44 +08:00
/// </summary>
/// <value>
2014-05-20 08:48:21 +08:00
/// The current source.
2014-04-06 04:48:44 +08:00
/// </value>
2014-09-15 19:24:13 +08:00
public DataSource CurrentSource { get ; private set ; }
2014-05-20 19:25:57 +08:00
/// <summary>
/// Gets or sets the default source for this application.
/// While this can be get as long as the session is open,
/// it can only be set at State 3.
/// </summary>
/// <value>
/// The default source.
/// </value>
2014-09-15 19:24:13 +08:00
public DataSource DefaultSource
2014-05-20 19:25:57 +08:00
{
get
{
TWIdentity id ;
2014-05-24 07:01:18 +08:00
if ( ( ( ITwainSessionInternal ) this ) . DGControl . Identity . GetDefault ( out id ) = = ReturnCode . Success )
2014-05-20 19:25:57 +08:00
{
2014-05-24 06:41:18 +08:00
return GetSourceInstance ( this , id ) ;
2014-05-20 19:25:57 +08:00
}
return null ;
}
set
{
if ( value ! = null )
{
2014-05-24 07:01:18 +08:00
( ( ITwainSessionInternal ) this ) . DGControl . Identity . Set ( value . Identity ) ;
2014-05-20 19:25:57 +08:00
}
}
}
/// <summary>
/// Try to show the built-in source selector dialog and return the selected source.
/// This is not recommended and is only included for completeness.
/// </summary>
/// <returns></returns>
2014-09-15 19:24:13 +08:00
public DataSource ShowSourceSelector ( )
2014-05-20 19:25:57 +08:00
{
TWIdentity id ;
2014-05-24 07:01:18 +08:00
if ( ( ( ITwainSessionInternal ) this ) . DGControl . Identity . UserSelect ( out id ) = = ReturnCode . Success )
2014-05-20 19:25:57 +08:00
{
2014-05-24 06:41:18 +08:00
return GetSourceInstance ( this , id ) ;
2014-05-20 19:25:57 +08:00
}
return null ;
}
2014-04-06 04:48:44 +08:00
2015-01-08 10:34:56 +08:00
int _state = 1 ;
2014-04-06 04:48:44 +08:00
/// <summary>
/// Gets the current state number as defined by the TWAIN spec.
/// </summary>
/// <value>
/// The state.
/// </value>
2014-04-06 08:14:19 +08:00
public int State
{
get { return _state ; }
2014-04-22 18:50:58 +08:00
private set
2014-04-06 08:14:19 +08:00
{
if ( value > 0 & & value < 8 )
{
_state = value ;
2014-04-21 08:45:08 +08:00
OnPropertyChanged ( "State" ) ;
2014-04-17 08:39:30 +08:00
SafeAsyncSyncableRaiseOnEvent ( OnStateChanged , StateChanged ) ;
2014-04-06 08:14:19 +08:00
}
}
}
2014-04-06 04:48:44 +08:00
2015-01-08 10:34:56 +08:00
/// <summary>
/// Gets the named state value as defined by the TWAIN spec.
/// </summary>
/// <value>
/// The state.
/// </value>
public State StateEx
{
get
{
return ( State ) _state ;
}
}
2014-09-12 10:24:27 +08:00
/// <summary>
/// Quick flag to check if the DSM has been opened.
/// </summary>
public bool IsDsmOpen { get { return State > 2 ; } }
/// <summary>
/// Quick flag to check if a source has been opened.
/// </summary>
public bool IsSourceOpen { get { return State > 3 ; } }
/// <summary>
/// Quick flag to check if a source has been enabled.
/// </summary>
public bool IsSourceEnabled { get { return State > 4 ; } }
/// <summary>
/// Quick flag to check if a source is in the transferring state.
/// </summary>
public bool IsTransferring { get { return State > 5 ; } }
2014-04-06 06:33:21 +08:00
/// <summary>
/// Opens the data source manager. This must be the first method used
2014-09-15 19:24:13 +08:00
/// before using other TWAIN functions. Calls to this must be followed by
2014-05-25 20:45:52 +08:00
/// <see cref="Close" /> when done with a TWAIN session.
2014-04-06 06:33:21 +08:00
/// </summary>
2014-09-15 19:24:13 +08:00
/// <returns></returns>
2014-05-20 09:26:44 +08:00
public ReturnCode Open ( )
2014-04-06 06:33:21 +08:00
{
2014-05-25 20:45:52 +08:00
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.
/// </summary>
/// <param name="messageLoopHook">The message loop hook.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">messageLoopHook</exception>
public ReturnCode Open ( MessageLoopHook messageLoopHook )
{
if ( messageLoopHook = = null ) { throw new ArgumentNullException ( "messageLoopHook" ) ; }
_msgLoopHook = messageLoopHook ;
_msgLoopHook . Start ( this ) ;
2014-04-20 09:02:44 +08:00
var rc = ReturnCode . Failure ;
2014-05-25 20:45:52 +08:00
_msgLoopHook . Invoke ( ( ) = >
2014-04-06 06:33:21 +08:00
{
2014-04-21 06:42:51 +08:00
Debug . WriteLine ( string . Format ( CultureInfo . InvariantCulture , "Thread {0}: OpenManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
2014-04-15 07:30:25 +08:00
2014-05-25 20:45:52 +08:00
rc = ( ( ITwainSessionInternal ) this ) . DGControl . Parent . OpenDsm ( _msgLoopHook . Handle ) ;
2014-04-15 07:30:25 +08:00
if ( rc = = ReturnCode . Success )
2014-04-06 06:33:21 +08:00
{
2014-04-15 07:30:25 +08:00
// if twain2 then get memory management functions
if ( ( _appId . DataFunctionalities & DataFunctionalities . Dsm2 ) = = DataFunctionalities . Dsm2 )
2014-04-06 06:33:21 +08:00
{
2014-04-15 07:30:25 +08:00
TWEntryPoint entry ;
2014-05-24 07:01:18 +08:00
rc = ( ( ITwainSessionInternal ) this ) . DGControl . EntryPoint . Get ( out entry ) ;
2014-04-15 07:30:25 +08:00
if ( rc = = ReturnCode . Success )
{
2014-09-15 19:24:13 +08:00
PlatformInfo . __global . MemoryManager = entry ;
2014-04-15 07:30:25 +08:00
Debug . WriteLine ( "Using TWAIN2 memory functions." ) ;
}
else
{
2014-05-20 09:26:44 +08:00
Close ( ) ;
2014-04-15 07:30:25 +08:00
}
2014-04-06 06:33:21 +08:00
}
}
2014-04-15 07:30:25 +08:00
} ) ;
2014-04-06 06:33:21 +08:00
return rc ;
}
/// <summary>
/// Closes the data source manager.
/// </summary>
/// <returns></returns>
2014-05-20 09:26:44 +08:00
public ReturnCode Close ( )
2014-04-06 06:33:21 +08:00
{
2014-04-20 20:10:20 +08:00
var rc = ReturnCode . Failure ;
2014-05-25 20:45:52 +08:00
_msgLoopHook . Invoke ( ( ) = >
2014-04-15 07:30:25 +08:00
{
2014-04-21 06:42:51 +08:00
Debug . WriteLine ( string . Format ( CultureInfo . InvariantCulture , "Thread {0}: CloseManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
2014-04-06 06:33:21 +08:00
2014-05-25 20:45:52 +08:00
rc = ( ( ITwainSessionInternal ) this ) . DGControl . Parent . CloseDsm ( _msgLoopHook . Handle ) ;
2014-04-22 18:50:58 +08:00
if ( rc = = ReturnCode . Success )
{
2014-09-15 19:24:13 +08:00
PlatformInfo . __global . MemoryManager = null ;
2014-05-25 20:45:52 +08:00
_msgLoopHook . Stop ( ) ;
2014-04-22 18:50:58 +08:00
}
2014-04-15 07:30:25 +08:00
} ) ;
2014-04-06 06:33:21 +08:00
return rc ;
}
2014-05-20 09:26:44 +08:00
/// <summary>
/// Gets list of sources available in the system.
/// Only call this at state 2 or higher.
/// </summary>
/// <returns></returns>
2014-09-15 19:24:13 +08:00
public IEnumerable < DataSource > GetSources ( )
2014-05-20 09:26:44 +08:00
{
2014-09-15 19:24:13 +08:00
return this ;
2014-05-20 09:26:44 +08:00
}
2014-05-24 07:01:18 +08:00
2014-05-31 19:13:18 +08:00
/// <summary>
/// Quick shortcut to open a source.
/// </summary>
/// <param name="sourceName">Name of the source.</param>
/// <returns></returns>
public ReturnCode OpenSource ( string sourceName )
{
2014-09-12 10:24:27 +08:00
var curSrc = CurrentSource ;
if ( curSrc ! = null )
{
// TODO: close any open sources first
}
2014-09-15 19:24:13 +08:00
var hit = this . Where ( s = > string . Equals ( s . Name , sourceName ) ) . FirstOrDefault ( ) ;
2014-05-31 19:13:18 +08:00
if ( hit ! = null )
{
return hit . Open ( ) ;
}
return ReturnCode . Failure ;
}
2014-05-20 09:26:44 +08:00
/// <summary>
/// Gets the manager status. Only call this at state 2 or higher.
/// </summary>
/// <returns></returns>
public TWStatus GetStatus ( )
{
TWStatus stat ;
2014-05-24 07:01:18 +08:00
( ( ITwainSessionInternal ) this ) . DGControl . Status . GetManager ( out stat ) ;
2014-05-20 09:26:44 +08:00
return stat ;
}
2014-05-23 09:05:28 +08:00
/// <summary>
/// Gets the manager status. Only call this at state 3 or higher.
/// </summary>
/// <returns></returns>
public TWStatusUtf8 GetStatusUtf8 ( )
{
TWStatusUtf8 stat ;
2014-05-24 07:01:18 +08:00
( ( ITwainSessionInternal ) this ) . DGControl . StatusUtf8 . GetManager ( out stat ) ;
2014-05-23 09:05:28 +08:00
return stat ;
}
2014-04-06 06:33:21 +08:00
/// <summary>
/// Forces the stepping down of an opened source when things gets out of control.
/// Used when session state and source state become out of sync.
/// </summary>
/// <param name="targetState">State of the target.</param>
public void ForceStepDown ( int targetState )
{
2014-04-21 06:42:51 +08:00
Debug . WriteLine ( string . Format ( CultureInfo . InvariantCulture , "Thread {0}: ForceStepDown." , Thread . CurrentThread . ManagedThreadId ) ) ;
2014-04-15 07:30:25 +08:00
bool origFlag = EnforceState ;
EnforceState = false ;
// From the twain spec
// Stepping Back Down the States
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS → state 5 to 4
// DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS → state 4 to 3
// Ignore the status returns from the calls prior to the one yielding the desired state. For instance, if a
// call during scanning returns TWCC_SEQERROR and the desire is to return to state 5, then use the
// following commands.
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// Being sure to confirm that DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET returned
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
// be ignored.
2014-05-25 20:45:52 +08:00
_msgLoopHook . Invoke ( ( ) = >
2014-04-15 07:30:25 +08:00
{
2014-07-01 07:40:08 +08:00
if ( targetState < 7 & & CurrentSource ! = null )
2014-04-15 07:51:36 +08:00
{
2014-05-24 07:01:18 +08:00
( ( ITwainSessionInternal ) this ) . DGControl . PendingXfers . EndXfer ( new TWPendingXfers ( ) ) ;
2014-04-15 07:51:36 +08:00
}
2014-07-01 07:40:08 +08:00
if ( targetState < 6 & & CurrentSource ! = null )
2014-04-15 07:51:36 +08:00
{
2014-05-24 07:01:18 +08:00
( ( ITwainSessionInternal ) this ) . DGControl . PendingXfers . Reset ( new TWPendingXfers ( ) ) ;
2014-04-15 07:51:36 +08:00
}
2014-07-01 07:40:08 +08:00
if ( targetState < 5 & & CurrentSource ! = null )
2014-04-15 07:51:36 +08:00
{
2014-04-21 06:42:51 +08:00
( ( ITwainSessionInternal ) this ) . DisableSource ( ) ;
2014-04-15 07:51:36 +08:00
}
2014-05-20 19:25:57 +08:00
if ( targetState < 4 & & CurrentSource ! = null )
2014-04-15 07:51:36 +08:00
{
2014-05-20 19:25:57 +08:00
CurrentSource . Close ( ) ;
2014-04-15 07:51:36 +08:00
}
if ( targetState < 3 )
{
2014-05-20 09:26:44 +08:00
Close ( ) ;
2014-04-15 07:51:36 +08:00
}
} ) ;
2014-04-15 07:30:25 +08:00
EnforceState = origFlag ;
2014-04-06 06:33:21 +08:00
}
2014-04-06 04:48:44 +08:00
2014-04-06 07:39:41 +08:00
/// <summary>
/// Occurs when <see cref="State"/> has changed.
/// </summary>
public event EventHandler StateChanged ;
/// <summary>
2014-05-20 19:25:57 +08:00
/// Occurs when <see cref="CurrentSource"/> has changed.
2014-04-06 07:39:41 +08:00
/// </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 ;
2014-04-12 23:07:13 +08:00
/// <summary>
/// Occurs when an error has been encountered during transfer.
/// </summary>
public event EventHandler < TransferErrorEventArgs > TransferError ;
2014-04-21 06:42:51 +08:00
2014-09-12 10:24:27 +08:00
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged ;
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
protected void OnPropertyChanged ( string propertyName )
{
var syncer = SynchronizationContext ;
if ( syncer = = null )
{
try
{
var hand = PropertyChanged ;
if ( hand ! = null ) { hand ( this , new PropertyChangedEventArgs ( propertyName ) ) ; }
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( "PropertyChanged event error: " + ex . ToString ( ) ) ;
}
2014-09-12 10:24:27 +08:00
}
else
{
syncer . Post ( o = >
{
try
{
var hand = PropertyChanged ;
if ( hand ! = null ) { hand ( this , new PropertyChangedEventArgs ( propertyName ) ) ; }
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( "PropertyChanged event error: " + ex . ToString ( ) ) ;
}
2014-09-12 10:24:27 +08:00
} , null ) ;
}
}
#endregion
2014-09-15 19:24:13 +08:00
#region IEnumerable < DataSource > Members
/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns></returns>
public IEnumerator < DataSource > GetEnumerator ( )
{
TWIdentity srcId ;
var rc = ( ( ITwainSessionInternal ) this ) . DGControl . Identity . GetFirst ( out srcId ) ;
while ( rc = = ReturnCode . Success )
{
yield return GetSourceInstance ( this , srcId ) ;
rc = ( ( ITwainSessionInternal ) this ) . DGControl . Identity . GetNext ( out srcId ) ;
}
}
#endregion
#region IEnumerable Members
System . Collections . IEnumerator System . Collections . IEnumerable . GetEnumerator ( )
{
return GetEnumerator ( ) ;
}
#endregion
2014-09-12 10:24:27 +08:00
#region events overridables
2014-04-06 04:48:44 +08:00
/// <summary>
2014-04-22 18:50:58 +08:00
/// Raises event and if applicable marshal it asynchronously to the <see cref="SynchronizationContext"/> thread
/// without exceptions.
2014-04-06 04:48:44 +08:00
/// </summary>
2014-04-17 08:39:30 +08:00
/// <param name="onEventFunc">The on event function.</param>
/// <param name="handler">The handler.</param>
2014-04-21 06:42:51 +08:00
void SafeAsyncSyncableRaiseOnEvent ( Action onEventFunc , EventHandler handler )
2014-04-06 07:39:41 +08:00
{
2014-04-17 08:39:30 +08:00
var syncer = SynchronizationContext ;
if ( syncer = = null )
2014-04-06 07:39:41 +08:00
{
2014-04-06 08:50:13 +08:00
try
{
2014-04-21 06:42:51 +08:00
onEventFunc ( ) ;
if ( handler ! = null ) { handler ( this , EventArgs . Empty ) ; }
2014-04-06 08:50:13 +08:00
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( handler . Method . Name + " event error: " + ex . ToString ( ) ) ;
}
2014-04-06 07:39:41 +08:00
}
2014-04-17 08:39:30 +08:00
else
2014-04-06 07:39:41 +08:00
{
2014-04-21 06:42:51 +08:00
syncer . Post ( o = >
2014-04-06 08:50:13 +08:00
{
2014-04-17 08:39:30 +08:00
try
{
2014-04-21 06:42:51 +08:00
onEventFunc ( ) ;
if ( handler ! = null ) { handler ( this , EventArgs . Empty ) ; }
2014-04-17 08:39:30 +08:00
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( handler . Method . Name + " event error: " + ex . ToString ( ) ) ;
}
2014-04-17 08:39:30 +08:00
} , null ) ;
2014-04-06 07:39:41 +08:00
}
}
2014-04-06 04:48:44 +08:00
2014-04-06 06:33:21 +08:00
/// <summary>
2014-04-22 18:50:58 +08:00
/// Raises event and if applicable marshal it synchronously to the <see cref="SynchronizationContext" /> thread
/// without exceptions.
2014-04-06 06:33:21 +08:00
/// </summary>
2014-04-21 06:42:51 +08:00
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
2014-04-17 08:39:30 +08:00
/// <param name="onEventFunc">The on event function.</param>
/// <param name="handler">The handler.</param>
2014-04-21 06:42:51 +08:00
/// <param name="e">The TEventArgs instance containing the event data.</param>
void SafeSyncableRaiseOnEvent < TEventArgs > ( Action < TEventArgs > onEventFunc , EventHandler < TEventArgs > handler , TEventArgs e ) where TEventArgs : EventArgs
2014-04-06 07:39:41 +08:00
{
2014-04-17 08:39:30 +08:00
var syncer = SynchronizationContext ;
if ( syncer = = null )
2014-04-06 07:39:41 +08:00
{
2014-05-15 07:58:45 +08:00
Debug . WriteLine ( string . Format ( CultureInfo . InvariantCulture , "Trying to raise event {0} on thread {1} without sync." , e . GetType ( ) . Name , Thread . CurrentThread . ManagedThreadId ) ) ;
2014-04-06 08:50:13 +08:00
try
{
2014-04-21 06:42:51 +08:00
onEventFunc ( e ) ;
if ( handler ! = null ) { handler ( this , e ) ; }
2014-04-06 08:50:13 +08:00
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( handler . Method . Name + " event error: " + ex . ToString ( ) ) ;
}
2014-04-06 07:39:41 +08:00
}
2014-04-17 08:39:30 +08:00
else
{
2014-05-15 07:58:45 +08:00
Debug . WriteLine ( string . Format ( CultureInfo . InvariantCulture , "Trying to raise event {0} on thread {1} with sync." , e . GetType ( ) . Name , Thread . CurrentThread . ManagedThreadId ) ) ;
// on some consumer desktop scanner with poor drivers this can frequently hang. there's nothing I can do here.
2014-04-21 06:42:51 +08:00
syncer . Send ( o = >
2014-04-17 08:39:30 +08:00
{
try
{
2014-04-21 06:42:51 +08:00
onEventFunc ( e ) ;
if ( handler ! = null ) { handler ( this , e ) ; }
2014-04-17 08:39:30 +08:00
}
2014-09-17 06:33:35 +08:00
catch ( Exception ex )
{
Debug . WriteLine ( handler . Method . Name + " event error: " + ex . ToString ( ) ) ;
}
2014-04-17 08:39:30 +08:00
} , null ) ;
}
2014-04-06 07:39:41 +08:00
}
2014-04-06 06:33:21 +08:00
2014-04-06 07:39:41 +08:00
/// <summary>
2014-04-17 08:39:30 +08:00
/// Called when <see cref="State"/> changed.
/// </summary>
protected virtual void OnStateChanged ( ) { }
/// <summary>
2014-05-20 19:25:57 +08:00
/// Called when <see cref="CurrentSource"/> changed.
2014-04-17 08:39:30 +08:00
/// </summary>
protected virtual void OnSourceChanged ( ) { }
/// <summary>
/// Called when source has been disabled (back to state 4).
/// </summary>
protected virtual void OnSourceDisabled ( ) { }
/// <summary>
/// Called when the source has generated an event.
2014-04-06 07:39:41 +08:00
/// </summary>
/// <param name="e">The <see cref="DeviceEventArgs"/> instance containing the event data.</param>
2014-04-17 08:39:30 +08:00
protected virtual void OnDeviceEvent ( DeviceEventArgs e ) { }
2014-04-06 06:33:21 +08:00
2014-04-06 07:39:41 +08:00
/// <summary>
2014-04-17 08:39:30 +08:00
/// Called when a data transfer is ready.
2014-04-06 07:39:41 +08:00
/// </summary>
/// <param name="e">The <see cref="TransferReadyEventArgs"/> instance containing the event data.</param>
2014-04-22 18:50:58 +08:00
protected virtual void OnTransferReady ( TransferReadyEventArgs e ) { }
2014-04-06 06:33:21 +08:00
2014-04-06 07:39:41 +08:00
/// <summary>
2014-04-17 08:39:30 +08:00
/// Called when data has been transferred.
2014-04-06 07:39:41 +08:00
/// </summary>
/// <param name="e">The <see cref="DataTransferredEventArgs"/> instance containing the event data.</param>
2014-04-22 18:50:58 +08:00
protected virtual void OnDataTransferred ( DataTransferredEventArgs e ) { }
2014-04-06 07:39:41 +08:00
2014-04-12 23:07:13 +08:00
/// <summary>
2014-04-17 08:39:30 +08:00
/// Called when an error has been encountered during transfer.
2014-04-12 23:07:13 +08:00
/// </summary>
/// <param name="e">The <see cref="TransferErrorEventArgs"/> instance containing the event data.</param>
2014-04-22 18:50:58 +08:00
protected virtual void OnTransferError ( TransferErrorEventArgs e ) { }
2014-04-17 08:39:30 +08:00
2014-04-06 07:39:41 +08:00
#endregion
2014-04-06 04:48:44 +08:00
}
}