From b4e1435987df7be4c36c211fe91a29b01a3ef2c0 Mon Sep 17 00:00:00 2001 From: Eugene Wang Date: Sun, 25 Nov 2018 06:42:12 -0500 Subject: [PATCH] First attempt with TW_CAPABILITY generator with one value support. --- src/NTwain/CapWriter.cs | 199 ++++++++++++++++++ src/NTwain/Data/TwainTypes.cs | 28 +-- src/NTwain/Data/TwainTypesExtended.cs | 59 ++---- src/NTwain/Data/TwainValues.cs | 8 +- src/NTwain/Data/TypeExtensions.cs | 190 +++++++++++++++++ ...moryManager.cs => MarshalMemoryManager.cs} | 3 +- src/NTwain/Threading/UIThreadContext.cs | 2 +- src/NTwain/Triplets/Control/Capability.cs | 10 +- src/NTwain/TwainConfig.cs | 27 ++- src/NTwain/TwainConfigBuilder.cs | 11 +- src/NTwain/TwainSession.Props.cs | 9 + src/NTwain/TwainSession.cs | 2 + 12 files changed, 465 insertions(+), 83 deletions(-) create mode 100644 src/NTwain/CapWriter.cs create mode 100644 src/NTwain/Data/TypeExtensions.cs rename src/NTwain/Internals/{LinuxMemoryManager.cs => MarshalMemoryManager.cs} (89%) diff --git a/src/NTwain/CapWriter.cs b/src/NTwain/CapWriter.cs new file mode 100644 index 0000000..55ff0d4 --- /dev/null +++ b/src/NTwain/CapWriter.cs @@ -0,0 +1,199 @@ +using NTwain.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace NTwain +{ + /// + /// CLass that can generate for use in capability negotiation. + /// + public class CapWriter + { + private readonly TwainConfig config; + + /// + /// Creates a new . + /// + /// + internal CapWriter(TwainConfig config) + { + this.config = config; + } + + /// + /// Generates a using single value (aka TW_ONEVALUE). + /// + /// + /// + /// + /// + /// + public TW_CAPABILITY Generate(CapabilityId cap, ItemType type, T value) + { + // size of data + uint16 item type + var valueSz = type.GetSize(); + if (valueSz < 4) valueSz = 4; // onevalue container value minimum is 32bit + var memSz = valueSz + 2; // + item type field + + var twCap = new TW_CAPABILITY + { + Capability = cap, + ContainerType = ContainerType.OneValue, + hContainer = config.MemoryManager.Allocate((uint)memSz) + }; + + if (twCap.hContainer != IntPtr.Zero) + { + IntPtr baseAddr = config.MemoryManager.Lock(twCap.hContainer); + try + { + int offset = 0; + // TODO: type size may be different on mac + baseAddr.WriteValue(ref offset, ItemType.UInt16, value); + // ONEVALUE is special in value can be uint32 or string + // if less than uint32 put it in lower word + // (string value seems undocumented but internet says put it as-is and not a pointer) + if (valueSz < 4) + { + Marshal.WriteInt16(baseAddr, offset, 0); + offset += 2; + } + baseAddr.WriteValue(ref offset, type, value); + } + finally + { + config.MemoryManager.Unlock(twCap.hContainer); + } + } + + return twCap; + } + + ///// + ///// Generates a for use in capability negotiation + ///// using TWAIN's array value. + ///// + ///// + ///// + ///// + ///// + //public TW_CAPABILITY Generate(CapabilityId cap, ArrayValue value) + //{ + // var twCap = new TW_CAPABILITY + // { + // Capability = cap, + // ContainerType = ContainerType.Array + // }; + // SetOneValue() + // return twCap; + //} + + ///// + ///// Generates a for use in capability negotiation + ///// using TWAIN's enum value. + ///// + ///// + ///// + ///// + ///// + //public TW_CAPABILITY Generate(CapabilityId cap, EnumValue value) + //{ + // var twCap = new TW_CAPABILITY + // { + // Capability = cap, + // ContainerType = ContainerType.Enum + // }; + + // return twCap; + //} + + ///// + ///// Generates a for use in capability negotiation + ///// using TWAIN's range value. + ///// + ///// + ///// + ///// + //public TW_CAPABILITY Generate(CapabilityId cap, RangeValue value) + //{ + // var twCap = new TW_CAPABILITY + // { + // Capability = cap, + // ContainerType = ContainerType.Range + // }; + + // return twCap; + //} + + + //void SetEnumValue(TW_ENUMERATION value, IMemoryManager memoryManager) + //{ + // if (value == null) { throw new ArgumentNullException("value"); } + // ContainerType = ContainerType.Enum; + + + // Int32 valueSize = TW_ENUMERATION.ItemOffset + value.ItemList.Length * TypeExtensions.GetItemTypeSize(value.ItemType); + + // int offset = 0; + // _hContainer = memoryManager.Allocate((uint)valueSize); + // if (_hContainer != IntPtr.Zero) + // { + // IntPtr baseAddr = memoryManager.Lock(_hContainer); + + // // can't safely use StructureToPtr here so write it our own + // baseAddr.WriteValue(ref offset, ItemType.UInt16, value.ItemType); + // baseAddr.WriteValue(ref offset, ItemType.UInt32, (uint)value.ItemList.Length); + // baseAddr.WriteValue(ref offset, ItemType.UInt32, value.CurrentIndex); + // baseAddr.WriteValue(ref offset, ItemType.UInt32, value.DefaultIndex); + // foreach (var item in value.ItemList) + // { + // baseAddr.WriteValue(ref offset, value.ItemType, item); + // } + // memoryManager.Unlock(_hContainer); + // } + //} + + //void SetRangeValue(TW_RANGE value, IMemoryManager memoryManager) + //{ + // if (value == null) { throw new ArgumentNullException("value"); } + // ContainerType = ContainerType.Range; + + // // since range value can only house UInt32 we will not allow type size > 4 + // if (TypeExtensions.GetItemTypeSize(value.ItemType) > 4) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.BadValueType, "TW_RANGE")); } + + // _hContainer = memoryManager.Allocate((uint)Marshal.SizeOf(value)); + // if (_hContainer != IntPtr.Zero) + // { + // Marshal.StructureToPtr(value, _hContainer, false); + // } + //} + + //void SetArrayValue(TW_ARRAY value, IMemoryManager memoryManager) + //{ + // if (value == null) { throw new ArgumentNullException("value"); } + // ContainerType = ContainerType.Array; + + // Int32 valueSize = 6 + value.ItemList.Length * TypeExtensions.GetItemTypeSize(value.ItemType); + + // int offset = 0; + // _hContainer = memoryManager.Allocate((uint)valueSize); + // if (_hContainer != IntPtr.Zero) + // { + // IntPtr baseAddr = memoryManager.Lock(_hContainer); + + // // can't safely use StructureToPtr here so write it our own + // baseAddr.WriteValue(ref offset, ItemType.UInt16, value.ItemType); + // baseAddr.WriteValue(ref offset, ItemType.UInt32, (uint)value.ItemList.Length); + // foreach (var item in value.ItemList) + // { + // baseAddr.WriteValue(ref offset, value.ItemType, item); + // } + // memoryManager.Unlock(_hContainer); + // } + //} + + } +} diff --git a/src/NTwain/Data/TwainTypes.cs b/src/NTwain/Data/TwainTypes.cs index 68892b2..23df481 100644 --- a/src/NTwain/Data/TwainTypes.cs +++ b/src/NTwain/Data/TwainTypes.cs @@ -35,17 +35,17 @@ namespace NTwain.Data [StructLayout(LayoutKind.Sequential, Pack = 2)] partial struct TW_FIX32 { - TW_INT16 _whole; - TW_UINT16 _frac; + internal TW_INT16 Whole; + internal TW_UINT16 Fraction; } [StructLayout(LayoutKind.Sequential, Pack = 2)] partial struct TW_FRAME { - TW_FIX32 _left; - TW_FIX32 _top; - TW_FIX32 _right; - TW_FIX32 _bottom; + internal TW_FIX32 _left; + internal TW_FIX32 _top; + internal TW_FIX32 _right; + internal TW_FIX32 _bottom; } [StructLayout(LayoutKind.Sequential, Pack = 2)] @@ -111,7 +111,7 @@ namespace NTwain.Data { TW_UINT16 _cap; TW_UINT16 _conType; - TW_HANDLE _hContainer; + internal TW_HANDLE hContainer; } [StructLayout(LayoutKind.Sequential, Pack = 2)] @@ -427,13 +427,13 @@ namespace NTwain.Data TW_UINT32 _sheetCount; } - [StructLayout(LayoutKind.Sequential, Pack = 2)] - partial class TW_ONEVALUE - { - // TODO: mac is different? - public TW_UINT16 ItemType; - public TW_UINT32 Item; - } + //[StructLayout(LayoutKind.Sequential, Pack = 2)] + //partial class TW_ONEVALUE + //{ + // // TODO: mac is different? + // public TW_UINT16 ItemType; + // public TW_UINT32 Item; + //} [StructLayout(LayoutKind.Sequential, Pack = 2)] partial struct TW_PALETTE8 diff --git a/src/NTwain/Data/TwainTypesExtended.cs b/src/NTwain/Data/TwainTypesExtended.cs index 5c48386..c25c62f 100644 --- a/src/NTwain/Data/TwainTypesExtended.cs +++ b/src/NTwain/Data/TwainTypesExtended.cs @@ -32,19 +32,19 @@ namespace NTwain.Data float ToFloat() { - return (float)_whole + _frac / 65536f; + return (float)Whole + Fraction / 65536f; } TW_FIX32(float value) { //int temp = (int)(value * 65536.0 + 0.5); - //_whole = (short)(temp >> 16); - //_frac = (ushort)(temp & 0x0000ffff); + //Whole = (short)(temp >> 16); + //Fraction = (ushort)(temp & 0x0000ffff); // different version from twain faq bool sign = value < 0; int temp = (int)(value * 65536.0 + (sign ? (-0.5) : 0.5)); - _whole = (short)(temp >> 16); - _frac = (ushort)(temp & 0x0000ffff); + Whole = (short)(temp >> 16); + Fraction = (ushort)(temp & 0x0000ffff); } @@ -59,33 +59,6 @@ namespace NTwain.Data return ToFloat().ToString(CultureInfo.InvariantCulture); } - ///// - ///// Converts this to for capability set methods. - ///// - ///// - //public TW_ONEVALUE ToOneValue() - //{ - // // copy struct parts as-is. - // // probably has a faster way but can't think now - - // byte[] array = new byte[4]; - // var part = BitConverter.GetBytes(Whole); - // Buffer.BlockCopy(part, 0, array, 0, 2); - - // part = BitConverter.GetBytes(Fraction); - // Buffer.BlockCopy(part, 0, array, 2, 2); - - // var converted = BitConverter.ToUInt32(array, 0); - - // return new TW_ONEVALUE - // { - // ItemType = ItemType.Fix32, - // Item = converted - // // old wrong conversion - // // (uint)this,// ((uint)dpi) << 16; - // }; - //} - #region equals /// @@ -109,7 +82,7 @@ namespace NTwain.Data /// public bool Equals(TW_FIX32 other) { - return _whole == other._whole && _frac == other._frac; + return Whole == other.Whole && Fraction == other.Fraction; } /// /// Returns a hash code for this instance. @@ -119,7 +92,7 @@ namespace NTwain.Data /// public override int GetHashCode() { - return _whole ^ _frac; + return Whole ^ Fraction; } #endregion @@ -621,16 +594,14 @@ namespace NTwain.Data // #region properties - // /// - // /// Id of capability to set or get. - // /// - // public CapabilityId Capability { get { return (CapabilityId)_cap; } set { _cap = (ushort)value; } } - // /// - // /// The type of the container structure referenced by the pointer internally. The container - // /// will be one of four types: , , - // /// , or . - // /// - // public ContainerType ContainerType { get { return (ContainerType)_conType; } set { _conType = (ushort)value; } } + /// + /// Id of capability to set or get. + /// + public CapabilityId Capability { get { return (CapabilityId)_cap; } internal set { _cap = (ushort)value; } } + /// + /// The type of the container structure referenced by the pointer internally. + /// + public ContainerType ContainerType { get { return (ContainerType)_conType; } internal set { _conType = (ushort)value; } } // internal IntPtr Container { get { return _hContainer; } } diff --git a/src/NTwain/Data/TwainValues.cs b/src/NTwain/Data/TwainValues.cs index a6c95ad..4c0efa3 100644 --- a/src/NTwain/Data/TwainValues.cs +++ b/src/NTwain/Data/TwainValues.cs @@ -84,19 +84,19 @@ namespace NTwain.Data /// Invalid = 0, /// - /// The container is . + /// The container is TW_ARRAY. /// Array = 3, /// - /// The container is . + /// The container is TW_ENUMERATION. /// Enum = 4, /// - /// The container is . + /// The container is TW_ONEVALUE. /// OneValue = 5, /// - /// The container is . + /// The container is TW_RANGE. /// Range = 6, /// diff --git a/src/NTwain/Data/TypeExtensions.cs b/src/NTwain/Data/TypeExtensions.cs new file mode 100644 index 0000000..06e97f0 --- /dev/null +++ b/src/NTwain/Data/TypeExtensions.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace NTwain.Data +{ + /// + /// Contains extension methods for reading/writing primitive + /// TWAIN data types. + /// + static class TypeExtensions + { + static readonly Dictionary _sizes = new Dictionary + { + { ItemType.Int8, 1 }, + { ItemType.UInt8, 1 }, + { ItemType.Int16, 2 }, + { ItemType.UInt16, 2 }, + { ItemType.Int32, 4 }, + { ItemType.UInt32, 4 }, + { ItemType.Bool, 2 }, + { ItemType.Fix32, Marshal.SizeOf(typeof(TW_FIX32)) }, + { ItemType.Frame, Marshal.SizeOf(typeof(TW_FRAME)) }, + { ItemType.String128, TwainConst.String128 }, + { ItemType.String255, TwainConst.String255 }, + { ItemType.String32, TwainConst.String32 }, + { ItemType.String64, TwainConst.String64 }, + // TODO: find out if it should be fixed 4 bytes or intptr size + { ItemType.Handle, IntPtr.Size }, + }; + + public static int GetSize(this ItemType type) + { + if(_sizes.TryGetValue(type, out int size)) return size; + + throw new NotSupportedException($"Unsupported item type {type}."); + } + + + #region writes + + /// + /// Writes a TWAIN value. + /// + /// The base addr. + /// The offset. + /// The TWAIN type. + /// The value. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public static void WriteValue(this IntPtr baseAddr, ref int offset, ItemType type, object value) + { + switch (type) + { + case ItemType.Int8: + case ItemType.UInt8: + Marshal.WriteByte(baseAddr, offset, Convert.ToByte(value, CultureInfo.InvariantCulture));// (byte)value); + break; + case ItemType.Bool: + case ItemType.Int16: + case ItemType.UInt16: + Marshal.WriteInt16(baseAddr, offset, Convert.ToInt16(value, CultureInfo.InvariantCulture));//(short)value); + break; + case ItemType.UInt32: + case ItemType.Int32: + Marshal.WriteInt32(baseAddr, offset, Convert.ToInt32(value, CultureInfo.InvariantCulture));//(int)value); + break; + case ItemType.Fix32: + TW_FIX32 f32 = (TW_FIX32)value; + WriteFix32(baseAddr, ref offset, f32); + return; // no need to update offset for this + case ItemType.Frame: + TW_FRAME frame = (TW_FRAME)value; + WriteFix32(baseAddr, ref offset, frame._left); + WriteFix32(baseAddr, ref offset, frame._top); + WriteFix32(baseAddr, ref offset, frame._right); + WriteFix32(baseAddr, ref offset, frame._bottom); + return; // no need to update offset for this + //case ItemType.String1024: + // WriteString(baseAddr, offset, value as string, 1024); + // break; + case ItemType.String128: + WriteString(baseAddr, offset, (string)value, 128); + break; + case ItemType.String255: + WriteString(baseAddr, offset, (string)value, 255); + break; + case ItemType.String32: + WriteString(baseAddr, offset, (string)value, 32); + break; + case ItemType.String64: + WriteString(baseAddr, offset, (string)value, 64); + break; + //case ItemType.Unicode512: + // WriteUString(baseAddr, offset, value as string, 512); + // break; + } + offset += type.GetSize(); + } + + private static void WriteFix32(IntPtr baseAddr, ref int offset, TW_FIX32 f32) + { + Marshal.WriteInt16(baseAddr, offset, f32.Whole); + if (f32.Fraction > Int16.MaxValue) + { + Marshal.WriteInt16(baseAddr, offset + 2, (Int16)(f32.Fraction - 32768)); + } + else + { + Marshal.WriteInt16(baseAddr, offset + 2, (Int16)f32.Fraction); + } + offset += _sizes[ItemType.Fix32]; + } + + /// + /// Writes string value. THIS MAY BE WRONG. + /// + /// + /// + /// + /// + static void WriteString(IntPtr baseAddr, int offset, string value, int maxLength) + { + if (string.IsNullOrEmpty(value)) + { + // write zero + Marshal.WriteByte(baseAddr, offset, 0); + } + else + { + for (int i = 0; i < maxLength; i++) + { + if (i == value.Length) + { + // string end reached, so write \0 and quit + Marshal.WriteByte(baseAddr, offset, 0); + return; + } + else + { + Marshal.WriteByte(baseAddr, offset, (byte)value[i]); + offset++; + } + } + // when ended normally also write \0 + Marshal.WriteByte(baseAddr, offset, 0); + } + } + ///// + ///// Writes unicode string value. + ///// + ///// + ///// + ///// + ///// + //[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)] + //private void WriteUString(IntPtr baseAddr, int offset, string item, int maxLength) + //{ + // if (string.IsNullOrEmpty(item)) + // { + // // write zero + // Marshal.WriteInt16(baseAddr, offset, (char)0); + // } + // else + // { + // // use 2 bytes per char + // for (int i = 0; i < maxLength; i++) + // { + // if (i == item.Length) + // { + // // string end reached, so write \0 and quit + // Marshal.WriteInt16(baseAddr, offset, (char)0); + // return; + // } + // else + // { + // Marshal.WriteInt16(baseAddr, offset, item[i]); + // offset += 2; + // } + // } + // // when ended normally also write \0 + // Marshal.WriteByte(baseAddr, offset, 0); + // } + //} + + #endregion + } +} diff --git a/src/NTwain/Internals/LinuxMemoryManager.cs b/src/NTwain/Internals/MarshalMemoryManager.cs similarity index 89% rename from src/NTwain/Internals/LinuxMemoryManager.cs rename to src/NTwain/Internals/MarshalMemoryManager.cs index 3254807..1d6c5a5 100644 --- a/src/NTwain/Internals/LinuxMemoryManager.cs +++ b/src/NTwain/Internals/MarshalMemoryManager.cs @@ -8,7 +8,7 @@ namespace NTwain.Internals { // probably wrong - class LinuxMemoryManager : IMemoryManager + class MarshalMemoryManager : IMemoryManager { public IntPtr Allocate(uint size) { @@ -22,6 +22,7 @@ namespace NTwain.Internals public IntPtr Lock(IntPtr handle) { + // no op return handle; } diff --git a/src/NTwain/Threading/UIThreadContext.cs b/src/NTwain/Threading/UIThreadContext.cs index 770b186..e273353 100644 --- a/src/NTwain/Threading/UIThreadContext.cs +++ b/src/NTwain/Threading/UIThreadContext.cs @@ -41,7 +41,7 @@ namespace NTwain.Threading { action(); } - catch (Exception ex) + catch { // TODO: do something } diff --git a/src/NTwain/Triplets/Control/Capability.cs b/src/NTwain/Triplets/Control/Capability.cs index 4c8403b..23d135a 100644 --- a/src/NTwain/Triplets/Control/Capability.cs +++ b/src/NTwain/Triplets/Control/Capability.cs @@ -99,7 +99,7 @@ namespace NTwain.Triplets.Control } /// - /// Return all of the labels for a capability of type or , for example + /// Return all of the labels for a capability of type TW_ARRAY or TW_ENUMERATION, for example /// "US Letter" for ICapSupportedSizes’ TWSS_USLETTER. /// /// The capability. @@ -145,8 +145,8 @@ namespace NTwain.Triplets.Control /// Changes the Current Value(s) and Available Values of the specified capability to those specified /// by the application. As of TWAIN 2.2 this only modifies the Current Value of the specified capability, constraints cannot be /// changed with this. - /// Current Values are set when the container is a or . Available and - /// Current Values are set when the container is a or . + /// Current Values are set when the container is a TW_ONEVALUE or TW_ARRAY. Available and + /// Current Values are set when the container is a TW_ENUMERATION or TW_RANGE. /// /// The capability. /// @@ -159,8 +159,8 @@ namespace NTwain.Triplets.Control /// Changes the Current Value(s) and Available Value(s) of the specified capability to those specified /// by the application. /// - /// Current Values are set when the container is a or . Available and - /// Current Values are set when the container is a or . + /// Current Values are set when the container is a TW_ONEVALUE or TW_ARRAY. Available and + /// Current Values are set when the container is a TW_ENUMERATION or TW_RANGE. /// The capability. /// public ReturnCode SetConstraint(ref TW_CAPABILITY capability) diff --git a/src/NTwain/TwainConfig.cs b/src/NTwain/TwainConfig.cs index 9be6b0c..59c0945 100644 --- a/src/NTwain/TwainConfig.cs +++ b/src/NTwain/TwainConfig.cs @@ -14,17 +14,34 @@ namespace NTwain /// public class TwainConfig { - internal TwainConfig() { } + internal TwainConfig(PlatformID platform, bool is32Bit) + { + Platform = platform; + Is32Bit = is32Bit; + + // initial default until twain entry is available + switch (platform) + { + case PlatformID.Win32NT: + _defaultMemoryManager = new WinMemoryManager(); + break; + default: + _defaultMemoryManager = new MarshalMemoryManager(); + break; + } + } + + readonly IMemoryManager _defaultMemoryManager; /// /// Gets whether the app is running in 32bit. /// - public bool Is32Bit { get; internal set; } + public bool Is32Bit { get; private set; } /// /// Gets the platform the app is running on. /// - public PlatformID Platform { get; internal set; } + public PlatformID Platform { get; private set; } //public bool PreferLegacyDsm { get; internal set; } @@ -43,11 +60,9 @@ namespace NTwain { get { - return _memMgr ?? DefaultMemoryManager; + return _memMgr ?? _defaultMemoryManager; } internal set { _memMgr = value; } } - - internal IMemoryManager DefaultMemoryManager { get; set; } } } diff --git a/src/NTwain/TwainConfigBuilder.cs b/src/NTwain/TwainConfigBuilder.cs index 60ecb03..374f009 100644 --- a/src/NTwain/TwainConfigBuilder.cs +++ b/src/NTwain/TwainConfigBuilder.cs @@ -18,8 +18,8 @@ namespace NTwain private string _companyName; private Language _lang; private DataGroups _dg = DataGroups.Image; - private bool _32bit; - private PlatformID _platform; + readonly bool _32bit; + readonly PlatformID _platform; private Country _country; /// @@ -106,17 +106,12 @@ namespace NTwain /// public TwainConfig Build() { - var config = new TwainConfig - { - Platform = _platform, - Is32Bit = _32bit - }; + var config = new TwainConfig(_platform, _32bit); // todo: change id based on platform switch (_platform) { case PlatformID.Win32NT: - config.DefaultMemoryManager = new WinMemoryManager(); // initial default config.App32 = new TW_IDENTITY { DataFlags = DataFlags.App2, diff --git a/src/NTwain/TwainSession.Props.cs b/src/NTwain/TwainSession.Props.cs index 6d14953..1a3ecfa 100644 --- a/src/NTwain/TwainSession.Props.cs +++ b/src/NTwain/TwainSession.Props.cs @@ -43,6 +43,15 @@ namespace NTwain /// internal DGAudio DGAudio => dgAudio ?? (dgAudio = new DGAudio(this)); + + //public CapReader CapReader { get; } + + /// + /// Gets the generator. + /// + public CapWriter CapWriter { get; } + + /// /// Occurs when an enabled source has been disabled (back to state 4). /// diff --git a/src/NTwain/TwainSession.cs b/src/NTwain/TwainSession.cs index bb06e02..d5a8832 100644 --- a/src/NTwain/TwainSession.cs +++ b/src/NTwain/TwainSession.cs @@ -51,6 +51,8 @@ namespace NTwain _callback32Delegate = new Callback32(Handle32BitCallback); break; } + //CapReader = new CapReader(this); + CapWriter = new CapWriter(config); } ///