mirror of
https://github.com/soukoku/ntwain.git
synced 2025-09-19 10:08:00 +08:00
Added reading of supported caps.
This commit is contained in:
@@ -2546,6 +2546,7 @@ namespace TWAINWorkingGroup
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flags used in TW_MEMORY structure.
|
/// Flags used in TW_MEMORY structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
public enum TWMF : ushort
|
public enum TWMF : ushort
|
||||||
{
|
{
|
||||||
APPOWNS = 0x0001,
|
APPOWNS = 0x0001,
|
||||||
@@ -3753,6 +3754,7 @@ namespace TWAINWorkingGroup
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Groups...
|
/// Data Groups...
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
public enum DG : uint
|
public enum DG : uint
|
||||||
{
|
{
|
||||||
CONTROL = 0x1,
|
CONTROL = 0x1,
|
||||||
@@ -4273,6 +4275,7 @@ namespace TWAINWorkingGroup
|
|||||||
/// bit patterns: for query the operation that are supported by the data source on a capability
|
/// bit patterns: for query the operation that are supported by the data source on a capability
|
||||||
/// Application gets these through DG_CONTROL/DAT_CAPABILITY/MSG_QUERYSUPPORT
|
/// Application gets these through DG_CONTROL/DAT_CAPABILITY/MSG_QUERYSUPPORT
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
public enum TWQC : ushort
|
public enum TWQC : ushort
|
||||||
{
|
{
|
||||||
GET = 0x0001,
|
GET = 0x0001,
|
||||||
|
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace TWAINWorkingGroup
|
namespace TWAINWorkingGroup
|
||||||
{
|
{
|
||||||
// contains my additions
|
// contains my additions that makes twain types easier to work with.
|
||||||
|
|
||||||
partial struct TW_FIX32 : IEquatable<TW_FIX32>
|
partial struct TW_FIX32 : IEquatable<TW_FIX32>
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,11 @@ namespace TWAINWorkingGroup
|
|||||||
|
|
||||||
float ToFloat()
|
float ToFloat()
|
||||||
{
|
{
|
||||||
return (float)Whole + Frac / 65536f;
|
return Whole + Frac / 65536f;
|
||||||
|
}
|
||||||
|
double ToDouble()
|
||||||
|
{
|
||||||
|
return Whole + Frac / 65536.0;
|
||||||
}
|
}
|
||||||
TW_FIX32(float value)
|
TW_FIX32(float value)
|
||||||
{
|
{
|
||||||
@@ -55,7 +59,7 @@ namespace TWAINWorkingGroup
|
|||||||
public static implicit operator float(TW_FIX32 value) => value.ToFloat();
|
public static implicit operator float(TW_FIX32 value) => value.ToFloat();
|
||||||
public static implicit operator TW_FIX32(float value) => new TW_FIX32(value);
|
public static implicit operator TW_FIX32(float value) => new TW_FIX32(value);
|
||||||
|
|
||||||
public static implicit operator double(TW_FIX32 value) => value.ToFloat();
|
public static implicit operator double(TW_FIX32 value) => value.ToDouble();
|
||||||
public static implicit operator TW_FIX32(double value) => new TW_FIX32((float)value);
|
public static implicit operator TW_FIX32(double value) => new TW_FIX32((float)value);
|
||||||
|
|
||||||
public static bool operator ==(TW_FIX32 value1, TW_FIX32 value2) => value1.Equals(value2);
|
public static bool operator ==(TW_FIX32 value1, TW_FIX32 value2) => value1.Equals(value2);
|
||||||
@@ -64,9 +68,33 @@ namespace TWAINWorkingGroup
|
|||||||
|
|
||||||
partial struct TW_FRAME : IEquatable<TW_FRAME>
|
partial struct TW_FRAME : IEquatable<TW_FRAME>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <see cref="TW_FRAME"/> from a string representation of it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public TW_FRAME(string value) : this()
|
||||||
|
{
|
||||||
|
var parts = value.Split(',');
|
||||||
|
if (parts.Length == 4)
|
||||||
|
{
|
||||||
|
Left = float.Parse(parts[0]);
|
||||||
|
Top = float.Parse(parts[1]);
|
||||||
|
Right = float.Parse(parts[2]);
|
||||||
|
Bottom = float.Parse(parts[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Cannot create frame from \"{value}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String representation of Left,Top,Right,Bottom.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"L={Left}, T={Top}, R={Right}, B={Bottom}";
|
return $"{Left},{Top},{Right},{Bottom}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(TW_FRAME other)
|
public bool Equals(TW_FRAME other)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -285,6 +286,62 @@ namespace NTwain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries current device for supported capabilities.
|
||||||
|
/// Not all devices supports this.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IEnumerable<CAP> SupportedCaps()
|
||||||
|
{
|
||||||
|
List<CAP> caps = null;
|
||||||
|
if (State >= STATE.S4)
|
||||||
|
{
|
||||||
|
TW_CAPABILITY cap = default;
|
||||||
|
|
||||||
|
// get list of supported caps
|
||||||
|
cap.Cap = CAP.CAP_SUPPORTEDCAPS;
|
||||||
|
|
||||||
|
var sts = _twain.DatCapability(DG.CONTROL, MSG.GET, ref cap);
|
||||||
|
if (sts == STS.SUCCESS)
|
||||||
|
{
|
||||||
|
var csv = TWAIN.CapabilityToCsv(cap, true);
|
||||||
|
caps = csv.Split(',').Skip(4).Select(val =>
|
||||||
|
{
|
||||||
|
if (Enum.TryParse(val, out CAP c))
|
||||||
|
{
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else if (val.StartsWith("0x"))
|
||||||
|
{
|
||||||
|
return (CAP)Convert.ToUInt16(val, 16);
|
||||||
|
}
|
||||||
|
else if (ushort.TryParse(val, out ushort num))
|
||||||
|
{
|
||||||
|
return (CAP)num;
|
||||||
|
}
|
||||||
|
return (CAP)0;
|
||||||
|
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
//foreach (CAP capId in Enum.GetValues(typeof(CAP)))
|
||||||
|
//{
|
||||||
|
// cap.ConType = TWON.ONEVALUE;
|
||||||
|
// cap.Cap = capId;
|
||||||
|
|
||||||
|
// var sts = _twain.DatCapability(DG.CONTROL, MSG.QUERYSUPPORT, ref cap);
|
||||||
|
// if (sts == STS.SUCCESS)
|
||||||
|
// {
|
||||||
|
// if (Enum.TryParse(_twain.CapabilityOneValueToString(cap), out TWQC qc))
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return caps ?? Enumerable.Empty<CAP>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to show the current device's settings dialog if supported.
|
/// Attempts to show the current device's settings dialog if supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
115
NTwain/ValueReader.cs
Normal file
115
NTwain/ValueReader.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TWAINWorkingGroup;
|
||||||
|
|
||||||
|
namespace NTwain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for reading pointers into various things.
|
||||||
|
/// </summary>
|
||||||
|
public static class ValueReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Read capability's container content as ToString()'d one value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="twain">Low-level twain object.</param>
|
||||||
|
/// <param name="cap">Cap to read from.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string CapabilityOneValueToString(this TWAIN twain, TW_CAPABILITY cap)
|
||||||
|
{
|
||||||
|
if (cap.hContainer == IntPtr.Zero) return null;
|
||||||
|
|
||||||
|
var lockedPtr = twain.DsmMemLock(cap.hContainer);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TWTY itemType;
|
||||||
|
// Mac has a level of indirection and a different structure (ick)...
|
||||||
|
if (PlatformTools.GetPlatform() == Platform.MACOSX)
|
||||||
|
{
|
||||||
|
// Crack the container...
|
||||||
|
var onevalue = lockedPtr.MarshalTo<TW_ONEVALUE_MACOSX>();
|
||||||
|
itemType = (TWTY)onevalue.ItemType;
|
||||||
|
lockedPtr += Marshal.SizeOf(onevalue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Crack the container...
|
||||||
|
var onevalue = lockedPtr.MarshalTo<TW_ONEVALUE>();
|
||||||
|
itemType = onevalue.ItemType;
|
||||||
|
lockedPtr += Marshal.SizeOf(onevalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lockedPtr.ContainerToString(itemType);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// All done...
|
||||||
|
twain.DsmMemUnlock(cap.hContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the container pointer content as a string. Numeric values are ToString()'d.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="intptr">A locked pointer to the container data. If data is array this is the 0th item.</param>
|
||||||
|
/// <param name="type">The twain type.</param>
|
||||||
|
/// <param name="itemIndex">Index of the item if pointer is array.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
static string ContainerToString(this IntPtr intptr, TWTY type, int itemIndex = 0)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Unknown item type {type} to read as string.");
|
||||||
|
case TWTY.INT8:
|
||||||
|
intptr += 1 * itemIndex;
|
||||||
|
return intptr.MarshalToString<sbyte>();
|
||||||
|
case TWTY.INT16:
|
||||||
|
intptr += 2 * itemIndex;
|
||||||
|
return intptr.MarshalToString<short>();
|
||||||
|
case TWTY.INT32:
|
||||||
|
intptr += 4 * itemIndex;
|
||||||
|
return intptr.MarshalToString<int>();
|
||||||
|
case TWTY.UINT8:
|
||||||
|
intptr += 1 * itemIndex;
|
||||||
|
return intptr.MarshalToString<byte>();
|
||||||
|
case TWTY.BOOL:
|
||||||
|
case TWTY.UINT16:
|
||||||
|
intptr += 2 * itemIndex;
|
||||||
|
return intptr.MarshalToString<ushort>();
|
||||||
|
case TWTY.UINT32:
|
||||||
|
intptr += 4 * itemIndex;
|
||||||
|
return intptr.MarshalToString<uint>();
|
||||||
|
case TWTY.FIX32:
|
||||||
|
intptr += 4 * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_FIX32>();
|
||||||
|
case TWTY.FRAME:
|
||||||
|
intptr += 16 * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_FRAME>();
|
||||||
|
case TWTY.STR32:
|
||||||
|
intptr += TW_STR32.Size * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_STR32>();
|
||||||
|
case TWTY.STR64:
|
||||||
|
intptr += TW_STR64.Size * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_STR64>();
|
||||||
|
case TWTY.STR128:
|
||||||
|
intptr += TW_STR128.Size * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_STR128>();
|
||||||
|
case TWTY.STR255:
|
||||||
|
intptr += TW_STR255.Size * itemIndex;
|
||||||
|
return intptr.MarshalToString<TW_STR255>();
|
||||||
|
case TWTY.HANDLE:
|
||||||
|
intptr += IntPtr.Size * itemIndex;
|
||||||
|
return Marshal.ReadIntPtr(intptr).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string MarshalToString<T>(this IntPtr ptr) => Marshal.PtrToStructure(ptr, typeof(T)).ToString();
|
||||||
|
static T MarshalTo<T>(this IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
using NTwain;
|
using NTwain;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TWAINWorkingGroup;
|
using TWAINWorkingGroup;
|
||||||
@@ -12,6 +13,7 @@ namespace Net5Console
|
|||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Starting twain test in console...");
|
Console.WriteLine("Starting twain test in console...");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
using (var session = new TwainSession(Assembly.GetExecutingAssembly(), null, IntPtr.Zero))
|
using (var session = new TwainSession(Assembly.GetExecutingAssembly(), null, IntPtr.Zero))
|
||||||
using (var hold = new ManualResetEventSlim())
|
using (var hold = new ManualResetEventSlim())
|
||||||
@@ -19,10 +21,12 @@ namespace Net5Console
|
|||||||
session.DeviceEvent += (sender, e) =>
|
session.DeviceEvent += (sender, e) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Got device event " + (TWDE)e.Event);
|
Console.WriteLine($"Got device event " + (TWDE)e.Event);
|
||||||
|
Console.WriteLine();
|
||||||
};
|
};
|
||||||
session.ScanEvent += (sender, closing) =>
|
session.ScanEvent += (sender, closing) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Got scan event " + closing);
|
Console.WriteLine($"Got scan event " + closing);
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
// don't care, just end it
|
// don't care, just end it
|
||||||
TW_PENDINGXFERS pending = default;
|
TW_PENDINGXFERS pending = default;
|
||||||
@@ -32,10 +36,12 @@ namespace Net5Console
|
|||||||
if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS)
|
if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Disabled device.");
|
Console.WriteLine("Disabled device.");
|
||||||
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Failed to disabled device.");
|
Console.Error.WriteLine("Failed to disabled device.");
|
||||||
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
hold.Set();
|
hold.Set();
|
||||||
};
|
};
|
||||||
@@ -43,9 +49,11 @@ namespace Net5Console
|
|||||||
if (session.Open() == TWAINWorkingGroup.STS.SUCCESS)
|
if (session.Open() == TWAINWorkingGroup.STS.SUCCESS)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Opened DSM");
|
Console.WriteLine("Opened DSM");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
Console.WriteLine("Default device:");
|
Console.WriteLine("Default device:");
|
||||||
Console.WriteLine($"\t{session.DefaultDevice}");
|
Console.WriteLine($"\t{session.DefaultDevice}");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
Console.WriteLine("All devices:");
|
Console.WriteLine("All devices:");
|
||||||
TW_IDENTITY dsToUse = default;
|
TW_IDENTITY dsToUse = default;
|
||||||
@@ -57,28 +65,41 @@ namespace Net5Console
|
|||||||
dsToUse = dev;
|
dsToUse = dev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
session.CurrentDevice = dsToUse;
|
session.CurrentDevice = dsToUse;
|
||||||
if (session.CurrentDevice.HasValue)
|
if (session.CurrentDevice.HasValue)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Current device after opening attempt:");
|
Console.WriteLine("Current device after opening attempt:");
|
||||||
Console.WriteLine($"\t{session.CurrentDevice}");
|
Console.WriteLine($"\t{session.CurrentDevice}");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
var caps = session.SupportedCaps();
|
||||||
|
Console.WriteLine("Device supports these caps:");
|
||||||
|
foreach (var cap in caps.OrderBy(c => c))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{cap}");
|
||||||
|
}
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
var sts = session.StartCapture(false);
|
var sts = session.StartCapture(false);
|
||||||
if (sts == STS.SUCCESS)
|
if (sts == STS.SUCCESS)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Waiting for capture to complete.");
|
Console.Error.WriteLine("Waiting for capture to complete.");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
while (!hold.IsSet) Thread.Sleep(100);
|
while (!hold.IsSet) Thread.Sleep(100);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Failed to start capture: " + sts);
|
Console.Error.WriteLine("Failed to start capture: " + sts);
|
||||||
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("No devices opened.");
|
Console.WriteLine("No devices opened.");
|
||||||
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
Reference in New Issue
Block a user