From 80c6276f4ce5b8ef4384f73518e410cb33f47b40 Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Sun, 25 Apr 2021 17:33:54 -0400 Subject: [PATCH] Added range value reads. --- samples/Net5Console/Program.cs | 36 +++--- src/NTwain/CapWrapper.cs | 6 +- src/NTwain/NTwain.csproj | 1 + src/NTwain/TWAINH_EXTRAS.cs | 173 +++++++++++++++++++++++++- src/NTwain/TWAINWorkingGroup/TWAIN.cs | 2 +- src/NTwain/ValueReader.cs | 96 +++++++++++++- 6 files changed, 287 insertions(+), 27 deletions(-) diff --git a/samples/Net5Console/Program.cs b/samples/Net5Console/Program.cs index 67e5c8b..e5ba5f3 100644 --- a/samples/Net5Console/Program.cs +++ b/samples/Net5Console/Program.cs @@ -1,5 +1,6 @@ using NTwain; using System; +using System.Collections; using System.Linq; using System.Reflection; using System.Threading; @@ -75,24 +76,13 @@ namespace Net5Console Console.WriteLine(); var caps = session.Capabilities; - Console.WriteLine("Device supports these caps:"); + Console.WriteLine("All device caps:"); foreach (var cap in caps.CAP_SUPPORTEDCAPS.GetValues()) { WriteCapInfo(caps, cap); } Console.WriteLine(); - var pxWrapper = caps.ICAP_PIXELTYPE; - Console.WriteLine($"Details on {pxWrapper.Cap}:"); - Console.WriteLine($"\tDefault: {pxWrapper.GetDefault()}"); - Console.WriteLine($"\tCurrent: {pxWrapper.GetCurrent()}"); - Console.WriteLine($"\tValues:"); - foreach (var val in pxWrapper.GetValues()) - { - Console.WriteLine($"\t\t{val}"); - } - Console.WriteLine(); - var sts = session.StartCapture(false); if (sts == STS.SUCCESS) { @@ -124,15 +114,31 @@ namespace Net5Console private static void WriteCapInfo(Capabilities caps, CAP cap) { - // use reflection due to generics + // use reflection due to unknown generics var propInfo = typeof(Capabilities).GetProperty(cap.ToString()); if (propInfo == null) return; var capWrapper = propInfo.GetValue(caps); - var label = (string)capWrapper.GetType().GetMethod(nameof(CapWrapper.GetLabel)).Invoke(capWrapper, null); - var supports = (TWQC)capWrapper.GetType().GetMethod(nameof(CapWrapper.QuerySupport)).Invoke(capWrapper, null); + var wrapType = capWrapper.GetType(); + var label = (string)wrapType.GetMethod(nameof(CapWrapper.GetLabel)).Invoke(capWrapper, null); + var supports = (TWQC)wrapType.GetMethod(nameof(CapWrapper.QuerySupport)).Invoke(capWrapper, null); Console.WriteLine($"\t{label ?? cap.ToString()}: {supports}"); + Console.WriteLine($"\t\tDefault: {wrapType.GetMethod(nameof(CapWrapper.GetDefault)).Invoke(capWrapper, null)}"); + Console.WriteLine($"\t\tCurrent: {wrapType.GetMethod(nameof(CapWrapper.GetCurrent)).Invoke(capWrapper, null)}"); + bool first = true; + foreach (var val in (IEnumerable)wrapType.GetMethod(nameof(CapWrapper.GetValues)).Invoke(capWrapper, null)) + { + if (first) + { + Console.WriteLine($"\t\tValues:\t{val}"); + first = false; + } + else + { + Console.WriteLine($"\t\t\t{val}"); + } + } } } } diff --git a/src/NTwain/CapWrapper.cs b/src/NTwain/CapWrapper.cs index b2cedf8..37c803e 100644 --- a/src/NTwain/CapWrapper.cs +++ b/src/NTwain/CapWrapper.cs @@ -66,7 +66,7 @@ namespace NTwain case TWON.ARRAY: return ValueReader.ReadArray(_twain, twCap); case TWON.RANGE: - return ValueReader.ReadRange(_twain, twCap).values.ToList(); + return ValueReader.ReadRange(_twain, twCap).ToList(); } } return new TValue[0]; @@ -96,7 +96,7 @@ namespace NTwain return enumeration.Items[enumeration.CurrentIndex]; break; case TWON.RANGE: - return ValueReader.ReadRange(_twain, twCap).currentVal; + return ValueReader.ReadRange(_twain, twCap).CurrentValue; } } return default(TValue); @@ -126,7 +126,7 @@ namespace NTwain return enumeration.Items[enumeration.DefaultIndex]; break; case TWON.RANGE: - return ValueReader.ReadRange(_twain, twCap).defaultVal; + return ValueReader.ReadRange(_twain, twCap).DefaultValue; } } return default(TValue); diff --git a/src/NTwain/NTwain.csproj b/src/NTwain/NTwain.csproj index 7f539f8..92d4898 100644 --- a/src/NTwain/NTwain.csproj +++ b/src/NTwain/NTwain.csproj @@ -7,6 +7,7 @@ + diff --git a/src/NTwain/TWAINH_EXTRAS.cs b/src/NTwain/TWAINH_EXTRAS.cs index df5141f..e31fe8c 100644 --- a/src/NTwain/TWAINH_EXTRAS.cs +++ b/src/NTwain/TWAINH_EXTRAS.cs @@ -1,4 +1,6 @@ -using System; +using NTwain; +using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -30,14 +32,85 @@ namespace TWAINWorkingGroup /// public class Enumeration where TValue : struct { - public int CurrentIndex { get; set; } + public int CurrentIndex; - public int DefaultIndex { get; set; } + public int DefaultIndex; - public TValue[] Items { get; set; } + public TValue[] Items; } - partial struct TW_FIX32 : IEquatable + /// + /// A more dotnet-friendly representation of . + /// + /// + public partial class Range : IEnumerable where TValue : struct + { + public TValue MinValue; + public TValue MaxValue; + public TValue StepSize; + public TValue DefaultValue; + public TValue CurrentValue; + + IEnumerator IEnumerable.GetEnumerator() + { + if (!(MinValue is IConvertible)) + throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported as range."); + + return new DynamicEnumerator(MinValue, MaxValue, StepSize); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + + // dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric + class DynamicEnumerator : IEnumerator + { + private TValue _min; + private TValue _max; + private TValue _step; + private TValue _cur; + bool started = false; + + public DynamicEnumerator(TValue min, TValue max, TValue step) + { + _min = min; + _max = max; + _step = step; + _cur = min; + } + + public TValue Current => _cur; + + object IEnumerator.Current => this.Current; + + public void Dispose() { } + + public bool MoveNext() + { + if (!started) + { + started = true; + return true; + } + + var next = _cur + (dynamic)_step; + if (next == _cur || next < _min || next > _max) return false; + + _cur = next; + return true; + } + + public void Reset() + { + _cur = _min; + started = false; + } + } + } + + partial struct TW_FIX32 : IEquatable, IConvertible { // the conversion logic is found in the spec. @@ -84,6 +157,96 @@ namespace TWAINWorkingGroup return Whole ^ Frac; } + + #region IConvertable + + TypeCode IConvertible.GetTypeCode() + { + return TypeCode.Single; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return this != 0; + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte((float)this); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar((float)this); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + return Convert.ToDateTime((float)this); + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal((float)this); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble((float)this); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16((float)this); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32((float)this); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64((float)this); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte((float)this); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle((float)this); + } + + string IConvertible.ToString(IFormatProvider provider) + { + return this.ToString(); + } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + return Convert.ChangeType((float)this, conversionType, CultureInfo.InvariantCulture); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16((float)this); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32((float)this); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64((float)this); + } + + #endregion + public static implicit operator float(TW_FIX32 value) => value.ToFloat(); public static implicit operator TW_FIX32(float value) => new TW_FIX32(value); diff --git a/src/NTwain/TWAINWorkingGroup/TWAIN.cs b/src/NTwain/TWAINWorkingGroup/TWAIN.cs index 19db8c7..f6b1173 100644 --- a/src/NTwain/TWAINWorkingGroup/TWAIN.cs +++ b/src/NTwain/TWAINWorkingGroup/TWAIN.cs @@ -12503,7 +12503,7 @@ namespace TWAINWorkingGroup /// /// The platform we're running on... /// - private static Platform ms_platform; + static Platform ms_platform; /// /// Delegates for DAT_CALLBACK... diff --git a/src/NTwain/ValueReader.cs b/src/NTwain/ValueReader.cs index 2b6276d..1e82956 100644 --- a/src/NTwain/ValueReader.cs +++ b/src/NTwain/ValueReader.cs @@ -14,6 +14,8 @@ namespace NTwain /// public static class ValueReader { + // most of these are modified from the original TWAIN.CapabilityToCsv() + public static TValue ReadOneValue(TWAIN twain, TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct { if (cap.hContainer == IntPtr.Zero) return default(TValue); @@ -157,10 +159,98 @@ namespace NTwain if (freeMemory) twain.DsmMemFree(ref cap.hContainer); } } - public static (TValue defaultVal, TValue currentVal, IEnumerable values) - ReadRange(TWAIN twain, TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct + public static Range ReadRange(TWAIN twain, TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct { - throw new NotImplementedException(); + var retVal = new Range(); + + if (cap.hContainer == IntPtr.Zero) return retVal; + + var lockedPtr = twain.DsmMemLock(cap.hContainer); + + try + { + TW_RANGE twrange = default; + TW_RANGE_FIX32 twrangefix32 = default; + var platform = PlatformTools.GetPlatform(); + + // Mac has a level of indirection and a different structure (ick)... + if (platform == Platform.MACOSX) + { + var twrangemacosx = MarshalTo(lockedPtr); + var twrangefix32macosx = MarshalTo(lockedPtr); + twrange.ItemType = (TWTY)twrangemacosx.ItemType; + twrange.MinValue = twrangemacosx.MinValue; + twrange.MaxValue = twrangemacosx.MaxValue; + twrange.StepSize = twrangemacosx.StepSize; + twrange.DefaultValue = twrangemacosx.DefaultValue; + twrange.CurrentValue = twrangemacosx.CurrentValue; + twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; + twrangefix32.MinValue = twrangefix32macosx.MinValue; + twrangefix32.MaxValue = twrangefix32macosx.MaxValue; + twrangefix32.StepSize = twrangefix32macosx.StepSize; + twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; + twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; + } + // Windows or the 2.4+ Linux DSM... + else if ((platform == Platform.WINDOWS) || (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm) || + ((twain.m_blFoundLatestDsm || twain.m_blFoundLatestDsm64) && (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm))) + { + twrange = MarshalTo(lockedPtr); + twrangefix32 = MarshalTo(lockedPtr); + } + // The -2.3 Linux DSM... + else + { + var twrangelinux64 = MarshalTo(lockedPtr); + var twrangefix32macosx = MarshalTo(lockedPtr); + twrange.ItemType = twrangelinux64.ItemType; + twrange.MinValue = (uint)twrangelinux64.MinValue; + twrange.MaxValue = (uint)twrangelinux64.MaxValue; + twrange.StepSize = (uint)twrangelinux64.StepSize; + twrange.DefaultValue = (uint)twrangelinux64.DefaultValue; + twrange.CurrentValue = (uint)twrangelinux64.CurrentValue; + twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; + twrangefix32.MinValue = twrangefix32macosx.MinValue; + twrangefix32.MaxValue = twrangefix32macosx.MaxValue; + twrangefix32.StepSize = twrangefix32macosx.StepSize; + twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; + twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; + } + + switch (twrange.ItemType) + { + // use dynamic since I know they fit the type. + case TWTY.FIX32: + retVal.MinValue = (dynamic)twrangefix32.MinValue; + retVal.MaxValue = (dynamic)twrangefix32.MaxValue; + retVal.StepSize = (dynamic)twrangefix32.StepSize; + retVal.CurrentValue = (dynamic)twrangefix32.CurrentValue; + retVal.DefaultValue = (dynamic)twrangefix32.DefaultValue; + break; + case TWTY.INT8: + case TWTY.UINT8: + case TWTY.INT16: + case TWTY.BOOL: + case TWTY.UINT16: + case TWTY.INT32: + case TWTY.UINT32: + retVal.MinValue = (dynamic)twrange.MinValue; + retVal.MaxValue = (dynamic)twrange.MaxValue; + retVal.StepSize = (dynamic)twrange.StepSize; + retVal.CurrentValue = (dynamic)twrange.CurrentValue; + retVal.DefaultValue = (dynamic)twrange.DefaultValue; + break; + default: + throw new NotSupportedException($"The value type {twrange.ItemType} is not supported as range."); + + } + return retVal; + } + finally + { + twain.DsmMemUnlock(cap.hContainer); + if (freeMemory) twain.DsmMemFree(ref cap.hContainer); + } } ///