mirror of
https://github.com/soukoku/ntwain.git
synced 2026-01-09 11:21:06 +08:00
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<!--change these in each release-->
|
||||
<VersionPrefix>4.0.0.0</VersionPrefix>
|
||||
<VersionSuffix>alpha.18</VersionSuffix>
|
||||
<VersionSuffix>alpha.22</VersionSuffix>
|
||||
|
||||
<!--keep it the same until major # changes-->
|
||||
<AssemblyVersion>4.0.0.0</AssemblyVersion>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using NTwain.Data;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NTwain.Caps
|
||||
namespace NTwain.Caps;
|
||||
|
||||
public class CapWriter<TValue> : CapReader<TValue> where TValue : struct
|
||||
{
|
||||
public class CapWriter<TValue> : CapReader<TValue> where TValue : struct
|
||||
{
|
||||
public CapWriter(TwainAppSession twain, CAP cap, float introducedVersion = 1)
|
||||
: base(twain, cap, introducedVersion)
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS Set(TValue value)
|
||||
{
|
||||
return LastSTS = _twain.SetCap(Cap, value);
|
||||
return LastSTS = _twain.SetCap(Cap, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -27,8 +27,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS Set(IList<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateArrayCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetCap(ref twcap);
|
||||
return LastSTS = _twain.SetCap(Cap, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -38,8 +37,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS Set(Enumeration<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateEnumCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetCap(ref twcap);
|
||||
return LastSTS = _twain.SetCap(Cap, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,8 +47,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS Set(Range<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateRangeCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetCap(ref twcap);
|
||||
return LastSTS = _twain.SetCap(Cap, values);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,8 +58,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint(TValue value)
|
||||
{
|
||||
var twcap = ValueWriter.CreateOneValueCap(Cap, _twain, value);
|
||||
return LastSTS = _twain.SetConstraint(ref twcap);
|
||||
return LastSTS = _twain.SetConstraint(Cap, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,8 +68,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint(IList<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateArrayCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetConstraint(ref twcap);
|
||||
return LastSTS = _twain.SetConstraint(Cap, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,8 +78,7 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint(Enumeration<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateEnumCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetConstraint(ref twcap);
|
||||
return LastSTS = _twain.SetConstraint(Cap, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,19 +88,17 @@ namespace NTwain.Caps
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint(Range<TValue> values)
|
||||
{
|
||||
var twcap = ValueWriter.CreateRangeCap(Cap, _twain, values);
|
||||
return LastSTS = _twain.SetConstraint(ref twcap);
|
||||
return LastSTS = _twain.SetConstraint(Cap, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets this cap to power-on default.
|
||||
/// </summary>
|
||||
/// <param name="value">The current value after reset.</param>
|
||||
/// <returns></returns>
|
||||
public STS Reset()
|
||||
public STS Reset(out List<TValue> value)
|
||||
{
|
||||
LastSTS = _twain.ResetCap(Cap, out TW_CAPABILITY twcap);
|
||||
twcap.Free(_twain);
|
||||
return LastSTS;
|
||||
LastSTS = _twain.ResetCap(Cap, out value);
|
||||
return LastSTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
using NTwain.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NTwain.Caps;
|
||||
|
||||
public record ValueContainer<TValue> where TValue : struct
|
||||
{
|
||||
public TWON ContainerType { get; set; }
|
||||
|
||||
public TValue? OneValue { get; set; }
|
||||
|
||||
public IList<TValue>? ArrayValue { get; set; }
|
||||
|
||||
public EnumValue<TValue>? EnumValue { get; set; }
|
||||
|
||||
public RangeValue<TValue>? RangeValue { get; set; }
|
||||
|
||||
public IEnumerable<TValue> GetValues()
|
||||
{
|
||||
return ContainerType switch
|
||||
{
|
||||
TWON.ONEVALUE => OneValue.HasValue ? ToEnumerable(OneValue.Value) : [],
|
||||
TWON.ARRAY => ArrayValue ?? [],
|
||||
TWON.ENUMERATION => EnumValue?.Items ?? [],
|
||||
TWON.RANGE => RangeValue != null ? GenerateRangeValues(RangeValue) : [],
|
||||
_ => [],
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<TValue> ToEnumerable(TValue value)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
|
||||
private IEnumerable<TValue> GenerateRangeValues(RangeValue<TValue> range)
|
||||
{
|
||||
var de = new DynamicEnumerator<TValue>(range.Min, range.Max, range.Step);
|
||||
while (de.MoveNext())
|
||||
{
|
||||
yield return de.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record EnumValue<TValue> where TValue : struct
|
||||
{
|
||||
public TValue[] Items { get; set; } = [];
|
||||
|
||||
public int CurrentIndex { get; set; }
|
||||
|
||||
public int DefaultIndex { get; set; }
|
||||
}
|
||||
|
||||
public record RangeValue<TValue> where TValue : struct
|
||||
{
|
||||
public TValue Min { get; set; }
|
||||
|
||||
public TValue Max { get; set; }
|
||||
|
||||
public TValue Step { get; set; }
|
||||
|
||||
public TValue DefaultValue;
|
||||
|
||||
public TValue CurrentValue;
|
||||
}
|
||||
47
src/NTwain/CompilerTypes.cs
Normal file
47
src/NTwain/CompilerTypes.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
#if !NET5_0_OR_GREATER
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class IsExternalInit {}
|
||||
|
||||
#endif // !NET5_0_OR_GREATER
|
||||
|
||||
#if !NET7_0_OR_GREATER
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class RequiredMemberAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
|
||||
internal sealed class CompilerFeatureRequiredAttribute : Attribute
|
||||
{
|
||||
public CompilerFeatureRequiredAttribute(string featureName)
|
||||
{
|
||||
FeatureName = featureName;
|
||||
}
|
||||
|
||||
public string FeatureName { get; }
|
||||
public bool IsOptional { get; init; }
|
||||
|
||||
public const string RefStructs = nameof(RefStructs);
|
||||
public const string RequiredMembers = nameof(RequiredMembers);
|
||||
}
|
||||
|
||||
#endif // !NET7_0_OR_GREATER
|
||||
}
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
#if !NET7_0_OR_GREATER
|
||||
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class SetsRequiredMembersAttribute : Attribute {}
|
||||
#endif
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@@ -258,38 +260,80 @@ namespace NTwain.Data
|
||||
/// A more dotnet-friendly representation of <see cref="TW_ENUMERATION"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class Enumeration<TValue> where TValue : struct
|
||||
public record Enumeration<TValue>
|
||||
{
|
||||
public int CurrentIndex;
|
||||
public int CurrentIndex { get; set; }
|
||||
|
||||
public int DefaultIndex;
|
||||
public int DefaultIndex { get; set; }
|
||||
|
||||
public TValue[]? Items;
|
||||
public TValue[] Items { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A more dotnet-friendly representation of <see cref="TW_RANGE"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public partial class Range<TValue> : IEnumerable<TValue> where TValue : struct
|
||||
public partial record Range<TValue>
|
||||
{
|
||||
public TValue MinValue;
|
||||
public TValue MaxValue;
|
||||
public TValue StepSize;
|
||||
public TValue DefaultValue;
|
||||
public TValue CurrentValue;
|
||||
public required TValue MinValue { get; set; }
|
||||
public required TValue MaxValue { get; set; }
|
||||
public required TValue StepSize { get; set; }
|
||||
public required TValue DefaultValue { get; set; }
|
||||
public required TValue CurrentValue { get; set; }
|
||||
|
||||
IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
|
||||
/// <summary>
|
||||
/// Tries to enumerate the range values.
|
||||
/// This could be expensive depending on the range size.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<TValue> Enumerate()
|
||||
{
|
||||
if (MinValue is not IConvertible)
|
||||
throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported for range enumeration.");
|
||||
|
||||
return new DynamicEnumerator<TValue>(MinValue, MaxValue, StepSize);
|
||||
var dynamicType = typeof(DynamicEnumerator<>);
|
||||
var genericType = dynamicType.MakeGenericType(typeof(TValue));
|
||||
|
||||
var de = (IEnumerator<TValue>)Activator.CreateInstance(genericType, MinValue, MaxValue, StepSize)!;
|
||||
while (de.MoveNext())
|
||||
{
|
||||
yield return de.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A more dotnet-friendly container of CAP value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public record ValueContainer<TValue>
|
||||
{
|
||||
public TWON ContainerType { get; set; }
|
||||
|
||||
public TValue? OneValue { get; set; }
|
||||
|
||||
public IList<TValue>? ArrayValue { get; set; }
|
||||
|
||||
public Enumeration<TValue>? EnumValue { get; set; }
|
||||
|
||||
public Range<TValue>? RangeValue { get; set; }
|
||||
|
||||
public IEnumerable<TValue> GetValues()
|
||||
{
|
||||
return ContainerType switch
|
||||
{
|
||||
TWON.ONEVALUE => ToEnumerable(OneValue),
|
||||
TWON.ARRAY => ArrayValue ?? [],
|
||||
TWON.ENUMERATION => EnumValue?.Items ?? [],
|
||||
TWON.RANGE => RangeValue != null ? RangeValue.Enumerate() : [],
|
||||
_ => [],
|
||||
};
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
private IEnumerable<TValue> ToEnumerable(TValue? value)
|
||||
{
|
||||
return ((IEnumerable<TValue>)this).GetEnumerator();
|
||||
if (value == null) yield break;
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,51 @@ namespace NTwain.Data
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a boxed one value out of a cap. This can only be done once if memory is freed.
|
||||
/// </summary>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="memMgr"></param>
|
||||
/// <param name="freeMemory"></param>
|
||||
/// <returns></returns>
|
||||
public static object? ReadOneValueBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true)
|
||||
{
|
||||
if (cap.ConType != TWON.ONEVALUE || cap.hContainer == IntPtr.Zero) return default;
|
||||
|
||||
var lockedPtr = memMgr.Lock(cap.hContainer);
|
||||
|
||||
try
|
||||
{
|
||||
TWTY itemType;
|
||||
// Mac has a level of indirection and a different structure (ick)...
|
||||
if (TWPlatform.IsMacOSX)
|
||||
{
|
||||
// Crack the container...
|
||||
var onevalue = MarshalTo<TW_ONEVALUE_MACOSX>(lockedPtr);
|
||||
itemType = (TWTY)onevalue.ItemType;
|
||||
lockedPtr += Marshal.SizeOf(onevalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Crack the container...
|
||||
var onevalue = MarshalTo<TW_ONEVALUE>(lockedPtr);
|
||||
itemType = onevalue.ItemType;
|
||||
lockedPtr += Marshal.SizeOf(onevalue);
|
||||
}
|
||||
|
||||
return ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
|
||||
if (freeMemory)
|
||||
{
|
||||
memMgr.Free(cap.hContainer);
|
||||
cap.hContainer = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a one value out of a cap. This can only be done once if memory is freed.
|
||||
/// </summary>
|
||||
@@ -130,6 +175,78 @@ namespace NTwain.Data
|
||||
}
|
||||
}
|
||||
|
||||
public static Enumeration<object> ReadEnumerationBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true)
|
||||
{
|
||||
Enumeration<object> retVal = new();
|
||||
|
||||
if (cap.ConType != TWON.ENUMERATION || cap.hContainer == IntPtr.Zero) return retVal;
|
||||
|
||||
var lockedPtr = memMgr.Lock(cap.hContainer);
|
||||
|
||||
try
|
||||
{
|
||||
TWTY itemType;
|
||||
int count = 0;
|
||||
|
||||
// Mac has a level of indirection and a different structure (ick)...
|
||||
if (TWPlatform.IsMacOSX)
|
||||
{
|
||||
// Crack the container...
|
||||
var twenumerationmacosx = MarshalTo<TW_ENUMERATION_MACOSX>(lockedPtr);
|
||||
itemType = (TWTY)twenumerationmacosx.ItemType;
|
||||
count = (int)twenumerationmacosx.NumItems;
|
||||
retVal.DefaultIndex = (int)twenumerationmacosx.DefaultIndex;
|
||||
retVal.CurrentIndex = (int)twenumerationmacosx.CurrentIndex;
|
||||
lockedPtr += Marshal.SizeOf(twenumerationmacosx);
|
||||
}
|
||||
// Windows or the 2.4+ Linux DSM...
|
||||
else
|
||||
{
|
||||
// Crack the container...
|
||||
var twenumeration = MarshalTo<TW_ENUMERATION>(lockedPtr);
|
||||
itemType = twenumeration.ItemType;
|
||||
count = (int)twenumeration.NumItems;
|
||||
retVal.DefaultIndex = (int)twenumeration.DefaultIndex;
|
||||
retVal.CurrentIndex = (int)twenumeration.CurrentIndex;
|
||||
lockedPtr += Marshal.SizeOf(twenumeration);
|
||||
}
|
||||
// The -2.3 Linux DSM...
|
||||
//else if (twain.m_blFound020302Dsm64bit && (twain.m_linuxdsm == TWAIN.LinuxDsm.Is020302Dsm64bit))
|
||||
//{
|
||||
// // Crack the container...
|
||||
// var twenumerationlinux64 = MarshalTo<TW_ENUMERATION_LINUX64>(lockedPtr);
|
||||
// itemType = twenumerationlinux64.ItemType;
|
||||
// count = (int)twenumerationlinux64.NumItems;
|
||||
// retVal.DefaultIndex = (int)twenumerationlinux64.DefaultIndex;
|
||||
// retVal.CurrentIndex = (int)twenumerationlinux64.CurrentIndex;
|
||||
// lockedPtr += Marshal.SizeOf(twenumerationlinux64);
|
||||
//}
|
||||
// This shouldn't be possible, but what the hey...
|
||||
//else
|
||||
//{
|
||||
// Log.Error("This is serious, you win a cookie for getting here...");
|
||||
// return retVal;
|
||||
//}
|
||||
|
||||
retVal.Items = new object[count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
retVal.Items[i] = ReadTWTYDataBoxed(lockedPtr, itemType, i);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
|
||||
if (freeMemory)
|
||||
{
|
||||
memMgr.Free(cap.hContainer);
|
||||
cap.hContainer = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static Enumeration<TValue> ReadEnumeration<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
|
||||
{
|
||||
Enumeration<TValue> retVal = new();
|
||||
@@ -202,6 +319,53 @@ namespace NTwain.Data
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static IList<object> ReadArrayBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true)
|
||||
{
|
||||
if (cap.ConType != TWON.ARRAY || cap.hContainer == IntPtr.Zero) return Array.Empty<object>();
|
||||
|
||||
var lockedPtr = memMgr.Lock(cap.hContainer);
|
||||
|
||||
try
|
||||
{
|
||||
TWTY itemType;
|
||||
uint count;
|
||||
|
||||
// Mac has a level of indirection and a different structure (ick)...
|
||||
if (TWPlatform.IsMacOSX)
|
||||
{
|
||||
// Crack the container...
|
||||
var twarraymacosx = MarshalTo<TW_ARRAY_MACOSX>(lockedPtr);
|
||||
itemType = (TWTY)twarraymacosx.ItemType;
|
||||
count = twarraymacosx.NumItems;
|
||||
lockedPtr += Marshal.SizeOf(twarraymacosx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Crack the container...
|
||||
var twarray = MarshalTo<TW_ARRAY>(lockedPtr);
|
||||
itemType = twarray.ItemType;
|
||||
count = twarray.NumItems;
|
||||
lockedPtr += Marshal.SizeOf(twarray);
|
||||
}
|
||||
|
||||
var arr = new object[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
arr[i] = ReadTWTYDataBoxed(lockedPtr, itemType, i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
|
||||
if (freeMemory)
|
||||
{
|
||||
memMgr.Free(cap.hContainer);
|
||||
cap.hContainer = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IList<TValue> ReadArray<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
|
||||
{
|
||||
if (cap.ConType != TWON.ARRAY || cap.hContainer == IntPtr.Zero) return Array.Empty<TValue>();
|
||||
@@ -249,11 +413,9 @@ namespace NTwain.Data
|
||||
}
|
||||
}
|
||||
|
||||
public static Range<TValue> ReadRange<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
|
||||
public static Range<object>? ReadRangeBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true)
|
||||
{
|
||||
var retVal = new Range<TValue>();
|
||||
|
||||
if (cap.ConType != TWON.RANGE || cap.hContainer == IntPtr.Zero) return retVal;
|
||||
if (cap.ConType != TWON.RANGE || cap.hContainer == IntPtr.Zero) return null;
|
||||
|
||||
var lockedPtr = memMgr.Lock(cap.hContainer);
|
||||
|
||||
@@ -272,17 +434,75 @@ namespace NTwain.Data
|
||||
itemType = (TWTY)Marshal.ReadInt16(lockedPtr);
|
||||
lockedPtr += 2;
|
||||
}
|
||||
retVal.MinValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
var minValue = ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
retVal.MaxValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
var maxValue = ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
retVal.StepSize = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
var stepSize = ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
retVal.CurrentValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
var currentValue = ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
retVal.DefaultValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
var defaultValue = ReadTWTYDataBoxed(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
return retVal;
|
||||
return new Range<object>
|
||||
{
|
||||
MinValue = minValue,
|
||||
MaxValue = maxValue,
|
||||
StepSize = stepSize,
|
||||
CurrentValue = currentValue,
|
||||
DefaultValue = defaultValue
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
|
||||
if (freeMemory)
|
||||
{
|
||||
memMgr.Free(cap.hContainer);
|
||||
cap.hContainer = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Range<TValue>? ReadRange<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
|
||||
{
|
||||
if (cap.ConType != TWON.RANGE || cap.hContainer == IntPtr.Zero) return null;
|
||||
|
||||
var lockedPtr = memMgr.Lock(cap.hContainer);
|
||||
|
||||
try
|
||||
{
|
||||
TWTY itemType;
|
||||
// Mac has a level of indirection and a different structure (ick)...
|
||||
if (TWPlatform.IsMacOSX)
|
||||
{
|
||||
itemType = (TWTY)Marshal.ReadInt32(lockedPtr);
|
||||
lockedPtr += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Windows or the 2.4+ Linux DSM...
|
||||
itemType = (TWTY)Marshal.ReadInt16(lockedPtr);
|
||||
lockedPtr += 2;
|
||||
}
|
||||
var minValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
var maxValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
var stepSize = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
var currentValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
var defaultValue = ReadTWTYData<TValue>(lockedPtr, itemType, 0);
|
||||
lockedPtr += 4;
|
||||
return new Range<TValue>
|
||||
{
|
||||
MinValue = minValue,
|
||||
MaxValue = maxValue,
|
||||
StepSize = stepSize,
|
||||
CurrentValue = currentValue,
|
||||
DefaultValue = defaultValue
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -373,6 +593,64 @@ namespace NTwain.Data
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read the pointer content as a boxed value specified by <see cref="TWTY"/>, except <see cref="TWTY.HANDLE"/>.
|
||||
/// </summary>
|
||||
/// <param name="intptr">A locked pointer to the data pointer. If data is array this is the 0th item.</param>
|
||||
/// <param name="type">The twain type.</param>
|
||||
/// <param name="itemIndex">Index of the item if pointer is array.</param>
|
||||
/// <returns></returns>
|
||||
public static object ReadTWTYDataBoxed(this IntPtr intptr, TWTY type, int itemIndex)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported item type {type} for reading.");
|
||||
// TODO: verify if needs to read int32 for small types
|
||||
case TWTY.HANDLE:
|
||||
intptr += IntPtr.Size * itemIndex;
|
||||
return MarshalTo<IntPtr>(intptr);
|
||||
case TWTY.INT8:
|
||||
intptr += 1 * itemIndex;
|
||||
return MarshalTo<sbyte>(intptr);
|
||||
case TWTY.UINT8:
|
||||
intptr += 1 * itemIndex;
|
||||
return MarshalTo<byte>(intptr);
|
||||
case TWTY.INT16:
|
||||
intptr += 2 * itemIndex;
|
||||
return MarshalTo<short>(intptr);
|
||||
case TWTY.BOOL:
|
||||
case TWTY.UINT16:
|
||||
intptr += 2 * itemIndex;
|
||||
return MarshalTo<ushort>(intptr);
|
||||
case TWTY.INT32:
|
||||
intptr += 4 * itemIndex;
|
||||
return MarshalTo<int>(intptr);
|
||||
case TWTY.UINT32:
|
||||
intptr += 4 * itemIndex;
|
||||
return MarshalTo<uint>(intptr);
|
||||
case TWTY.FIX32:
|
||||
intptr += 4 * itemIndex;
|
||||
return MarshalTo<TW_FIX32>(intptr);
|
||||
case TWTY.FRAME:
|
||||
intptr += 16 * itemIndex;
|
||||
return MarshalTo<TW_FRAME>(intptr);
|
||||
case TWTY.STR32:
|
||||
intptr += TW_STR32.Size * itemIndex;
|
||||
return MarshalTo<TW_STR32>(intptr);
|
||||
case TWTY.STR64:
|
||||
intptr += TW_STR64.Size * itemIndex;
|
||||
return MarshalTo<TW_STR64>(intptr);
|
||||
case TWTY.STR128:
|
||||
intptr += TW_STR128.Size * itemIndex;
|
||||
return MarshalTo<TW_STR128>(intptr);
|
||||
case TWTY.STR255:
|
||||
intptr += TW_STR255.Size * itemIndex;
|
||||
return MarshalTo<TW_STR255>(intptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the pointer content as a value specified by <see cref="TWTY"/>, except <see cref="TWTY.HANDLE"/>.
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,123 +8,134 @@ using System.Windows.Forms;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
/// <summary>
|
||||
/// For use under Windows to host a message pump.
|
||||
/// </summary>
|
||||
class MessagePumpThread
|
||||
{
|
||||
DummyForm? _dummyForm;
|
||||
TwainAppSession? _twain;
|
||||
|
||||
public bool IsRunning => _dummyForm != null && _dummyForm.IsHandleCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Starts the thread, attaches a twain session to it,
|
||||
/// and opens the DSM.
|
||||
/// For use under Windows to host a message pump.
|
||||
/// </summary>
|
||||
/// <param name="twain"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public async Task<STS> AttachAsync(TwainAppSession twain)
|
||||
class MessagePumpThread
|
||||
{
|
||||
if (twain.State > STATE.S2) throw new InvalidOperationException("Cannot attach to an opened TWAIN session.");
|
||||
if (_twain != null || _dummyForm != null) throw new InvalidOperationException("Already attached previously.");
|
||||
DummyForm? _dummyForm;
|
||||
TwainAppSession? _twain;
|
||||
|
||||
Thread t = new(RunMessagePump);
|
||||
t.IsBackground = true;
|
||||
t.SetApartmentState(ApartmentState.STA);
|
||||
t.Start();
|
||||
public bool IsRunning => _dummyForm != null && _dummyForm.IsHandleCreated;
|
||||
|
||||
while (_dummyForm == null || !_dummyForm.IsHandleCreated)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
}
|
||||
|
||||
STS sts = default;
|
||||
TaskCompletionSource<bool> tcs = new();
|
||||
_dummyForm.BeginInvoke(() =>
|
||||
{
|
||||
try
|
||||
/// <summary>
|
||||
/// Starts the thread, attaches a twain session to it,
|
||||
/// and opens the DSM.
|
||||
/// </summary>
|
||||
/// <param name="twain"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public async Task<STS> AttachAsync(TwainAppSession twain)
|
||||
{
|
||||
sts = twain.OpenDSM(_dummyForm.Handle, SynchronizationContext.Current!);
|
||||
if (sts.IsSuccess)
|
||||
{
|
||||
twain.AddWinformFilter();
|
||||
_twain = twain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dummyForm.Close();
|
||||
}
|
||||
if (twain.State > STATE.S2) throw new InvalidOperationException("Cannot attach to an opened TWAIN session.");
|
||||
if (_twain != null || _dummyForm != null) throw new InvalidOperationException("Already attached previously.");
|
||||
|
||||
Thread t = new(RunMessagePump);
|
||||
t.IsBackground = true;
|
||||
t.SetApartmentState(ApartmentState.STA);
|
||||
t.Start();
|
||||
|
||||
while (_dummyForm == null || !_dummyForm.IsHandleCreated)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
}
|
||||
|
||||
STS sts = default;
|
||||
TaskCompletionSource<bool> tcs = new();
|
||||
_dummyForm.BeginInvoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
sts = twain.OpenDSM(_dummyForm.Handle, SynchronizationContext.Current!);
|
||||
if (sts.IsSuccess)
|
||||
{
|
||||
twain.AddWinformFilter();
|
||||
_twain = twain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dummyForm.Close();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
});
|
||||
await tcs.Task;
|
||||
return sts;
|
||||
}
|
||||
finally
|
||||
|
||||
/// <summary>
|
||||
/// Detatches a previously attached session and stops the thread.
|
||||
/// </summary>
|
||||
public async Task<STS> DetachAsync()
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
STS sts = default;
|
||||
if (_dummyForm != null && _twain != null)
|
||||
{
|
||||
TaskCompletionSource<STS> tcs = new();
|
||||
_dummyForm.BeginInvoke(() =>
|
||||
{
|
||||
sts = _twain.CloseDSMReal();
|
||||
if (sts.IsSuccess)
|
||||
{
|
||||
_twain.RemoveWinformFilter();
|
||||
_dummyForm.Close();
|
||||
_twain = null;
|
||||
}
|
||||
tcs.SetResult(sts);
|
||||
});
|
||||
await tcs.Task;
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
});
|
||||
await tcs.Task;
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detatches a previously attached session and stops the thread.
|
||||
/// </summary>
|
||||
public async Task<STS> DetachAsync()
|
||||
{
|
||||
STS sts = default;
|
||||
if (_dummyForm != null && _twain != null)
|
||||
{
|
||||
TaskCompletionSource<STS> tcs = new();
|
||||
_dummyForm.BeginInvoke(() =>
|
||||
public void BringWindowToFront()
|
||||
{
|
||||
sts = _twain.CloseDSMReal();
|
||||
if (sts.IsSuccess)
|
||||
{
|
||||
_twain.RemoveWinformFilter();
|
||||
_dummyForm.Close();
|
||||
_twain = null;
|
||||
}
|
||||
tcs.SetResult(sts);
|
||||
});
|
||||
await tcs.Task;
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
if (_dummyForm != null)
|
||||
{
|
||||
_dummyForm.BeginInvoke(_dummyForm.BringToFront);
|
||||
}
|
||||
}
|
||||
|
||||
public void BringWindowToFront()
|
||||
{
|
||||
if (_dummyForm != null)
|
||||
{
|
||||
_dummyForm.BeginInvoke(_dummyForm.BringToFront);
|
||||
}
|
||||
}
|
||||
void RunMessagePump()
|
||||
{
|
||||
Debug.WriteLine("TWAIN pump thread starting");
|
||||
_dummyForm = new DummyForm();
|
||||
_dummyForm.FormClosed += (s, e) =>
|
||||
{
|
||||
_dummyForm = null;
|
||||
};
|
||||
Application.Run(_dummyForm);
|
||||
Debug.WriteLine("TWAIN pump thread ended");
|
||||
}
|
||||
|
||||
void RunMessagePump()
|
||||
{
|
||||
Debug.WriteLine("TWAIN pump thread starting");
|
||||
_dummyForm = new DummyForm();
|
||||
_dummyForm.FormClosed += (s, e) =>
|
||||
{
|
||||
_dummyForm = null;
|
||||
};
|
||||
Application.Run(_dummyForm);
|
||||
Debug.WriteLine("TWAIN pump thread ended");
|
||||
}
|
||||
class DummyForm : Form
|
||||
{
|
||||
public DummyForm()
|
||||
{
|
||||
ShowInTaskbar = false;
|
||||
WindowState = FormWindowState.Minimized;
|
||||
Text = "NTwain Internal Loop";
|
||||
}
|
||||
|
||||
class DummyForm : Form
|
||||
{
|
||||
public DummyForm()
|
||||
{
|
||||
ShowInTaskbar = false;
|
||||
WindowState = FormWindowState.Minimized;
|
||||
}
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ExStyle |= 0x80; // WS_EX_TOOLWINDOW
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnShown(EventArgs e)
|
||||
{
|
||||
BringToFront();
|
||||
base.OnShown(e);
|
||||
}
|
||||
protected override void OnShown(EventArgs e)
|
||||
{
|
||||
BringToFront();
|
||||
base.OnShown(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -71,7 +71,7 @@ namespace NTwain
|
||||
/// <returns></returns>
|
||||
public STS GetCapCurrent<TValue>(CAP cap, out List<TValue> value) where TValue : struct
|
||||
{
|
||||
value = new List<TValue>();
|
||||
value = [];
|
||||
var sts = GetCapCurrent(cap, out TW_CAPABILITY twcap);
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
@@ -88,7 +88,8 @@ namespace NTwain
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
value.Add(twcap.ReadRange<TValue>(this).CurrentValue);
|
||||
var range = twcap.ReadRange<TValue>(this);
|
||||
if (range != null) value.Add(range.CurrentValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArray<TValue>(this);
|
||||
@@ -101,6 +102,47 @@ namespace NTwain
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's current value as boxed values. This is a simplified version that doesn't require
|
||||
/// manual reading, but may or may not work.
|
||||
/// </summary>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS GetCapCurrentBoxed(CAP cap, out List<object> value)
|
||||
{
|
||||
value = [];
|
||||
var sts = GetCapCurrent(cap, out TW_CAPABILITY twcap);
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
switch (twcap.ConType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
var read = twcap.ReadOneValueBoxed(this);
|
||||
if (read != null) value.Add(read);
|
||||
break;
|
||||
case TWON.ENUMERATION:
|
||||
var twenum = twcap.ReadEnumerationBoxed(this);
|
||||
if (twenum.Items != null && twenum.CurrentIndex < twenum.Items.Length)
|
||||
{
|
||||
value.Add(twenum.Items[twenum.CurrentIndex]);
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
var range = twcap.ReadRangeBoxed(this);
|
||||
if (range != null) value.Add(range.CurrentValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArrayBoxed(this);
|
||||
if (twarr != null && twarr.Count > 0) value.AddRange(twarr);
|
||||
break;
|
||||
default:
|
||||
twcap.Free(this); break;
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's raw default value.
|
||||
/// Caller will need to manually read and free the memory.
|
||||
@@ -125,7 +167,7 @@ namespace NTwain
|
||||
/// <returns></returns>
|
||||
public STS GetCapDefault<TValue>(CAP cap, out List<TValue> value) where TValue : struct
|
||||
{
|
||||
value = new List<TValue>();
|
||||
value = [];
|
||||
var sts = GetCapDefault(cap, out TW_CAPABILITY twcap);
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
@@ -142,7 +184,8 @@ namespace NTwain
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
value.Add(twcap.ReadRange<TValue>(this).DefaultValue);
|
||||
var range = twcap.ReadRange<TValue>(this);
|
||||
if (range != null) value.Add(range.DefaultValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArray<TValue>(this);
|
||||
@@ -155,6 +198,47 @@ namespace NTwain
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's default value. This is a simplified version that doesn't require
|
||||
/// manual reading, but may or may not work.
|
||||
/// </summary>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS GetCapDefaultBoxed(CAP cap, out List<object> value)
|
||||
{
|
||||
value = [];
|
||||
var sts = GetCapDefault(cap, out TW_CAPABILITY twcap);
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
switch (twcap.ConType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
var read = twcap.ReadOneValueBoxed(this);
|
||||
if (read != null) value.Add(read);
|
||||
break;
|
||||
case TWON.ENUMERATION:
|
||||
var twenum = twcap.ReadEnumerationBoxed(this);
|
||||
if (twenum.Items != null && twenum.DefaultIndex < twenum.Items.Length)
|
||||
{
|
||||
value.Add(twenum.Items[twenum.DefaultIndex]);
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
var range = twcap.ReadRangeBoxed(this);
|
||||
if (range != null) value.Add(range.DefaultValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArrayBoxed(this);
|
||||
if (twarr != null && twarr.Count > 0) value.AddRange(twarr);
|
||||
break;
|
||||
default:
|
||||
twcap.Free(this); break;
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's raw supported values.
|
||||
/// Caller will need to manually read and free the memory.
|
||||
@@ -193,24 +277,12 @@ namespace NTwain
|
||||
var twenum = twcap.ReadEnumeration<TValue>(this);
|
||||
if (twenum.Items != null)
|
||||
{
|
||||
value.EnumValue = new EnumValue<TValue>
|
||||
{
|
||||
CurrentIndex = twenum.CurrentIndex,
|
||||
DefaultIndex = twenum.DefaultIndex,
|
||||
Items = twenum.Items
|
||||
};
|
||||
value.EnumValue = twenum;
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
var range = twcap.ReadRange<TValue>(this);
|
||||
value.RangeValue = new RangeValue<TValue>
|
||||
{
|
||||
Min = range.MinValue,
|
||||
Max = range.MaxValue,
|
||||
Step = range.StepSize,
|
||||
DefaultValue = range.DefaultValue,
|
||||
CurrentValue = range.CurrentValue
|
||||
};
|
||||
value.RangeValue = range;
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArray<TValue>(this);
|
||||
@@ -226,6 +298,51 @@ namespace NTwain
|
||||
return sts;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's supported values. This is a simplified version that doesn't require
|
||||
/// manual reading, but may or may not work.
|
||||
/// </summary>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS GetCapValuesBoxed(CAP cap, out ValueContainer<object> value)
|
||||
{
|
||||
value = new ValueContainer<object> { ContainerType = TWON.DONTCARE };
|
||||
var sts = GetCapValues(cap, out TW_CAPABILITY twcap);
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
value.ContainerType = twcap.ConType;
|
||||
switch (twcap.ConType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
value.OneValue = twcap.ReadOneValueBoxed(this);
|
||||
break;
|
||||
case TWON.ENUMERATION:
|
||||
var twenum = twcap.ReadEnumerationBoxed(this);
|
||||
if (twenum.Items != null)
|
||||
{
|
||||
value.EnumValue = twenum;
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
var range = twcap.ReadRangeBoxed(this);
|
||||
value.RangeValue = range;
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArrayBoxed(this);
|
||||
if (twarr != null)
|
||||
{
|
||||
value.ArrayValue = twarr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
twcap.Free(this); break;
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CAP's help text (description).
|
||||
/// This may not work due to unclear spec.
|
||||
@@ -309,8 +426,7 @@ namespace NTwain
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simpler cap value setter for common one-value scenarios
|
||||
/// that's easier to use. Not for other container type sets.
|
||||
/// A simpler cap value setter for one-value type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
@@ -322,6 +438,76 @@ namespace NTwain
|
||||
return SetCap(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap value setter for enumeration container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetCap<TValue>(CAP cap, Enumeration<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateEnumCap(cap, this, value);
|
||||
return SetCap(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap value setter for range container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetCap<TValue>(CAP cap, Range<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateRangeCap(cap, this, value);
|
||||
return SetCap(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap value setter for array container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetCap<TValue>(CAP cap, IList<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateArrayCap(cap, this, value);
|
||||
return SetCap(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap value setter for all kinds of container types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public STS SetCap<TValue>(CAP cap, ValueContainer<TValue> value) where TValue : struct
|
||||
{
|
||||
switch (value.ContainerType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
return SetCap(cap, value.OneValue);
|
||||
case TWON.ENUMERATION:
|
||||
if (value.EnumValue == null)
|
||||
throw new ArgumentException("EnumValue cannot be null when ContainerType is ENUMERATION.", nameof(value));
|
||||
return SetCap(cap, value.EnumValue);
|
||||
case TWON.RANGE:
|
||||
if (value.RangeValue == null)
|
||||
throw new ArgumentException("RangeValue cannot be null when ContainerType is RANGE.", nameof(value));
|
||||
return SetCap(cap, value.RangeValue);
|
||||
case TWON.ARRAY:
|
||||
if (value.ArrayValue == null)
|
||||
throw new ArgumentException("ArrayValue cannot be null when ContainerType is ARRAY.", nameof(value));
|
||||
return SetCap(cap, value.ArrayValue);
|
||||
default:
|
||||
throw new ArgumentException("Unsupported ContainerType for setting CAP.", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a CAP's constraint values.
|
||||
/// An easy way to create a value is to use the
|
||||
@@ -338,6 +524,90 @@ namespace NTwain
|
||||
return WrapInSTS(rc);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A simpler cap constraint setter for one-value type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint<TValue>(CAP cap, TValue value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateOneValueCap(cap, this, value);
|
||||
return SetConstraint(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap constraint setter for enumeration container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint<TValue>(CAP cap, Enumeration<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateEnumCap(cap, this, value);
|
||||
return SetConstraint(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap constraint setter for range container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint<TValue>(CAP cap, Range<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateRangeCap(cap, this, value);
|
||||
return SetConstraint(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap constraint setter for array container type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS SetConstraint<TValue>(CAP cap, IList<TValue> value) where TValue : struct
|
||||
{
|
||||
var twcap = ValueWriter.CreateArrayCap(cap, this, value);
|
||||
return SetConstraint(ref twcap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A cap constraint setter for all kinds of container types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public STS SetConstraint<TValue>(CAP cap, ValueContainer<TValue> value) where TValue : struct
|
||||
{
|
||||
switch (value.ContainerType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
return SetConstraint(cap, value.OneValue);
|
||||
case TWON.ENUMERATION:
|
||||
if (value.EnumValue == null)
|
||||
throw new ArgumentException("EnumValue cannot be null when ContainerType is ENUMERATION.", nameof(value));
|
||||
return SetConstraint(cap, value.EnumValue);
|
||||
case TWON.RANGE:
|
||||
if (value.RangeValue == null)
|
||||
throw new ArgumentException("RangeValue cannot be null when ContainerType is RANGE.", nameof(value));
|
||||
return SetConstraint(cap, value.RangeValue);
|
||||
case TWON.ARRAY:
|
||||
if (value.ArrayValue == null)
|
||||
throw new ArgumentException("ArrayValue cannot be null when ContainerType is ARRAY.", nameof(value));
|
||||
return SetConstraint(cap, value.ArrayValue);
|
||||
default:
|
||||
throw new ArgumentException("Unsupported ContainerType for setting CAP constraint.", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets a CAP's current value to power-on default.
|
||||
/// Caller will need to manually read and free the memory.
|
||||
@@ -365,30 +635,73 @@ namespace NTwain
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS ResetCap<TValue>(CAP cap, out TValue value) where TValue : struct
|
||||
public STS ResetCap<TValue>(CAP cap, out List<TValue> value) where TValue : struct
|
||||
{
|
||||
value = default;
|
||||
value = [];
|
||||
var sts = ResetCap(cap, out TW_CAPABILITY twcap);
|
||||
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
switch (twcap.ConType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
value = twcap.ReadOneValue<TValue>(this);
|
||||
value.Add(twcap.ReadOneValue<TValue>(this));
|
||||
break;
|
||||
case TWON.ENUMERATION:
|
||||
var twenum = twcap.ReadEnumeration<TValue>(this);
|
||||
if (twenum.Items != null && twenum.CurrentIndex < twenum.Items.Length)
|
||||
{
|
||||
value = twenum.Items[twenum.CurrentIndex];
|
||||
value.Add(twenum.Items[twenum.CurrentIndex]);
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
value = twcap.ReadRange<TValue>(this).CurrentValue;
|
||||
var range = twcap.ReadRange<TValue>(this);
|
||||
if (range != null) value.Add(range.CurrentValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArray<TValue>(this);
|
||||
if (twarr != null && twarr.Count > 0) value = twarr[0];
|
||||
if (twarr != null && twarr.Count > 0) value.AddRange(twarr);
|
||||
break;
|
||||
default:
|
||||
twcap.Free(this); break;
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets a CAP's current value to power-on default.
|
||||
/// </summary>
|
||||
/// <param name="cap"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public STS ResetCapBoxed(CAP cap, out List<object> value)
|
||||
{
|
||||
value = [];
|
||||
var sts = ResetCap(cap, out TW_CAPABILITY twcap);
|
||||
|
||||
if (sts.RC == TWRC.SUCCESS)
|
||||
{
|
||||
switch (twcap.ConType)
|
||||
{
|
||||
case TWON.ONEVALUE:
|
||||
var read = twcap.ReadOneValueBoxed(this);
|
||||
if (read != null) value.Add(read);
|
||||
break;
|
||||
case TWON.ENUMERATION:
|
||||
var twenum = twcap.ReadEnumerationBoxed(this);
|
||||
if (twenum.Items != null && twenum.CurrentIndex < twenum.Items.Length)
|
||||
{
|
||||
value.Add(twenum.Items[twenum.CurrentIndex]);
|
||||
}
|
||||
break;
|
||||
case TWON.RANGE:
|
||||
var range = twcap.ReadRangeBoxed(this);
|
||||
if (range != null) value.Add(range.CurrentValue);
|
||||
break;
|
||||
case TWON.ARRAY:
|
||||
var twarr = twcap.ReadArrayBoxed(this);
|
||||
if (twarr != null && twarr.Count > 0) value.AddRange(twarr);
|
||||
break;
|
||||
default:
|
||||
twcap.Free(this); break;
|
||||
|
||||
Reference in New Issue
Block a user