2018-11-14 18:52:14 -05:00
|
|
|
|
using NTwain.Data;
|
2018-11-20 18:43:48 -05:00
|
|
|
|
using NTwain.Data.Win32;
|
2018-11-18 09:58:38 -05:00
|
|
|
|
using NTwain.Internals;
|
2018-11-20 18:43:48 -05:00
|
|
|
|
using NTwain.Threading;
|
2018-11-14 18:52:14 -05:00
|
|
|
|
using NTwain.Triplets;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
|
|
namespace NTwain
|
|
|
|
|
|
{
|
|
|
|
|
|
partial class TwainSession
|
|
|
|
|
|
{
|
2018-11-17 12:21:17 -05:00
|
|
|
|
internal TW_USERINTERFACE _lastEnableUI;
|
2018-11-18 10:51:08 -05:00
|
|
|
|
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})");
|
2018-11-23 19:10:34 -05:00
|
|
|
|
InternalBeginInvoke(() =>
|
2018-11-20 18:43:48 -05:00
|
|
|
|
{
|
|
|
|
|
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: in BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
|
|
|
|
|
HandleSourceMsg(msg);
|
|
|
|
|
|
});
|
|
|
|
|
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: after BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
2018-11-18 10:51:08 -05:00
|
|
|
|
return ReturnCode.Success;
|
|
|
|
|
|
}
|
2018-11-17 12:21:17 -05:00
|
|
|
|
|
2018-11-20 18:43:48 -05:00
|
|
|
|
internal bool HandleWindowsMessage(ref MSG msg)
|
2018-11-18 09:58:38 -05:00
|
|
|
|
{
|
|
|
|
|
|
var handled = false;
|
|
|
|
|
|
if (State > TwainState.S4)
|
|
|
|
|
|
{
|
|
|
|
|
|
// transform it into a pointer for twain
|
|
|
|
|
|
IntPtr msgPtr = IntPtr.Zero;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2018-11-20 18:43:48 -05:00
|
|
|
|
msgPtr = Config.MemoryManager.Allocate((uint)Marshal.SizeOf(msg));
|
2018-11-18 09:58:38 -05:00
|
|
|
|
IntPtr locked = Config.MemoryManager.Lock(msgPtr);
|
2018-11-20 18:43:48 -05:00
|
|
|
|
Marshal.StructureToPtr(msg, locked, false);
|
2018-11-18 09:58:38 -05:00
|
|
|
|
|
|
|
|
|
|
TW_EVENT evt = new TW_EVENT { pEvent = locked };
|
|
|
|
|
|
if (handled = DGControl.Event.ProcessEvent(ref evt) == ReturnCode.DSEvent)
|
|
|
|
|
|
{
|
2018-11-20 18:43:48 -05:00
|
|
|
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(HandleWindowsMessage)} with {evt.TWMessage}");
|
2018-11-18 09:58:38 -05:00
|
|
|
|
HandleSourceMsg((Message)evt.TWMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
if (msgPtr != IntPtr.Zero) { Config.MemoryManager.Free(msgPtr); }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return handled;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-14 18:52:14 -05:00
|
|
|
|
private void HandleSourceMsg(Message msg)
|
|
|
|
|
|
{
|
2018-11-20 18:43:48 -05:00
|
|
|
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(HandleSourceMsg)}({msg})");
|
2018-11-15 19:25:18 -05:00
|
|
|
|
switch (msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Message.DeviceEvent:
|
|
|
|
|
|
TW_DEVICEEVENT de = default;
|
|
|
|
|
|
var rc = DGControl.DeviceEvent.Get(ref de);
|
|
|
|
|
|
if (rc == ReturnCode.Success)
|
|
|
|
|
|
{
|
2018-11-15 19:28:59 -05:00
|
|
|
|
OnDeviceEventReceived(new DeviceEventArgs { Data = de });
|
2018-11-15 19:25:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case Message.XferReady:
|
2018-11-17 12:21:17 -05:00
|
|
|
|
if (State < TwainState.S6)
|
|
|
|
|
|
{
|
|
|
|
|
|
State = TwainState.S6;
|
|
|
|
|
|
}
|
|
|
|
|
|
DoTransferRoutine();
|
2018-11-15 19:25:18 -05:00
|
|
|
|
break;
|
|
|
|
|
|
case Message.CloseDSReq:
|
2018-11-18 10:51:08 -05:00
|
|
|
|
if (_state > TwainState.S5)
|
|
|
|
|
|
{
|
|
|
|
|
|
// do it after end of current xfer routine.
|
|
|
|
|
|
_disableDSNow = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DGControl.UserInterface.DisableDS(ref _lastEnableUI, false);
|
|
|
|
|
|
}
|
2018-11-15 19:25:18 -05:00
|
|
|
|
break;
|
|
|
|
|
|
case Message.CloseDSOK:
|
2018-11-17 12:45:30 -05:00
|
|
|
|
DGControl.UserInterface.DisableDS(ref _lastEnableUI, true);
|
2018-11-15 19:25:18 -05:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2018-11-14 18:52:14 -05:00
|
|
|
|
}
|
2018-11-17 12:21:17 -05:00
|
|
|
|
|
|
|
|
|
|
private void DoTransferRoutine()
|
2018-11-18 10:51:08 -05:00
|
|
|
|
{
|
|
|
|
|
|
var xMech = GetTransferMechs();
|
|
|
|
|
|
|
|
|
|
|
|
TW_PENDINGXFERS pending = default;
|
2018-11-18 11:55:43 -05:00
|
|
|
|
var rc = DGControl.PendingXfers.Get(ref pending);
|
2018-11-18 10:51:08 -05:00
|
|
|
|
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)
|
|
|
|
|
|
{
|
2018-11-18 11:55:43 -05:00
|
|
|
|
rc = DGControl.PendingXfers.Reset(ref pending);
|
2018-11-18 10:51:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
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?)
|
2018-11-18 11:55:43 -05:00
|
|
|
|
DGControl.PendingXfers.Reset(ref pending);
|
2018-11-18 10:51:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-11-18 11:55:43 -05:00
|
|
|
|
rc = DGControl.PendingXfers.EndXfer(ref pending);
|
2018-11-18 10:51:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#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()
|
2018-11-17 12:21:17 -05:00
|
|
|
|
{
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
}
|
2018-11-18 10:51:08 -05:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2018-11-18 11:01:25 -05:00
|
|
|
|
// TODO: restore this
|
2018-11-18 10:51:08 -05:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2018-11-14 18:52:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|