Attempt to read cap values.

This commit is contained in:
Eugene Wang 2023-04-05 21:59:50 -04:00
parent 513dc96f64
commit ab2947d03b
9 changed files with 498 additions and 440 deletions

View File

@ -41,9 +41,11 @@
lblState = new System.Windows.Forms.Label(); lblState = new System.Windows.Forms.Label();
label3 = new System.Windows.Forms.Label(); label3 = new System.Windows.Forms.Label();
btnOpenDef = new System.Windows.Forms.Button(); 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(); btnShowSettings = new System.Windows.Forms.Button();
btnClose = new System.Windows.Forms.Button(); btnClose = new System.Windows.Forms.Button();
btnStart = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
splitContainer1.Panel1.SuspendLayout(); splitContainer1.Panel1.SuspendLayout();
splitContainer1.Panel2.SuspendLayout(); splitContainer1.Panel2.SuspendLayout();
@ -56,7 +58,7 @@
btnSelect.Name = "btnSelect"; btnSelect.Name = "btnSelect";
btnSelect.Size = new System.Drawing.Size(151, 23); btnSelect.Size = new System.Drawing.Size(151, 23);
btnSelect.TabIndex = 0; btnSelect.TabIndex = 0;
btnSelect.Text = "Select default source"; btnSelect.Text = "Choose default source";
btnSelect.UseVisualStyleBackColor = true; btnSelect.UseVisualStyleBackColor = true;
btnSelect.Click += btnSelect_Click; 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.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.FormattingEnabled = true;
listSources.ItemHeight = 15; listSources.ItemHeight = 15;
listSources.Location = new System.Drawing.Point(13, 130); listSources.Location = new System.Drawing.Point(13, 170);
listSources.Name = "listSources"; listSources.Name = "listSources";
listSources.Size = new System.Drawing.Size(289, 379); listSources.Size = new System.Drawing.Size(297, 379);
listSources.TabIndex = 6; listSources.TabIndex = 6;
// //
// btnSetDef // btnSetDef
// //
btnSetDef.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; btnSetDef.Location = new System.Drawing.Point(13, 131);
btnSetDef.Location = new System.Drawing.Point(12, 515);
btnSetDef.Name = "btnSetDef"; btnSetDef.Name = "btnSetDef";
btnSetDef.Size = new System.Drawing.Size(169, 23); btnSetDef.Size = new System.Drawing.Size(169, 23);
btnSetDef.TabIndex = 7; btnSetDef.TabIndex = 7;
@ -129,10 +130,9 @@
// //
// btnOpen // btnOpen
// //
btnOpen.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; btnOpen.Location = new System.Drawing.Point(188, 131);
btnOpen.Location = new System.Drawing.Point(187, 515);
btnOpen.Name = "btnOpen"; btnOpen.Name = "btnOpen";
btnOpen.Size = new System.Drawing.Size(104, 23); btnOpen.Size = new System.Drawing.Size(114, 23);
btnOpen.TabIndex = 8; btnOpen.TabIndex = 8;
btnOpen.Text = "Open selected"; btnOpen.Text = "Open selected";
btnOpen.UseVisualStyleBackColor = true; btnOpen.UseVisualStyleBackColor = true;
@ -160,13 +160,15 @@
// //
// splitContainer1.Panel2 // splitContainer1.Panel2
// //
splitContainer1.Panel2.Controls.Add(listCaps);
splitContainer1.Panel2.Controls.Add(label4);
splitContainer1.Panel2.Controls.Add(btnStart); splitContainer1.Panel2.Controls.Add(btnStart);
splitContainer1.Panel2.Controls.Add(btnShowSettings); splitContainer1.Panel2.Controls.Add(btnShowSettings);
splitContainer1.Panel2.Controls.Add(btnClose); splitContainer1.Panel2.Controls.Add(btnClose);
splitContainer1.Panel2.Controls.Add(label2); splitContainer1.Panel2.Controls.Add(label2);
splitContainer1.Panel2.Controls.Add(lblCurrent); splitContainer1.Panel2.Controls.Add(lblCurrent);
splitContainer1.Size = new System.Drawing.Size(800, 564); splitContainer1.Size = new System.Drawing.Size(1023, 564);
splitContainer1.SplitterDistance = 305; splitContainer1.SplitterDistance = 325;
splitContainer1.TabIndex = 9; splitContainer1.TabIndex = 9;
// //
// lblState // lblState
@ -197,6 +199,34 @@
btnOpenDef.UseVisualStyleBackColor = true; btnOpenDef.UseVisualStyleBackColor = true;
btnOpenDef.Click += btnOpenDef_Click; 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
// //
btnShowSettings.Location = new System.Drawing.Point(161, 32); btnShowSettings.Location = new System.Drawing.Point(161, 32);
@ -217,21 +247,11 @@
btnClose.UseVisualStyleBackColor = true; btnClose.UseVisualStyleBackColor = true;
btnClose.Click += btnClose_Click; 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 // Form1
// //
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(800, 564); ClientSize = new System.Drawing.Size(1023, 564);
Controls.Add(splitContainer1); Controls.Add(splitContainer1);
Name = "Form1"; Name = "Form1";
Text = "TWAIN Test"; Text = "TWAIN Test";
@ -262,5 +282,7 @@
private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button btnShowSettings; private System.Windows.Forms.Button btnShowSettings;
private System.Windows.Forms.Button btnStart; private System.Windows.Forms.Button btnStart;
private System.Windows.Forms.ListBox listCaps;
private System.Windows.Forms.Label label4;
} }
} }

View File

@ -18,15 +18,6 @@ namespace WinFormSample
var libVer = FileVersionInfo.GetVersionInfo(typeof(TwainAppSession).Assembly.Location).FileVersion; var libVer = FileVersionInfo.GetVersionInfo(typeof(TwainAppSession).Assembly.Location).FileVersion;
Text += $"{(TwainPlatform.Is32bit ? " 32bit" : " 64bit")} on NTwain {libVer}"; 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; TwainPlatform.PreferLegacyDSM = false;
twain = new TwainAppSession(new WinformMarshaller(this), Assembly.GetExecutingAssembly().Location); 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) private void Twain_CurrentSourceChanged(TwainAppSession arg1, TW_IDENTITY_LEGACY ds)
{ {
lblCurrent.Text = ds.ProductName; 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) private void Twain_DefaultSourceChanged(TwainAppSession arg1, TW_IDENTITY_LEGACY ds)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Windows.Forms; using System.Windows.Forms;
namespace WinFormSample namespace WinFormSample
@ -11,6 +12,15 @@ namespace WinFormSample
[STAThread] [STAThread]
static void Main() 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, // To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration. // see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize(); ApplicationConfiguration.Initialize();

View File

@ -1,9 +1,7 @@
using NTwain; using System;
using System; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
namespace NTwain.Data namespace NTwain.Data
{ {
@ -236,89 +234,89 @@ namespace NTwain.Data
//} //}
} }
///// <summary> /// <summary>
///// A more dotnet-friendly representation of <see cref="TW_ENUMERATION"/>. /// A more dotnet-friendly representation of <see cref="TW_ENUMERATION"/>.
///// </summary> /// </summary>
///// <typeparam name="TValue"></typeparam> /// <typeparam name="TValue"></typeparam>
//public class Enumeration<TValue> where TValue : struct public class Enumeration<TValue> where TValue : struct
//{ {
// public int CurrentIndex; public int CurrentIndex;
// public int DefaultIndex; public int DefaultIndex;
// public TValue[] Items; public TValue[]? Items;
//} }
///// <summary> /// <summary>
///// A more dotnet-friendly representation of <see cref="TW_RANGE"/>. /// A more dotnet-friendly representation of <see cref="TW_RANGE"/>.
///// </summary> /// </summary>
///// <typeparam name="TValue"></typeparam> /// <typeparam name="TValue"></typeparam>
//public partial class Range<TValue> : IEnumerable<TValue> where TValue : struct public partial class Range<TValue> : IEnumerable<TValue> where TValue : struct
//{ {
// public TValue MinValue; public TValue MinValue;
// public TValue MaxValue; public TValue MaxValue;
// public TValue StepSize; public TValue StepSize;
// public TValue DefaultValue; public TValue DefaultValue;
// public TValue CurrentValue; public TValue CurrentValue;
// IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
// { {
// if (!(MinValue is IConvertible)) if (!(MinValue is IConvertible))
// throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported for range enumeration."); 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() System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
// { {
// return ((IEnumerable<TValue>)this).GetEnumerator(); return ((IEnumerable<TValue>)this).GetEnumerator();
// } }
// // dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric // dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric
// class DynamicEnumerator : IEnumerator<TValue> class DynamicEnumerator : IEnumerator<TValue>
// { {
// private readonly TValue _min; private readonly TValue _min;
// private readonly TValue _max; private readonly TValue _max;
// private readonly TValue _step; private readonly TValue _step;
// private TValue _cur; private TValue _cur;
// bool started = false; bool started = false;
// public DynamicEnumerator(TValue min, TValue max, TValue step) public DynamicEnumerator(TValue min, TValue max, TValue step)
// { {
// _min = min; _min = min;
// _max = max; _max = max;
// _step = step; _step = step;
// _cur = min; _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() public bool MoveNext()
// { {
// if (!started) if (!started)
// { {
// started = true; started = true;
// return true; return true;
// } }
// var next = _cur + (dynamic)_step; var next = _cur + (dynamic)_step;
// if (next == _cur || next < _min || next > _max) return false; if (next == _cur || next < _min || next > _max) return false;
// _cur = next; _cur = next;
// return true; return true;
// } }
// public void Reset() public void Reset()
// { {
// _cur = _min; _cur = _min;
// started = false; started = false;
// } }
// } }
//} }
partial struct TW_FIX32 : IEquatable<TW_FIX32>, IConvertible partial struct TW_FIX32 : IEquatable<TW_FIX32>, IConvertible
{ {
@ -716,7 +714,7 @@ namespace NTwain.Data
string? val = null; string? val = null;
if (UTF8string != IntPtr.Zero && Size > 0) if (UTF8string != IntPtr.Zero && Size > 0)
{ {
val = ValueReader.PtrToStringUTF8(mgr, UTF8string, (int)Size); val = UTF8string.PtrToStringUTF8(mgr, (int)Size);
} }
if (freeMemory) Free(mgr); if (freeMemory) Free(mgr);
return val; return val;
@ -741,11 +739,6 @@ namespace NTwain.Data
mgr.Free(hContainer); mgr.Free(hContainer);
hContainer = IntPtr.Zero; hContainer = IntPtr.Zero;
} }
public void Read(IMemoryManager mgr, bool freeMemory = true)
{
if (freeMemory) Free(mgr);
}
} }
//partial struct TW_DEVICEEVENT //partial struct TW_DEVICEEVENT

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -15,11 +14,11 @@ namespace NTwain.Data
/// <summary> /// <summary>
/// Reads pointer as UTF8 string. /// Reads pointer as UTF8 string.
/// </summary> /// </summary>
/// <param name="memMgr"></param>
/// <param name="data">Pointer to string.</param> /// <param name="data">Pointer to string.</param>
/// <param name="memMgr"></param>
/// <param name="length">Number of bytes to read.</param> /// <param name="length">Number of bytes to read.</param>
/// <returns></returns> /// <returns></returns>
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; string? val = null;
var locked = memMgr.Lock(data); var locked = memMgr.Lock(data);
@ -47,244 +46,271 @@ namespace NTwain.Data
return val; return val;
} }
static T MarshalTo<T>(IntPtr ptr) => Marshal.PtrToStructure<T>(ptr)!;
// most of these are modified from the original TWAIN.CapabilityToCsv() // these contain parts from the original TWAIN.CapabilityToCsv()
//public static TValue ReadOneValueContainer<TValue>(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct /// <summary>
//{ /// Reads a one value out of a cap. This can only be done once if memory is freed.
// if (cap.hContainer == IntPtr.Zero) return default; /// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="cap"></param>
/// <param name="memMgr"></param>
/// <param name="freeMemory"></param>
/// <returns></returns>
public static TValue ReadOneValue<TValue>(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 try
// { {
// TWTY itemType; TWTY itemType;
// // Mac has a level of indirection and a different structure (ick)... // Mac has a level of indirection and a different structure (ick)...
// if (TwainPlatform.IsMacOSX) if (TwainPlatform.IsMacOSX)
// { {
// // Crack the container... // Crack the container...
// var onevalue = MarshalTo<TW_ONEVALUE_MACOSX>(lockedPtr); var onevalue = MarshalTo<TW_ONEVALUE_MACOSX>(lockedPtr);
// itemType = (TWTY)onevalue.ItemType; itemType = (TWTY)onevalue.ItemType;
// lockedPtr += Marshal.SizeOf(onevalue); lockedPtr += Marshal.SizeOf(onevalue);
// } }
// else else
// { {
// // Crack the container... // Crack the container...
// var onevalue = MarshalTo<TW_ONEVALUE>(lockedPtr); var onevalue = MarshalTo<TW_ONEVALUE>(lockedPtr);
// itemType = onevalue.ItemType; itemType = onevalue.ItemType;
// lockedPtr += Marshal.SizeOf(onevalue); lockedPtr += Marshal.SizeOf(onevalue);
// } }
// return ReadContainerData<TValue>(lockedPtr, itemType, 0); return ReadContainerData<TValue>(lockedPtr, itemType, 0);
// } }
// finally finally
// { {
// if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
// if (freeMemory) memMgr.Free(ref cap.hContainer); if (freeMemory)
// } {
//} memMgr.Free(cap.hContainer);
//public static Enumeration<TValue> ReadEnumerationContainer<TValue>(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct cap.hContainer = IntPtr.Zero;
//{ }
// Enumeration<TValue> retVal = new Enumeration<TValue>(); }
}
// if (cap.hContainer == IntPtr.Zero) return retVal; public static Enumeration<TValue> ReadEnumeration<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
{
Enumeration<TValue> retVal = new();
// var lockedPtr = memMgr.Lock(cap.hContainer); if (cap.ConType != TWON.ENUMERATION || cap.hContainer == IntPtr.Zero) return retVal;
// try var lockedPtr = memMgr.Lock(cap.hContainer);
// {
// TWTY itemType;
// int count = 0;
// // Mac has a level of indirection and a different structure (ick)... try
// if (TwainPlatform.IsMacOSX) {
// { TWTY itemType;
// // Crack the container... int count = 0;
// var twenumerationmacosx = MarshalTo<TW_ENUMERATION_MACOSX>(lockedPtr);
// itemType = (TWTY)twenumerationmacosx.ItemType;
// count = (int)twenumerationmacosx.NumItems;
// retVal.DefaultIndex = (int)twenumerationmacosx.DefaultIndex;
// retVal.CurrentIndex = (int)twenumerationmacosx.CurrentIndex;
// lockedPtr += Marshal.SizeOf(twenumerationmacosx);
// }
// // Windows or the 2.4+ Linux DSM...
// else if (TWAIN.GetPlatform() == Platform.WINDOWS || ((twain.m_blFoundLatestDsm || twain.m_blFoundLatestDsm64) && (twain.m_linuxdsm == TWAIN.LinuxDsm.IsLatestDsm)))
// {
// // Crack the container...
// var twenumeration = MarshalTo<TW_ENUMERATION>(lockedPtr);
// itemType = twenumeration.ItemType;
// count = (int)twenumeration.NumItems;
// retVal.DefaultIndex = (int)twenumeration.DefaultIndex;
// retVal.CurrentIndex = (int)twenumeration.CurrentIndex;
// lockedPtr += Marshal.SizeOf(twenumeration);
// }
// // The -2.3 Linux DSM...
// else if (twain.m_blFound020302Dsm64bit && (twain.m_linuxdsm == TWAIN.LinuxDsm.Is020302Dsm64bit))
// {
// // Crack the container...
// var twenumerationlinux64 = MarshalTo<TW_ENUMERATION_LINUX64>(lockedPtr);
// itemType = twenumerationlinux64.ItemType;
// count = (int)twenumerationlinux64.NumItems;
// retVal.DefaultIndex = (int)twenumerationlinux64.DefaultIndex;
// retVal.CurrentIndex = (int)twenumerationlinux64.CurrentIndex;
// lockedPtr += Marshal.SizeOf(twenumerationlinux64);
// }
// // This shouldn't be possible, but what the hey...
// else
// {
// Log.Error("This is serious, you win a cookie for getting here...");
// return retVal;
// }
// retVal.Items = new TValue[count]; // Mac has a level of indirection and a different structure (ick)...
if (TwainPlatform.IsMacOSX)
{
// Crack the container...
var twenumerationmacosx = MarshalTo<TW_ENUMERATION_MACOSX>(lockedPtr);
itemType = (TWTY)twenumerationmacosx.ItemType;
count = (int)twenumerationmacosx.NumItems;
retVal.DefaultIndex = (int)twenumerationmacosx.DefaultIndex;
retVal.CurrentIndex = (int)twenumerationmacosx.CurrentIndex;
lockedPtr += Marshal.SizeOf(twenumerationmacosx);
}
// Windows or the 2.4+ Linux DSM...
else
{
// Crack the container...
var twenumeration = MarshalTo<TW_ENUMERATION>(lockedPtr);
itemType = twenumeration.ItemType;
count = (int)twenumeration.NumItems;
retVal.DefaultIndex = (int)twenumeration.DefaultIndex;
retVal.CurrentIndex = (int)twenumeration.CurrentIndex;
lockedPtr += Marshal.SizeOf(twenumeration);
}
// The -2.3 Linux DSM...
//else if (twain.m_blFound020302Dsm64bit && (twain.m_linuxdsm == TWAIN.LinuxDsm.Is020302Dsm64bit))
//{
// // Crack the container...
// var twenumerationlinux64 = MarshalTo<TW_ENUMERATION_LINUX64>(lockedPtr);
// itemType = twenumerationlinux64.ItemType;
// count = (int)twenumerationlinux64.NumItems;
// retVal.DefaultIndex = (int)twenumerationlinux64.DefaultIndex;
// retVal.CurrentIndex = (int)twenumerationlinux64.CurrentIndex;
// lockedPtr += Marshal.SizeOf(twenumerationlinux64);
//}
// This shouldn't be possible, but what the hey...
//else
//{
// Log.Error("This is serious, you win a cookie for getting here...");
// return retVal;
//}
// for (var i = 0; i < count; i++) retVal.Items = new TValue[count];
// {
// retVal.Items[i] = ReadContainerData<TValue>(lockedPtr, itemType, i);
// }
// }
// finally
// {
// if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
// if (freeMemory) memMgr.Free(ref cap.hContainer);
// }
// return retVal;
//}
//public static IList<TValue> ReadArrayContainer<TValue>(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct
//{
// if (cap.hContainer == IntPtr.Zero) return EmptyArray<TValue>.Value;
// var lockedPtr = memMgr.Lock(cap.hContainer); for (var i = 0; i < count; i++)
{
retVal.Items[i] = ReadContainerData<TValue>(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 public static IList<TValue> ReadArray<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
// { {
// TWTY itemType; if (cap.ConType != TWON.ARRAY || cap.hContainer == IntPtr.Zero) return Array.Empty<TValue>();
// uint count;
// // Mac has a level of indirection and a different structure (ick)... var lockedPtr = memMgr.Lock(cap.hContainer);
// if (TwainPlatform.IsMacOSX)
// {
// // Crack the container...
// var twarraymacosx = MarshalTo<TW_ARRAY_MACOSX>(lockedPtr);
// itemType = (TWTY)twarraymacosx.ItemType;
// count = twarraymacosx.NumItems;
// lockedPtr += Marshal.SizeOf(twarraymacosx);
// }
// else
// {
// // Crack the container...
// var twarray = MarshalTo<TW_ARRAY>(lockedPtr);
// itemType = twarray.ItemType;
// count = twarray.NumItems;
// lockedPtr += Marshal.SizeOf(twarray);
// }
// var arr = new TValue[count]; try
// for (var i = 0; i < count; i++) {
// { TWTY itemType;
// arr[i] = ReadContainerData<TValue>(lockedPtr, itemType, i); uint count;
// }
// return arr;
// }
// finally
// {
// if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer);
// if (freeMemory) memMgr.Free(ref cap.hContainer);
// }
//}
//public static Range<TValue> ReadRangeContainer<TValue>(IMemoryManager memMgr, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct
//{
// var retVal = new Range<TValue>();
// 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<TW_ARRAY_MACOSX>(lockedPtr);
itemType = (TWTY)twarraymacosx.ItemType;
count = twarraymacosx.NumItems;
lockedPtr += Marshal.SizeOf(twarraymacosx);
}
else
{
// Crack the container...
var twarray = MarshalTo<TW_ARRAY>(lockedPtr);
itemType = twarray.ItemType;
count = twarray.NumItems;
lockedPtr += Marshal.SizeOf(twarray);
}
// var lockedPtr = memMgr.Lock(cap.hContainer); var arr = new TValue[count];
for (var i = 0; i < count; i++)
{
arr[i] = ReadContainerData<TValue>(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 public static Range<TValue> ReadRange<TValue>(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct
// { {
// TW_RANGE twrange = default; var retVal = new Range<TValue>();
// TW_RANGE_FIX32 twrangefix32 = default;
// // Mac has a level of indirection and a different structure (ick)... if (cap.ConType != TWON.RANGE || cap.hContainer == IntPtr.Zero) return retVal;
// if (TwainPlatform.IsMacOSX)
// {
// var twrangemacosx = MarshalTo<TW_RANGE_MACOSX>(lockedPtr);
// var twrangefix32macosx = MarshalTo<TW_RANGE_FIX32_MACOSX>(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<TW_RANGE>(lockedPtr);
// twrangefix32 = MarshalTo<TW_RANGE_FIX32>(lockedPtr);
// }
// // The -2.3 Linux DSM...
// else
// {
// var twrangelinux64 = MarshalTo<TW_RANGE_LINUX64>(lockedPtr);
// var twrangefix32macosx = MarshalTo<TW_RANGE_FIX32_MACOSX>(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) var lockedPtr = memMgr.Lock(cap.hContainer);
// {
// // 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.");
// } try
// return retVal; {
// } TW_RANGE twrange = default;
// finally TW_RANGE_FIX32 twrangefix32 = default;
// {
// if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); // Mac has a level of indirection and a different structure (ick)...
// if (freeMemory) memMgr.Free(ref cap.hContainer); if (TwainPlatform.IsMacOSX)
// } {
//} var twrangemacosx = MarshalTo<TW_RANGE_MACOSX>(lockedPtr);
var twrangefix32macosx = MarshalTo<TW_RANGE_FIX32_MACOSX>(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<TW_RANGE>(lockedPtr);
twrangefix32 = MarshalTo<TW_RANGE_FIX32>(lockedPtr);
}
// The -2.3 Linux DSM...
//else
//{
// var twrangelinux64 = MarshalTo<TW_RANGE_LINUX64>(lockedPtr);
// var twrangefix32macosx = MarshalTo<TW_RANGE_FIX32_MACOSX>(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;
}
}
}
///// <summary> ///// <summary>
///// Read the one value of a cap as string. Only STR* and HANDLE types are supported. ///// 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; // return null;
//} //}
///// <summary> /// <summary>
///// Read the container pointer content. /// Read the container pointer content.
///// </summary> /// </summary>
///// <param name="intptr">A locked pointer to the container's data pointer. If data is array this is the 0th item.</param> /// <param name="intptr">A locked pointer to the container's data pointer. If data is array this is the 0th item.</param>
///// <param name="type">The twain type.</param> /// <param name="type">The twain type.</param>
///// <param name="itemIndex">Index of the item if pointer is array.</param> /// <param name="itemIndex">Index of the item if pointer is array.</param>
///// <returns></returns> /// <returns></returns>
//static TValue ReadContainerData<TValue>(IntPtr intptr, TWTY type, int itemIndex) where TValue : struct static TValue ReadContainerData<TValue>(IntPtr intptr, TWTY type, int itemIndex) where TValue : struct
//{ {
// var isEnum = typeof(TValue).IsEnum; var isEnum = typeof(TValue).IsEnum;
// switch (type) switch (type)
// { {
// default: default:
// throw new NotSupportedException($"Unsupported item type {type} for reading."); throw new NotSupportedException($"Unsupported item type {type} for reading.");
// // TODO: verify if needs to read int32 for small types // TODO: verify if needs to read int32 for small types
// case TWTY.INT8: case TWTY.INT8:
// intptr += 1 * itemIndex; intptr += 1 * itemIndex;
// if (isEnum) if (isEnum)
// { {
// return NumericToEnum<sbyte, TValue>(MarshalTo<sbyte>(intptr)); return NumericToEnum<sbyte, TValue>(MarshalTo<sbyte>(intptr));
// } }
// return MarshalTo<TValue>(intptr); return MarshalTo<TValue>(intptr);
// case TWTY.UINT8: case TWTY.UINT8:
// intptr += 1 * itemIndex; intptr += 1 * itemIndex;
// if (isEnum) if (isEnum)
// { {
// return NumericToEnum<byte, TValue>(MarshalTo<byte>(intptr)); return NumericToEnum<byte, TValue>(MarshalTo<byte>(intptr));
// } }
// return MarshalTo<TValue>(intptr); return MarshalTo<TValue>(intptr);
// case TWTY.INT16: case TWTY.INT16:
// intptr += 2 * itemIndex; intptr += 2 * itemIndex;
// if (isEnum) if (isEnum)
// { {
// return NumericToEnum<short, TValue>(MarshalTo<short>(intptr)); return NumericToEnum<short, TValue>(MarshalTo<short>(intptr));
// } }
// return MarshalTo<TValue>(intptr); return MarshalTo<TValue>(intptr);
// case TWTY.BOOL: case TWTY.BOOL:
// case TWTY.UINT16: case TWTY.UINT16:
// intptr += 2 * itemIndex; intptr += 2 * itemIndex;
// if (isEnum) if (isEnum)
// { {
// return NumericToEnum<ushort, TValue>(MarshalTo<ushort>(intptr)); var shor = MarshalTo<short>(intptr);
// } if (shor == 0x1125) Debugger.Break();
// return MarshalTo<TValue>(intptr); return NumericToEnum<ushort, TValue>(MarshalTo<ushort>(intptr));
// case TWTY.INT32: }
// intptr += 4 * itemIndex; return MarshalTo<TValue>(intptr);
// if (isEnum) case TWTY.INT32:
// { intptr += 4 * itemIndex;
// return NumericToEnum<int, TValue>(MarshalTo<int>(intptr)); if (isEnum)
// } {
// return MarshalTo<TValue>(intptr); return NumericToEnum<int, TValue>(MarshalTo<int>(intptr));
// case TWTY.UINT32: }
// intptr += 4 * itemIndex; return MarshalTo<TValue>(intptr);
// if (isEnum) case TWTY.UINT32:
// { intptr += 4 * itemIndex;
// return NumericToEnum<uint, TValue>(MarshalTo<uint>(intptr)); if (isEnum)
// } {
// return MarshalTo<TValue>(intptr); return NumericToEnum<uint, TValue>(MarshalTo<uint>(intptr));
// case TWTY.FIX32: }
// intptr += 4 * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.FIX32:
// case TWTY.FRAME: intptr += 4 * itemIndex;
// intptr += 16 * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.FRAME:
// case TWTY.STR32: intptr += 16 * itemIndex;
// intptr += TW_STR32.Size * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.STR32:
// case TWTY.STR64: intptr += TW_STR32.Size * itemIndex;
// intptr += TW_STR64.Size * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.STR64:
// case TWTY.STR128: intptr += TW_STR64.Size * itemIndex;
// intptr += TW_STR128.Size * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.STR128:
// case TWTY.STR255: intptr += TW_STR128.Size * itemIndex;
// intptr += TW_STR255.Size * itemIndex; return MarshalTo<TValue>(intptr);
// return MarshalTo<TValue>(intptr); case TWTY.STR255:
// } intptr += TW_STR255.Size * itemIndex;
//} return MarshalTo<TValue>(intptr);
}
}
//static TEnum NumericToEnum<TNumber, TEnum>(TNumber num) where TEnum : struct static TEnum NumericToEnum<TNumber, TEnum>(TNumber num) where TEnum : struct
//{ {
// // some caps returns a data type that's not the underlying datatype for the enum // TODO: 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. // so foolproof way is to ToString() it and parse it as the enum type.
// var str = num.ToString(); // this is bad for perf so find better way later
var str = num!.ToString();
// if (Enum.TryParse(str, out TEnum parsed)) if (Enum.TryParse(str, out TEnum parsed))
// { {
// return parsed; return parsed;
// } }
// return default; return default;
//} }
//static T MarshalTo<T>(IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T));
} }
} }

View File

@ -13,11 +13,11 @@ namespace NTwain.Data
/// <summary> /// <summary>
/// Allocates and copies the string value into a pointer in UTF8 that's null-terminated. /// Allocates and copies the string value into a pointer in UTF8 that's null-terminated.
/// </summary> /// </summary>
/// <param name="memMgr"></param>
/// <param name="value"></param> /// <param name="value"></param>
/// <param name="memMgr"></param>
/// <param name="finalLength">Final length to use with the pointer (includes the null).</param> /// <param name="finalLength">Final length to use with the pointer (includes the null).</param>
/// <returns></returns> /// <returns></returns>
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; finalLength = 0;
if (value == null) return IntPtr.Zero; if (value == null) return IntPtr.Zero;

View File

@ -13,7 +13,11 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net462'"> <ItemGroup Condition=" '$(TargetFramework)' == 'net462'">
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net462'"> <ItemGroup Condition=" '$(TargetFramework)' != 'net462'">

View File

@ -1,7 +1,7 @@
using NTwain.Data; using NTwain.Data;
using NTwain.Triplets; using NTwain.Triplets;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace NTwain namespace NTwain
{ {
@ -9,20 +9,20 @@ namespace NTwain
partial class TwainAppSession partial class TwainAppSession
{ {
///// <summary> /// <summary>
///// Gets all the supported caps for the current source. /// Gets all the supported caps for the current source.
///// </summary> /// </summary>
///// <returns></returns> /// <returns></returns>
//public IEnumerable<CAP> GetAllCaps() public IList<CAP> GetAllCaps()
//{ {
// // just as a sample of how to read cap values // just as a sample of how to read cap values
// if (GetCapValues(CAP.CAP_SUPPORTEDCAPS, out TW_CAPABILITY value) == TWRC.SUCCESS) if (GetCapValues(CAP.CAP_SUPPORTEDCAPS, out TW_CAPABILITY value).RC == TWRC.SUCCESS)
// { {
// value.Read(this); return value.ReadArray<CAP>(this);
// } }
// return Enumerable.Empty<CAP>(); return Array.Empty<CAP>();
//} }
/// <summary> /// <summary>
/// Gets a CAP's actual supported operations. /// Gets a CAP's actual supported operations.
@ -32,10 +32,10 @@ namespace NTwain
/// <returns></returns> /// <returns></returns>
public TWQC QueryCapSupport(CAP cap) 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) if (DGControl.Capability.QuerySupport(ref _appIdentity, ref _currentDS, ref value) == TWRC.SUCCESS)
{ {
value.Read(this); return value.ReadOneValue<TWQC>(this);
} }
return TWQC.Unknown; return TWQC.Unknown;
} }

View File

@ -47,9 +47,9 @@ namespace NTwain
var rc = DGControl.Identity.OpenDS(ref _appIdentity, ref source); var rc = DGControl.Identity.OpenDS(ref _appIdentity, ref source);
if (rc == TWRC.SUCCESS) if (rc == TWRC.SUCCESS)
{ {
State = STATE.S4;
RegisterCallback(); RegisterCallback();
CurrentSource = source; CurrentSource = source;
State = STATE.S4;
} }
return WrapInSTS(rc); return WrapInSTS(rc);
} }
@ -158,7 +158,7 @@ namespace NTwain
{ {
task.SizeOf = (uint)Marshal.SizeOf(typeof(TW_TWAINDIRECT)); task.SizeOf = (uint)Marshal.SizeOf(typeof(TW_TWAINDIRECT));
task.CommunicationManager = communicationManager; task.CommunicationManager = communicationManager;
task.Send = ValueWriter.StringToPtrUTF8(this, taskJson, out uint length); task.Send = taskJson.StringToPtrUTF8(this, out uint length);
task.SendSize = length; task.SendSize = length;
result.ReturnCode = DGControl.TwainDirect.SetTask(ref _appIdentity, ref _currentDS, ref task); 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) 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 finally