2014-04-06 04:48:44 +08:00
using NTwain.Data ;
using NTwain.Triplets ;
2014-04-06 06:33:21 +08:00
using NTwain.Values ;
2014-04-06 04:48:44 +08:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
2014-04-06 06:33:21 +08:00
using System.Diagnostics ;
2014-04-06 09:54:08 +08:00
using System.IO ;
2014-04-06 04:48:44 +08:00
using System.Linq ;
2014-04-06 06:33:21 +08:00
using System.Runtime.InteropServices ;
2014-04-06 04:48:44 +08:00
using System.Text ;
2014-04-06 06:33:21 +08:00
using System.Threading ;
2014-04-06 04:48:44 +08:00
namespace NTwain
{
2014-04-06 06:33:21 +08:00
/// <summary>
2014-04-06 09:54:08 +08:00
/// Basic class for interfacing with TWAIN.
2014-04-06 06:33:21 +08:00
/// </summary>
2014-04-06 09:54:08 +08:00
public class TwainSession : ITwainStateInternal , ITwainOperation
2014-04-06 04:48:44 +08:00
{
/// <summary>
2014-04-06 08:14:19 +08:00
/// Initializes a new instance of the <see cref="TwainSessionOld" /> class.
2014-04-06 04:48:44 +08:00
/// </summary>
/// <param name="appId">The app id.</param>
/// <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-06 06:33:21 +08:00
_appId = appId ;
2014-04-06 20:22:59 +08:00
( ( ITwainStateInternal ) this ) . ChangeState ( 1 , false ) ;
2014-04-06 04:48:44 +08:00
EnforceState = true ;
}
2014-04-06 06:33:21 +08:00
TWIdentity _appId ;
HandleRef _appHandle ;
SynchronizationContext _syncer ;
object _callbackObj ; // kept around so it doesn't get gc'ed
TWUserInterface _twui ;
2014-04-06 04:48:44 +08:00
2014-04-06 08:14:19 +08:00
private IList < CapabilityId > _supportedCaps ;
/// <summary>
/// Gets the supported caps for the current source.
/// </summary>
/// <value>
/// The supported caps.
/// </value>
public IList < CapabilityId > SupportedCaps
{
get
{
if ( _supportedCaps = = null & & State > 3 )
{
_supportedCaps = this . GetCapabilities ( ) ;
}
return _supportedCaps ? ? new CapabilityId [ 0 ] ;
}
private set
{
_supportedCaps = value ;
RaisePropertyChanged ( "SupportedCaps" ) ;
}
}
2014-04-06 04:48:44 +08:00
#region ITwainStateInternal Members
2014-04-06 06:33:21 +08:00
/// <summary>
/// Gets the app id used for the session.
/// </summary>
/// <value>The app id.</value>
TWIdentity ITwainStateInternal . GetAppId ( ) { return _appId ; }
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 ; }
void ITwainStateInternal . ChangeState ( int newState , bool notifyChange )
{
2014-04-06 08:14:19 +08:00
_state = newState ;
2014-04-06 04:48:44 +08:00
if ( notifyChange )
{
RaisePropertyChanged ( "State" ) ;
OnStateChanged ( ) ;
}
}
ICommitable ITwainStateInternal . GetPendingStateChanger ( int newState )
{
return new TentativeStateCommitable ( this , newState ) ;
}
void ITwainStateInternal . ChangeSourceId ( TWIdentity sourceId )
{
SourceId = sourceId ;
RaisePropertyChanged ( "SourceId" ) ;
2014-04-06 08:14:19 +08:00
OnSourceChanged ( ) ;
2014-04-06 04:48:44 +08:00
}
#endregion
#region ITwainState Members
/// <summary>
/// Gets the source id used for the session.
/// </summary>
/// <value>
/// The source id.
/// </value>
public TWIdentity SourceId { get ; private set ; }
2014-04-06 08:14:19 +08:00
int _state ;
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 ; }
protected set
{
if ( value > 0 & & value < 8 )
{
_state = value ;
RaisePropertyChanged ( "State" ) ;
OnStateChanged ( ) ;
}
}
}
2014-04-06 04:48:44 +08:00
#endregion
#region ITwainOperation Members
DGAudio _dgAudio ;
/// <summary>
/// Gets the triplet operations defined for audio data group.
/// </summary>
public DGAudio DGAudio
{
get
{
if ( _dgAudio = = null ) { _dgAudio = new DGAudio ( this ) ; }
return _dgAudio ;
}
}
DGControl _dgControl ;
/// <summary>
/// Gets the triplet operations defined for control data group.
/// </summary>
public DGControl DGControl
{
get
{
if ( _dgControl = = null ) { _dgControl = new DGControl ( this ) ; }
return _dgControl ;
}
}
DGImage _dgImage ;
/// <summary>
/// Gets the triplet operations defined for image data group.
/// </summary>
public DGImage DGImage
{
get
{
if ( _dgImage = = null ) { _dgImage = new DGImage ( this ) ; }
return _dgImage ;
}
}
#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 RaisePropertyChanged ( string propertyName )
{
var hand = PropertyChanged ;
if ( hand ! = null ) { hand ( this , new PropertyChangedEventArgs ( propertyName ) ) ; }
}
#endregion
2014-04-06 06:33:21 +08:00
#region privileged calls that causes s tate change in TWAIN
/// <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="CloseManager"/> when done.
/// </summary>
/// <param name="appHandle">On Windows = points to the window handle (hWnd) that will act as the Source’ s
/// "parent". On Macintosh = should be a NULL value.</param>
/// <returns></returns>
public ReturnCode OpenManager ( HandleRef appHandle )
{
Debug . WriteLine ( string . Format ( "Thread {0}: OpenManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . Parent . OpenDsm ( appHandle . Handle ) ;
if ( rc = = ReturnCode . Success )
{
_appHandle = appHandle ;
// if twain2 then get memory management functions
if ( ( _appId . DataFunctionalities & DataFunctionalities . Dsm2 ) = = DataFunctionalities . Dsm2 )
{
TWEntryPoint entry ;
rc = DGControl . EntryPoint . Get ( out entry ) ;
if ( rc = = ReturnCode . Success )
{
MemoryManager . Instance . UpdateEntryPoint ( entry ) ;
Debug . WriteLine ( "Using TWAIN2 memory functions." ) ;
}
else
{
CloseManager ( ) ;
}
}
}
return rc ;
}
/// <summary>
/// Closes the data source manager.
/// </summary>
/// <returns></returns>
public ReturnCode CloseManager ( )
{
Debug . WriteLine ( string . Format ( "Thread {0}: CloseManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . Parent . CloseDsm ( _appHandle . Handle ) ;
if ( rc = = ReturnCode . Success )
{
_appHandle = default ( HandleRef ) ;
}
return rc ;
}
/// <summary>
/// Loads the specified source into main memory and causes its initialization.
/// Calls to this must be followed by
/// <see cref="CloseSource" /> when done.
/// </summary>
/// <param name="sourceProductName">Name of the source.</param>
/// <returns></returns>
/// <exception cref="ArgumentException">Source name is required.;sourceProductName</exception>
public ReturnCode OpenSource ( string sourceProductName )
{
if ( string . IsNullOrEmpty ( sourceProductName ) ) { throw new ArgumentException ( "Source name is required." , "sourceProductName" ) ; }
Debug . WriteLine ( string . Format ( "Thread {0}: OpenSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var source = new TWIdentity ( ) ;
source . ProductName = sourceProductName ;
var rc = DGControl . Identity . OpenDS ( source ) ;
if ( rc = = ReturnCode . Success )
{
}
return rc ;
}
/// <summary>
/// When an application is finished with a Source, it must formally close the session between them
/// using this operation. This is necessary in case the Source only supports connection with a single
/// application (many desktop scanners will behave this way). A Source such as this cannot be
/// accessed by other applications until its current session is terminated
/// </summary>
/// <returns></returns>
public ReturnCode CloseSource ( )
{
Debug . WriteLine ( string . Format ( "Thread {0}: CloseSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . Identity . CloseDS ( ) ;
if ( rc = = ReturnCode . Success )
{
_callbackObj = null ;
}
return rc ;
}
/// <summary>
/// Enables the source.
/// </summary>
/// <param name="mode">The mode.</param>
/// <param name="modal">if set to <c>true</c> any driver UI will display as modal.</param>
/// <param name="windowHandle">The window handle if modal.</param>
/// <param name="context">The
2014-04-06 08:50:13 +08:00
/// Windows only.
2014-04-06 06:33:21 +08:00
/// <see cref="SynchronizationContext" /> that is required for certain operations.
2014-04-06 08:14:19 +08:00
/// It is recommended you call this method in an UI thread and pass in
2014-04-06 06:33:21 +08:00
/// <see cref="SynchronizationContext.Current" />
/// if you do not have a custom one setup.</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">context</exception>
public ReturnCode EnableSource ( SourceEnableMode mode , bool modal , HandleRef windowHandle , SynchronizationContext context )
{
2014-04-07 03:22:11 +08:00
if ( context = = null ) { throw new ArgumentNullException ( "context" ) ; }
2014-04-06 08:50:13 +08:00
2014-04-06 06:33:21 +08:00
Debug . WriteLine ( string . Format ( "Thread {0}: EnableSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
_syncer = context ;
// app v2.2 or higher uses callback2
if ( _appId . ProtocolMajor > = 2 & & _appId . ProtocolMinor > = 2 )
{
2014-04-06 07:39:41 +08:00
var cb = new TWCallback2 ( HandleCallback ) ;
2014-04-06 06:33:21 +08:00
var rc2 = DGControl . Callback2 . RegisterCallback ( cb ) ;
if ( rc2 = = ReturnCode . Success )
{
Debug . WriteLine ( "Registered callback2 OK." ) ;
_callbackObj = cb ;
}
}
else
{
2014-04-06 07:39:41 +08:00
var cb = new TWCallback ( HandleCallback ) ;
2014-04-06 06:33:21 +08:00
var rc2 = DGControl . Callback . RegisterCallback ( cb ) ;
if ( rc2 = = ReturnCode . Success )
{
Debug . WriteLine ( "Registered callback OK." ) ;
_callbackObj = cb ;
}
}
_twui = new TWUserInterface ( ) ;
_twui . ShowUI = mode = = SourceEnableMode . ShowUI ;
_twui . ModalUI = modal ;
_twui . hParent = windowHandle . Handle ;
if ( mode = = SourceEnableMode . ShowUIOnly )
{
return DGControl . UserInterface . EnableDSUIOnly ( _twui ) ;
}
else
{
return DGControl . UserInterface . EnableDS ( _twui ) ;
}
}
/// <summary>
/// Disables the source to end data acquisition.
/// </summary>
/// <returns></returns>
2014-04-06 08:14:19 +08:00
protected ReturnCode DisableSource ( )
2014-04-06 06:33:21 +08:00
{
Debug . WriteLine ( string . Format ( "Thread {0}: DisableSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . UserInterface . DisableDS ( _twui ) ;
if ( rc = = ReturnCode . Success )
{
OnSourceDisabled ( ) ;
}
return rc ;
}
/// <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.
2014-04-07 03:22:11 +08:00
/// This should be called on the Thread that originally called the <see cref="EnableSource"/>
/// method, if applicable.
2014-04-06 06:33:21 +08:00
/// </summary>
/// <param name="targetState">State of the target.</param>
public void ForceStepDown ( int targetState )
{
Debug . WriteLine ( string . Format ( "Thread {0}: ForceStepDown." , Thread . CurrentThread . ManagedThreadId ) ) ;
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.
if ( targetState < 7 )
{
DGControl . PendingXfers . EndXfer ( new TWPendingXfers ( ) ) ;
}
if ( targetState < 6 )
{
DGControl . PendingXfers . Reset ( new TWPendingXfers ( ) ) ;
}
if ( targetState < 5 )
{
DisableSource ( ) ;
}
if ( targetState < 4 )
{
CloseSource ( ) ;
}
if ( targetState < 3 )
{
CloseManager ( ) ;
}
EnforceState = origFlag ;
}
#endregion
2014-04-06 04:48:44 +08:00
#region custom events and overridables
2014-04-06 07:39:41 +08:00
/// <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 ;
2014-04-06 04:48:44 +08:00
/// <summary>
2014-04-06 19:23:49 +08:00
/// Called when <see cref="State"/> changed
/// and raises the <see cref="StateChanged" /> event.
2014-04-06 04:48:44 +08:00
/// </summary>
2014-04-06 07:39:41 +08:00
protected virtual void OnStateChanged ( )
{
var hand = StateChanged ;
if ( hand ! = null )
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , EventArgs . Empty ) ;
}
catch { }
2014-04-06 07:39:41 +08:00
}
}
2014-04-06 04:48:44 +08:00
/// <summary>
2014-04-06 19:23:49 +08:00
/// Called when <see cref="SourceId"/> changed
/// and raises the <see cref="SourceChanged" /> event.
2014-04-06 04:48:44 +08:00
/// </summary>
2014-04-06 07:39:41 +08:00
protected virtual void OnSourceChanged ( )
{
var hand = SourceChanged ;
if ( hand ! = null )
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , EventArgs . Empty ) ;
}
catch { }
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-06 19:23:49 +08:00
/// Called when source has been disabled (back to state 4)
/// and raises the <see cref="SourceDisabled" /> event.
2014-04-06 06:33:21 +08:00
/// </summary>
2014-04-06 07:39:41 +08:00
protected virtual void OnSourceDisabled ( )
{
var hand = SourceDisabled ;
if ( hand ! = null )
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , EventArgs . Empty ) ;
}
catch { }
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>
/// 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 )
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , e ) ;
}
catch { }
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>
/// 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 )
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , e ) ;
}
catch { }
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>
/// 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 )
2014-04-06 06:33:21 +08:00
{
2014-04-06 07:39:41 +08:00
var hand = DataTransferred ;
if ( hand ! = null )
2014-04-06 06:33:21 +08:00
{
2014-04-06 08:50:13 +08:00
try
{
hand ( this , e ) ;
}
catch { }
2014-04-06 06:33:21 +08:00
}
2014-04-06 07:39:41 +08:00
}
#endregion
2014-04-06 06:33:21 +08:00
2014-04-06 19:23:49 +08:00
#region TWAIN logic during xfer work
2014-04-06 06:33:21 +08:00
2014-04-06 07:39:41 +08:00
/// <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>
2014-04-06 08:30:49 +08:00
protected bool HandleWndProcMessage ( ref MESSAGE message )
2014-04-06 07:39:41 +08:00
{
2014-04-06 08:50:13 +08:00
var handled = false ;
2014-04-06 07:39:41 +08:00
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
{
// 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 ;
2014-04-06 09:14:46 +08:00
if ( handled = DGControl . Event . ProcessEvent ( evt ) = = ReturnCode . DSEvent )
{
2014-04-06 09:54:08 +08:00
Debug . WriteLine ( string . Format ( "Thread {0}: HandleWndProcMessage at state {1} with MSG={2}." , Thread . CurrentThread . ManagedThreadId , State , evt . TWMessage ) ) ;
2014-04-06 09:14:46 +08:00
HandleSourceMsg ( evt . TWMessage ) ;
}
2014-04-06 07:39:41 +08:00
}
finally
{
if ( msgPtr ! = IntPtr . Zero ) { Marshal . FreeHGlobal ( msgPtr ) ; }
}
}
2014-04-06 08:50:13 +08:00
return handled ;
2014-04-06 06:33:21 +08:00
}
2014-04-06 07:39:41 +08:00
ReturnCode HandleCallback ( TWIdentity origin , TWIdentity destination , DataGroups dg , DataArgumentType dat , Message msg , IntPtr data )
2014-04-06 06:33:21 +08:00
{
if ( origin ! = null & & SourceId ! = null & & origin . Id = = SourceId . Id )
{
2014-04-06 09:54:08 +08:00
Debug . WriteLine ( string . Format ( "Thread {0}: CallbackHandler at state {1} with MSG={2}." , Thread . CurrentThread . ManagedThreadId , State , msg ) ) ;
2014-04-06 06:33:21 +08:00
// spec says we must handle this on the thread that enabled the DS,
// but it's usually already the same thread and doesn't work (failure + seqError) w/o jumping to another thread and back.
// My guess is the DS needs to see the Success return code first before letting transfer happen
// so this is an hack to make it happen.
2014-04-06 08:50:13 +08:00
2014-04-07 04:25:48 +08:00
// TODO: find a better method without needing a SynchronizationContext.
2014-04-06 06:33:21 +08:00
ThreadPool . QueueUserWorkItem ( o = >
{
var ctx = o as SynchronizationContext ;
if ( ctx ! = null )
{
2014-04-06 09:14:46 +08:00
_syncer . Post ( blah = >
2014-04-06 06:33:21 +08:00
{
2014-04-06 08:50:13 +08:00
HandleSourceMsg ( msg ) ;
2014-04-06 06:33:21 +08:00
} , null ) ;
}
else
{
// no context? better hope for the best!
2014-04-06 08:50:13 +08:00
HandleSourceMsg ( msg ) ;
2014-04-06 06:33:21 +08:00
}
} , _syncer ) ;
return ReturnCode . Success ;
}
return ReturnCode . Failure ;
}
2014-04-06 08:50:13 +08:00
// method that handles msg from the source, whether it's from wndproc or callbacks
void HandleSourceMsg ( Message msg )
2014-04-06 07:39:41 +08:00
{
2014-04-06 08:50:13 +08:00
switch ( msg )
{
case Message . XferReady :
if ( State < 6 )
{
State = 6 ;
}
DoTransferRoutine ( ) ;
break ;
case Message . DeviceEvent :
TWDeviceEvent de ;
var rc = DGControl . DeviceEvent . Get ( out de ) ;
if ( rc = = ReturnCode . Success )
{
OnDeviceEvent ( new DeviceEventArgs ( de ) ) ;
}
break ;
case Message . CloseDSReq :
case Message . CloseDSOK :
// 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
DisableSource ( ) ;
}
break ;
}
}
2014-04-06 07:39:41 +08:00
2014-04-06 08:50:13 +08:00
/// <summary>
2014-04-06 19:08:52 +08:00
/// Performs the TWAIN transfer routine at state 6.
2014-04-06 08:50:13 +08:00
/// </summary>
protected virtual void DoTransferRoutine ( )
{
2014-04-07 04:25:48 +08:00
DataGroups xferGroup = DataGroups . None ;
if ( DGControl . XferGroup . Get ( ref xferGroup ) ! = ReturnCode . Success )
{
xferGroup = DataGroups . None ;
}
// support one or the other or both?
if ( ( xferGroup & DataGroups . Image ) = = DataGroups . Image )
2014-04-06 19:08:52 +08:00
{
DoImageXfer ( ) ;
}
2014-04-07 04:25:48 +08:00
else if ( ( xferGroup & DataGroups . Audio ) = = DataGroups . Audio )
2014-04-06 19:08:52 +08:00
{
DoAudioXfer ( ) ;
}
else
{
// ??? just cancel it
var pending = new TWPendingXfers ( ) ;
var rc = ReturnCode . Success ;
do
{
rc = DGControl . PendingXfers . Reset ( pending ) ;
} while ( rc = = ReturnCode . Success & & pending . Count ! = 0 ) ;
State = 5 ;
DisableSource ( ) ;
}
}
#region audio xfers
private void DoAudioXfer ( )
{
var pending = new TWPendingXfers ( ) ;
2014-04-06 09:54:08 +08:00
var rc = ReturnCode . Success ;
do
{
2014-04-06 19:08:52 +08:00
#region build pre xfer info
2014-04-06 19:23:49 +08:00
2014-04-06 19:08:52 +08:00
TWAudioInfo audInfo ;
DGAudio . AudioInfo . Get ( out audInfo ) ;
// ask consumer for xfer details
var preXferArgs = new TransferReadyEventArgs
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
AudioInfo = audInfo ,
PendingTransferCount = pending . Count ,
EndOfJob = pending . EndOfJob = = 0
} ;
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
OnTransferReady ( preXferArgs ) ;
#endregion
if ( preXferArgs . CancelAll )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
rc = DGControl . PendingXfers . Reset ( pending ) ;
if ( rc = = ReturnCode . Success )
{
2014-04-07 04:25:48 +08:00
break ;
2014-04-06 19:08:52 +08:00
}
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
else if ( ! preXferArgs . CancelCurrent )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
var mech = this . GetCurrentCap < XferMech > ( CapabilityId . ACapXferMech ) ;
switch ( mech )
{
case XferMech . Native :
DoAudioNativeXfer ( ) ;
break ;
case XferMech . File :
DoAudioFileXfer ( ) ;
break ;
}
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
rc = DGControl . PendingXfers . EndXfer ( pending ) ;
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
} while ( rc = = ReturnCode . Success & & pending . Count ! = 0 ) ;
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
State = 5 ;
DisableSource ( ) ;
}
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
private void DoAudioNativeXfer ( )
{
IntPtr dataPtr = IntPtr . Zero ;
IntPtr lockedPtr = IntPtr . Zero ;
try
{
var xrc = DGAudio . AudioNativeXfer . Get ( ref dataPtr ) ;
if ( xrc = = ReturnCode . XferDone )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
State = 7 ;
if ( dataPtr ! = IntPtr . Zero )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
lockedPtr = MemoryManager . Instance . Lock ( dataPtr ) ;
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
OnDataTransferred ( new DataTransferredEventArgs ( lockedPtr , null ) ) ;
}
}
finally
{
State = 6 ;
// data here is allocated by source so needs to use shared mem calls
if ( lockedPtr ! = IntPtr . Zero )
{
MemoryManager . Instance . Unlock ( lockedPtr ) ;
lockedPtr = IntPtr . Zero ;
}
if ( dataPtr ! = IntPtr . Zero )
{
MemoryManager . Instance . Free ( dataPtr ) ;
dataPtr = IntPtr . Zero ;
}
}
}
private void DoAudioFileXfer ( )
{
2014-04-06 19:23:49 +08:00
string filePath = null ;
TWSetupFileXfer setupInfo ;
if ( DGControl . SetupFileXfer . Get ( out setupInfo ) = = ReturnCode . Success )
{
filePath = setupInfo . FileName ;
}
var xrc = DGAudio . AudioFileXfer . Get ( ) ;
if ( xrc = = ReturnCode . XferDone )
{
OnDataTransferred ( new DataTransferredEventArgs ( IntPtr . Zero , filePath ) ) ;
}
2014-04-06 19:08:52 +08:00
}
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
#endregion
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
#region image xfers
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
private void DoImageXfer ( )
{
var pending = new TWPendingXfers ( ) ;
var rc = ReturnCode . Success ;
2014-04-06 09:54:08 +08:00
2014-04-06 19:08:52 +08:00
do
{
#region build pre xfer info
2014-04-06 19:23:49 +08:00
2014-04-06 19:08:52 +08:00
TWImageInfo imgInfo ;
DGImage . ImageInfo . Get ( out imgInfo ) ;
// ask consumer for xfer details
var preXferArgs = new TransferReadyEventArgs
{
PendingImageInfo = imgInfo ,
PendingTransferCount = pending . Count ,
EndOfJob = pending . EndOfJob = = 0
} ;
OnTransferReady ( preXferArgs ) ;
#endregion
if ( preXferArgs . CancelAll )
{
rc = DGControl . PendingXfers . Reset ( pending ) ;
}
else if ( ! preXferArgs . CancelCurrent )
{
var mech = this . GetCurrentCap < XferMech > ( CapabilityId . ICapXferMech ) ;
switch ( mech )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
case XferMech . Native :
DoImageNativeXfer ( ) ;
break ;
case XferMech . Memory :
DoImageMemoryXfer ( ) ;
break ;
case XferMech . File :
DoImageFileXfer ( ) ;
break ;
case XferMech . MemFile :
DoImageMemoryFileXfer ( ) ;
break ;
2014-04-06 09:54:08 +08:00
}
}
2014-04-06 19:08:52 +08:00
rc = DGControl . PendingXfers . EndXfer ( pending ) ;
} while ( rc = = ReturnCode . Success & & pending . Count ! = 0 ) ;
State = 5 ;
DisableSource ( ) ;
}
private void DoImageNativeXfer ( )
{
IntPtr dataPtr = IntPtr . Zero ;
IntPtr lockedPtr = IntPtr . Zero ;
try
{
2014-04-06 19:23:49 +08:00
var xrc = DGImage . ImageNativeXfer . Get ( ref dataPtr ) ;
2014-04-06 19:08:52 +08:00
if ( xrc = = ReturnCode . XferDone )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
State = 7 ;
if ( dataPtr ! = IntPtr . Zero )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
lockedPtr = MemoryManager . Instance . Lock ( dataPtr ) ;
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
OnDataTransferred ( new DataTransferredEventArgs ( lockedPtr , null ) ) ;
}
}
finally
{
State = 6 ;
// data here is allocated by source so needs to use shared mem calls
if ( lockedPtr ! = IntPtr . Zero )
{
MemoryManager . Instance . Unlock ( lockedPtr ) ;
lockedPtr = IntPtr . Zero ;
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
if ( dataPtr ! = IntPtr . Zero )
2014-04-06 09:54:08 +08:00
{
2014-04-06 19:08:52 +08:00
MemoryManager . Instance . Free ( dataPtr ) ;
dataPtr = IntPtr . Zero ;
2014-04-06 09:54:08 +08:00
}
2014-04-06 19:08:52 +08:00
}
}
2014-04-06 09:54:08 +08:00
2014-04-06 19:23:49 +08:00
private void DoImageFileXfer ( )
2014-04-06 19:08:52 +08:00
{
2014-04-06 19:23:49 +08:00
string filePath = null ;
TWSetupFileXfer setupInfo ;
if ( DGControl . SetupFileXfer . Get ( out setupInfo ) = = ReturnCode . Success )
{
filePath = setupInfo . FileName ;
}
var xrc = DGImage . ImageFileXfer . Get ( ) ;
if ( xrc = = ReturnCode . XferDone )
{
OnDataTransferred ( new DataTransferredEventArgs ( IntPtr . Zero , filePath ) ) ;
}
2014-04-06 19:08:52 +08:00
}
2014-04-06 09:54:08 +08:00
2014-04-06 20:22:59 +08:00
private void DoImageMemoryXfer ( )
2014-04-06 19:08:52 +08:00
{
throw new NotImplementedException ( ) ;
2014-04-06 20:22:59 +08:00
TWSetupMemXfer memInfo ;
if ( DGControl . SetupMemXfer . Get ( out memInfo ) = = ReturnCode . Success )
{
TWImageMemXfer xferInfo = new TWImageMemXfer ( ) ;
try
{
xferInfo . Memory = new TWMemory
{
Length = memInfo . Preferred ,
TheMem = MemoryManager . Instance . Allocate ( memInfo . Preferred )
} ;
var xrc = ReturnCode . Success ;
do
{
xrc = DGImage . ImageMemXfer . Get ( xferInfo ) ;
if ( xrc = = ReturnCode . XferDone )
{
}
} while ( xrc = = ReturnCode . Success ) ;
}
finally
{
if ( xferInfo . Memory . TheMem ! = IntPtr . Zero )
{
MemoryManager . Instance . Free ( xferInfo . Memory . TheMem ) ;
}
}
}
2014-04-06 19:08:52 +08:00
}
2014-04-06 20:22:59 +08:00
private void DoImageMemoryFileXfer ( )
2014-04-06 19:08:52 +08:00
{
2014-04-06 20:22:59 +08:00
// no way to test, not supported by sample source
2014-04-06 19:08:52 +08:00
throw new NotImplementedException ( ) ;
2014-04-06 07:39:41 +08:00
}
2014-04-06 04:48:44 +08:00
#endregion
2014-04-06 08:30:49 +08:00
2014-04-06 19:08:52 +08:00
#endregion
2014-04-06 08:30:49 +08:00
/// <summary>
/// The MSG structure in Windows for TWAIN use.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
protected struct MESSAGE
{
2014-04-06 20:22:59 +08:00
/// <summary>
/// Initializes a new instance of the <see cref="MESSAGE"/> struct.
/// </summary>
/// <param name="hwnd">The HWND.</param>
/// <param name="message">The message.</param>
/// <param name="wParam">The w parameter.</param>
/// <param name="lParam">The l parameter.</param>
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 ;
2014-04-06 08:30:49 +08:00
}
2014-04-06 04:48:44 +08:00
}
}