From 059b8dd60ee0fcf547c4bce86f33eb3282f9da29 Mon Sep 17 00:00:00 2001
From: Eugene Wang <8755753+soukoku@users.noreply.github.com>
Date: Tue, 4 Apr 2023 22:21:40 -0400
Subject: [PATCH] Resurrected metrics and twaindirect task calls.
---
src/NTwain/DSM/DSMGenerator.tt | 7 +++
src/NTwain/DSM/Linux64DSM.cs | 7 +++
src/NTwain/DSM/LinuxBotched64DSM.cs | 7 +++
src/NTwain/DSM/LinuxDSM.cs | 7 +++
src/NTwain/DSM/OSXLegacyDSM.cs | 7 +++
src/NTwain/DSM/OSXNewDSM.cs | 7 +++
src/NTwain/DSM/WinLegacyDSM.cs | 7 +++
src/NTwain/DSM/WinNewDSM.cs | 7 +++
src/NTwain/Data/TWAINH_EXTRAS.cs | 10 +++-
src/NTwain/Data/ValueReader.cs | 15 +++---
src/NTwain/Data/ValueWriter.cs | 62 ++++++++--------------
src/NTwain/NTwain.csproj | 2 +-
src/NTwain/Triplets/ControlDATs/Metrics.cs | 45 ++++++++++++++++
src/NTwain/Triplets/DGControl.cs | 2 +
src/NTwain/TwainAppSession.PropEvents.cs | 6 +--
src/NTwain/TwainAppSession.Sources.cs | 46 ++++++++++++++++
16 files changed, 191 insertions(+), 53 deletions(-)
create mode 100644 src/NTwain/Triplets/ControlDATs/Metrics.cs
diff --git a/src/NTwain/DSM/DSMGenerator.tt b/src/NTwain/DSM/DSMGenerator.tt
index 71407ac..b91acd5 100644
--- a/src/NTwain/DSM/DSMGenerator.tt
+++ b/src/NTwain/DSM/DSMGenerator.tt
@@ -168,6 +168,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref <#= file.identityClass #> origin, ref <#= file.identityClass #> dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/Linux64DSM.cs b/src/NTwain/DSM/Linux64DSM.cs
index f9f0a53..7713db0 100644
--- a/src/NTwain/DSM/Linux64DSM.cs
+++ b/src/NTwain/DSM/Linux64DSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_LEGACY origin, ref TW_IDENTITY_LEGACY dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/LinuxBotched64DSM.cs b/src/NTwain/DSM/LinuxBotched64DSM.cs
index cd93707..f94dd42 100644
--- a/src/NTwain/DSM/LinuxBotched64DSM.cs
+++ b/src/NTwain/DSM/LinuxBotched64DSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY origin, ref TW_IDENTITY dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/LinuxDSM.cs b/src/NTwain/DSM/LinuxDSM.cs
index 4d1ae05..bddb350 100644
--- a/src/NTwain/DSM/LinuxDSM.cs
+++ b/src/NTwain/DSM/LinuxDSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_LEGACY origin, ref TW_IDENTITY_LEGACY dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/OSXLegacyDSM.cs b/src/NTwain/DSM/OSXLegacyDSM.cs
index 2b17618..3fb1293 100644
--- a/src/NTwain/DSM/OSXLegacyDSM.cs
+++ b/src/NTwain/DSM/OSXLegacyDSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_MACOSX origin, ref TW_IDENTITY_MACOSX dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/OSXNewDSM.cs b/src/NTwain/DSM/OSXNewDSM.cs
index d0fce04..0613a09 100644
--- a/src/NTwain/DSM/OSXNewDSM.cs
+++ b/src/NTwain/DSM/OSXNewDSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_MACOSX origin, ref TW_IDENTITY_MACOSX dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/WinLegacyDSM.cs b/src/NTwain/DSM/WinLegacyDSM.cs
index 0e556ca..4398b08 100644
--- a/src/NTwain/DSM/WinLegacyDSM.cs
+++ b/src/NTwain/DSM/WinLegacyDSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_LEGACY origin, ref TW_IDENTITY_LEGACY dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/DSM/WinNewDSM.cs b/src/NTwain/DSM/WinNewDSM.cs
index e46fc83..e6b136e 100644
--- a/src/NTwain/DSM/WinNewDSM.cs
+++ b/src/NTwain/DSM/WinNewDSM.cs
@@ -148,6 +148,13 @@ namespace NTwain.DSM
DG dg, DAT dat, MSG msg, ref TW_SETUPFILEXFER filexfer
);
+ [DllImport(DsmName, CharSet = CharSet.Ansi)]
+ public static extern TWRC DSM_Entry
+ (
+ ref TW_IDENTITY_LEGACY origin, ref TW_IDENTITY_LEGACY dest,
+ DG dg, DAT dat, MSG msg, ref TW_METRICS metrics
+ );
+
[DllImport(DsmName, CharSet = CharSet.Ansi)]
public static extern TWRC DSM_Entry
(
diff --git a/src/NTwain/Data/TWAINH_EXTRAS.cs b/src/NTwain/Data/TWAINH_EXTRAS.cs
index 5770886..d4a03ba 100644
--- a/src/NTwain/Data/TWAINH_EXTRAS.cs
+++ b/src/NTwain/Data/TWAINH_EXTRAS.cs
@@ -86,7 +86,7 @@ namespace NTwain.Data
/// Don't care values...
///
public const byte TWON_DONTCARE8 = 0xff;
- public const ushort TWON_DONTCARE16 = 0xff;
+ public const ushort TWON_DONTCARE16 = 0xffff;
public const uint TWON_DONTCARE32 = 0xffffffff;
///
/// We're departing from a strict translation of H so that
@@ -144,10 +144,15 @@ namespace NTwain.Data
///
public TWRC ReturnCode;
+ ///
+ /// Status if code is failure.
+ ///
+ public TW_STATUS Status;
+
///
/// The response of the task in JSON if successful.
///
- public string ResponseJson;
+ public string? ResponseJson;
}
public enum TWRC : ushort
@@ -173,6 +178,7 @@ namespace NTwain.Data
{
// Condition codes (always associated with TWRC_FAILURE)...
CUSTOMBASE = 0x8000,
+ None = 0,
BUMMER = 1,
LOWMEMORY = 2,
NODS = 3,
diff --git a/src/NTwain/Data/ValueReader.cs b/src/NTwain/Data/ValueReader.cs
index 14a87b3..dd8e3e5 100644
--- a/src/NTwain/Data/ValueReader.cs
+++ b/src/NTwain/Data/ValueReader.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -17,24 +18,22 @@ namespace NTwain.Data
/// Pointer to string.
/// Number of bytes to read.
///
- public static string? PtrToStringUTF8(IMemoryManager memMgr, IntPtr data, int length)
+ public static unsafe string? PtrToStringUTF8(IMemoryManager memMgr, IntPtr data, int length)
{
string? val = null;
var locked = memMgr.Lock(data);
if (locked != IntPtr.Zero)
{
- // does this work? who knows.
try
{
#if NETFRAMEWORK
// safe method but with 2 copies (arr and parsed string)
- var bytes = new byte[length];
- Marshal.Copy(locked, bytes, 0, bytes.Length);
- val = Encoding.UTF8.GetString(bytes);
+ //var bytes = new byte[length];
+ //Marshal.Copy(locked, bytes, 0, bytes.Length);
+ //val = Encoding.UTF8.GetString(bytes);
- //// unsafe method with 1 copy (does it work?)
- //sbyte* bytes = (sbyte*)locked;
- //val = new string(bytes, 0, length, Encoding.UTF8);
+ // does this work?
+ val = Encoding.UTF8.GetString((byte*)locked, length);
#else
val = Marshal.PtrToStringUTF8(locked, length);
#endif
diff --git a/src/NTwain/Data/ValueWriter.cs b/src/NTwain/Data/ValueWriter.cs
index 92c5125..d9d1773 100644
--- a/src/NTwain/Data/ValueWriter.cs
+++ b/src/NTwain/Data/ValueWriter.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
@@ -9,46 +10,29 @@ namespace NTwain.Data
///
static class ValueWriter
{
- /////
- ///// Allocates and copies the string value into a pointer in UTF8 that's null-terminated.
- /////
- /////
- /////
- ///// Actual number of bytes used to encode the string without the null.
- /////
- //public static unsafe IntPtr StringToPtrUTF8(IMemoryManager memMgr, string value, out int length)
- //{
- // if (value == null)
- // {
- // length = 0;
- // return IntPtr.Zero;
- // }
+ ///
+ /// 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)
+ {
+ finalLength = 0;
+ if (value == null) return IntPtr.Zero;
- // var utf8 = Encoding.UTF8;
- // length = utf8.GetByteCount(value);
-
- // var ptr = memMgr.Alloc((uint)length + 1); // +1 for null-terminated
-
- // // TODO: test if this works
- // int written;
- // byte* bytes = (byte*)ptr;
- // try
- // {
- // // fixed for managed pointer
- // fixed (char* firstChar = value)
- // {
- // written = Encoding.UTF8.GetBytes(firstChar, value.Length, bytes, length);
- // }
-
- // bytes[written] = 0;
- // }
- // finally
- // {
- // if (ptr != IntPtr.Zero) memMgr.Free(ptr);
- // }
-
- // return ptr;
- //}
+ fixed (char* pInput = value)
+ {
+ var len = Encoding.UTF8.GetByteCount(pInput, value.Length);
+ finalLength = (uint)len + 1;
+ var pResult = (byte*)memMgr.Alloc(finalLength);
+ var bytesWritten = Encoding.UTF8.GetBytes(pInput, value.Length, pResult, len);
+ Trace.Assert(len == bytesWritten);
+ pResult[len] = 0;
+ return (IntPtr)pResult;
+ }
+ }
diff --git a/src/NTwain/NTwain.csproj b/src/NTwain/NTwain.csproj
index 0f27b17..bd52a54 100644
--- a/src/NTwain/NTwain.csproj
+++ b/src/NTwain/NTwain.csproj
@@ -5,7 +5,7 @@
Library containing the TWAIN API for dotnet.
net6.0;net6.0-windows;net462;
enable
-
+ true
diff --git a/src/NTwain/Triplets/ControlDATs/Metrics.cs b/src/NTwain/Triplets/ControlDATs/Metrics.cs
new file mode 100644
index 0000000..6f9f4e1
--- /dev/null
+++ b/src/NTwain/Triplets/ControlDATs/Metrics.cs
@@ -0,0 +1,45 @@
+using NTwain.Data;
+using NTwain.DSM;
+using System.Runtime.InteropServices;
+
+namespace NTwain.Triplets.ControlDATs
+{
+ ///
+ /// Contains calls used with and .
+ ///
+ public class Metrics
+ {
+ public TWRC Get(ref TW_IDENTITY_LEGACY app, ref TW_IDENTITY_LEGACY ds, out TW_METRICS data)
+ {
+ data = default;
+ data.SizeOf = (uint)Marshal.SizeOf();
+
+ var rc = TWRC.FAILURE;
+ if (TwainPlatform.IsWindows)
+ {
+ if (TwainPlatform.Is32bit && TwainPlatform.PreferLegacyDSM)
+ {
+ rc = WinLegacyDSM.DSM_Entry(ref app, ref ds, DG.CONTROL, DAT.METRICS, MSG.GET, ref data);
+ }
+ else
+ {
+ rc = WinNewDSM.DSM_Entry(ref app, ref ds, DG.CONTROL, DAT.METRICS, MSG.GET, ref data);
+ }
+ }
+ else if (TwainPlatform.IsMacOSX)
+ {
+ TW_IDENTITY_MACOSX app2 = app;
+ TW_IDENTITY_MACOSX ds2 = ds;
+ if (TwainPlatform.PreferLegacyDSM)
+ {
+ rc = OSXLegacyDSM.DSM_Entry(ref app2, ref ds2, DG.CONTROL, DAT.METRICS, MSG.GET, ref data);
+ }
+ else
+ {
+ rc = OSXNewDSM.DSM_Entry(ref app2, ref ds2, DG.CONTROL, DAT.METRICS, MSG.GET, ref data);
+ }
+ }
+ return rc;
+ }
+ }
+}
diff --git a/src/NTwain/Triplets/DGControl.cs b/src/NTwain/Triplets/DGControl.cs
index 55ca624..f610c3c 100644
--- a/src/NTwain/Triplets/DGControl.cs
+++ b/src/NTwain/Triplets/DGControl.cs
@@ -27,6 +27,8 @@ namespace NTwain.Triplets
public static readonly Identity Identity = new();
+ public static readonly Metrics Metrics = new();
+
public static readonly Parent Parent = new();
public static readonly Passthru Passthru = new();
diff --git a/src/NTwain/TwainAppSession.PropEvents.cs b/src/NTwain/TwainAppSession.PropEvents.cs
index 3a07809..84dd050 100644
--- a/src/NTwain/TwainAppSession.PropEvents.cs
+++ b/src/NTwain/TwainAppSession.PropEvents.cs
@@ -72,8 +72,8 @@ namespace NTwain
{
get
{
- var sts = DGControl.CustomDsData.Get(ref _appIdentity, ref _currentDS, out TW_CUSTOMDSDATA data);
- if (sts == TWRC.SUCCESS)
+ var rc = DGControl.CustomDsData.Get(ref _appIdentity, ref _currentDS, out TW_CUSTOMDSDATA data);
+ if (rc == TWRC.SUCCESS)
{
if (data.hData != IntPtr.Zero && data.InfoLength > 0)
{
@@ -105,7 +105,7 @@ namespace NTwain
var lockedPtr = Lock(data.hData);
Marshal.Copy(value, 0, lockedPtr, value.Length);
Unlock(data.hData);
- var sts = DGControl.CustomDsData.Set(ref _appIdentity, ref _currentDS, ref data);
+ var rc = DGControl.CustomDsData.Set(ref _appIdentity, ref _currentDS, ref data);
}
finally
{
diff --git a/src/NTwain/TwainAppSession.Sources.cs b/src/NTwain/TwainAppSession.Sources.cs
index 476d5d7..d727cce 100644
--- a/src/NTwain/TwainAppSession.Sources.cs
+++ b/src/NTwain/TwainAppSession.Sources.cs
@@ -1,6 +1,8 @@
using NTwain.Data;
using NTwain.Triplets;
+using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
namespace NTwain
{
@@ -132,5 +134,49 @@ namespace NTwain
}
return WrapInSTS(rc);
}
+
+ ///
+ /// Reads information relating to the last capture run.
+ /// Only valid on state 4 after a capture.
+ ///
+ public STS GetMetrics(out TW_METRICS metrics)
+ {
+ return WrapInSTS(DGControl.Metrics.Get(ref _appIdentity, ref _currentDS, out metrics));
+ }
+
+ ///
+ /// Sends a TWAIN Direct task from the application to the driver.
+ ///
+ /// The TWAIN Direct task in JSON.
+ /// The current system being used to connect the application to the scanner.
+ ///
+ public TwainDirectTaskResult SetTwainDirectTask(string taskJson, ushort communicationManager = 0)
+ {
+ var result = new TwainDirectTaskResult { ReturnCode = TWRC.FAILURE };
+ TW_TWAINDIRECT task = default;
+ try
+ {
+ task.SizeOf = (uint)Marshal.SizeOf(typeof(TW_TWAINDIRECT));
+ task.CommunicationManager = communicationManager;
+ task.Send = ValueWriter.StringToPtrUTF8(this, taskJson, out uint length);
+ task.SendSize = length;
+
+ result.ReturnCode = DGControl.TwainDirect.SetTask(ref _appIdentity, ref _currentDS, ref task);
+ if (result.ReturnCode == TWRC.FAILURE)
+ {
+ result.Status = GetLastStatus();
+ }
+ else if (result.ReturnCode == TWRC.SUCCESS && task.ReceiveSize > 0 && task.Receive != IntPtr.Zero)
+ {
+ result.ResponseJson = ValueReader.PtrToStringUTF8(this, task.Receive, (int)task.ReceiveSize);
+ }
+ }
+ finally
+ {
+ //if (task.Send != IntPtr.Zero) Free(task.Send); // does source free the Send?
+ if (task.Receive != IntPtr.Zero) Free(task.Receive);
+ }
+ return result;
+ }
}
}