mirror of
https://github.com/soukoku/ntwain.git
synced 2025-07-15 15:32:05 +08:00
Started adding the higher level wrapper TwainSession.
This commit is contained in:
parent
0cd99efc0a
commit
20216b52a7
13
NTwain/Internal/TwainUtility.cs
Normal file
13
NTwain/Internal/TwainUtility.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NTwain.Internal
|
||||
{
|
||||
static class TwainUtility
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<PackageId>NTwain</PackageId>
|
||||
<Version>4.0.0</Version>
|
||||
<Description>Library containing the TWAIN API for dotnet.</Description>
|
||||
<TargetFrameworks>net45;netstandard2.0;</TargetFrameworks>
|
||||
<TargetFrameworks>net45;netcoreapp3.1;net5.0-windows</TargetFrameworks>
|
||||
<PackageProjectUrl>https://github.com/soukoku/ntwain</PackageProjectUrl>
|
||||
<PackageTags>twain scan</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
@ -17,6 +17,8 @@
|
||||
<AssemblyVersion>4.0.0.0</AssemblyVersion>
|
||||
<FileVersion>4.0.0.0</FileVersion>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
|
104
NTwain/ThreadMarshaller.cs
Normal file
104
NTwain/ThreadMarshaller.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows work to be marshalled to a different (usually UI) thread if necessary.
|
||||
/// </summary>
|
||||
public interface IThreadMarshaller
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts work asynchronously and returns immediately.
|
||||
/// </summary>
|
||||
/// <param name="work"></param>
|
||||
void BeginInvoke(Delegate work, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Starts work synchronously until it returns.
|
||||
/// </summary>
|
||||
/// <param name="work"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
object Invoke(Delegate work, params object[] args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't actually use any particular thread.
|
||||
/// Should only be used in non-UI apps.
|
||||
/// </summary>
|
||||
public class NoParticularMarshaller : IThreadMarshaller
|
||||
{
|
||||
public bool InvokeRequired => throw new NotImplementedException();
|
||||
|
||||
public void BeginInvoke(Delegate work, params object[] args)
|
||||
{
|
||||
Task.Run(() => work.DynamicInvoke(args));
|
||||
}
|
||||
|
||||
public object Invoke(Delegate work, params object[] args)
|
||||
{
|
||||
return work.DynamicInvoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uses a winform UI thread to do the work.
|
||||
/// </summary>
|
||||
public class WinformMarshaller : IThreadMarshaller
|
||||
{
|
||||
private readonly System.Windows.Forms.Control _uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Uses a control whose UI thread is used to run the work.
|
||||
/// </summary>
|
||||
/// <param name="uiControl"></param>
|
||||
public WinformMarshaller(System.Windows.Forms.Control uiControl)
|
||||
{
|
||||
_uiControl = uiControl ?? throw new ArgumentNullException(nameof(uiControl));
|
||||
}
|
||||
|
||||
public void BeginInvoke(Delegate work, params object[] args)
|
||||
{
|
||||
_uiControl.BeginInvoke(work, args);
|
||||
}
|
||||
|
||||
public object Invoke(Delegate work, params object[] args)
|
||||
{
|
||||
return _uiControl.Invoke(work, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a WPF dispatcher to do the work.
|
||||
/// </summary>
|
||||
public class DispatcherMarshaller : IThreadMarshaller
|
||||
{
|
||||
private readonly System.Windows.Threading.Dispatcher _dispatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Uses a dispatcher whose UI thread is used to run the work.
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
public DispatcherMarshaller(System.Windows.Threading.Dispatcher dispatcher)
|
||||
{
|
||||
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
|
||||
}
|
||||
|
||||
public void BeginInvoke(Delegate work, params object[] args)
|
||||
{
|
||||
_dispatcher.BeginInvoke(work, args);
|
||||
}
|
||||
|
||||
public object Invoke(Delegate work, params object[] args)
|
||||
{
|
||||
return _dispatcher.Invoke(work, args);
|
||||
}
|
||||
}
|
||||
}
|
263
NTwain/TwainSession.cs
Normal file
263
NTwain/TwainSession.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TWAINWorkingGroup;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
public class TwainSession : IDisposable
|
||||
{
|
||||
private TWAIN _twain;
|
||||
private bool _disposed;
|
||||
private readonly IThreadMarshaller _threadMarshaller;
|
||||
private IntPtr _hWnd;
|
||||
|
||||
public TwainSession(Assembly application,
|
||||
IThreadMarshaller threadMarshaller, IntPtr hWnd,
|
||||
TWLG language = TWLG.ENGLISH_USA, TWCY country = TWCY.USA)
|
||||
{
|
||||
var info = FileVersionInfo.GetVersionInfo(application.Location);
|
||||
|
||||
_twain = new TWAIN(info.CompanyName, info.ProductName, info.ProductName,
|
||||
(ushort)TWON_PROTOCOL.MAJOR, (ushort)TWON_PROTOCOL.MINOR,
|
||||
(uint)(DG.APP2 | DG.IMAGE),
|
||||
country, "", language, 2, 4, false, true, HandleDeviceEvent, HandleScanEvent, HandleUIThreadAction, hWnd);
|
||||
|
||||
_threadMarshaller = threadMarshaller ?? new NoParticularMarshaller();
|
||||
_hWnd = hWnd;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_twain != null)
|
||||
{
|
||||
Close();
|
||||
_twain.Dispose();
|
||||
_twain = null;
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#region event callbacks
|
||||
|
||||
private void HandleUIThreadAction(Action action)
|
||||
{
|
||||
_threadMarshaller.Invoke(action);
|
||||
}
|
||||
|
||||
private STS HandleScanEvent(bool a_blClosing)
|
||||
{
|
||||
_threadMarshaller.BeginInvoke(new Action<bool>(RaiseScanEvent), a_blClosing);
|
||||
return STS.SUCCESS;
|
||||
}
|
||||
void RaiseScanEvent(bool closing)
|
||||
{
|
||||
OnScanEvent(closing);
|
||||
ScanEvent?.Invoke(this, closing);
|
||||
}
|
||||
|
||||
protected virtual void OnScanEvent(bool closing) { }
|
||||
public event EventHandler<bool> ScanEvent;
|
||||
|
||||
private STS HandleDeviceEvent()
|
||||
{
|
||||
STS sts;
|
||||
TW_DEVICEEVENT twdeviceevent;
|
||||
|
||||
// Drain the event queue...
|
||||
while (true)
|
||||
{
|
||||
// Try to get an event...
|
||||
twdeviceevent = default(TW_DEVICEEVENT);
|
||||
sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent);
|
||||
if (sts != STS.SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseDeviceEvent(twdeviceevent);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a status, in case we ever need it for anything...
|
||||
return STS.SUCCESS;
|
||||
}
|
||||
|
||||
void RaiseDeviceEvent(TW_DEVICEEVENT twdeviceevent)
|
||||
{
|
||||
OnDeviceEvent(twdeviceevent);
|
||||
DeviceEvent?.Invoke(this, twdeviceevent);
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceEvent(TW_DEVICEEVENT twdeviceevent) { }
|
||||
|
||||
public event EventHandler<TW_DEVICEEVENT> DeviceEvent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region TWAIN operations
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current TWAIN state.
|
||||
/// </summary>
|
||||
public STATE State
|
||||
{
|
||||
get { return _twain.GetState(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the TWAIN data source manager.
|
||||
/// This needs to be done before anything else.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public STS Open()
|
||||
{
|
||||
var sts = _twain.DatParent(DG.CONTROL, MSG.OPENDSM, ref _hWnd);
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the TWAIN data source manager.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
StepDown(STATE.S2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets list of TWAIN devices.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IList<TW_IDENTITY> GetDevices()
|
||||
{
|
||||
var list = new List<TW_IDENTITY>();
|
||||
if (State > STATE.S2)
|
||||
{
|
||||
var twidentity = default(TW_IDENTITY);
|
||||
STS sts;
|
||||
|
||||
for (sts = _twain.DatIdentity(DG.CONTROL, MSG.GETFIRST, ref twidentity);
|
||||
sts != STS.ENDOFLIST;
|
||||
sts = _twain.DatIdentity(DG.CONTROL, MSG.GETNEXT, ref twidentity))
|
||||
{
|
||||
list.Add(twidentity);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default device.
|
||||
/// </summary>
|
||||
public TW_IDENTITY? DefaultDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
var twidentity = default(TW_IDENTITY);
|
||||
var sts = _twain.DatIdentity(DG.CONTROL, MSG.GETDEFAULT, ref twidentity);
|
||||
if (sts == STS.SUCCESS) return twidentity;
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
// Make it the default, we don't care if this succeeds...
|
||||
if (value.HasValue)
|
||||
{
|
||||
var twidentity = value.Value;
|
||||
_twain.DatIdentity(DG.CONTROL, MSG.SET, ref twidentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently open device.
|
||||
/// </summary>
|
||||
public TW_IDENTITY? CurrentDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State > STATE.S3)
|
||||
{
|
||||
var twidentity = default(TW_IDENTITY);
|
||||
if (TWAIN.CsvToIdentity(ref twidentity, _twain.GetDsIdentity()))
|
||||
{
|
||||
return twidentity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Steps down the TWAIN state to the specified state.
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public void StepDown(STATE state)
|
||||
{
|
||||
var twpendingxfers = default(TW_PENDINGXFERS);
|
||||
|
||||
// Make sure we have something to work with...
|
||||
if (_twain == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk the states, we don't care about the status returns. Basically,
|
||||
// these need to work, or we're guaranteed to hang...
|
||||
|
||||
// 7 --> 6
|
||||
if ((_twain.GetState() == STATE.S7) && (state < STATE.S7))
|
||||
{
|
||||
_twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers);
|
||||
}
|
||||
|
||||
// 6 --> 5
|
||||
if ((_twain.GetState() == STATE.S6) && (state < STATE.S6))
|
||||
{
|
||||
_twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers);
|
||||
}
|
||||
|
||||
// 5 --> 4
|
||||
if ((_twain.GetState() == STATE.S5) && (state < STATE.S5))
|
||||
{
|
||||
var twuserinterface = default(TW_USERINTERFACE);
|
||||
_twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface);
|
||||
}
|
||||
|
||||
// 4 --> 3
|
||||
if ((_twain.GetState() == STATE.S4) && (state < STATE.S4))
|
||||
{
|
||||
var twidentity = default(TW_IDENTITY);
|
||||
TWAIN.CsvToIdentity(ref twidentity, _twain.GetDsIdentity());
|
||||
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref twidentity);
|
||||
}
|
||||
|
||||
// 3 --> 2
|
||||
if ((_twain.GetState() == STATE.S3) && (state < STATE.S3))
|
||||
{
|
||||
_twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ V4 of this lib has these goals:
|
||||
* Targets latest TWAIN version (2.4 as of this writing).
|
||||
* Supports all the TWAIN functions in the spec (directly or through dotnet wrapper).
|
||||
* Works with both 32 or 64 bit data sources as appropriate for the 32 or 64 bit apps.
|
||||
* Supports full framework (4.5+) and netcore (2+) apps.
|
||||
* Supports full framework (4.5+) and netcore (3.1+) apps.
|
||||
|
||||
## Using the lib
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user