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);