From e0f1f96947e82837224fe70a3b7aa1e97cb0063a Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Sat, 24 Apr 2021 22:24:24 -0400 Subject: [PATCH] Added reading of supported caps. --- NTwain/TWAINWorkingGroup/TWAINH.cs | 3 + NTwain/TWAINWorkingGroup/TWAINH_EXTRAS.cs | 36 ++++++- NTwain/TwainSession.cs | 57 +++++++++++ NTwain/ValueReader.cs | 115 ++++++++++++++++++++++ samples/Net5Console/Program.cs | 21 ++++ 5 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 NTwain/ValueReader.cs diff --git a/NTwain/TWAINWorkingGroup/TWAINH.cs b/NTwain/TWAINWorkingGroup/TWAINH.cs index 863d657..5032f61 100644 --- a/NTwain/TWAINWorkingGroup/TWAINH.cs +++ b/NTwain/TWAINWorkingGroup/TWAINH.cs @@ -2546,6 +2546,7 @@ namespace TWAINWorkingGroup /// /// Flags used in TW_MEMORY structure. /// + [Flags] public enum TWMF : ushort { APPOWNS = 0x0001, @@ -3753,6 +3754,7 @@ namespace TWAINWorkingGroup /// /// Data Groups... /// + [Flags] public enum DG : uint { CONTROL = 0x1, @@ -4273,6 +4275,7 @@ namespace TWAINWorkingGroup /// bit patterns: for query the operation that are supported by the data source on a capability /// Application gets these through DG_CONTROL/DAT_CAPABILITY/MSG_QUERYSUPPORT /// + [Flags] public enum TWQC : ushort { GET = 0x0001, diff --git a/NTwain/TWAINWorkingGroup/TWAINH_EXTRAS.cs b/NTwain/TWAINWorkingGroup/TWAINH_EXTRAS.cs index 9f55bce..fd168ab 100644 --- a/NTwain/TWAINWorkingGroup/TWAINH_EXTRAS.cs +++ b/NTwain/TWAINWorkingGroup/TWAINH_EXTRAS.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace TWAINWorkingGroup { - // contains my additions + // contains my additions that makes twain types easier to work with. partial struct TW_FIX32 : IEquatable { @@ -15,7 +15,11 @@ namespace TWAINWorkingGroup float ToFloat() { - return (float)Whole + Frac / 65536f; + return Whole + Frac / 65536f; + } + double ToDouble() + { + return Whole + Frac / 65536.0; } TW_FIX32(float value) { @@ -55,7 +59,7 @@ namespace TWAINWorkingGroup public static implicit operator float(TW_FIX32 value) => value.ToFloat(); public static implicit operator TW_FIX32(float value) => new TW_FIX32(value); - public static implicit operator double(TW_FIX32 value) => value.ToFloat(); + public static implicit operator double(TW_FIX32 value) => value.ToDouble(); public static implicit operator TW_FIX32(double value) => new TW_FIX32((float)value); public static bool operator ==(TW_FIX32 value1, TW_FIX32 value2) => value1.Equals(value2); @@ -64,9 +68,33 @@ namespace TWAINWorkingGroup partial struct TW_FRAME : IEquatable { + /// + /// Creates from a string representation of it. + /// + /// + public TW_FRAME(string value) : this() + { + var parts = value.Split(','); + if (parts.Length == 4) + { + Left = float.Parse(parts[0]); + Top = float.Parse(parts[1]); + Right = float.Parse(parts[2]); + Bottom = float.Parse(parts[3]); + } + else + { + throw new ArgumentException($"Cannot create frame from \"{value}\"."); + } + } + + /// + /// String representation of Left,Top,Right,Bottom. + /// + /// public override string ToString() { - return $"L={Left}, T={Top}, R={Right}, B={Bottom}"; + return $"{Left},{Top},{Right},{Bottom}"; } public bool Equals(TW_FRAME other) diff --git a/NTwain/TwainSession.cs b/NTwain/TwainSession.cs index 90d0621..41ecf03 100644 --- a/NTwain/TwainSession.cs +++ b/NTwain/TwainSession.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Reflection; using System.Text; @@ -285,6 +286,62 @@ namespace NTwain } } + /// + /// Queries current device for supported capabilities. + /// Not all devices supports this. + /// + /// + public IEnumerable SupportedCaps() + { + List caps = null; + if (State >= STATE.S4) + { + TW_CAPABILITY cap = default; + + // get list of supported caps + cap.Cap = CAP.CAP_SUPPORTEDCAPS; + + var sts = _twain.DatCapability(DG.CONTROL, MSG.GET, ref cap); + if (sts == STS.SUCCESS) + { + var csv = TWAIN.CapabilityToCsv(cap, true); + caps = csv.Split(',').Skip(4).Select(val => + { + if (Enum.TryParse(val, out CAP c)) + { + return c; + } + else if (val.StartsWith("0x")) + { + return (CAP)Convert.ToUInt16(val, 16); + } + else if (ushort.TryParse(val, out ushort num)) + { + return (CAP)num; + } + return (CAP)0; + + }).ToList(); + } + + //foreach (CAP capId in Enum.GetValues(typeof(CAP))) + //{ + // cap.ConType = TWON.ONEVALUE; + // cap.Cap = capId; + + // var sts = _twain.DatCapability(DG.CONTROL, MSG.QUERYSUPPORT, ref cap); + // if (sts == STS.SUCCESS) + // { + // if (Enum.TryParse(_twain.CapabilityOneValueToString(cap), out TWQC qc)) + // { + + // } + // } + //} + } + return caps ?? Enumerable.Empty(); + } + /// /// Attempts to show the current device's settings dialog if supported. /// diff --git a/NTwain/ValueReader.cs b/NTwain/ValueReader.cs new file mode 100644 index 0000000..c286c87 --- /dev/null +++ b/NTwain/ValueReader.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using TWAINWorkingGroup; + +namespace NTwain +{ + /// + /// Contains extension methods for reading pointers into various things. + /// + public static class ValueReader + { + /// + /// Read capability's container content as ToString()'d one value. + /// + /// Low-level twain object. + /// Cap to read from. + /// + public static string CapabilityOneValueToString(this TWAIN twain, TW_CAPABILITY cap) + { + if (cap.hContainer == IntPtr.Zero) return null; + + var lockedPtr = twain.DsmMemLock(cap.hContainer); + + try + { + TWTY itemType; + // Mac has a level of indirection and a different structure (ick)... + if (PlatformTools.GetPlatform() == Platform.MACOSX) + { + // Crack the container... + var onevalue = lockedPtr.MarshalTo(); + itemType = (TWTY)onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } + else + { + // Crack the container... + var onevalue = lockedPtr.MarshalTo(); + itemType = onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } + + return lockedPtr.ContainerToString(itemType); + } + finally + { + // All done... + twain.DsmMemUnlock(cap.hContainer); + } + } + + /// + /// Read the container pointer content as a string. Numeric values are ToString()'d. + /// + /// A locked pointer to the container data. If data is array this is the 0th item. + /// The twain type. + /// Index of the item if pointer is array. + /// + static string ContainerToString(this IntPtr intptr, TWTY type, int itemIndex = 0) + { + switch (type) + { + default: + throw new NotSupportedException($"Unknown item type {type} to read as string."); + case TWTY.INT8: + intptr += 1 * itemIndex; + return intptr.MarshalToString(); + case TWTY.INT16: + intptr += 2 * itemIndex; + return intptr.MarshalToString(); + case TWTY.INT32: + intptr += 4 * itemIndex; + return intptr.MarshalToString(); + case TWTY.UINT8: + intptr += 1 * itemIndex; + return intptr.MarshalToString(); + case TWTY.BOOL: + case TWTY.UINT16: + intptr += 2 * itemIndex; + return intptr.MarshalToString(); + case TWTY.UINT32: + intptr += 4 * itemIndex; + return intptr.MarshalToString(); + case TWTY.FIX32: + intptr += 4 * itemIndex; + return intptr.MarshalToString(); + case TWTY.FRAME: + intptr += 16 * itemIndex; + return intptr.MarshalToString(); + case TWTY.STR32: + intptr += TW_STR32.Size * itemIndex; + return intptr.MarshalToString(); + case TWTY.STR64: + intptr += TW_STR64.Size * itemIndex; + return intptr.MarshalToString(); + case TWTY.STR128: + intptr += TW_STR128.Size * itemIndex; + return intptr.MarshalToString(); + case TWTY.STR255: + intptr += TW_STR255.Size * itemIndex; + return intptr.MarshalToString(); + case TWTY.HANDLE: + intptr += IntPtr.Size * itemIndex; + return Marshal.ReadIntPtr(intptr).ToString(); + } + } + + static string MarshalToString(this IntPtr ptr) => Marshal.PtrToStructure(ptr, typeof(T)).ToString(); + static T MarshalTo(this IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); + } +} diff --git a/samples/Net5Console/Program.cs b/samples/Net5Console/Program.cs index 2627751..c132f35 100644 --- a/samples/Net5Console/Program.cs +++ b/samples/Net5Console/Program.cs @@ -1,5 +1,6 @@ using NTwain; using System; +using System.Linq; using System.Reflection; using System.Threading; using TWAINWorkingGroup; @@ -12,6 +13,7 @@ namespace Net5Console static void Main(string[] args) { Console.WriteLine("Starting twain test in console..."); + Console.WriteLine(); using (var session = new TwainSession(Assembly.GetExecutingAssembly(), null, IntPtr.Zero)) using (var hold = new ManualResetEventSlim()) @@ -19,10 +21,12 @@ namespace Net5Console session.DeviceEvent += (sender, e) => { Console.WriteLine($"Got device event " + (TWDE)e.Event); + Console.WriteLine(); }; session.ScanEvent += (sender, closing) => { Console.WriteLine($"Got scan event " + closing); + Console.WriteLine(); // don't care, just end it TW_PENDINGXFERS pending = default; @@ -32,10 +36,12 @@ namespace Net5Console if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS) { Console.WriteLine("Disabled device."); + Console.WriteLine(); } else { Console.Error.WriteLine("Failed to disabled device."); + Console.WriteLine(); } hold.Set(); }; @@ -43,9 +49,11 @@ namespace Net5Console if (session.Open() == TWAINWorkingGroup.STS.SUCCESS) { Console.WriteLine("Opened DSM"); + Console.WriteLine(); Console.WriteLine("Default device:"); Console.WriteLine($"\t{session.DefaultDevice}"); + Console.WriteLine(); Console.WriteLine("All devices:"); TW_IDENTITY dsToUse = default; @@ -57,28 +65,41 @@ namespace Net5Console dsToUse = dev; } } + Console.WriteLine(); session.CurrentDevice = dsToUse; if (session.CurrentDevice.HasValue) { Console.WriteLine("Current device after opening attempt:"); Console.WriteLine($"\t{session.CurrentDevice}"); + Console.WriteLine(); + + var caps = session.SupportedCaps(); + Console.WriteLine("Device supports these caps:"); + foreach (var cap in caps.OrderBy(c => c)) + { + Console.WriteLine($"\t{cap}"); + } + Console.WriteLine(); var sts = session.StartCapture(false); if (sts == STS.SUCCESS) { Console.Error.WriteLine("Waiting for capture to complete."); + Console.WriteLine(); while (!hold.IsSet) Thread.Sleep(100); } else { Console.Error.WriteLine("Failed to start capture: " + sts); + Console.WriteLine(); } } else { Console.WriteLine("No devices opened."); + Console.WriteLine(); } } else