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