mirror of
https://github.com/soukoku/ntwain.git
synced 2025-09-20 02:37:57 +08:00
progress on transfer loop.
This commit is contained in:
@@ -11,7 +11,7 @@ namespace Net5Console
|
|||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main(string[] args)
|
static void Main()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Starting twain test in console...");
|
Console.WriteLine("Starting twain test in console...");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
@@ -21,29 +21,21 @@ namespace Net5Console
|
|||||||
{
|
{
|
||||||
session.DeviceEvent += (sender, e) =>
|
session.DeviceEvent += (sender, e) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Got device event " + (TWDE)e.Event);
|
Console.WriteLine($"Got device event " + e.Event);
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
};
|
};
|
||||||
session.ScanEvent += (sender, closing) =>
|
session.TransferError += (sender, e) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Got scan event " + closing);
|
Console.WriteLine($"Transfer error {e.Code} {e.Exception}.");
|
||||||
Console.WriteLine();
|
};
|
||||||
|
session.TransferReady += (sender, e) =>
|
||||||
// don't care, just end it
|
{
|
||||||
TW_PENDINGXFERS pending = default;
|
Console.WriteLine($"Transfer ready, count={e.PendingCount}.");
|
||||||
var sts = session.TWAIN.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
|
};
|
||||||
|
session.SourceDisabled += (sender,e)=>
|
||||||
TW_USERINTERFACE twuserinterface = default;
|
|
||||||
if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Disabled device.");
|
Console.WriteLine("Disabled device.");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("Failed to disabled device.");
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
hold.Set();
|
hold.Set();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,6 +127,11 @@ namespace Net5Console
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Session_SourceDisabled(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteCapInfo(Capabilities caps, CAP cap)
|
private static void WriteCapInfo(Capabilities caps, CAP cap)
|
||||||
{
|
{
|
||||||
// use reflection due to unknown generics
|
// use reflection due to unknown generics
|
||||||
|
@@ -10,20 +10,8 @@ namespace NTwain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Some utility extension methods.
|
/// Some utility extension methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ExtensionMethods
|
static class ExtensionMethods
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Checks if the returned <see cref="STS"/> contains a particular
|
|
||||||
/// <see cref="STS"/> value since normal TWAIN
|
|
||||||
/// return code is merged with condition code into <see cref="STS"/>.
|
|
||||||
/// This is only useful for non-success values.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <param name="checkFor">Non-success code to check for.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool Is(this STS status, STS checkFor)
|
|
||||||
{
|
|
||||||
return (status & checkFor) == checkFor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/NTwain/TransferErrorEventArgs.cs
Normal file
27
src/NTwain/TransferErrorEventArgs.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using TWAINWorkingGroup;
|
||||||
|
|
||||||
|
namespace NTwain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains TWAIN codes and source status when an error is encountered during transfer.
|
||||||
|
/// </summary>
|
||||||
|
public class TransferErrorEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public TransferErrorEventArgs(STS code, Exception error = null)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Exception = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the error code from the transfer.
|
||||||
|
/// </summary>
|
||||||
|
public STS Code { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the exception if the error is from some exception.
|
||||||
|
/// </summary>
|
||||||
|
public Exception Exception { get; }
|
||||||
|
}
|
||||||
|
}
|
85
src/NTwain/TransferReadyEventArgs.cs
Normal file
85
src/NTwain/TransferReadyEventArgs.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TWAINWorkingGroup;
|
||||||
|
|
||||||
|
namespace NTwain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains event data when a data transfer is ready to be processed.
|
||||||
|
/// </summary>
|
||||||
|
public class TransferReadyEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public TransferReadyEventArgs(TWAIN twain, int pendingCount, TWEJ endOfJobFlag)
|
||||||
|
{
|
||||||
|
_twain = twain;
|
||||||
|
PendingCount = pendingCount;
|
||||||
|
EndOfJobFlag = endOfJobFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the current transfer should be skipped
|
||||||
|
/// and continue next transfer if there are more data.
|
||||||
|
/// </summary>
|
||||||
|
public bool SkipCurrent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to cancel the capture phase.
|
||||||
|
/// </summary>
|
||||||
|
public CancelType CancelCapture { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the end of job flag value for this transfer if job control is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public TWEJ EndOfJobFlag { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the known pending transfer count. This may not be appilicable
|
||||||
|
/// for certain scanning modes.
|
||||||
|
/// </summary>
|
||||||
|
public int PendingCount { get; private set; }
|
||||||
|
|
||||||
|
TW_IMAGEINFO? _imgInfo;
|
||||||
|
private readonly TWAIN _twain;
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public TW_IMAGEINFO? PendingImageInfo
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_imgInfo.HasValue)
|
||||||
|
{
|
||||||
|
TW_IMAGEINFO info = default;
|
||||||
|
if (_twain.DatImageinfo(DG.IMAGE, MSG.GET, ref info) == STS.SUCCESS)
|
||||||
|
{
|
||||||
|
_imgInfo = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _imgInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CancelType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No cancel.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// Stops feeder but continue receiving scanned images.
|
||||||
|
/// </summary>
|
||||||
|
Graceful,
|
||||||
|
/// <summary>
|
||||||
|
/// Stops feeder and discard pending images.
|
||||||
|
/// </summary>
|
||||||
|
Immediate
|
||||||
|
}
|
||||||
|
}
|
@@ -4,8 +4,10 @@ using System.Diagnostics;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using TWAINWorkingGroup;
|
using TWAINWorkingGroup;
|
||||||
|
|
||||||
@@ -80,8 +82,20 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler SourceDisabled;
|
public event EventHandler SourceDisabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when there's some error during transfer.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<TransferErrorEventArgs> TransferError;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when there's a pending transfer. Can be used to cancel transfers.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<TransferReadyEventArgs> TransferReady;
|
||||||
|
|
||||||
private void HandleUIThreadAction(Action action)
|
private void HandleUIThreadAction(Action action)
|
||||||
{
|
{
|
||||||
|
DebugThreadInfo("begin");
|
||||||
|
|
||||||
_threadMarshaller.Invoke(action);
|
_threadMarshaller.Invoke(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +107,8 @@ namespace NTwain
|
|||||||
// Drain the event queue...
|
// Drain the event queue...
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
DebugThreadInfo("in loop");
|
||||||
|
|
||||||
// Try to get an event...
|
// Try to get an event...
|
||||||
twdeviceevent = default;
|
twdeviceevent = default;
|
||||||
sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent);
|
sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent);
|
||||||
@@ -116,23 +132,103 @@ namespace NTwain
|
|||||||
|
|
||||||
private STS HandleScanEvent(bool closing)
|
private STS HandleScanEvent(bool closing)
|
||||||
{
|
{
|
||||||
|
DebugThreadInfo("begin");
|
||||||
|
|
||||||
// the scan event needs to return asap since it can come from msg loop
|
// the scan event needs to return asap since it can come from msg loop
|
||||||
// so fire off the handling work to another thread
|
// so fire off the handling work to another thread
|
||||||
_threadMarshaller.BeginInvoke(new Func<bool, STS>(HandleScanEventReal), closing);
|
_threadMarshaller.BeginInvoke(new Action<bool>(HandleScanEventReal), closing);
|
||||||
return STS.SUCCESS;
|
return STS.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tracks transfer state
|
|
||||||
bool _xferReadySent;
|
|
||||||
bool _disableDsSent;
|
|
||||||
|
|
||||||
void HandleScanEventReal(bool closing)
|
void HandleScanEventReal(bool closing)
|
||||||
{
|
{
|
||||||
//// Scoot...
|
DebugThreadInfo("begin");
|
||||||
//if (_twain == null) return STS.FAILURE;
|
|
||||||
|
|
||||||
//// We're superfluous...
|
if (_twain == null || State <= STATE.S4 || closing) return;
|
||||||
//if (_twain.GetState() <= STATE.S4 || closing) return STS.SUCCESS;
|
|
||||||
|
if (_twain.IsMsgCloseDsReq() || _twain.IsMsgCloseDsOk())
|
||||||
|
{
|
||||||
|
StepDown(STATE.S4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all except mem xfer will run this once and raise event.
|
||||||
|
// mem xfer will run this multiple times until complete image is assembled
|
||||||
|
if (_twain.IsMsgXferReady())
|
||||||
|
{
|
||||||
|
TW_PENDINGXFERS pending = default;
|
||||||
|
var sts = _twain.DatPendingxfers(DG.CONTROL, MSG.GET, ref pending);
|
||||||
|
if (sts != STS.SUCCESS)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TransferError?.Invoke(this, new TransferErrorEventArgs(sts));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return; // do more?
|
||||||
|
}
|
||||||
|
|
||||||
|
var xferMech = Capabilities.ICAP_XFERMECH.GetCurrent();
|
||||||
|
|
||||||
|
var readyArgs = new TransferReadyEventArgs(_twain, pending.Count, (TWEJ)pending.EOJ);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TransferReady?.Invoke(this, readyArgs);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (readyArgs.CancelCapture == CancelType.Immediate)
|
||||||
|
{
|
||||||
|
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (readyArgs.CancelCapture == CancelType.Graceful) StopCapture();
|
||||||
|
|
||||||
|
if (!readyArgs.SkipCurrent)
|
||||||
|
{
|
||||||
|
switch (xferMech)
|
||||||
|
{
|
||||||
|
case TWSX.NATIVE:
|
||||||
|
RunImageNativeXfer();
|
||||||
|
break;
|
||||||
|
case TWSX.MEMFILE:
|
||||||
|
RunImageMemFileXfer();
|
||||||
|
break;
|
||||||
|
case TWSX.FILE:
|
||||||
|
RunImageFileXfer();
|
||||||
|
break;
|
||||||
|
case TWSX.MEMORY:
|
||||||
|
RunImageMemoryXfer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: may be wrong for now
|
||||||
|
if (pending.Count == 0 || sts == STS.CANCEL || sts == STS.XFERDONE)
|
||||||
|
{
|
||||||
|
StepDown(STATE.S4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleScanEvent(State <= STATE.S3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
private void DebugThreadInfo(string description, [CallerMemberName] string callerName = "")
|
||||||
|
{
|
||||||
|
var tid = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
Debug.WriteLine($"[Thread {tid}] {callerName}() {description}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunImageMemoryXfer()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
|
||||||
//// Handle DAT_NULL/MSG_XFERREADY...
|
//// Handle DAT_NULL/MSG_XFERREADY...
|
||||||
//if (_twain.IsMsgXferReady() && !_xferReadySent)
|
//if (_twain.IsMsgXferReady() && !_xferReadySent)
|
||||||
@@ -161,20 +257,6 @@ namespace NTwain
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//// Handle DAT_NULL/MSG_CLOSEDSREQ...
|
|
||||||
//if (_twain.IsMsgCloseDsReq() && !_disableDsSent)
|
|
||||||
//{
|
|
||||||
// _disableDsSent = true;
|
|
||||||
// StepDown(STATE.S4);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// Handle DAT_NULL/MSG_CLOSEDSOK...
|
|
||||||
//if (_twain.IsMsgCloseDsOk() && !_disableDsSent)
|
|
||||||
//{
|
|
||||||
// _disableDsSent = true;
|
|
||||||
// StepDown(STATE.S4);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// This is where the statemachine runs that transfers and optionally
|
//// This is where the statemachine runs that transfers and optionally
|
||||||
//// saves the images to disk (it also displays them). It'll go back
|
//// saves the images to disk (it also displays them). It'll go back
|
||||||
//// and forth between states 6 and 7 until an error occurs, or until
|
//// and forth between states 6 and 7 until an error occurs, or until
|
||||||
@@ -183,13 +265,21 @@ namespace NTwain
|
|||||||
//{
|
//{
|
||||||
// CaptureImages();
|
// CaptureImages();
|
||||||
//}
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunImageFileXfer()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunImageMemFileXfer()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunImageNativeXfer()
|
||||||
|
{
|
||||||
|
|
||||||
//// Trigger the next event, this is where things all chain together.
|
|
||||||
//// We need begininvoke to prevent blockking, so that we don't get
|
|
||||||
//// backed up into a messy kind of recursion. We need DoEvents,
|
|
||||||
//// because if things really start moving fast it's really hard for
|
|
||||||
//// application events, like button clicks to break through...
|
|
||||||
//HandleScanEvent(_twain.GetState() <= STATE.S3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//protected virtual void OnScanEvent(bool closing) { }
|
//protected virtual void OnScanEvent(bool closing) { }
|
||||||
@@ -301,34 +391,31 @@ namespace NTwain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Steps down the TWAIN state to the specified state.
|
/// Steps down the TWAIN state to the specified state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state"></param>
|
/// <param name="target"></param>
|
||||||
public void StepDown(STATE state)
|
public void StepDown(STATE target)
|
||||||
{
|
{
|
||||||
TW_PENDINGXFERS twpendingxfers = default;
|
|
||||||
|
|
||||||
// Make sure we have something to work with...
|
// Make sure we have something to work with...
|
||||||
if (_twain == null)
|
if (_twain == null) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the states, we don't care about the status returns. Basically,
|
// Walk the states, we don't care about the status returns. Basically,
|
||||||
// these need to work, or we're guaranteed to hang...
|
// these need to work, or we're guaranteed to hang...
|
||||||
|
|
||||||
// 7 --> 6
|
// 7 --> 6
|
||||||
if ((_twain.GetState() == STATE.S7) && (state < STATE.S7))
|
if ((State == STATE.S7) && (target < STATE.S7))
|
||||||
{
|
{
|
||||||
|
TW_PENDINGXFERS twpendingxfers = default;
|
||||||
_twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers);
|
_twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6 --> 5
|
// 6 --> 5
|
||||||
if ((_twain.GetState() == STATE.S6) && (state < STATE.S6))
|
if ((State == STATE.S6) && (target < STATE.S6))
|
||||||
{
|
{
|
||||||
|
TW_PENDINGXFERS twpendingxfers = default;
|
||||||
_twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers);
|
_twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 --> 4
|
// 5 --> 4
|
||||||
if ((_twain.GetState() == STATE.S5) && (state < STATE.S5))
|
if ((State == STATE.S5) && (target < STATE.S5))
|
||||||
{
|
{
|
||||||
TW_USERINTERFACE twuserinterface = default;
|
TW_USERINTERFACE twuserinterface = default;
|
||||||
_twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface);
|
_twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface);
|
||||||
@@ -336,14 +423,14 @@ namespace NTwain
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4 --> 3
|
// 4 --> 3
|
||||||
if ((_twain.GetState() == STATE.S4) && (state < STATE.S4))
|
if ((State == STATE.S4) && (target < STATE.S4))
|
||||||
{
|
{
|
||||||
_caps = null;
|
_caps = null;
|
||||||
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref _twain.m_twidentityDs);
|
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref _twain.m_twidentityDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 --> 2
|
// 3 --> 2
|
||||||
if ((_twain.GetState() == STATE.S3) && (state < STATE.S3))
|
if ((State == STATE.S3) && (target < STATE.S3))
|
||||||
{
|
{
|
||||||
_twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd);
|
_twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user