Started resurrecting my old Capabilities class.

This commit is contained in:
Eugene Wang
2021-04-25 09:36:21 -04:00
parent ab6a546bba
commit c8e6b7bbe3
8 changed files with 213 additions and 138 deletions

View File

@@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using TWAINWorkingGroup; using TWAINWorkingGroup;
@@ -8,9 +7,11 @@ namespace NTwain
/// <summary> /// <summary>
/// Contains operations for a <see cref="CAP"/>. /// Contains operations for a <see cref="CAP"/>.
/// </summary> /// </summary>
public class CapWrapper /// <typeparam name="TValue">Individual value type of the cap. Must be one of TWAIN's supported cap value types.
/// You are responsible for using the correct type for a cap.</typeparam>
public class CapWrapper<TValue> where TValue : struct
{ {
private readonly TWAIN _twain; protected readonly TWAIN _twain;
public CapWrapper(TWAIN twain, CAP cap) public CapWrapper(TWAIN twain, CAP cap)
{ {
@@ -23,31 +24,29 @@ namespace NTwain
}; };
var sts = _twain.DatCapability(DG.CONTROL, MSG.QUERYSUPPORT, ref twCap); var sts = _twain.DatCapability(DG.CONTROL, MSG.QUERYSUPPORT, ref twCap);
if (sts == STS.SUCCESS) if (sts == STS.SUCCESS && twCap.ConType == TWON.ONEVALUE)
{ {
if (Enum.TryParse(_twain.CapabilityOneValueToString(twCap), out TWQC qc)) Supports = ValueReader.ReadOneValue<TWQC>(_twain, twCap);
{
Supports = qc;
}
} }
} }
/// <summary>
/// The cap in question.
/// </summary>
public CAP Cap { get; }
/// <summary> /// <summary>
/// The operations supported by the cap. /// The operations supported by the cap.
/// Not all sources supports this so it may be unknown. /// Not all sources supports this so it may be unknown.
/// </summary> /// </summary>
public TWQC Supports { get; } public TWQC Supports { get; }
/// <summary> /// <summary>
/// Try to get string representation of the cap's supported values. /// The cap being targeted.
/// </summary>
public CAP Cap { get; }
/// <summary>
/// Try to get list of the cap's supported values.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public IList<string> GetValues() public IList<TValue> GetValues()
{ {
var twCap = new TW_CAPABILITY var twCap = new TW_CAPABILITY
{ {
@@ -60,23 +59,23 @@ namespace NTwain
switch (twCap.ConType) switch (twCap.ConType)
{ {
case TWON.ONEVALUE: case TWON.ONEVALUE:
return new[] { _twain.CapabilityOneValueToString(twCap) }; return new[] { ValueReader.ReadOneValue<TValue>(_twain, twCap) };
case TWON.ENUMERATION: case TWON.ENUMERATION:
var csv = _twain.CapabilityToCsv(twCap, true); return ValueReader.ReadEnumeration<TValue>(_twain, twCap);
return csv.Split(',').Skip(6).ToList(); case TWON.ARRAY:
default: return ValueReader.ReadArray<TValue>(_twain, twCap);
csv = _twain.CapabilityToCsv(twCap, true); case TWON.RANGE:
return csv.Split(',').Skip(4).ToList(); return ValueReader.ReadRange<TValue>(_twain, twCap).values.ToList();
} }
} }
return new string[0]; return new TValue[0];
} }
/// <summary> /// <summary>
/// Try to get string representation of the cap's current value. /// Try to get the cap's current value.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public string GetCurrent() public TValue GetCurrent()
{ {
var twCap = new TW_CAPABILITY var twCap = new TW_CAPABILITY
{ {
@@ -89,23 +88,21 @@ namespace NTwain
switch (twCap.ConType) switch (twCap.ConType)
{ {
case TWON.ONEVALUE: case TWON.ONEVALUE:
return _twain.CapabilityOneValueToString(twCap); return ValueReader.ReadOneValue<TValue>(_twain, twCap);
case TWON.ENUMERATION: case TWON.ENUMERATION:
var csv = _twain.CapabilityToCsv(twCap, true); return ValueReader.ReadEnumeration<TValue>(_twain, twCap)[0];
return csv.Split(new[] { ',' }, 7)[6]; case TWON.RANGE:
default: return ValueReader.ReadRange<TValue>(_twain, twCap).currentVal;
csv = _twain.CapabilityToCsv(twCap, true);
return csv.Split(new[] { ',' }, 5)[4];
} }
} }
return null; return default(TValue);
} }
/// <summary> /// <summary>
/// Try to get string representation of the cap's default value. /// Try to get the cap's default value.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public string GetDefault() public TValue GetDefault()
{ {
var twCap = new TW_CAPABILITY var twCap = new TW_CAPABILITY
{ {
@@ -118,16 +115,30 @@ namespace NTwain
switch (twCap.ConType) switch (twCap.ConType)
{ {
case TWON.ONEVALUE: case TWON.ONEVALUE:
return _twain.CapabilityOneValueToString(twCap); return ValueReader.ReadOneValue<TValue>(_twain, twCap);
case TWON.ENUMERATION: case TWON.ENUMERATION:
var csv = _twain.CapabilityToCsv(twCap, true); return ValueReader.ReadEnumeration<TValue>(_twain, twCap)[0];
return csv.Split(new[] { ',' }, 7)[6]; case TWON.RANGE:
default: return ValueReader.ReadRange<TValue>(_twain, twCap).defaultVal;
csv = _twain.CapabilityToCsv(twCap, true);
return csv.Split(new[] { ',' }, 5)[4];
} }
} }
return null; return default(TValue);
}
/// <summary>
/// Resets the cap's current value to power-on default.
/// </summary>
/// <returns></returns>
public STS Reset()
{
var twCap = new TW_CAPABILITY
{
Cap = Cap
};
var sts = _twain.DatCapability(DG.CONTROL, MSG.RESET, ref twCap);
return sts;
} }
} }
} }

39
NTwain/Capabilities.cs Normal file
View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TWAINWorkingGroup;
namespace NTwain
{
/// <summary>
/// Contains known capabilities for TWAIN 2.4.
/// </summary>
public class Capabilities
{
private readonly TWAIN _twain;
public Capabilities(TWAIN twain)
{
_twain = twain;
}
/// <summary>
/// Resets all cap values and constraint to power-on defaults.
/// Not all sources will support this.
/// </summary>
/// <returns></returns>
public STS ResetAll()
{
var twCap = new TW_CAPABILITY
{
Cap = CAP.CAP_SUPPORTEDCAPS
};
var sts = _twain.DatCapability(DG.CONTROL, MSG.RESETALL, ref twCap);
return sts;
}
}
}

View File

@@ -31,6 +31,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<!--<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <!--<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

View File

@@ -1228,7 +1228,7 @@ namespace TWAINWorkingGroup
szCvt = t.ToString(); szCvt = t.ToString();
if (szCvt != i32.ToString()) if (szCvt != i32.ToString())
{ {
return (typeof(T).ToString().Replace("TWAINWorkingGroup.TWAIN+", "") + "_" + szCvt); return (typeof(T).Name + "_" + szCvt);
} }
} }
} }
@@ -1285,7 +1285,7 @@ namespace TWAINWorkingGroup
// Everybody else needs the name decoration removed... // Everybody else needs the name decoration removed...
else else
{ {
return (typeof(T).ToString().Replace("TWAINWorkingGroup.TWAIN+", "") + "_" + szCvt); return (typeof(T).Name + "_" + szCvt);
} }
} }

View File

@@ -9,6 +9,21 @@ namespace TWAINWorkingGroup
{ {
// contains my additions that makes twain types easier to work with. // contains my additions that makes twain types easier to work with.
/// <summary>
/// TWAIN's boolean values.
/// </summary>
public enum BoolType : ushort
{
/// <summary>
/// The false value (0).
/// </summary>
False = 0,
/// <summary>
/// The true value (1).
/// </summary>
True = 1
}
partial struct TW_FIX32 : IEquatable<TW_FIX32> partial struct TW_FIX32 : IEquatable<TW_FIX32>
{ {
// the conversion logic is found in the spec. // the conversion logic is found in the spec.

View File

@@ -274,6 +274,7 @@ namespace NTwain
// 4 --> 3 // 4 --> 3
if ((_twain.GetState() == STATE.S4) && (state < STATE.S4)) if ((_twain.GetState() == STATE.S4) && (state < STATE.S4))
{ {
_caps = null;
TW_IDENTITY twidentity = default; TW_IDENTITY twidentity = default;
CsvSerializer.CsvToIdentity(ref twidentity, _twain.GetDsIdentity()); CsvSerializer.CsvToIdentity(ref twidentity, _twain.GetDsIdentity());
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref twidentity); _twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref twidentity);
@@ -286,52 +287,22 @@ namespace NTwain
} }
} }
private Capabilities _caps;
/// <summary> /// <summary>
/// Get current device's capabilities. /// Get current device's capabilities. Will be null if no device is open.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public Dictionary<CAP, CapWrapper> Capabilities() public Capabilities Capabilities
{ {
Dictionary<CAP, CapWrapper> caps = null; get
if (State >= STATE.S4)
{ {
TW_CAPABILITY cap = default; if (State >= STATE.S4)
// 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); return _caps ?? (_caps = new Capabilities(_twain));
caps = csv.Split(',').Skip(4).Select(val =>
{
if (Enum.TryParse(val, out CAP c))
{
return new CapWrapper(_twain, c);
}
else if (val.StartsWith("0x"))
{
return new CapWrapper(_twain, (CAP)Convert.ToUInt16(val, 16));
}
else if (ushort.TryParse(val, out ushort num))
{
return new CapWrapper(_twain, (CAP)num);
}
return null;
})
.Where(cs => cs != null)
.ToDictionary(cs => cs.Cap, cs => cs);
}
else
{
// don't support list, just give everything
caps = Enum.GetValues(typeof(CAP)).Cast<CAP>()
.ToDictionary(c => c, c => new CapWrapper(_twain, c));
} }
return null;
} }
return caps ?? new Dictionary<CAP, CapWrapper>();
} }
/// <summary> /// <summary>

View File

@@ -9,21 +9,13 @@ using TWAINWorkingGroup;
namespace NTwain namespace NTwain
{ {
/// <summary> /// <summary>
/// Contains extension methods for reading pointers into various things. /// Contains methods for reading pointers into various things.
/// </summary> /// </summary>
public static class ValueReader public static class ValueReader
{ {
/// <summary> public static TValue ReadOneValue<TValue>(TWAIN twain, TW_CAPABILITY cap) where TValue : struct
/// Read capability's container content as ToString()'d one value.
/// </summary>
/// <param name="twain">Low-level twain object.</param>
/// <param name="cap">Cap to read from.</param>
/// <returns></returns>
public static string CapabilityOneValueToString(this TWAIN twain, TW_CAPABILITY cap)
{ {
if (cap.ConType != TWON.ONEVALUE) throw new InvalidOperationException($"Cannot read container {cap.ConType} as one value."); if (cap.hContainer == IntPtr.Zero) return default(TValue);
if (cap.hContainer == IntPtr.Zero) return null;
var lockedPtr = twain.DsmMemLock(cap.hContainer); var lockedPtr = twain.DsmMemLock(cap.hContainer);
@@ -34,84 +26,130 @@ namespace NTwain
if (PlatformTools.GetPlatform() == Platform.MACOSX) if (PlatformTools.GetPlatform() == Platform.MACOSX)
{ {
// Crack the container... // Crack the container...
var onevalue = lockedPtr.MarshalTo<TW_ONEVALUE_MACOSX>(); var onevalue = MarshalTo<TW_ONEVALUE_MACOSX>(lockedPtr);
itemType = (TWTY)onevalue.ItemType; itemType = (TWTY)onevalue.ItemType;
lockedPtr += Marshal.SizeOf(onevalue); lockedPtr += Marshal.SizeOf(onevalue);
} }
else else
{ {
// Crack the container... // Crack the container...
var onevalue = lockedPtr.MarshalTo<TW_ONEVALUE>(); var onevalue = MarshalTo<TW_ONEVALUE>(lockedPtr);
itemType = onevalue.ItemType; itemType = onevalue.ItemType;
lockedPtr += Marshal.SizeOf(onevalue); lockedPtr += Marshal.SizeOf(onevalue);
} }
return lockedPtr.ContainerToString(itemType); return ReadContainerData<TValue>(lockedPtr, itemType);
} }
finally finally
{ {
// All done...
twain.DsmMemUnlock(cap.hContainer); twain.DsmMemUnlock(cap.hContainer);
} }
} }
public static IList<TValue> ReadEnumeration<TValue>(TWAIN twain, TW_CAPABILITY twCap) where TValue : struct
{
throw new NotImplementedException();
}
public static IList<TValue> ReadArray<TValue>(TWAIN twain, TW_CAPABILITY twCap) where TValue : struct
{
throw new NotImplementedException();
}
public static (TValue defaultVal, TValue currentVal, IEnumerable<TValue> values) ReadRange<TValue>(TWAIN twain, TW_CAPABILITY twCap) where TValue : struct
{
throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Read the container pointer content as a string. Numeric values are ToString()'d. /// Read the container pointer content.
/// </summary> /// </summary>
/// <param name="intptr">A locked pointer to the container data. If data is array this is the 0th item.</param> /// <param name="intptr">A locked pointer to the container's data pointer. If data is array this is the 0th item.</param>
/// <param name="type">The twain type.</param> /// <param name="type">The twain type.</param>
/// <param name="itemIndex">Index of the item if pointer is array.</param> /// <param name="itemIndex">Index of the item if pointer is array.</param>
/// <returns></returns> /// <returns></returns>
static string ContainerToString(this IntPtr intptr, TWTY type, int itemIndex = 0) static TValue ReadContainerData<TValue>(IntPtr intptr, TWTY type, int itemIndex = 0) where TValue : struct
{ {
var isEnum = typeof(TValue).IsEnum;
switch (type) switch (type)
{ {
default: default:
throw new NotSupportedException($"Unknown item type {type} to read as string."); throw new NotSupportedException($"Unsupported item type {type} for reading.");
case TWTY.INT8: case TWTY.INT8:
intptr += 1 * itemIndex; intptr += 1 * itemIndex;
return intptr.MarshalToString<sbyte>(); if (isEnum)
case TWTY.INT16: {
intptr += 2 * itemIndex; return NumericToEnum<sbyte, TValue>(MarshalTo<sbyte>(intptr));
return intptr.MarshalToString<short>(); }
case TWTY.INT32: return MarshalTo<TValue>(intptr);
intptr += 4 * itemIndex;
return intptr.MarshalToString<int>();
case TWTY.UINT8: case TWTY.UINT8:
intptr += 1 * itemIndex; intptr += 1 * itemIndex;
return intptr.MarshalToString<byte>(); if (isEnum)
{
return NumericToEnum<byte, TValue>(MarshalTo<byte>(intptr));
}
return MarshalTo<TValue>(intptr);
case TWTY.INT16:
intptr += 2 * itemIndex;
if (isEnum)
{
return NumericToEnum<short, TValue>(MarshalTo<short>(intptr));
}
return MarshalTo<TValue>(intptr);
case TWTY.BOOL: case TWTY.BOOL:
case TWTY.UINT16: case TWTY.UINT16:
intptr += 2 * itemIndex; intptr += 2 * itemIndex;
return intptr.MarshalToString<ushort>(); if (isEnum)
{
return NumericToEnum<ushort, TValue>(MarshalTo<ushort>(intptr));
}
return MarshalTo<TValue>(intptr);
case TWTY.INT32:
intptr += 4 * itemIndex;
if (isEnum)
{
return NumericToEnum<int, TValue>(MarshalTo<int>(intptr));
}
return MarshalTo<TValue>(intptr);
case TWTY.UINT32: case TWTY.UINT32:
intptr += 4 * itemIndex; intptr += 4 * itemIndex;
return intptr.MarshalToString<uint>(); if (isEnum)
{
return NumericToEnum<uint, TValue>(MarshalTo<uint>(intptr));
}
return MarshalTo<TValue>(intptr);
case TWTY.FIX32: case TWTY.FIX32:
intptr += 4 * itemIndex; intptr += 4 * itemIndex;
return intptr.MarshalToString<TW_FIX32>(); return MarshalTo<TValue>(intptr);
case TWTY.FRAME: case TWTY.FRAME:
intptr += 16 * itemIndex; intptr += 16 * itemIndex;
return intptr.MarshalToString<TW_FRAME>(); return MarshalTo<TValue>(intptr);
case TWTY.STR32: case TWTY.STR32:
intptr += TW_STR32.Size * itemIndex; intptr += TW_STR32.Size * itemIndex;
return intptr.MarshalToString<TW_STR32>(); return MarshalTo<TValue>(intptr);
case TWTY.STR64: case TWTY.STR64:
intptr += TW_STR64.Size * itemIndex; intptr += TW_STR64.Size * itemIndex;
return intptr.MarshalToString<TW_STR64>(); return MarshalTo<TValue>(intptr);
case TWTY.STR128: case TWTY.STR128:
intptr += TW_STR128.Size * itemIndex; intptr += TW_STR128.Size * itemIndex;
return intptr.MarshalToString<TW_STR128>(); return MarshalTo<TValue>(intptr);
case TWTY.STR255: case TWTY.STR255:
intptr += TW_STR255.Size * itemIndex; intptr += TW_STR255.Size * itemIndex;
return intptr.MarshalToString<TW_STR255>(); return MarshalTo<TValue>(intptr);
case TWTY.HANDLE:
intptr += IntPtr.Size * itemIndex;
return Marshal.ReadIntPtr(intptr).ToString();
} }
} }
static string MarshalToString<T>(this IntPtr ptr) => Marshal.PtrToStructure(ptr, typeof(T)).ToString(); static TEnum NumericToEnum<TNumber, TEnum>(TNumber num) where TEnum : struct
static T MarshalTo<T>(this IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); {
// some caps returns a data type that's not the underlying datatype for the enum
// so best way is to ToString() it and parse it as the enum type.
var str = num.ToString();
if (Enum.TryParse(str, out TEnum parsed))
{
return parsed;
}
return default(TEnum);
}
static T MarshalTo<T>(IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T));
} }
} }

View File

@@ -74,26 +74,26 @@ namespace Net5Console
Console.WriteLine($"\t{session.CurrentDevice}"); Console.WriteLine($"\t{session.CurrentDevice}");
Console.WriteLine(); Console.WriteLine();
var caps = session.Capabilities(); var caps = session.Capabilities;
Console.WriteLine("Device supports these caps:"); Console.WriteLine("Device supports these caps:");
foreach (var cap in caps.Keys.OrderBy(c => c)) //foreach (var cap in caps.Keys.OrderBy(c => c))
{ //{
Console.WriteLine($"\t{cap}: {caps[cap].Supports}"); // Console.WriteLine($"\t{cap}: {caps[cap].Supports}");
} //}
Console.WriteLine(); Console.WriteLine();
if (caps.TryGetValue(CAP.ICAP_BITDEPTH, out CapWrapper wrapper)) //if (caps.TryGetValue(CAP.ICAP_PIXELTYPE, out CapWrapper wrapper))
{ //{
Console.WriteLine($"Details on {wrapper.Cap}:"); // Console.WriteLine($"Details on {wrapper.Cap}:");
Console.WriteLine($"\tDefault: {wrapper.GetDefault()}"); // Console.WriteLine($"\tDefault: {wrapper.GetDefault()}");
Console.WriteLine($"\tCurrent: {wrapper.GetCurrent()}"); // Console.WriteLine($"\tCurrent: {wrapper.GetCurrent()}");
Console.WriteLine($"\tValues:"); // Console.WriteLine($"\tValues:");
foreach (var val in wrapper.GetValues()) // foreach (var val in wrapper.GetValues())
{ // {
Console.WriteLine($"\t\t{val}"); // Console.WriteLine($"\t\t{val}");
} // }
} //}
Console.WriteLine(); //Console.WriteLine();
var sts = session.StartCapture(false); var sts = session.StartCapture(false);
if (sts == STS.SUCCESS) if (sts == STS.SUCCESS)