Added transfer logic skeleton from old code.

This commit is contained in:
Eugene Wang
2018-11-18 10:51:08 -05:00
parent ec153e6f3d
commit 8f8a9a2f16
12 changed files with 609 additions and 15 deletions

View File

@@ -31,4 +31,164 @@ namespace NTwain
/// </summary>
public bool UIOnly { get; internal set; }
}
/// <summary>
/// Contains event data when a data transfer is ready to be processed.
/// </summary>
public class TransferReadyEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="TransferReadyEventArgs"/> class.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="pendingCount">The pending count.</param>
/// <param name="endOfJobFlag"></param>
public TransferReadyEventArgs(DataSource source, int pendingCount, EndXferJob endOfJobFlag)
{
DataSource = source;
PendingTransferCount = pendingCount;
EndOfJobFlag = endOfJobFlag;
}
/// <summary>
/// Gets the data source.
/// </summary>
/// <value>
/// The data source.
/// </value>
public DataSource DataSource { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether the current transfer should be canceled
/// and continue next transfer if there are more data.
/// </summary>
/// <value><c>true</c> to cancel current transfer; otherwise, <c>false</c>.</value>
public bool CancelCurrent { get; set; }
/// <summary>
/// Gets or sets a value indicating whether all transfers should be canceled.
/// </summary>
/// <value><c>true</c> to cancel all transfers; otherwise, <c>false</c>.</value>
public bool CancelAll { get; set; }
/// <summary>
/// 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 { return EndOfJobFlag != EndXferJob.None; } }
/// <summary>
/// Gets the end of job flag value for this transfer (if job control is enabled).
/// </summary>
/// <value>
/// The end of job flag.
/// </value>
public EndXferJob EndOfJobFlag { 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; private set; }
TW_IMAGEINFO? _imgInfo;
/// <summary>
/// Gets the tentative image information for the current transfer if applicable.
/// This may differ from the final image depending on the transfer mode used (mostly when doing mem xfer).
/// </summary>
/// <value>
/// The image info.
/// </value>
public TW_IMAGEINFO? PendingImageInfo
{
get
{
if (!_imgInfo.HasValue)
{
TW_IMAGEINFO img = default;
if (DataSource.Session.DGImage.ImageInfo.Get(ref img) == ReturnCode.Success)
{
_imgInfo = img;
}
}
return _imgInfo;
}
}
TW_AUDIOINFO? _audInfo;
/// <summary>
/// Gets the audio information for the current transfer if applicable.
/// </summary>
/// <value>
/// The audio information.
/// </value>
public TW_AUDIOINFO? AudioInfo
{
get
{
if (_audInfo == null)
{
TW_AUDIOINFO aud = default;
if (DataSource.Session.DGAudio.AudioInfo.Get(ref aud) == ReturnCode.Success)
{
_audInfo = aud;
}
}
return _audInfo;
}
}
}
/// <summary>
/// Contains TWAIN codes and source status when an error is encountered during transfer.
/// </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, TW_STATUS status)
{
ReturnCode = code;
SourceStatus = status;
}
/// <summary>
/// Gets the return code from the transfer.
/// </summary>
/// <value>
/// The return code.
/// </value>
public ReturnCode ReturnCode { get; private set; }
/// <summary>
/// Gets the source status.
/// </summary>
/// <value>
/// The source status.
/// </value>
public TW_STATUS SourceStatus { get; private set; }
/// <summary>
/// Gets the exception if the error is from some exception.
/// </summary>
/// <value>
/// The exception.
/// </value>
public Exception Exception { get; private set; }
}
}

View File

@@ -48,6 +48,7 @@ namespace NTwain
ModalUI = (ushort)(modal ? 1 : 0),
hParent = windowHandle
};
Session._disableDSNow = false;
var rc = Session.DGControl.UserInterface.EnableDSUIOnly(ref ui);
if (rc == ReturnCode.Success)
{
@@ -71,6 +72,7 @@ namespace NTwain
ModalUI = (ushort)(modal ? 1 : 0),
hParent = windowHandle
};
Session._disableDSNow = false;
var rc = Session.DGControl.UserInterface.EnableDS(ref ui);
if (rc == ReturnCode.Success)
{

View File

@@ -37,9 +37,4 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Triplets\Image\" />
<Folder Include="Triplets\Audio\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,38 @@
using NTwain.Data;
using NTwain.Internals;
namespace NTwain.Triplets.Audio
{
sealed class AudioInfo : BaseTriplet
{
internal AudioInfo(TwainSession session) : base(session) { }
public ReturnCode Get(ref TW_AUDIOINFO info)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Audio, DataArgumentType.AudioInfo, Message.Get, ref info);
return ReturnCode.Failure;
}
}
}

View File

@@ -0,0 +1,149 @@
using NTwain.Data;
using NTwain.Internals;
namespace NTwain.Triplets
{
sealed class PendingXfers : BaseTriplet
{
internal PendingXfers(TwainSession session) : base(session) { }
/// <summary>
/// This triplet is used to cancel or terminate a transfer. Issued in state 6, this triplet cancels the next
/// pending transfer, discards the transfer data, and decrements the pending transfers count. In
/// state 7, this triplet terminates the current transfer. If any data has not been transferred (this is
/// only possible during a memory transfer) that data is discarded.
/// </summary>
/// <param name="pendingXfers">The pending xfers.</param>
/// <returns></returns>
internal ReturnCode EndXfer(TW_PENDINGXFERS pendingXfers)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.EndXfer, ref pendingXfers);
return ReturnCode.Failure;
}
/// <summary>
/// Returns the number of transfers the Source is ready to supply to the application, upon demand.
/// If DAT_XFERGROUP is set to DG_Image, this is the number of images. If DAT_XFERGROUP is set
/// to DG_AUDIO, this is the number of audio snippets for the current image. If there is no current
/// image, this call must return Failure / SeqError.
/// </summary>
/// <param name="pendingXfers">The pending xfers.</param>
/// <returns></returns>
public ReturnCode Get(TW_PENDINGXFERS pendingXfers)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Get, ref pendingXfers);
return ReturnCode.Failure;
}
/// <summary>
/// Sets the number of pending transfers in the Source to zero.
/// </summary>
/// <param name="pendingXfers">The pending xfers.</param>
/// <returns></returns>
internal ReturnCode Reset(TW_PENDINGXFERS pendingXfers)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.Reset, ref pendingXfers);
return ReturnCode.Failure;
}
/// <summary>
/// If CapAutoScan is TRUE, this command will stop the operation of the scanners automatic
/// feeder. No other action is taken.
/// </summary>
/// <param name="pendingXfers">The pending xfers.</param>
/// <returns></returns>
public ReturnCode StopFeeder(TW_PENDINGXFERS pendingXfers)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Control, DataArgumentType.PendingXfers, Message.StopFeeder, ref pendingXfers);
return ReturnCode.Failure;
}
}
}

View File

@@ -1,4 +1,5 @@
using NTwain.Data;
using NTwain.Triplets.Audio;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -12,6 +13,8 @@ namespace NTwain.Triplets
public partial class DGAudio : BaseTriplet
{
internal DGAudio(TwainSession session) : base(session) { }
AudioInfo _info;
internal AudioInfo AudioInfo => _info ?? (_info = new AudioInfo(Session));
}
}

View File

@@ -45,6 +45,9 @@ namespace NTwain.Triplets
Event _event;
internal Event Event => _event ?? (_event = new Event(Session));
PendingXfers _pending;
internal PendingXfers PendingXfers => _pending ?? (_pending = new PendingXfers(Session));
XferGroup _xferGroup;
/// <summary>
/// Gets the operations defined for DAT_XFERGROUP.

View File

@@ -1,4 +1,5 @@
using NTwain.Data;
using NTwain.Triplets.Image;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,5 +14,7 @@ namespace NTwain.Triplets
{
internal DGImage(TwainSession session) : base(session) { }
ImageInfo _info;
internal ImageInfo ImageInfo => _info ?? (_info = new ImageInfo(Session));
}
}

View File

@@ -0,0 +1,38 @@
using NTwain.Data;
using NTwain.Internals;
namespace NTwain.Triplets.Image
{
sealed class ImageInfo : BaseTriplet
{
internal ImageInfo(TwainSession session) : base(session) { }
public ReturnCode Get(ref TW_IMAGEINFO info)
{
if (Is32Bit)
{
if (IsWin)
return NativeMethods.DsmWin32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
if (IsLinux)
return NativeMethods.DsmLinux32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
if (IsMac)
return NativeMethods.DsmMac32(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
}
if (IsWin)
return NativeMethods.DsmWin64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
if (IsLinux)
return NativeMethods.DsmLinux64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
if (IsMac)
return NativeMethods.DsmMac64(Session.Config.App32, Session.CurrentSource.Identity32,
DataGroups.Image, DataArgumentType.ImageInfo, Message.Get, ref info);
return ReturnCode.Failure;
}
}
}

View File

@@ -15,6 +15,15 @@ namespace NTwain
partial class TwainSession
{
internal TW_USERINTERFACE _lastEnableUI;
internal bool _disableDSNow;
ReturnCode Handle32BitCallback(TW_IDENTITY origin, TW_IDENTITY destination,
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
{
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
HandleSourceMsg(msg);
return ReturnCode.Success;
}
/// <summary>
/// If on Windows pass all messages from WndProc here to handle it
@@ -76,7 +85,15 @@ namespace NTwain
DoTransferRoutine();
break;
case Message.CloseDSReq:
DGControl.UserInterface.DisableDS(ref _lastEnableUI, false);
if (_state > TwainState.S5)
{
// do it after end of current xfer routine.
_disableDSNow = true;
}
else
{
DGControl.UserInterface.DisableDS(ref _lastEnableUI, false);
}
break;
case Message.CloseDSOK:
DGControl.UserInterface.DisableDS(ref _lastEnableUI, true);
@@ -85,8 +102,168 @@ namespace NTwain
}
private void DoTransferRoutine()
{
var xMech = GetTransferMechs();
TW_PENDINGXFERS pending = default;
var rc = DGControl.PendingXfers.Get(pending);
if (rc == ReturnCode.Success)
{
do
{
var readyArgs = new TransferReadyEventArgs(CurrentSource, pending.Count, pending.EndOfJob)
{
CancelAll = _disableDSNow
};
OnTransferReady(readyArgs);
#region actually handle xfer
if (readyArgs.CancelAll || _disableDSNow)
{
rc = DGControl.PendingXfers.Reset(pending);
}
else
{
if (!readyArgs.CancelCurrent)
{
if (xMech.ImageMech.HasValue)
{
switch (xMech.ImageMech.Value)
{
case XferMech.Memory:
rc = DoImageMemoryXfer();
break;
case XferMech.File:
rc = DoImageFileXfer();
break;
case XferMech.MemFile:
rc = DoImageMemoryFileXfer();
break;
case XferMech.Native:
default: // always assume native
rc = DoImageNativeXfer();
break;
}
}
if (xMech.AudioMech.HasValue)
{
switch (xMech.AudioMech.Value)
{
case XferMech.File:
rc = DoAudioFileXfer();
break;
case XferMech.Native:
default: // always assume native
rc = DoAudioNativeXfer();
break;
}
}
}
if (rc != ReturnCode.Success)// && StopOnTransferError)
{
// end xfer without setting rc to exit (good/bad?)
DGControl.PendingXfers.Reset(pending);
}
else
{
rc = DGControl.PendingXfers.EndXfer(pending);
}
}
#endregion
} while (rc == ReturnCode.Success && pending.Count != 0 && !_disableDSNow);
}
else
{
HandleXferReturnCode(rc);
}
if (_disableDSNow)
{
DGControl.UserInterface.DisableDS(ref _lastEnableUI, false);
}
}
private ReturnCode DoImageNativeXfer()
{
throw new NotImplementedException();
}
private ReturnCode DoImageMemoryFileXfer()
{
throw new NotImplementedException();
}
private ReturnCode DoImageFileXfer()
{
throw new NotImplementedException();
}
private ReturnCode DoImageMemoryXfer()
{
throw new NotImplementedException();
}
private ReturnCode DoAudioNativeXfer()
{
throw new NotImplementedException();
}
private ReturnCode DoAudioFileXfer()
{
throw new NotImplementedException();
}
private void HandleXferReturnCode(ReturnCode rc)
{
switch (rc)
{
case ReturnCode.Success:
case ReturnCode.XferDone:
case ReturnCode.Cancel:
// ok to keep going
break;
default:
var status = CurrentSource.GetStatus();
OnTransferError(new TransferErrorEventArgs(rc, status));
break;
}
}
TransferMechs GetTransferMechs()
{
TransferMechs retVal = default;
bool xferImage = true; // default to always xfer image
bool xferAudio = false;
DataGroups xferGroup = DataGroups.None;
XferMech imgXferMech = XferMech.Native;
XferMech audXferMech = XferMech.Native;
if (DGControl.XferGroup.Get(ref xferGroup) == ReturnCode.Success)
{
xferAudio = (xferGroup & DataGroups.Audio) == DataGroups.Audio;
// some DS returns none but we will assume it's image
xferImage = xferGroup == DataGroups.None || (xferGroup & DataGroups.Image) == DataGroups.Image;
}
if (xferImage)
{
//imgXferMech = CurrentSource.Capabilities.ICapXferMech.GetCurrent();
retVal.ImageMech = imgXferMech;
}
if (xferAudio)
{
//audXferMech = CurrentSource.Capabilities.ACapXferMech.GetCurrent();
retVal.AudioMech = audXferMech;
}
return retVal;
}
struct TransferMechs
{
public XferMech? ImageMech;
public XferMech? AudioMech;
}
}
}

View File

@@ -77,6 +77,40 @@ namespace NTwain
handler?.Invoke(this, e);
}
/// <summary>
/// Occurs when a data transfer is ready.
/// </summary>
public event EventHandler<TransferReadyEventArgs> TransferReady;
/// <summary>
/// Raises the <see cref="TransferReady"/> event.
/// </summary>
/// <param name="e"></param>
protected virtual void OnTransferReady(TransferReadyEventArgs e)
{
var handler = TransferReady;
handler?.Invoke(this, e);
}
///// <summary>
///// Occurs when data has been transferred.
///// </summary>
//public event EventHandler<DataTransferredEventArgs> DataTransferred;
/// <summary>
/// Occurs when an error has been encountered during transfer.
/// </summary>
public event EventHandler<TransferErrorEventArgs> TransferError;
/// <summary>
/// Raises the <see cref="TransferError"/> event.
/// </summary>
/// <param name="e"></param>
protected virtual void OnTransferError(TransferErrorEventArgs e)
{
var handler = TransferError;
handler?.Invoke(this, e);
}

View File

@@ -223,14 +223,6 @@ namespace NTwain
return source;
}
ReturnCode Handle32BitCallback(TW_IDENTITY origin, TW_IDENTITY destination,
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
{
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
HandleSourceMsg(msg);
return ReturnCode.Success;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>