From ab2947d03b98483c47e8b3e16d8ee18d1c660311 Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:59:50 -0400 Subject: [PATCH] Attempt to read cap values. --- samples/WinForm32/Form1.Designer.cs | 66 ++- samples/WinForm32/Form1.cs | 19 +- samples/WinForm32/Program.cs | 10 + src/NTwain/Data/TWAINH_EXTRAS.cs | 149 +++--- src/NTwain/Data/ValueReader.cs | 648 ++++++++++++++------------ src/NTwain/Data/ValueWriter.cs | 4 +- src/NTwain/NTwain.csproj | 4 + src/NTwain/TwainAppSession.Caps.cs | 32 +- src/NTwain/TwainAppSession.Sources.cs | 6 +- 9 files changed, 498 insertions(+), 440 deletions(-) diff --git a/samples/WinForm32/Form1.Designer.cs b/samples/WinForm32/Form1.Designer.cs index 32fbf78..1ee4b54 100644 --- a/samples/WinForm32/Form1.Designer.cs +++ b/samples/WinForm32/Form1.Designer.cs @@ -41,9 +41,11 @@ lblState = new System.Windows.Forms.Label(); label3 = new System.Windows.Forms.Label(); btnOpenDef = new System.Windows.Forms.Button(); + listCaps = new System.Windows.Forms.ListBox(); + label4 = new System.Windows.Forms.Label(); + btnStart = new System.Windows.Forms.Button(); btnShowSettings = new System.Windows.Forms.Button(); btnClose = new System.Windows.Forms.Button(); - btnStart = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); splitContainer1.Panel1.SuspendLayout(); splitContainer1.Panel2.SuspendLayout(); @@ -56,7 +58,7 @@ btnSelect.Name = "btnSelect"; btnSelect.Size = new System.Drawing.Size(151, 23); btnSelect.TabIndex = 0; - btnSelect.Text = "Select default source"; + btnSelect.Text = "Choose default source"; btnSelect.UseVisualStyleBackColor = true; btnSelect.Click += btnSelect_Click; // @@ -111,15 +113,14 @@ listSources.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; listSources.FormattingEnabled = true; listSources.ItemHeight = 15; - listSources.Location = new System.Drawing.Point(13, 130); + listSources.Location = new System.Drawing.Point(13, 170); listSources.Name = "listSources"; - listSources.Size = new System.Drawing.Size(289, 379); + listSources.Size = new System.Drawing.Size(297, 379); listSources.TabIndex = 6; // // btnSetDef // - btnSetDef.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - btnSetDef.Location = new System.Drawing.Point(12, 515); + btnSetDef.Location = new System.Drawing.Point(13, 131); btnSetDef.Name = "btnSetDef"; btnSetDef.Size = new System.Drawing.Size(169, 23); btnSetDef.TabIndex = 7; @@ -129,10 +130,9 @@ // // btnOpen // - btnOpen.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - btnOpen.Location = new System.Drawing.Point(187, 515); + btnOpen.Location = new System.Drawing.Point(188, 131); btnOpen.Name = "btnOpen"; - btnOpen.Size = new System.Drawing.Size(104, 23); + btnOpen.Size = new System.Drawing.Size(114, 23); btnOpen.TabIndex = 8; btnOpen.Text = "Open selected"; btnOpen.UseVisualStyleBackColor = true; @@ -160,13 +160,15 @@ // // splitContainer1.Panel2 // + splitContainer1.Panel2.Controls.Add(listCaps); + splitContainer1.Panel2.Controls.Add(label4); splitContainer1.Panel2.Controls.Add(btnStart); splitContainer1.Panel2.Controls.Add(btnShowSettings); splitContainer1.Panel2.Controls.Add(btnClose); splitContainer1.Panel2.Controls.Add(label2); splitContainer1.Panel2.Controls.Add(lblCurrent); - splitContainer1.Size = new System.Drawing.Size(800, 564); - splitContainer1.SplitterDistance = 305; + splitContainer1.Size = new System.Drawing.Size(1023, 564); + splitContainer1.SplitterDistance = 325; splitContainer1.TabIndex = 9; // // lblState @@ -197,6 +199,34 @@ btnOpenDef.UseVisualStyleBackColor = true; btnOpenDef.Click += btnOpenDef_Click; // + // listCaps + // + listCaps.FormattingEnabled = true; + listCaps.ItemHeight = 15; + listCaps.Location = new System.Drawing.Point(13, 87); + listCaps.Name = "listCaps"; + listCaps.Size = new System.Drawing.Size(234, 469); + listCaps.TabIndex = 8; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(13, 62); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(91, 15); + label4.TabIndex = 7; + label4.Text = "Supported Caps"; + // + // btnStart + // + btnStart.Location = new System.Drawing.Point(299, 32); + btnStart.Name = "btnStart"; + btnStart.Size = new System.Drawing.Size(132, 23); + btnStart.TabIndex = 6; + btnStart.Text = "Start acquisition"; + btnStart.UseVisualStyleBackColor = true; + btnStart.Click += btnStart_Click; + // // btnShowSettings // btnShowSettings.Location = new System.Drawing.Point(161, 32); @@ -217,21 +247,11 @@ btnClose.UseVisualStyleBackColor = true; btnClose.Click += btnClose_Click; // - // btnStart - // - btnStart.Location = new System.Drawing.Point(299, 32); - btnStart.Name = "btnStart"; - btnStart.Size = new System.Drawing.Size(132, 23); - btnStart.TabIndex = 6; - btnStart.Text = "Start acquisition"; - btnStart.UseVisualStyleBackColor = true; - btnStart.Click += btnStart_Click; - // // Form1 // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(800, 564); + ClientSize = new System.Drawing.Size(1023, 564); Controls.Add(splitContainer1); Name = "Form1"; Text = "TWAIN Test"; @@ -262,5 +282,7 @@ private System.Windows.Forms.Label label3; private System.Windows.Forms.Button btnShowSettings; private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.ListBox listCaps; + private System.Windows.Forms.Label label4; } } \ No newline at end of file diff --git a/samples/WinForm32/Form1.cs b/samples/WinForm32/Form1.cs index 60136c1..49a8216 100644 --- a/samples/WinForm32/Form1.cs +++ b/samples/WinForm32/Form1.cs @@ -18,15 +18,6 @@ namespace WinFormSample var libVer = FileVersionInfo.GetVersionInfo(typeof(TwainAppSession).Assembly.Location).FileVersion; Text += $"{(TwainPlatform.Is32bit ? " 32bit" : " 64bit")} on NTwain {libVer}"; - if (DsmLoader.TryUseCustomDSM()) - { - Debug.WriteLine("Using our own dsm now :)"); - } - else - { - Debug.WriteLine("Will attempt to use default dsm :("); - } - TwainPlatform.PreferLegacyDSM = false; twain = new TwainAppSession(new WinformMarshaller(this), Assembly.GetExecutingAssembly().Location); @@ -45,6 +36,16 @@ namespace WinFormSample private void Twain_CurrentSourceChanged(TwainAppSession arg1, TW_IDENTITY_LEGACY ds) { lblCurrent.Text = ds.ProductName; + if (twain.State == STATE.S4) + { + var caps = twain.GetAllCaps(); + foreach (var c in caps) + listCaps.Items.Add(c); + } + else + { + listCaps.Items.Clear(); + } } private void Twain_DefaultSourceChanged(TwainAppSession arg1, TW_IDENTITY_LEGACY ds) diff --git a/samples/WinForm32/Program.cs b/samples/WinForm32/Program.cs index 7ef8f15..c05f3b6 100644 --- a/samples/WinForm32/Program.cs +++ b/samples/WinForm32/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Windows.Forms; namespace WinFormSample @@ -11,6 +12,15 @@ namespace WinFormSample [STAThread] static void Main() { + if (DsmLoader.TryUseCustomDSM()) + { + Debug.WriteLine("Using our own dsm now :)"); + } + else + { + Debug.WriteLine("Will attempt to use default dsm :("); + } + // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); diff --git a/src/NTwain/Data/TWAINH_EXTRAS.cs b/src/NTwain/Data/TWAINH_EXTRAS.cs index d4a03ba..1a9100b 100644 --- a/src/NTwain/Data/TWAINH_EXTRAS.cs +++ b/src/NTwain/Data/TWAINH_EXTRAS.cs @@ -1,9 +1,7 @@ -using NTwain; -using System; +using System; +using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Text; namespace NTwain.Data { @@ -236,89 +234,89 @@ namespace NTwain.Data //} } - ///// - ///// A more dotnet-friendly representation of . - ///// - ///// - //public class Enumeration where TValue : struct - //{ - // public int CurrentIndex; + /// + /// A more dotnet-friendly representation of . + /// + /// + public class Enumeration where TValue : struct + { + public int CurrentIndex; - // public int DefaultIndex; + public int DefaultIndex; - // public TValue[] Items; - //} + public TValue[]? Items; + } - ///// - ///// A more dotnet-friendly representation of . - ///// - ///// - //public partial class Range : IEnumerable where TValue : struct - //{ - // public TValue MinValue; - // public TValue MaxValue; - // public TValue StepSize; - // public TValue DefaultValue; - // public TValue CurrentValue; + /// + /// A more dotnet-friendly representation of . + /// + /// + public partial class Range : IEnumerable where TValue : struct + { + public TValue MinValue; + public TValue MaxValue; + public TValue StepSize; + public TValue DefaultValue; + public TValue CurrentValue; - // IEnumerator IEnumerable.GetEnumerator() - // { - // if (!(MinValue is IConvertible)) - // throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported for range enumeration."); + IEnumerator IEnumerable.GetEnumerator() + { + if (!(MinValue is IConvertible)) + throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported for range enumeration."); - // return new DynamicEnumerator(MinValue, MaxValue, StepSize); - // } + return new DynamicEnumerator(MinValue, MaxValue, StepSize); + } - // IEnumerator IEnumerable.GetEnumerator() - // { - // return ((IEnumerable)this).GetEnumerator(); - // } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } - // // dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric - // class DynamicEnumerator : IEnumerator - // { - // private readonly TValue _min; - // private readonly TValue _max; - // private readonly TValue _step; - // private TValue _cur; - // bool started = false; + // dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric + class DynamicEnumerator : IEnumerator + { + private readonly TValue _min; + private readonly TValue _max; + private readonly TValue _step; + private TValue _cur; + bool started = false; - // public DynamicEnumerator(TValue min, TValue max, TValue step) - // { - // _min = min; - // _max = max; - // _step = step; - // _cur = min; - // } + public DynamicEnumerator(TValue min, TValue max, TValue step) + { + _min = min; + _max = max; + _step = step; + _cur = min; + } - // public TValue Current => _cur; + public TValue Current => _cur; - // object IEnumerator.Current => this.Current; + object System.Collections.IEnumerator.Current => this.Current; - // public void Dispose() { } + public void Dispose() { } - // public bool MoveNext() - // { - // if (!started) - // { - // started = true; - // return true; - // } + public bool MoveNext() + { + if (!started) + { + started = true; + return true; + } - // var next = _cur + (dynamic)_step; - // if (next == _cur || next < _min || next > _max) return false; + var next = _cur + (dynamic)_step; + if (next == _cur || next < _min || next > _max) return false; - // _cur = next; - // return true; - // } + _cur = next; + return true; + } - // public void Reset() - // { - // _cur = _min; - // started = false; - // } - // } - //} + public void Reset() + { + _cur = _min; + started = false; + } + } + } partial struct TW_FIX32 : IEquatable, IConvertible { @@ -716,7 +714,7 @@ namespace NTwain.Data string? val = null; if (UTF8string != IntPtr.Zero && Size > 0) { - val = ValueReader.PtrToStringUTF8(mgr, UTF8string, (int)Size); + val = UTF8string.PtrToStringUTF8(mgr, (int)Size); } if (freeMemory) Free(mgr); return val; @@ -741,11 +739,6 @@ namespace NTwain.Data mgr.Free(hContainer); hContainer = IntPtr.Zero; } - - public void Read(IMemoryManager mgr, bool freeMemory = true) - { - if (freeMemory) Free(mgr); - } } //partial struct TW_DEVICEEVENT diff --git a/src/NTwain/Data/ValueReader.cs b/src/NTwain/Data/ValueReader.cs index 69120ac..06dd741 100644 --- a/src/NTwain/Data/ValueReader.cs +++ b/src/NTwain/Data/ValueReader.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; @@ -15,11 +14,11 @@ namespace NTwain.Data /// /// Reads pointer as UTF8 string. /// - /// /// Pointer to string. + /// /// Number of bytes to read. /// - public static unsafe string? PtrToStringUTF8(IMemoryManager memMgr, IntPtr data, int length) + public static unsafe string? PtrToStringUTF8(this IntPtr data, IMemoryManager memMgr, int length) { string? val = null; var locked = memMgr.Lock(data); @@ -47,244 +46,271 @@ namespace NTwain.Data return val; } + static T MarshalTo(IntPtr ptr) => Marshal.PtrToStructure(ptr)!; - // most of these are modified from the original TWAIN.CapabilityToCsv() + // these contain parts from the original TWAIN.CapabilityToCsv() - //public static TValue ReadOneValueContainer(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct - //{ - // if (cap.hContainer == IntPtr.Zero) return default; + /// + /// Reads a one value out of a cap. This can only be done once if memory is freed. + /// + /// + /// + /// + /// + /// + public static TValue ReadOneValue(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct + { + if (cap.ConType != TWON.ONEVALUE || cap.hContainer == IntPtr.Zero) return default; - // var lockedPtr = memMgr.Lock(cap.hContainer); + var lockedPtr = memMgr.Lock(cap.hContainer); - // try - // { - // TWTY itemType; - // // Mac has a level of indirection and a different structure (ick)... - // if (TwainPlatform.IsMacOSX) - // { - // // Crack the container... - // var onevalue = MarshalTo(lockedPtr); - // itemType = (TWTY)onevalue.ItemType; - // lockedPtr += Marshal.SizeOf(onevalue); - // } - // else - // { - // // Crack the container... - // var onevalue = MarshalTo(lockedPtr); - // itemType = onevalue.ItemType; - // lockedPtr += Marshal.SizeOf(onevalue); - // } + try + { + TWTY itemType; + // Mac has a level of indirection and a different structure (ick)... + if (TwainPlatform.IsMacOSX) + { + // Crack the container... + var onevalue = MarshalTo(lockedPtr); + itemType = (TWTY)onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } + else + { + // Crack the container... + var onevalue = MarshalTo(lockedPtr); + itemType = onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } - // return ReadContainerData(lockedPtr, itemType, 0); - // } - // finally - // { - // if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); - // if (freeMemory) memMgr.Free(ref cap.hContainer); - // } - //} - //public static Enumeration ReadEnumerationContainer(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct - //{ - // Enumeration retVal = new Enumeration(); + return ReadContainerData(lockedPtr, itemType, 0); + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + } - // if (cap.hContainer == IntPtr.Zero) return retVal; + public static Enumeration ReadEnumeration(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct + { + Enumeration retVal = new(); - // var lockedPtr = memMgr.Lock(cap.hContainer); + if (cap.ConType != TWON.ENUMERATION || cap.hContainer == IntPtr.Zero) return retVal; - // try - // { - // TWTY itemType; - // int count = 0; + var lockedPtr = memMgr.Lock(cap.hContainer); - // // Mac has a level of indirection and a different structure (ick)... - // if (TwainPlatform.IsMacOSX) - // { - // // Crack the container... - // var twenumerationmacosx = MarshalTo(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 if (TWAIN.GetPlatform() == Platform.WINDOWS || ((twain.m_blFoundLatestDsm || twain.m_blFoundLatestDsm64) && (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm))) - // { - // // Crack the container... - // var twenumeration = MarshalTo(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(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; - // } + try + { + TWTY itemType; + int count = 0; - // retVal.Items = new TValue[count]; + // Mac has a level of indirection and a different structure (ick)... + if (TwainPlatform.IsMacOSX) + { + // Crack the container... + var twenumerationmacosx = MarshalTo(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(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(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; + //} - // for (var i = 0; i < count; i++) - // { - // retVal.Items[i] = ReadContainerData(lockedPtr, itemType, i); - // } - // } - // finally - // { - // if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); - // if (freeMemory) memMgr.Free(ref cap.hContainer); - // } - // return retVal; - //} - //public static IList ReadArrayContainer(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct - //{ - // if (cap.hContainer == IntPtr.Zero) return EmptyArray.Value; + retVal.Items = new TValue[count]; - // var lockedPtr = memMgr.Lock(cap.hContainer); + for (var i = 0; i < count; i++) + { + retVal.Items[i] = ReadContainerData(lockedPtr, itemType, i); + } + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + return retVal; + } - // try - // { - // TWTY itemType; - // uint count; + public static IList ReadArray(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(); - // // Mac has a level of indirection and a different structure (ick)... - // if (TwainPlatform.IsMacOSX) - // { - // // Crack the container... - // var twarraymacosx = MarshalTo(lockedPtr); - // itemType = (TWTY)twarraymacosx.ItemType; - // count = twarraymacosx.NumItems; - // lockedPtr += Marshal.SizeOf(twarraymacosx); - // } - // else - // { - // // Crack the container... - // var twarray = MarshalTo(lockedPtr); - // itemType = twarray.ItemType; - // count = twarray.NumItems; - // lockedPtr += Marshal.SizeOf(twarray); - // } + var lockedPtr = memMgr.Lock(cap.hContainer); - // var arr = new TValue[count]; - // for (var i = 0; i < count; i++) - // { - // arr[i] = ReadContainerData(lockedPtr, itemType, i); - // } - // return arr; - // } - // finally - // { - // if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); - // if (freeMemory) memMgr.Free(ref cap.hContainer); - // } - //} - //public static Range ReadRangeContainer(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct - //{ - // var retVal = new Range(); + try + { + TWTY itemType; + uint count; - // if (cap.hContainer == IntPtr.Zero) return retVal; + // Mac has a level of indirection and a different structure (ick)... + if (TwainPlatform.IsMacOSX) + { + // Crack the container... + var twarraymacosx = MarshalTo(lockedPtr); + itemType = (TWTY)twarraymacosx.ItemType; + count = twarraymacosx.NumItems; + lockedPtr += Marshal.SizeOf(twarraymacosx); + } + else + { + // Crack the container... + var twarray = MarshalTo(lockedPtr); + itemType = twarray.ItemType; + count = twarray.NumItems; + lockedPtr += Marshal.SizeOf(twarray); + } - // var lockedPtr = memMgr.Lock(cap.hContainer); + var arr = new TValue[count]; + for (var i = 0; i < count; i++) + { + arr[i] = ReadContainerData(lockedPtr, itemType, i); + } + return arr; + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + } - // try - // { - // TW_RANGE twrange = default; - // TW_RANGE_FIX32 twrangefix32 = default; + public static Range ReadRange(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct + { + var retVal = new Range(); - // // Mac has a level of indirection and a different structure (ick)... - // if (TwainPlatform.IsMacOSX) - // { - // var twrangemacosx = MarshalTo(lockedPtr); - // var twrangefix32macosx = MarshalTo(lockedPtr); - // twrange.ItemType = (TWTY)twrangemacosx.ItemType; - // twrange.MinValue = twrangemacosx.MinValue; - // twrange.MaxValue = twrangemacosx.MaxValue; - // twrange.StepSize = twrangemacosx.StepSize; - // twrange.DefaultValue = twrangemacosx.DefaultValue; - // twrange.CurrentValue = twrangemacosx.CurrentValue; - // twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; - // twrangefix32.MinValue = twrangefix32macosx.MinValue; - // twrangefix32.MaxValue = twrangefix32macosx.MaxValue; - // twrangefix32.StepSize = twrangefix32macosx.StepSize; - // twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; - // twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; - // } - // // Windows or the 2.4+ Linux DSM... - // else if (TWAIN.GetPlatform() == Platform.WINDOWS || (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm) || - // ((twain.m_blFoundLatestDsm || twain.m_blFoundLatestDsm64) && (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm))) - // { - // twrange = MarshalTo(lockedPtr); - // twrangefix32 = MarshalTo(lockedPtr); - // } - // // The -2.3 Linux DSM... - // else - // { - // var twrangelinux64 = MarshalTo(lockedPtr); - // var twrangefix32macosx = MarshalTo(lockedPtr); - // twrange.ItemType = twrangelinux64.ItemType; - // twrange.MinValue = (uint)twrangelinux64.MinValue; - // twrange.MaxValue = (uint)twrangelinux64.MaxValue; - // twrange.StepSize = (uint)twrangelinux64.StepSize; - // twrange.DefaultValue = (uint)twrangelinux64.DefaultValue; - // twrange.CurrentValue = (uint)twrangelinux64.CurrentValue; - // twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; - // twrangefix32.MinValue = twrangefix32macosx.MinValue; - // twrangefix32.MaxValue = twrangefix32macosx.MaxValue; - // twrangefix32.StepSize = twrangefix32macosx.StepSize; - // twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; - // twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; - // } + if (cap.ConType != TWON.RANGE || cap.hContainer == IntPtr.Zero) return retVal; - // switch (twrange.ItemType) - // { - // // use dynamic since I know they fit the type. - // case TWTY.FIX32: - // retVal.MinValue = (dynamic)twrangefix32.MinValue; - // retVal.MaxValue = (dynamic)twrangefix32.MaxValue; - // retVal.StepSize = (dynamic)twrangefix32.StepSize; - // retVal.CurrentValue = (dynamic)twrangefix32.CurrentValue; - // retVal.DefaultValue = (dynamic)twrangefix32.DefaultValue; - // break; - // case TWTY.INT8: - // case TWTY.UINT8: - // case TWTY.INT16: - // case TWTY.BOOL: - // case TWTY.UINT16: - // case TWTY.INT32: - // case TWTY.UINT32: - // retVal.MinValue = (dynamic)twrange.MinValue; - // retVal.MaxValue = (dynamic)twrange.MaxValue; - // retVal.StepSize = (dynamic)twrange.StepSize; - // retVal.CurrentValue = (dynamic)twrange.CurrentValue; - // retVal.DefaultValue = (dynamic)twrange.DefaultValue; - // break; - // default: - // throw new NotSupportedException($"The value type {twrange.ItemType} is not supported as range."); + var lockedPtr = memMgr.Lock(cap.hContainer); - // } - // return retVal; - // } - // finally - // { - // if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); - // if (freeMemory) memMgr.Free(ref cap.hContainer); - // } - //} + try + { + TW_RANGE twrange = default; + TW_RANGE_FIX32 twrangefix32 = default; + + // Mac has a level of indirection and a different structure (ick)... + if (TwainPlatform.IsMacOSX) + { + var twrangemacosx = MarshalTo(lockedPtr); + var twrangefix32macosx = MarshalTo(lockedPtr); + twrange.ItemType = (TWTY)twrangemacosx.ItemType; + twrange.MinValue = twrangemacosx.MinValue; + twrange.MaxValue = twrangemacosx.MaxValue; + twrange.StepSize = twrangemacosx.StepSize; + twrange.DefaultValue = twrangemacosx.DefaultValue; + twrange.CurrentValue = twrangemacosx.CurrentValue; + twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; + twrangefix32.MinValue = twrangefix32macosx.MinValue; + twrangefix32.MaxValue = twrangefix32macosx.MaxValue; + twrangefix32.StepSize = twrangefix32macosx.StepSize; + twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; + twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; + } + // Windows or the 2.4+ Linux DSM... + else + { + twrange = MarshalTo(lockedPtr); + twrangefix32 = MarshalTo(lockedPtr); + } + // The -2.3 Linux DSM... + //else + //{ + // var twrangelinux64 = MarshalTo(lockedPtr); + // var twrangefix32macosx = MarshalTo(lockedPtr); + // twrange.ItemType = twrangelinux64.ItemType; + // twrange.MinValue = (uint)twrangelinux64.MinValue; + // twrange.MaxValue = (uint)twrangelinux64.MaxValue; + // twrange.StepSize = (uint)twrangelinux64.StepSize; + // twrange.DefaultValue = (uint)twrangelinux64.DefaultValue; + // twrange.CurrentValue = (uint)twrangelinux64.CurrentValue; + // twrangefix32.ItemType = (TWTY)twrangefix32macosx.ItemType; + // twrangefix32.MinValue = twrangefix32macosx.MinValue; + // twrangefix32.MaxValue = twrangefix32macosx.MaxValue; + // twrangefix32.StepSize = twrangefix32macosx.StepSize; + // twrangefix32.DefaultValue = twrangefix32macosx.DefaultValue; + // twrangefix32.CurrentValue = twrangefix32macosx.CurrentValue; + //} + + switch (twrange.ItemType) + { + // use dynamic since I know they fit the type. + case TWTY.FIX32: + retVal.MinValue = (dynamic)twrangefix32.MinValue; + retVal.MaxValue = (dynamic)twrangefix32.MaxValue; + retVal.StepSize = (dynamic)twrangefix32.StepSize; + retVal.CurrentValue = (dynamic)twrangefix32.CurrentValue; + retVal.DefaultValue = (dynamic)twrangefix32.DefaultValue; + break; + case TWTY.INT8: + case TWTY.UINT8: + case TWTY.INT16: + case TWTY.BOOL: + case TWTY.UINT16: + case TWTY.INT32: + case TWTY.UINT32: + retVal.MinValue = (dynamic)twrange.MinValue; + retVal.MaxValue = (dynamic)twrange.MaxValue; + retVal.StepSize = (dynamic)twrange.StepSize; + retVal.CurrentValue = (dynamic)twrange.CurrentValue; + retVal.DefaultValue = (dynamic)twrange.DefaultValue; + break; + default: + throw new NotSupportedException($"The value type {twrange.ItemType} is not supported as range."); + + } + return retVal; + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + } ///// ///// Read the one value of a cap as string. Only STR* and HANDLE types are supported. @@ -356,99 +382,101 @@ namespace NTwain.Data // } // return null; //} - ///// - ///// Read the container pointer content. - ///// - ///// A locked pointer to the container's data pointer. If data is array this is the 0th item. - ///// The twain type. - ///// Index of the item if pointer is array. - ///// - //static TValue ReadContainerData(IntPtr intptr, TWTY type, int itemIndex) where TValue : struct - //{ - // var isEnum = typeof(TValue).IsEnum; + /// + /// Read the container pointer content. + /// + /// A locked pointer to the container's data pointer. If data is array this is the 0th item. + /// The twain type. + /// Index of the item if pointer is array. + /// + static TValue ReadContainerData(IntPtr intptr, TWTY type, int itemIndex) where TValue : struct + { + var isEnum = typeof(TValue).IsEnum; - // switch (type) - // { - // default: - // throw new NotSupportedException($"Unsupported item type {type} for reading."); - // // TODO: verify if needs to read int32 for small types - // case TWTY.INT8: - // intptr += 1 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.UINT8: - // intptr += 1 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.INT16: - // intptr += 2 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.BOOL: - // case TWTY.UINT16: - // intptr += 2 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.INT32: - // intptr += 4 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.UINT32: - // intptr += 4 * itemIndex; - // if (isEnum) - // { - // return NumericToEnum(MarshalTo(intptr)); - // } - // return MarshalTo(intptr); - // case TWTY.FIX32: - // intptr += 4 * itemIndex; - // return MarshalTo(intptr); - // case TWTY.FRAME: - // intptr += 16 * itemIndex; - // return MarshalTo(intptr); - // case TWTY.STR32: - // intptr += TW_STR32.Size * itemIndex; - // return MarshalTo(intptr); - // case TWTY.STR64: - // intptr += TW_STR64.Size * itemIndex; - // return MarshalTo(intptr); - // case TWTY.STR128: - // intptr += TW_STR128.Size * itemIndex; - // return MarshalTo(intptr); - // case TWTY.STR255: - // intptr += TW_STR255.Size * itemIndex; - // return MarshalTo(intptr); - // } - //} + switch (type) + { + default: + throw new NotSupportedException($"Unsupported item type {type} for reading."); + // TODO: verify if needs to read int32 for small types + case TWTY.INT8: + intptr += 1 * itemIndex; + if (isEnum) + { + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.UINT8: + intptr += 1 * itemIndex; + if (isEnum) + { + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.INT16: + intptr += 2 * itemIndex; + if (isEnum) + { + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.BOOL: + case TWTY.UINT16: + intptr += 2 * itemIndex; + if (isEnum) + { + var shor = MarshalTo(intptr); + if (shor == 0x1125) Debugger.Break(); + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.INT32: + intptr += 4 * itemIndex; + if (isEnum) + { + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.UINT32: + intptr += 4 * itemIndex; + if (isEnum) + { + return NumericToEnum(MarshalTo(intptr)); + } + return MarshalTo(intptr); + case TWTY.FIX32: + intptr += 4 * itemIndex; + return MarshalTo(intptr); + case TWTY.FRAME: + intptr += 16 * itemIndex; + return MarshalTo(intptr); + case TWTY.STR32: + intptr += TW_STR32.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR64: + intptr += TW_STR64.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR128: + intptr += TW_STR128.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR255: + intptr += TW_STR255.Size * itemIndex; + return MarshalTo(intptr); + } + } - //static TEnum NumericToEnum(TNumber num) where TEnum : struct - //{ - // // 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(); + static TEnum NumericToEnum(TNumber num) where TEnum : struct + { + // TODO: some caps returns a data type that's not the underlying datatype for the enum + // so foolproof way is to ToString() it and parse it as the enum type. + // this is bad for perf so find better way later + var str = num!.ToString(); - // if (Enum.TryParse(str, out TEnum parsed)) - // { - // return parsed; - // } - // return default; - //} + if (Enum.TryParse(str, out TEnum parsed)) + { + return parsed; + } + return default; + } - //static T MarshalTo(IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); } } diff --git a/src/NTwain/Data/ValueWriter.cs b/src/NTwain/Data/ValueWriter.cs index d9d1773..345a061 100644 --- a/src/NTwain/Data/ValueWriter.cs +++ b/src/NTwain/Data/ValueWriter.cs @@ -13,11 +13,11 @@ namespace NTwain.Data /// /// Allocates and copies the string value into a pointer in UTF8 that's null-terminated. /// - /// /// + /// /// Final length to use with the pointer (includes the null). /// - public static unsafe IntPtr StringToPtrUTF8(IMemoryManager memMgr, string? value, out uint finalLength) + public static unsafe IntPtr StringToPtrUTF8(this string? value, IMemoryManager memMgr, out uint finalLength) { finalLength = 0; if (value == null) return IntPtr.Zero; diff --git a/src/NTwain/NTwain.csproj b/src/NTwain/NTwain.csproj index bd52a54..6d751b2 100644 --- a/src/NTwain/NTwain.csproj +++ b/src/NTwain/NTwain.csproj @@ -13,7 +13,11 @@ true + + + + diff --git a/src/NTwain/TwainAppSession.Caps.cs b/src/NTwain/TwainAppSession.Caps.cs index a7fd815..783abdb 100644 --- a/src/NTwain/TwainAppSession.Caps.cs +++ b/src/NTwain/TwainAppSession.Caps.cs @@ -1,7 +1,7 @@ using NTwain.Data; using NTwain.Triplets; +using System; using System.Collections.Generic; -using System.Linq; namespace NTwain { @@ -9,20 +9,20 @@ namespace NTwain partial class TwainAppSession { - ///// - ///// Gets all the supported caps for the current source. - ///// - ///// - //public IEnumerable GetAllCaps() - //{ - // // just as a sample of how to read cap values + /// + /// Gets all the supported caps for the current source. + /// + /// + public IList GetAllCaps() + { + // just as a sample of how to read cap values - // if (GetCapValues(CAP.CAP_SUPPORTEDCAPS, out TW_CAPABILITY value) == TWRC.SUCCESS) - // { - // value.Read(this); - // } - // return Enumerable.Empty(); - //} + if (GetCapValues(CAP.CAP_SUPPORTEDCAPS, out TW_CAPABILITY value).RC == TWRC.SUCCESS) + { + return value.ReadArray(this); + } + return Array.Empty(); + } /// /// Gets a CAP's actual supported operations. @@ -32,10 +32,10 @@ namespace NTwain /// public TWQC QueryCapSupport(CAP cap) { - var value = new TW_CAPABILITY(cap); + var value = new TW_CAPABILITY(cap) { ConType = TWON.ONEVALUE }; if (DGControl.Capability.QuerySupport(ref _appIdentity, ref _currentDS, ref value) == TWRC.SUCCESS) { - value.Read(this); + return value.ReadOneValue(this); } return TWQC.Unknown; } diff --git a/src/NTwain/TwainAppSession.Sources.cs b/src/NTwain/TwainAppSession.Sources.cs index d727cce..088eb8e 100644 --- a/src/NTwain/TwainAppSession.Sources.cs +++ b/src/NTwain/TwainAppSession.Sources.cs @@ -47,9 +47,9 @@ namespace NTwain var rc = DGControl.Identity.OpenDS(ref _appIdentity, ref source); if (rc == TWRC.SUCCESS) { + State = STATE.S4; RegisterCallback(); CurrentSource = source; - State = STATE.S4; } return WrapInSTS(rc); } @@ -158,7 +158,7 @@ namespace NTwain { task.SizeOf = (uint)Marshal.SizeOf(typeof(TW_TWAINDIRECT)); task.CommunicationManager = communicationManager; - task.Send = ValueWriter.StringToPtrUTF8(this, taskJson, out uint length); + task.Send = taskJson.StringToPtrUTF8(this, out uint length); task.SendSize = length; result.ReturnCode = DGControl.TwainDirect.SetTask(ref _appIdentity, ref _currentDS, ref task); @@ -168,7 +168,7 @@ namespace NTwain } else if (result.ReturnCode == TWRC.SUCCESS && task.ReceiveSize > 0 && task.Receive != IntPtr.Zero) { - result.ResponseJson = ValueReader.PtrToStringUTF8(this, task.Receive, (int)task.ReceiveSize); + result.ResponseJson = task.Receive.PtrToStringUTF8(this, (int)task.ReceiveSize); } } finally