using NTwain.Data; using NTwain.Properties; using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; namespace NTwain { /// /// The one-stop class for reading raw TWAIN cap values from the cap container. /// This contains all the properties for the 4 container types. /// public class CapabilityReader { /// /// Reads the value from a that was returned /// from a TWAIN source. /// /// The capability. /// /// capability /// Capability contains no data.;capability /// or /// capability public static CapabilityReader ReadValue(TWCapability capability) { return ReadValue(capability, PlatformInfo.Current.MemoryManager); } /// /// Reads the value from a that was returned /// from a TWAIN source. /// /// The capability. /// The memory manager. /// /// capability /// or /// platformInfo /// Capability contains no data.;capability /// or /// capability public static CapabilityReader ReadValue(TWCapability capability, IMemoryManager memoryManager) { if (capability == null) { throw new ArgumentNullException("capability"); } if (capability.Container == IntPtr.Zero) { throw new ArgumentException(Resources.CapHasNoData, "capability"); } if (memoryManager == null) { throw new ArgumentNullException("memoryManager"); } IntPtr baseAddr = IntPtr.Zero; try { baseAddr = memoryManager.Lock(capability.Container); switch (capability.ContainerType) { case ContainerType.Array: return new CapabilityReader { ContainerType = capability.ContainerType, }.ReadArrayValue(baseAddr); case ContainerType.Enum: return new CapabilityReader { ContainerType = capability.ContainerType, }.ReadEnumValue(baseAddr); case ContainerType.OneValue: return new CapabilityReader { ContainerType = capability.ContainerType, }.ReadOneValue(baseAddr); case ContainerType.Range: return new CapabilityReader { ContainerType = capability.ContainerType, }.ReadRangeValue(baseAddr); default: throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.CapHasBadContainer, capability.ContainerType), "capability"); } } finally { if (baseAddr != IntPtr.Zero) { //memoryManager.Unlock(baseAddr); memoryManager.Unlock(capability.Container); } } } #region common prop /// /// Gets the underlying container type. /// /// /// The container. /// public ContainerType ContainerType { get; private set; } /// /// Gets the type of the TWAIN value. /// /// /// The type of the value. /// public ItemType ItemType { get; private set; } /// /// Gets the one value if container is . /// /// /// The one value. /// public object OneValue { get; private set; } /// /// Gets the collection values if container is or . /// /// /// The collection values. /// public IList CollectionValues { get; private set; } #endregion #region enum prop /// /// Gets the current value index if container is . /// public int EnumCurrentIndex { get; private set; } /// /// Gets the default value index if container is . /// public int EnumDefaultIndex { get; private set; } #endregion #region range prop /// /// Gets the current value if container is . /// /// /// The range current value. /// public object RangeCurrentValue { get; private set; } /// /// Gets the default value if container is . /// /// /// The range default value. /// public object RangeDefaultValue { get; private set; } /// /// The least positive/most negative value of the range. /// /// /// The range minimum value. /// public object RangeMinValue { get; private set; } /// /// The most positive/least negative value of the range. /// /// /// The range maximum value. /// public object RangeMaxValue { get; private set; } /// /// The delta between two adjacent values of the range. /// e.g. Item2 - Item1 = StepSize; /// /// /// The size of the range step. /// public object RangeStepSize { get; private set; } #endregion #region reader methods /// /// Don't care what contain it is, just populates the specified list with the capability values (count be one or many). /// /// The list to populate the values. /// public IList PopulateFromCapValues(IList toPopulate) { if (toPopulate == null) { toPopulate = new List(); } switch (ContainerType) { case ContainerType.OneValue: if (OneValue != null) { toPopulate.Add(OneValue); } break; case ContainerType.Array: case ContainerType.Enum: if (CollectionValues != null) { foreach (var o in CollectionValues) { toPopulate.Add(o); } } break; case ContainerType.Range: // horrible cast but should work. // in the for loop we also compare against min in case the step // is parsed as negative number and causes infinite loop. switch (ItemType) { case Data.ItemType.Fix32: { var min = (TWFix32)RangeMinValue; var counter = min; var max = (TWFix32)RangeMaxValue; var step = (TWFix32)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; case Data.ItemType.UInt32: { var min = (uint)RangeMinValue; var counter = min; var max = (uint)RangeMaxValue; var step = (uint)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; case Data.ItemType.Int32: { var min = (int)RangeMinValue; var counter = min; var max = (int)RangeMaxValue; var step = (int)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; // these should never happen since TW_ENUM fields are 4 bytes but you never know case Data.ItemType.UInt16: { var min = (ushort)RangeMinValue; var counter = min; var max = (ushort)RangeMaxValue; var step = (ushort)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; case Data.ItemType.Int16: { var min = (short)RangeMinValue; var counter = min; var max = (short)RangeMaxValue; var step = (short)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; case Data.ItemType.UInt8: { var min = (byte)RangeMinValue; var counter = min; var max = (byte)RangeMaxValue; var step = (byte)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; case Data.ItemType.Int8: { var min = (sbyte)RangeMinValue; var counter = min; var max = (sbyte)RangeMaxValue; var step = (sbyte)RangeStepSize; for (var i = min; i >= min && i <= max; i += step) { toPopulate.Add(i); } } break; } break; } return toPopulate; } CapabilityReader ReadOneValue(IntPtr baseAddr) { int offset = 0; ItemType = (ItemType)(ushort)Marshal.ReadInt16(baseAddr, offset); offset += 2; OneValue = baseAddr.ReadValue(ref offset, ItemType); return this; } CapabilityReader ReadArrayValue(IntPtr baseAddr) { int offset = 0; ItemType = (ItemType)(ushort)Marshal.ReadInt16(baseAddr, offset); offset += 2; var count = Marshal.ReadInt32(baseAddr, offset); offset += 4; if (count > 0) { CollectionValues = new object[count]; for (int i = 0; i < count; i++) { CollectionValues[i] = baseAddr.ReadValue(ref offset, ItemType); } } return this; } CapabilityReader ReadEnumValue(IntPtr baseAddr) { int offset = 0; ItemType = (ItemType)(ushort)Marshal.ReadInt16(baseAddr, offset); offset += 2; int count = Marshal.ReadInt32(baseAddr, offset); offset += 4; EnumCurrentIndex = Marshal.ReadInt32(baseAddr, offset); offset += 4; EnumDefaultIndex = Marshal.ReadInt32(baseAddr, offset); offset += 4; if (count > 0) { CollectionValues = new object[count]; for (int i = 0; i < count; i++) { CollectionValues[i] = baseAddr.ReadValue(ref offset, ItemType); } } return this; } CapabilityReader ReadRangeValue(IntPtr baseAddr) { int offset = 0; ItemType = (ItemType)(ushort)Marshal.ReadInt16(baseAddr, offset); offset += 2; RangeMinValue = baseAddr.ReadValue(ref offset, ItemType); RangeMaxValue = baseAddr.ReadValue(ref offset, ItemType); RangeStepSize = baseAddr.ReadValue(ref offset, ItemType); RangeDefaultValue = baseAddr.ReadValue(ref offset, ItemType); RangeCurrentValue = baseAddr.ReadValue(ref offset, ItemType); //RangeMinValue = (uint)Marshal.ReadInt32(baseAddr, offset); //offset += 4; //RangeMaxValue = (uint)Marshal.ReadInt32(baseAddr, offset); //offset += 4; //RangeStepSize = (uint)Marshal.ReadInt32(baseAddr, offset); //offset += 4; //RangeDefaultValue = (uint)Marshal.ReadInt32(baseAddr, offset); //offset += 4; //RangeCurrentValue = (uint)Marshal.ReadInt32(baseAddr, offset); return this; } #endregion } }