Resurrected metrics and twaindirect task calls.

This commit is contained in:
Eugene Wang
2023-04-04 22:21:40 -04:00
parent 9e59e2355d
commit 059b8dd60e
16 changed files with 191 additions and 53 deletions

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -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
(

View File

@@ -86,7 +86,7 @@ namespace NTwain.Data
/// Don't care values...
/// </summary>
public const byte TWON_DONTCARE8 = 0xff;
public const ushort TWON_DONTCARE16 = 0xff;
public const ushort TWON_DONTCARE16 = 0xffff;
public const uint TWON_DONTCARE32 = 0xffffffff;
/// <summary>
/// We're departing from a strict translation of H so that
@@ -144,10 +144,15 @@ namespace NTwain.Data
/// </summary>
public TWRC ReturnCode;
/// <summary>
/// Status if code is failure.
/// </summary>
public TW_STATUS Status;
/// <summary>
/// The response of the task in JSON if successful.
/// </summary>
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,

View File

@@ -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
/// <param name="data">Pointer to string.</param>
/// <param name="length">Number of bytes to read.</param>
/// <returns></returns>
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

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
@@ -9,46 +10,29 @@ namespace NTwain.Data
/// </summary>
static class ValueWriter
{
///// <summary>
///// Allocates and copies the string value into a pointer in UTF8 that's null-terminated.
///// </summary>
///// <param name="twain"></param>
///// <param name="value"></param>
///// <param name="length">Actual number of bytes used to encode the string without the null.</param>
///// <returns></returns>
//public static unsafe IntPtr StringToPtrUTF8(IMemoryManager memMgr, string value, out int length)
//{
// if (value == null)
// {
// length = 0;
// return IntPtr.Zero;
// }
/// <summary>
/// Allocates and copies the string value into a pointer in UTF8 that's null-terminated.
/// </summary>
/// <param name="memMgr"></param>
/// <param name="value"></param>
/// <param name="finalLength">Final length to use with the pointer (includes the null).</param>
/// <returns></returns>
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;
}
}

View File

@@ -5,7 +5,7 @@
<Description>Library containing the TWAIN API for dotnet.</Description>
<TargetFrameworks>net6.0;net6.0-windows;net462;</TargetFrameworks>
<Nullable>enable</Nullable>
<!--<AllowUnsafeBlocks>true</AllowUnsafeBlocks>-->
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net6.0-windows'">

View File

@@ -0,0 +1,45 @@
using NTwain.Data;
using NTwain.DSM;
using System.Runtime.InteropServices;
namespace NTwain.Triplets.ControlDATs
{
/// <summary>
/// Contains calls used with <see cref="DG.CONTROL"/> and <see cref="DAT.METRICS"/>.
/// </summary>
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<TW_METRICS>();
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;
}
}
}

View File

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

View File

@@ -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
{

View File

@@ -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);
}
/// <summary>
/// Reads information relating to the last capture run.
/// Only valid on state 4 after a capture.
/// </summary>
public STS GetMetrics(out TW_METRICS metrics)
{
return WrapInSTS(DGControl.Metrics.Get(ref _appIdentity, ref _currentDS, out metrics));
}
/// <summary>
/// Sends a TWAIN Direct task from the application to the driver.
/// </summary>
/// <param name="taskJson">The TWAIN Direct task in JSON.</param>
/// <param name="communicationManager">The current system being used to connect the application to the scanner.</param>
/// <returns></returns>
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;
}
}
}