From 714caf4935896f79c2139b32638fd93c8e1413b2 Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Tue, 27 Apr 2021 07:37:52 -0400 Subject: [PATCH] Added SetTwainDirectTask method to session. --- src/NTwain/NTwain.csproj | 1 + src/NTwain/TWAINH_EXTRAS.cs | 13 +++++++++ src/NTwain/TwainSession.cs | 56 ++++++++++++++++++++++++++----------- src/NTwain/ValueReader.cs | 24 ++++++++++++++++ src/NTwain/ValueWriter.cs | 40 +++++++++++++++++++++++++- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/NTwain/NTwain.csproj b/src/NTwain/NTwain.csproj index ff2621e..ddd2768 100644 --- a/src/NTwain/NTwain.csproj +++ b/src/NTwain/NTwain.csproj @@ -4,6 +4,7 @@ NTwain Library containing the TWAIN API for dotnet. net45;netstandard2.0; + true diff --git a/src/NTwain/TWAINH_EXTRAS.cs b/src/NTwain/TWAINH_EXTRAS.cs index bb3877f..b69cc11 100644 --- a/src/NTwain/TWAINH_EXTRAS.cs +++ b/src/NTwain/TWAINH_EXTRAS.cs @@ -49,6 +49,19 @@ namespace TWAINWorkingGroup public int Images; } + public struct TwainDirectTaskResult + { + /// + /// Return code of task. + /// + public STS ReturnCode; + + /// + /// The response of the task in JSON if successful. + /// + public string ResponseJson; + } + /// /// A more dotnet-friendly representation of . /// diff --git a/src/NTwain/TwainSession.cs b/src/NTwain/TwainSession.cs index fe1b577..c0d5def 100644 --- a/src/NTwain/TwainSession.cs +++ b/src/NTwain/TwainSession.cs @@ -131,16 +131,16 @@ namespace NTwain get { return _twain.GetState(); } } - /// - /// Gets the manager status. Useful after getting a non-success return code. - /// - /// - public TW_STATUS GetStatus() - { - TW_STATUS stat = default; - _ = _twain.DatStatus(DG.CONTROL, MSG.GET, ref stat); - return stat; - } + ///// + ///// Gets the manager status. Useful after getting a non-success return code. + ///// + ///// + //public TW_STATUS GetStatus() + //{ + // TW_STATUS stat = default; + // _ = _twain.DatStatus(DG.CONTROL, MSG.GET, ref stat); + // return stat; + //} /// /// Opens the TWAIN data source manager. @@ -352,12 +352,36 @@ namespace NTwain return new Metrics { ReturnCode = sts }; } - //public sts SetTwainDirectTask() - //{ - // TW_TWAINDIRECT task = default; - // var sts = _twain.DatTwaindirect(DG.CONTROL, MSG.SETTASK, ref task); - // return sts; - //} + /// + /// 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 = STS.FAILURE }; + TW_TWAINDIRECT task = default; + try + { + task.SizeOf = (uint)Marshal.SizeOf(typeof(TW_TWAINDIRECT)); + task.CommunicationManager = communicationManager; + task.Send = ValueWriter.StringToPtrUTF8(_twain, taskJson, out int length); + task.SendSize = (uint)length; + + result.ReturnCode = _twain.DatTwaindirect(DG.CONTROL, MSG.SETTASK, ref task); + if (result.ReturnCode == STS.SUCCESS && task.ReceiveSize > 0 && task.Receive != IntPtr.Zero) + { + result.ResponseJson = ValueReader.PtrToStringUTF8(task.Receive, (int)task.ReceiveSize); + } + } + finally + { + if (task.Send != IntPtr.Zero) _twain.DsmMemFree(ref task.Send); // just in case + if (task.Receive != IntPtr.Zero) _twain.DsmMemFree(ref task.Receive); + } + return result; + } #endregion } diff --git a/src/NTwain/ValueReader.cs b/src/NTwain/ValueReader.cs index b44d7e8..7569210 100644 --- a/src/NTwain/ValueReader.cs +++ b/src/NTwain/ValueReader.cs @@ -14,6 +14,30 @@ namespace NTwain /// public static class ValueReader { + /// + /// Reads pointer as UTF8 string. + /// + /// Pointer to string. + /// Number of bytes to read. + /// + public static unsafe string PtrToStringUTF8(IntPtr intPtr, int length) + { + if (intPtr == IntPtr.Zero) throw new ArgumentNullException(nameof(intPtr)); + if (length == 0) throw new ArgumentOutOfRangeException(nameof(length), length, "Length must be greater than 0."); + + //// safe method with 2 copies + //var bytes = new byte[length]; + //Marshal.Copy(intPtr, bytes, 0, length); + //return Encoding.UTF8.GetString(bytes); + + // unsafe method with 1 copy (does it work?) + sbyte* bytes = (sbyte*)intPtr; + var str = new string(bytes, 0, length, Encoding.UTF8); + return str; + } + + + // most of these are modified from the original TWAIN.CapabilityToCsv() public static TValue ReadOneValueContainer(TWAIN twain, ref TW_CAPABILITY cap, bool freeMemory = true) where TValue : struct diff --git a/src/NTwain/ValueWriter.cs b/src/NTwain/ValueWriter.cs index 4adc405..256edac 100644 --- a/src/NTwain/ValueWriter.cs +++ b/src/NTwain/ValueWriter.cs @@ -13,6 +13,45 @@ namespace NTwain /// public 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. + /// + internal static unsafe IntPtr StringToPtrUTF8(TWAIN twain, string value, out int length) + { + var utf8 = Encoding.UTF8; + length = utf8.GetByteCount(value); + + var ptr = twain.DsmMemAlloc((uint)length + 1); // +1 for null-terminated + + // TODO: test if this works + int written; + byte* bytes = (byte*)ptr; + try + { + fixed (char* firstChar = value) + { + written = Encoding.UTF8.GetBytes(firstChar, value.Length, bytes, length); + } + + bytes[written] = 0; + } + catch + { + // just in case + if (ptr != IntPtr.Zero) twain.DsmMemFree(ref ptr); + + throw; + } + + return ptr; + } + + + // most of these are modified from the original TWAIN.CsvToCapability() public static void WriteOneValueContainer(TWAIN twain, ref TW_CAPABILITY twCap, TValue value) where TValue : struct @@ -470,7 +509,6 @@ namespace NTwain } } - static TWTY GetItemType() where TValue : struct { var type = typeof(TValue);