diff --git a/samples/NetCoreConsole/Program.cs b/samples/NetCoreConsole/Program.cs index b9d27f1..e25973c 100644 --- a/samples/NetCoreConsole/Program.cs +++ b/samples/NetCoreConsole/Program.cs @@ -15,6 +15,7 @@ namespace NetCoreConsole .Build(); Console.WriteLine($"App = {(config.Is64Bit ? "64bit" : "32bit")}"); Console.WriteLine($"Platform = {config.Platform}"); + Console.WriteLine(); using (var session = new TwainSession(config)) { @@ -23,18 +24,33 @@ namespace NetCoreConsole var handle = IntPtr.Zero; if (session.Open(ref handle) == NTwain.Data.ReturnCode.Success) { + Console.WriteLine("Available data sources:"); + foreach (var src in session.GetSources()) + { + Console.WriteLine($"\t{src}"); + } + Console.WriteLine(); + var defaultSrc = session.DefaultSource; + Console.WriteLine($"Default data source = {defaultSrc}"); + Console.WriteLine(); + + var selectSrc = session.ShowSourceSelector(); + Console.WriteLine($"Selected data source = {selectSrc}"); + Console.WriteLine(); + + //session.DefaultSource = null; } } - - Console.WriteLine("Test ended, press Enter to exit..."); - Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.ToString()); } + + Console.WriteLine("Test ended, press Enter to exit..."); + Console.ReadLine(); } private static void Session_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/src/NTwain/Data/TwainTypes.cs b/src/NTwain/Data/TwainTypes.cs index c58de00..165b51e 100644 --- a/src/NTwain/Data/TwainTypes.cs +++ b/src/NTwain/Data/TwainTypes.cs @@ -310,7 +310,7 @@ namespace NTwain.Data partial class TW_IDENTITY { // TODO: id needs to be 64bit in 64bit? - TW_UINT32 _id; + internal TW_UINT32 Id; TW_VERSION _version; TW_UINT16 _protocolMajor; TW_UINT16 _protocolMinor; diff --git a/src/NTwain/Data/TwainTypesExtended.cs b/src/NTwain/Data/TwainTypesExtended.cs index b9617e7..ea13cf3 100644 --- a/src/NTwain/Data/TwainTypesExtended.cs +++ b/src/NTwain/Data/TwainTypesExtended.cs @@ -1584,7 +1584,8 @@ namespace NTwain.Data /// public override string ToString() { - return string.Format(CultureInfo.InvariantCulture, "{0}.{1} {2}", Major, Minor, Info); + if (Info == null) return $"v{Major}.{Minor}"; + return $"v{Major}.{Minor} ({Info})"; } #region equals diff --git a/src/NTwain/DataSource.cs b/src/NTwain/DataSource.cs new file mode 100644 index 0000000..4f749b6 --- /dev/null +++ b/src/NTwain/DataSource.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NTwain.Data; + +namespace NTwain +{ + /// + /// A TWAIN data source. + /// + public class DataSource + { + internal readonly TwainSession Session; + + internal TW_IDENTITY Identity { get; } + + internal DataSource(TwainSession session, TW_IDENTITY src) + { + this.Session = session; + this.Identity = src; + } + + /// + /// Gets the source name. + /// + public string Name => Identity.ProductName; + + /// + /// Gets the source version info. + /// + public TW_VERSION Version => Identity.Version; + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return $"{Name} {Version}"; + } + } +} diff --git a/src/NTwain/Triplets/BaseTriplet.cs b/src/NTwain/Triplets/BaseTriplet.cs index 063502a..10275de 100644 --- a/src/NTwain/Triplets/BaseTriplet.cs +++ b/src/NTwain/Triplets/BaseTriplet.cs @@ -1,12 +1,25 @@ -namespace NTwain.Triplets +using System; + +namespace NTwain.Triplets { + /// + /// Base class for grouping triplet operations. + /// public abstract class BaseTriplet { - protected TwainSession session; + /// + /// Gets the associated . + /// + protected readonly TwainSession Session; - public BaseTriplet(TwainSession session) + /// + /// Initializes a new instance of the class. + /// + /// The session. + /// + protected BaseTriplet(TwainSession session) { - this.session = session; + this.Session = session ?? throw new ArgumentNullException(nameof(session)); } } } \ No newline at end of file diff --git a/src/NTwain/Triplets/DGControl.EntryPoint.cs b/src/NTwain/Triplets/DGControl.EntryPoint.cs index ca60712..3ad28f0 100644 --- a/src/NTwain/Triplets/DGControl.EntryPoint.cs +++ b/src/NTwain/Triplets/DGControl.EntryPoint.cs @@ -13,7 +13,7 @@ namespace NTwain.Triplets public ReturnCode Get(out TW_ENTRYPOINT entryPoint) { entryPoint = new TW_ENTRYPOINT(); - return NativeMethods.DsmWin32(session.Config.AppWin32, null, + return NativeMethods.DsmWin32(Session.Config.AppWin32, null, DataGroups.Control, DataArgumentType.EntryPoint, Message.Get, entryPoint); } } diff --git a/src/NTwain/Triplets/DGControl.Identity.cs b/src/NTwain/Triplets/DGControl.Identity.cs new file mode 100644 index 0000000..7f71049 --- /dev/null +++ b/src/NTwain/Triplets/DGControl.Identity.cs @@ -0,0 +1,70 @@ +using NTwain.Data; +using NTwain.Internals; +using System; + +namespace NTwain.Triplets +{ + sealed class Identity : BaseTriplet + { + internal Identity(TwainSession session) : base(session) { } + + public ReturnCode CloseDS(TW_IDENTITY source) + { + var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.CloseDS, source); + if (rc == ReturnCode.Success) + { + Session.State = TwainState.S3; + Session.CurrentSource = null; + } + return rc; + } + + public ReturnCode GetDefault(out TW_IDENTITY source) + { + source = new TW_IDENTITY(); + return NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.GetDefault, source); + } + + + public ReturnCode GetFirst(out TW_IDENTITY source) + { + source = new TW_IDENTITY(); + return NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.GetFirst, source); + } + + public ReturnCode GetNext(out TW_IDENTITY source) + { + source = new TW_IDENTITY(); + return NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.GetNext, source); + } + + public ReturnCode OpenDS(TW_IDENTITY source) + { + var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.OpenDS, source); + if (rc == ReturnCode.Success) + { + Session.CurrentSource = Session.GetSourceSingleton(source); + Session.State = TwainState.S4; + } + return rc; + } + + public ReturnCode Set(DataSource source) + { + return NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.Set, source?.Identity); + } + + public ReturnCode UserSelect(out TW_IDENTITY source) + { + source = new TW_IDENTITY(); + return NativeMethods.DsmWin32(Session.Config.AppWin32, IntPtr.Zero, + DataGroups.Control, DataArgumentType.Identity, Message.UserSelect, source); + } + } +} \ No newline at end of file diff --git a/src/NTwain/Triplets/DGControl.Parent.cs b/src/NTwain/Triplets/DGControl.Parent.cs index db7948d..324b300 100644 --- a/src/NTwain/Triplets/DGControl.Parent.cs +++ b/src/NTwain/Triplets/DGControl.Parent.cs @@ -12,20 +12,20 @@ namespace NTwain.Triplets public ReturnCode OpenDSM(ref IntPtr hWnd) { - var rc = NativeMethods.DsmWin32(session.Config.AppWin32, null, + var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, null, DataGroups.Control, DataArgumentType.Parent, Message.OpenDSM, ref hWnd); if (rc == ReturnCode.Success) { - session.State = TwainState.DsmOpened; + Session.State = TwainState.DsmOpened; // if twain2 then get memory management functions - if ((session.Config.AppWin32.DataFunctionalities & DataFunctionalities.Dsm2) == DataFunctionalities.Dsm2) + if ((Session.Config.AppWin32.DataFunctionalities & DataFunctionalities.Dsm2) == DataFunctionalities.Dsm2) { TW_ENTRYPOINT entry; - rc = session.DGControl.EntryPoint.Get(out entry); + rc = Session.DGControl.EntryPoint.Get(out entry); if (rc == ReturnCode.Success) { - session.Config.MemoryManager = entry; + Session.Config.MemoryManager = entry; } else { @@ -38,9 +38,9 @@ namespace NTwain.Triplets public ReturnCode CloseDSM(ref IntPtr hWnd) { - var rc = NativeMethods.DsmWin32(session.Config.AppWin32, null, DataGroups.Control, + var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, null, DataGroups.Control, DataArgumentType.Parent, Message.CloseDSM, ref hWnd); - if (rc == ReturnCode.Success) session.State = TwainState.DsmLoaded; + if (rc == ReturnCode.Success) Session.State = TwainState.DsmLoaded; return rc; } } diff --git a/src/NTwain/Triplets/DGControl.cs b/src/NTwain/Triplets/DGControl.cs index a2b2217..a07d157 100644 --- a/src/NTwain/Triplets/DGControl.cs +++ b/src/NTwain/Triplets/DGControl.cs @@ -14,9 +14,12 @@ namespace NTwain.Triplets internal DGControl(TwainSession session) : base(session) { } Parent _parent; - internal Parent Parent => _parent ?? (_parent = new Parent(session)); + internal Parent Parent => _parent ?? (_parent = new Parent(Session)); EntryPoint _entryPoint; - internal EntryPoint EntryPoint => _entryPoint ?? (_entryPoint = new EntryPoint(session)); + internal EntryPoint EntryPoint => _entryPoint ?? (_entryPoint = new EntryPoint(Session)); + + Identity _identity; + internal Identity Identity => _identity ?? (_identity = new Identity(Session)); } } diff --git a/src/NTwain/TwainConfigBuilder.cs b/src/NTwain/TwainConfigBuilder.cs index f65cbfc..b34955e 100644 --- a/src/NTwain/TwainConfigBuilder.cs +++ b/src/NTwain/TwainConfigBuilder.cs @@ -11,7 +11,7 @@ namespace NTwain /// public class TwainConfigBuilder { - private bool _legacy; + //private bool _legacy; private string _appName; private Version _version; private string _companyName; diff --git a/src/NTwain/TwainSession.cs b/src/NTwain/TwainSession.cs index 4bed415..1318ddc 100644 --- a/src/NTwain/TwainSession.cs +++ b/src/NTwain/TwainSession.cs @@ -8,22 +8,41 @@ using System.Text; namespace NTwain { + /// + /// Manages a TWAIN session. + /// public partial class TwainSession { - internal TwainConfig Config; + internal readonly TwainConfig Config; private IntPtr _hWnd; + // cache generated twain sources so if you get same source from same session it'll return the same object + readonly Dictionary _ownedSources = new Dictionary(); + /// + /// Constructs a new . + /// + /// public TwainSession(TwainConfig config) { Config = config; } + /// + /// Opens the TWAIN session. + /// + /// + /// public ReturnCode Open(ref IntPtr hWnd) { _hWnd = hWnd; return DGControl.Parent.OpenDSM(ref hWnd); } + /// + /// Steps down to the target session state. + /// + /// + /// public ReturnCode StepDown(TwainState targetState) { var rc = ReturnCode.Failure; @@ -39,5 +58,82 @@ namespace NTwain } return rc; } + + /// + /// Gets list of sources available on the machine. + /// + /// + public IEnumerable GetSources() + { + TW_IDENTITY srcId; + var rc = DGControl.Identity.GetFirst(out srcId); + while (rc == ReturnCode.Success) + { + yield return GetSourceSingleton(srcId); + rc = DGControl.Identity.GetNext(out srcId); + } + } + + /// + /// Gets/sets the default data source. + /// + public DataSource DefaultSource + { + get + { + TW_IDENTITY src; + if (DGControl.Identity.GetDefault(out src) == ReturnCode.Success) + { + return GetSourceSingleton(src); + } + return null; + } + set + { + //if (value != null && value.Session != this) + //{ + // throw new InvalidOperationException("Source is not from this session."); + //} + DGControl.Identity.Set(value); + RaisePropertyChanged(nameof(DefaultSource)); + } + } + + /// + /// Tries to show the built-in source selector dialog and return the selected source. + /// + /// + public DataSource ShowSourceSelector() + { + TW_IDENTITY id; + if (DGControl.Identity.UserSelect(out id) == ReturnCode.Success) + { + return GetSourceSingleton(id); + } + return null; + } + + /// + /// Gets the currently open data source. + /// + /// + /// The current source. + /// + public DataSource CurrentSource { get; internal set; } + + internal DataSource GetSourceSingleton(TW_IDENTITY sourceId) + { + DataSource source = null; + var key = $"{sourceId.Id}|{sourceId.Manufacturer}|{sourceId.ProductFamily}|{sourceId.ProductName}"; + if (_ownedSources.ContainsKey(key)) + { + source = _ownedSources[key]; + } + else + { + _ownedSources[key] = source = new DataSource(this, sourceId); + } + return source; + } } }