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