Made old session inherit the refactored version.

This commit is contained in:
soukoku 2014-04-05 20:14:19 -04:00
parent 0064db2c2d
commit 3dc90eff89
13 changed files with 472 additions and 936 deletions

View File

@ -972,42 +972,42 @@ namespace NTwain.Data
Marshal.WriteByte(baseAddr, offset, 0);
}
}
/// <summary>
/// Writes unicode string value.
/// </summary>
/// <param name="baseAddr"></param>
/// <param name="offset"></param>
/// <param name="item"></param>
/// <param name="maxLength"></param>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
private void WriteUString(IntPtr baseAddr, int offset, string item, int maxLength)
{
if (string.IsNullOrEmpty(item))
{
// write zero
Marshal.WriteInt16(baseAddr, offset, (char)0);
}
else
{
// use 2 bytes per char
for (int i = 0; i < maxLength; i++)
{
if (i == item.Length)
{
// string end reached, so write \0 and quit
Marshal.WriteInt16(baseAddr, offset, (char)0);
return;
}
else
{
Marshal.WriteInt16(baseAddr, offset, item[i]);
offset += 2;
}
}
// when ended normally also write \0
Marshal.WriteByte(baseAddr, offset, 0);
}
}
///// <summary>
///// Writes unicode string value.
///// </summary>
///// <param name="baseAddr"></param>
///// <param name="offset"></param>
///// <param name="item"></param>
///// <param name="maxLength"></param>
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
//private void WriteUString(IntPtr baseAddr, int offset, string item, int maxLength)
//{
// if (string.IsNullOrEmpty(item))
// {
// // write zero
// Marshal.WriteInt16(baseAddr, offset, (char)0);
// }
// else
// {
// // use 2 bytes per char
// for (int i = 0; i < maxLength; i++)
// {
// if (i == item.Length)
// {
// // string end reached, so write \0 and quit
// Marshal.WriteInt16(baseAddr, offset, (char)0);
// return;
// }
// else
// {
// Marshal.WriteInt16(baseAddr, offset, item[i]);
// offset += 2;
// }
// }
// // when ended normally also write \0
// Marshal.WriteByte(baseAddr, offset, 0);
// }
//}
/// <summary>
/// Entry call for reading values.
/// </summary>

View File

@ -72,8 +72,8 @@
<Compile Include="Triplets\DGControl\DGControl.Callback2.cs" />
<Compile Include="Triplets\DGImage\DGImage.Filter.cs" />
<Compile Include="Triplets\OpBase.cs" />
<Compile Include="Triplets\PInvokes.32bit.cs" />
<Compile Include="Triplets\PInvokes.64bit.cs" />
<Compile Include="Triplets\PInvoke.Win32.cs" />
<Compile Include="Triplets\PInvoke.Win64.cs" />
<Compile Include="TwainSessionExtensions.cs" />
<Compile Include="TwainSessionBase.cs" />
<Compile Include="TwainSessionWPF.cs" />
@ -119,7 +119,7 @@
<Compile Include="Triplets\DGImage\DGImage.RgbResponse.cs" />
<Compile Include="Triplets\PInvoke.cs" />
<Compile Include="TwainException.cs" />
<Compile Include="TwainSession.cs" />
<Compile Include="TwainSessionOld.cs" />
<Compile Include="Values\DataValues.cs" />
<Compile Include="Values\SourceEnableMode.cs" />
<Compile Include="Values\TwainConst.cs" />

View File

@ -7,7 +7,7 @@ namespace NTwain.Triplets
{
static partial class PInvoke
{
static partial class NativeMethods
static partial class WinNativeMethods
{
[DllImport("twain_32", EntryPoint = "#1")]
public static extern ReturnCode DsmEntry32(

View File

@ -7,7 +7,7 @@ namespace NTwain.Triplets
{
static partial class PInvoke
{
static partial class NativeMethods
static partial class WinNativeMethods
{
[DllImport("twaindsm", EntryPoint = "#1")]
public static extern ReturnCode DsmEntry64(

View File

@ -35,8 +35,8 @@ namespace NTwain.Triplets
Message msg,
ref IntPtr data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, dg, dat, msg, ref data); }
else { return NativeMethods.DsmEntry32(origin, destination, dg, dat, msg, ref data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, dg, dat, msg, ref data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, dg, dat, msg, ref data); }
}
public static ReturnCode DsmEntry(
@ -47,8 +47,8 @@ namespace NTwain.Triplets
Message msg,
ref uint data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, dg, dat, msg, ref data); }
else { return NativeMethods.DsmEntry32(origin, destination, dg, dat, msg, ref data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, dg, dat, msg, ref data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, dg, dat, msg, ref data); }
}
public static ReturnCode DsmEntry(
@ -57,8 +57,8 @@ namespace NTwain.Triplets
Message msg,
TWAudioInfo data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Audio, DataArgumentType.AudioInfo, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Audio, DataArgumentType.AudioInfo, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Audio, DataArgumentType.AudioInfo, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Audio, DataArgumentType.AudioInfo, msg, data); }
}
@ -68,8 +68,8 @@ namespace NTwain.Triplets
Message msg,
TWCapability data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Capability, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Capability, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Capability, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Capability, msg, data); }
}
@ -79,8 +79,8 @@ namespace NTwain.Triplets
Message msg,
TWCustomDSData data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.CustomDSData, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.CustomDSData, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.CustomDSData, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.CustomDSData, msg, data); }
}
@ -90,8 +90,8 @@ namespace NTwain.Triplets
Message msg,
TWDeviceEvent data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.DeviceEvent, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.DeviceEvent, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.DeviceEvent, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.DeviceEvent, msg, data); }
}
@ -101,8 +101,8 @@ namespace NTwain.Triplets
Message msg,
TWCallback data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
}
@ -112,8 +112,8 @@ namespace NTwain.Triplets
Message msg,
TWCallback2 data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Callback, msg, data); }
}
@ -123,8 +123,8 @@ namespace NTwain.Triplets
Message msg,
TWEntryPoint data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.EntryPoint, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.EntryPoint, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.EntryPoint, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.EntryPoint, msg, data); }
}
@ -134,8 +134,8 @@ namespace NTwain.Triplets
Message msg,
TWEvent data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Event, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Event, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Event, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Event, msg, data); }
}
@ -145,8 +145,8 @@ namespace NTwain.Triplets
Message msg,
TWFileSystem data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.FileSystem, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.FileSystem, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.FileSystem, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.FileSystem, msg, data); }
}
public static ReturnCode DsmEntry(
@ -154,8 +154,8 @@ namespace NTwain.Triplets
Message msg,
TWIdentity data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, IntPtr.Zero, DataGroups.Control, DataArgumentType.Identity, msg, data); }
else { return NativeMethods.DsmEntry32(origin, IntPtr.Zero, DataGroups.Control, DataArgumentType.Identity, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, IntPtr.Zero, DataGroups.Control, DataArgumentType.Identity, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, IntPtr.Zero, DataGroups.Control, DataArgumentType.Identity, msg, data); }
}
@ -165,8 +165,8 @@ namespace NTwain.Triplets
Message msg,
TWPassThru data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.PassThru, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.PassThru, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.PassThru, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.PassThru, msg, data); }
}
@ -176,8 +176,8 @@ namespace NTwain.Triplets
Message msg,
TWPendingXfers data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.PendingXfers, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.PendingXfers, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.PendingXfers, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.PendingXfers, msg, data); }
}
@ -187,8 +187,8 @@ namespace NTwain.Triplets
Message msg,
TWSetupFileXfer data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.SetupFileXfer, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.SetupFileXfer, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.SetupFileXfer, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.SetupFileXfer, msg, data); }
}
@ -198,8 +198,8 @@ namespace NTwain.Triplets
Message msg,
TWSetupMemXfer data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.SetupMemXfer, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.SetupMemXfer, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.SetupMemXfer, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.SetupMemXfer, msg, data); }
}
@ -209,8 +209,8 @@ namespace NTwain.Triplets
Message msg,
TWStatusUtf8 data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.StatusUtf8, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.StatusUtf8, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.StatusUtf8, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.StatusUtf8, msg, data); }
}
@ -220,8 +220,8 @@ namespace NTwain.Triplets
Message msg,
TWUserInterface data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.UserInterface, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.UserInterface, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.UserInterface, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.UserInterface, msg, data); }
}
@ -231,8 +231,8 @@ namespace NTwain.Triplets
Message msg,
TWCieColor data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.CieColor, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.CieColor, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.CieColor, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.CieColor, msg, data); }
}
@ -242,8 +242,8 @@ namespace NTwain.Triplets
Message msg,
TWExtImageInfo data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ExtImageInfo, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ExtImageInfo, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ExtImageInfo, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ExtImageInfo, msg, data); }
}
public static ReturnCode DsmEntry(
@ -252,8 +252,8 @@ namespace NTwain.Triplets
Message msg,
TWFilter data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.Filter, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.Filter, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.Filter, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.Filter, msg, data); }
}
public static ReturnCode DsmEntry(
@ -262,8 +262,8 @@ namespace NTwain.Triplets
Message msg,
TWGrayResponse data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.GrayResponse, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.GrayResponse, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.GrayResponse, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.GrayResponse, msg, data); }
}
@ -273,8 +273,8 @@ namespace NTwain.Triplets
Message msg,
TWImageInfo data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageInfo, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageInfo, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageInfo, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageInfo, msg, data); }
}
@ -284,8 +284,8 @@ namespace NTwain.Triplets
Message msg,
TWImageLayout data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageLayout, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageLayout, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageLayout, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageLayout, msg, data); }
}
@ -295,8 +295,8 @@ namespace NTwain.Triplets
Message msg,
TWImageMemXfer data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageMemXfer, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageMemXfer, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.ImageMemXfer, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.ImageMemXfer, msg, data); }
}
@ -306,8 +306,8 @@ namespace NTwain.Triplets
Message msg,
TWJpegCompression data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.JpegCompression, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.JpegCompression, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.JpegCompression, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.JpegCompression, msg, data); }
}
@ -317,8 +317,8 @@ namespace NTwain.Triplets
Message msg,
TWPalette8 data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.Palette8, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.Palette8, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.Palette8, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.Palette8, msg, data); }
}
@ -328,8 +328,8 @@ namespace NTwain.Triplets
Message msg,
TWRgbResponse data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.RgbResponse, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.RgbResponse, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Image, DataArgumentType.RgbResponse, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Image, DataArgumentType.RgbResponse, msg, data); }
}
@ -339,8 +339,8 @@ namespace NTwain.Triplets
Message msg,
TWStatus data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Status, msg, data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Status, msg, data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, DataArgumentType.Status, msg, data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, DataArgumentType.Status, msg, data); }
}
@ -351,8 +351,8 @@ namespace NTwain.Triplets
Message msg,
ref TWMemory data)
{
if (Is64Bit) { return NativeMethods.DsmEntry64(origin, destination, DataGroups.Control, dat, msg, ref data); }
else { return NativeMethods.DsmEntry32(origin, destination, DataGroups.Control, dat, msg, ref data); }
if (Is64Bit) { return WinNativeMethods.DsmEntry64(origin, destination, DataGroups.Control, dat, msg, ref data); }
else { return WinNativeMethods.DsmEntry32(origin, destination, DataGroups.Control, dat, msg, ref data); }
}

View File

@ -1,791 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NTwain.Triplets;
using NTwain.Data;
using NTwain.Values;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Diagnostics;
using System.Security.Permissions;
using System.IO;
using System.ComponentModel;
using System.Threading;
namespace NTwain
{
/// <summary>
/// Provides a session for working with TWAIN api in an application.
/// </summary>
public class TwainSession : ITwainStateInternal, IMessageFilter, INotifyPropertyChanged
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TwainSession(TWIdentity appId)
{
if (appId == null) { throw new ArgumentNullException("appId"); }
_appId = appId;
State = 1;
EnforceState = true;
}
#region properties
object _callbackObj;
SynchronizationContext _syncer;
TWIdentity _appId;
/// <summary>
/// Gets the app id used for the session.
/// </summary>
/// <value>The app id.</value>
TWIdentity ITwainStateInternal.GetAppId() { return _appId; }
/// <summary>
/// Gets the source id used for the session.
/// </summary>
/// <value>The source id.</value>
public TWIdentity SourceId { get; private set; }
/// <summary>
/// Gets the current state number as defined by the TWAIN spec.
/// </summary>
/// <value>The state.</value>
public int State { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether callback is used parts of source communication
/// if supported. May be required if things don't work. This does not take effect if
/// the source is already open.
/// </summary>
/// <value>
/// <c>true</c> to disable callback; otherwise, <c>false</c>.
/// </value>
public bool DisableCallback { get; set; }
/// <summary>
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
/// </summary>
/// <value>
/// <c>true</c> if state value is enforced; otherwise, <c>false</c>.
/// </value>
public bool EnforceState { get; set; }
DGAudio _dgAudio;
/// <summary>
/// Gets the triplet operations defined for audio data group.
/// </summary>
/// <value>The DG audio.</value>
public DGAudio DGAudio
{
get
{
if (_dgAudio == null) { _dgAudio = new DGAudio(this); }
return _dgAudio;
}
}
DGControl _dgControl;
/// <summary>
/// Gets the triplet operations defined for control data group.
/// </summary>
/// <value>The DG control.</value>
public DGControl DGControl
{
get
{
if (_dgControl == null) { _dgControl = new DGControl(this); }
return _dgControl;
}
}
DGImage _dgImage;
/// <summary>
/// Gets the triplet operations defined for image data group.
/// </summary>
/// <value>The DG image.</value>
public DGImage DGImage
{
get
{
if (_dgImage == null) { _dgImage = new DGImage(this); }
return _dgImage;
}
}
private IList<CapabilityId> _supportedCaps;
/// <summary>
/// Gets the supported caps for the current source.
/// </summary>
/// <value>
/// The supported caps.
/// </value>
public IList<CapabilityId> SupportedCaps
{
get
{
if (_supportedCaps == null && State > 3)
{
_supportedCaps = this.GetCapabilities();
}
return _supportedCaps ?? new CapabilityId[0];
}
private set
{
_supportedCaps = value;
RaisePropertyChanged("SupportedCaps");
}
}
#endregion
#region state transition calls
void ITwainStateInternal.ChangeState(int newState, bool notifyChange)
{
Debug.WriteLine("TWAIN State = " + newState);
State = newState;
if (notifyChange) { RaisePropertyChanged("State"); }
}
ICommitable ITwainStateInternal.GetPendingStateChanger(int newState)
{
return new TentativeStateCommitable(this, newState);
}
void ITwainStateInternal.ChangeSourceId(TWIdentity sourceId)
{
SourceId = sourceId;
RaisePropertyChanged("SourceId");
}
HandleRef _parentHandle;
/// <summary>
/// Opens the data source manager.
/// </summary>
/// <param name="handle">The handle. On Windows = points to the window handle (hWnd) that will act as the Sources
/// "parent". On Macintosh = should be a NULL value.</param>
/// <returns></returns>
public ReturnCode OpenManager(HandleRef handle)
{
Debug.WriteLine(string.Format("Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
_parentHandle = handle;
var rc = DGControl.Parent.OpenDsm(handle.Handle);
if (rc == ReturnCode.Success)
{
// if twain2 then get mem management stuff
if ((_appId.DataFunctionalities & DataFunctionalities.Dsm2) == DataFunctionalities.Dsm2)
{
TWEntryPoint entry;
rc = DGControl.EntryPoint.Get(out entry);
if (rc == ReturnCode.Success)
{
MemoryManager.Instance.UpdateEntryPoint(entry);
Debug.WriteLine("Using TWAIN2 memory functions.");
}
else
{
CloseManager();
}
}
}
return rc;
}
/// <summary>
/// Closes the data source manager.
/// </summary>
/// <returns></returns>
public ReturnCode CloseManager()
{
Debug.WriteLine(string.Format("Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
var rc = DGControl.Parent.CloseDsm(_parentHandle.Handle);
if (rc == ReturnCode.Success)
{
_parentHandle = default(HandleRef);
MemoryManager.Instance.UpdateEntryPoint(null);
}
return rc;
}
/// <summary>
/// Loads the specified source into main memory and causes its initialization.
/// </summary>
/// <param name="sourceProductName">Name of the source.</param>
/// <returns></returns>
public ReturnCode OpenSource(string sourceProductName)
{
var source = new TWIdentity();
source.ProductName = sourceProductName;
return OpenSource(source);
}
/// <summary>
/// Loads the specified Source into main memory and causes its initialization.
/// </summary>
/// <param name="sourceId">The source id.</param>
/// <returns></returns>
public ReturnCode OpenSource(TWIdentity sourceId)
{
if (sourceId == null) { throw new ArgumentNullException("sourceId"); }
Debug.WriteLine(string.Format("Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
var rc = DGControl.Identity.OpenDS(sourceId);
if (rc == ReturnCode.Success)
{
SupportedCaps = this.GetCapabilities();
// TODO: does it work?
_syncer = SynchronizationContext.Current ?? new SynchronizationContext();
if (!DisableCallback)
{
// app v2.2 or higher uses callback2
if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
{
var cb = new TWCallback2(CallbackHandler);
var rc2 = DGControl.Callback2.RegisterCallback(cb);
if (rc2 == ReturnCode.Success)
{
Debug.WriteLine("Registered callback2.");
_callbackObj = cb;
}
}
else
{
var cb = new TWCallback(CallbackHandler);
var rc2 = DGControl.Callback.RegisterCallback(cb);
if (rc2 == ReturnCode.Success)
{
Debug.WriteLine("Registered callback.");
_callbackObj = cb;
}
}
}
}
return rc;
}
ReturnCode CallbackHandler(TWIdentity origin, TWIdentity dest,
DataGroups dg, DataArgumentType dat, Values.Message msg, IntPtr data)
{
if (origin != null && SourceId != null && origin.Id == SourceId.Id)
{
Debug.WriteLine(string.Format("Thread {0}: GOT TWAIN callback for msg {1}.", Thread.CurrentThread.ManagedThreadId, msg));
// spec says should handle this on the thread that enabled the DS,
// but it's already the same and doesn't work (failure + seqError) w/o jumping to another thread and back.
// My guess is the DS needs to see the Success first before letting transfer happen
// so this is an artificial delay to make it happen.
// TODO: find a better method.
ThreadPool.QueueUserWorkItem(o =>
{
_syncer.Send(blah =>
{
HandleSourceMsg(origin, dest, dg, dat, msg, data);
}, null);
}, null);
return ReturnCode.Success;
}
return ReturnCode.Failure;
}
/// <summary>
/// When an application is finished with a Source, it must formally close the session between them
/// using this operation. This is necessary in case the Source only supports connection with a single
/// application (many desktop scanners will behave this way). A Source such as this cannot be
/// accessed by other applications until its current session is terminated
/// </summary>
/// <returns></returns>
public ReturnCode CloseSource()
{
Debug.WriteLine(string.Format("Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
var rc = DGControl.Identity.CloseDS();
if (rc == ReturnCode.Success)
{
_callbackObj = null;
SupportedCaps = null;
}
return rc;
}
TWUserInterface _twui;
/// <summary>
/// Enables the source for data acquisition.
/// </summary>
/// <param name="mode">The mode.</param>
/// <param name="modal">if set to <c>true</c> any driver UI will display as modal.</param>
/// <param name="windowHandle">The window handle if modal.</param>
/// <returns></returns>
public ReturnCode EnableSource(SourceEnableMode mode, bool modal, HandleRef windowHandle)
{
Debug.WriteLine(string.Format("Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
_twui = new TWUserInterface();
_twui.ShowUI = mode == SourceEnableMode.ShowUI;
_twui.ModalUI = modal;
_twui.hParent = windowHandle.Handle;
if (mode == SourceEnableMode.ShowUIOnly)
{
return DGControl.UserInterface.EnableDSUIOnly(_twui);
}
else
{
return DGControl.UserInterface.EnableDS(_twui);
}
}
/// <summary>
/// Disables the source to end data acquisition.
/// </summary>
/// <returns></returns>
ReturnCode DisableSource()
{
Debug.WriteLine(string.Format("Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
var rc = DGControl.UserInterface.DisableDS(_twui);
if (rc == ReturnCode.Success)
{
var hand = SourceDisabled;
if (hand != null)
{
try
{
hand(this, EventArgs.Empty);
}
catch { }
}
}
return rc;
}
#endregion
#region consumer to handle
/// <summary>
/// Occurs when source has been disabled (back to state 4).
/// </summary>
public event EventHandler SourceDisabled;
/// <summary>
/// Occurs when a data transfer is ready.
/// </summary>
public event EventHandler<TransferReadyEventArgs> TransferReady;
/// <summary>
/// Occurs when the source has generated an event.
/// </summary>
public event EventHandler<DeviceEventArgs> DeviceEvent;
/// <summary>
/// Occurs when data has been transferred.
/// </summary>
public event EventHandler<DataTransferredEventArgs> DataTransferred;
private void DoTransferRoutine()
{
TWPendingXfers pending = new TWPendingXfers();
var rc = ReturnCode.Success;
do
{
IList<FileFormat> formats = Enumerable.Empty<FileFormat>().ToList();
IList<Compression> compressions = Enumerable.Empty<Compression>().ToList();
bool canDoFileXfer = this.CapGetImageXferMech().Contains(XferMech.File);
var curFormat = this.GetCurrentCap<FileFormat>(CapabilityId.ICapImageFileFormat);
var curComp = this.GetCurrentCap<Compression>(CapabilityId.ICapCompression);
TWImageInfo imgInfo;
bool skip = false;
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
// bad!
skip = true;
}
try
{
formats = this.CapGetImageFileFormat();
}
catch { }
try
{
compressions = this.CapGetCompression();
}
catch { }
// ask consumer for cancel in case of non-ui multi-page transfers
TransferReadyEventArgs args = new TransferReadyEventArgs(pending, formats, curFormat, compressions,
curComp, canDoFileXfer, imgInfo);
args.CancelCurrent = skip;
var hand = TransferReady;
if (hand != null)
{
try
{
hand(this, args);
}
catch { }
}
if (!args.CancelAll && !args.CancelCurrent)
{
Values.XferMech mech = this.GetCurrentCap<XferMech>(CapabilityId.ICapXferMech);
if (args.CanDoFileXfer && !string.IsNullOrEmpty(args.OutputFile))
{
var setXferRC = DGControl.SetupFileXfer.Set(new TWSetupFileXfer
{
FileName = args.OutputFile,
Format = args.ImageFormat
});
if (setXferRC == ReturnCode.Success)
{
mech = XferMech.File;
}
}
// I don't know how this is supposed to work so it probably doesn't
//this.CapSetImageFormat(args.ImageFormat);
//this.CapSetImageCompression(args.ImageCompression);
#region do xfer
// TODO: expose all swallowed exceptions somehow later
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
string file = null;
try
{
ReturnCode xrc = ReturnCode.Cancel;
switch (mech)
{
case Values.XferMech.Native:
xrc = DGImage.ImageNativeXfer.Get(ref dataPtr);
break;
case Values.XferMech.File:
xrc = DGImage.ImageFileXfer.Get();
if (File.Exists(args.OutputFile))
{
file = args.OutputFile;
}
break;
case Values.XferMech.MemFile:
// not supported yet
//TWImageMemXfer memxfer = new TWImageMemXfer();
//xrc = DGImage.ImageMemXfer.Get(memxfer);
break;
}
if (xrc == ReturnCode.XferDone)
{
State = 7;
try
{
var dtHand = DataTransferred;
if (dtHand != null)
{
if (dataPtr != IntPtr.Zero)
{
lockedPtr = MemoryManager.Instance.Lock(dataPtr);
}
dtHand(this, new DataTransferredEventArgs(lockedPtr, file));
}
}
catch { }
}
//}
//else if (group == DataGroups.Audio)
//{
// var xrc = DGAudio.AudioNativeXfer.Get(ref dataPtr);
// if (xrc == ReturnCode.XferDone)
// {
// State = 7;
// try
// {
// var dtHand = DataTransferred;
// if (dtHand != null)
// {
// lockedPtr = MemoryManager.Instance.MemLock(dataPtr);
// dtHand(this, new DataTransferredEventArgs(lockedPtr));
// }
// }
// catch { }
// }
//}
}
finally
{
State = 6;
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
MemoryManager.Instance.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
MemoryManager.Instance.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
#endregion
}
if (args.CancelAll)
{
rc = DGControl.PendingXfers.Reset(pending);
if (rc == ReturnCode.Success)
{
// if audio exit here
//if (group == DataGroups.Audio)
//{
// //???
// return;
//}
}
}
else
{
rc = DGControl.PendingXfers.EndXfer(pending);
}
} while (rc == ReturnCode.Success && pending.Count != 0);
State = 5;
DisableSource();
}
#endregion
#region messaging use
ReturnCode HandleSourceMsg(TWIdentity origin, TWIdentity destination,
DataGroups dg, DataArgumentType dat, NTwain.Values.Message msg, IntPtr data)
{
Debug.WriteLine(string.Format("Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}.", Thread.CurrentThread.ManagedThreadId, State, dg, dat, msg));
ReturnCode rc = ReturnCode.Success;
switch (msg)
{
case Values.Message.XferReady:
if (State < 6)
State = 6;
// this is the meat of all twain stuff
DoTransferRoutine();
break;
case Values.Message.DeviceEvent:
TWDeviceEvent de;
rc = DGControl.DeviceEvent.Get(out de);
if (rc == ReturnCode.Success)
{
var hand = this.DeviceEvent;
if (hand != null)
{
try
{
hand(this, new DeviceEventArgs(de));
}
catch { }
}
}
break;
case Values.Message.CloseDSReq:
case Values.Message.CloseDSOK:
// even though it says closeDS it's really disable.
// dsok is sent if source is enabled with uionly
// some sources send this at other states so do a step down
if (State > 5)
{
ForceStepDown(4);
}
else if (State == 5)
{
// needs this state check since some source sends this more than once
DisableSource();
}
break;
}
return rc;
}
/// <summary>
/// Forces the stepping down of an opened source ignoring return values.
/// Used when session state and source state become out of sync.
/// </summary>
/// <param name="targetState">State of the target.</param>
public void ForceStepDown(int targetState)
{
Debug.WriteLine(string.Format("Thread {0}: ForceStepDown.", Thread.CurrentThread.ManagedThreadId));
bool origFlag = EnforceState;
EnforceState = false;
// From the twain spec
// Stepping Back Down the States
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS → state 5 to 4
// DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS → state 4 to 3
// Ignore the status returns from the calls prior to the one yielding the desired state. For instance, if a
// call during scanning returns TWCC_SEQERROR and the desire is to return to state 5, then use the
// following commands.
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// Being sure to confirm that DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET returned
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
// be ignored.
if (targetState < 7)
{
DGControl.PendingXfers.EndXfer(new TWPendingXfers());
}
if (targetState < 6)
{
DGControl.PendingXfers.Reset(new TWPendingXfers());
}
if (targetState < 5)
{
DisableSource();
}
if (targetState < 4)
{
CloseSource();
}
if (targetState < 3)
{
CloseManager();
}
EnforceState = origFlag;
}
/// <summary>
/// Handles the message from a message loop.
/// </summary>
/// <param name="msgPtr">Pointer to message structure.</param>
/// <returns>True if handled by TWAIN.</returns>
bool HandleLoopMsgEvent(ref IntPtr msgPtr)
{
TWEvent evt = new TWEvent();
evt.pEvent = msgPtr;
var rc = DGControl.Event.ProcessEvent(evt);
HandleSourceMsg(null, null, DataGroups.Control, DataArgumentType.Null, evt.TWMessage, IntPtr.Zero);
return rc == ReturnCode.DSEvent;
}
/// <summary>
/// Message loop processor for winform.
/// Use this by adding the <see cref="TwainSession"/> as an <see cref="IMessageFilter "/>.
/// </summary>
/// <param name="m">The message to be dispatched. You cannot modify this message.</param>
/// <returns>
/// true to filter the message and stop it from being dispatched; false to allow the message to continue to the next filter or control.
/// </returns>
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
bool IMessageFilter.PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (State > 3)
{
MSG winmsg = default(MSG);
winmsg.hwnd = m.HWnd;
winmsg.lParam = m.LParam;
winmsg.message = m.Msg;
winmsg.wParam = m.WParam;
IntPtr msgPtr = IntPtr.Zero;
try
{
// no need to lock for marshal alloc
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
Marshal.StructureToPtr(winmsg, msgPtr, false);
return HandleLoopMsgEvent(ref msgPtr);
}
finally
{
if (msgPtr != IntPtr.Zero)
Marshal.FreeHGlobal(msgPtr);
//MemoryManager.Instance.Free(msgPtr);
}
}
return false;
}
/// <summary>
/// Message loop processor for wpf.
/// Use this as the target of <see cref="HwndSourceHook"/> delegate.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="msg">The message ID.</param>
/// <param name="wParam">The message's wParam value.</param>
/// <param name="lParam">The message's lParam value.</param>
/// <param name="handled">A value that indicates whether the message was handled. Set the value to true if the message was handled; otherwise, false.</param>
/// <returns></returns>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public IntPtr PreFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// always pass message since it works whether there's a callback or not?
if (State > 3)// && _callbackObj == null)
{
MSG winmsg = default(MSG);
winmsg.hwnd = hwnd;
winmsg.lParam = lParam;
winmsg.message = msg;
winmsg.wParam = wParam;
IntPtr msgPtr = IntPtr.Zero;
try
{
// no need to lock for marshal alloc
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
Marshal.StructureToPtr(winmsg, msgPtr, false);
handled = HandleLoopMsgEvent(ref msgPtr);
}
finally
{
if (msgPtr != IntPtr.Zero)
Marshal.FreeHGlobal(msgPtr);
//MemoryManager.Instance.Free(msgPtr);
}
}
return IntPtr.Zero;
}
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string property)
{
var hand = PropertyChanged;
if (hand != null) { hand(this, new PropertyChangedEventArgs(property)); }
}
#endregion
}
}

View File

@ -19,7 +19,7 @@ namespace NTwain
public class TwainSessionBase : ITwainStateInternal, ITwainOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession" /> class.
/// Initializes a new instance of the <see cref="TwainSessionOld" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
@ -37,6 +37,32 @@ namespace NTwain
object _callbackObj; // kept around so it doesn't get gc'ed
TWUserInterface _twui;
private IList<CapabilityId> _supportedCaps;
/// <summary>
/// Gets the supported caps for the current source.
/// </summary>
/// <value>
/// The supported caps.
/// </value>
public IList<CapabilityId> SupportedCaps
{
get
{
if (_supportedCaps == null && State > 3)
{
_supportedCaps = this.GetCapabilities();
}
return _supportedCaps ?? new CapabilityId[0];
}
private set
{
_supportedCaps = value;
RaisePropertyChanged("SupportedCaps");
}
}
#region ITwainStateInternal Members
/// <summary>
@ -55,7 +81,7 @@ namespace NTwain
void ITwainStateInternal.ChangeState(int newState, bool notifyChange)
{
State = newState;
_state = newState;
if (notifyChange)
{
RaisePropertyChanged("State");
@ -72,7 +98,7 @@ namespace NTwain
{
SourceId = sourceId;
RaisePropertyChanged("SourceId");
OnSourceIdChanged();
OnSourceChanged();
}
#endregion
@ -87,13 +113,26 @@ namespace NTwain
/// </value>
public TWIdentity SourceId { get; private set; }
int _state;
/// <summary>
/// Gets the current state number as defined by the TWAIN spec.
/// </summary>
/// <value>
/// The state.
/// </value>
public int State { get; private set; }
public int State
{
get { return _state; }
protected set
{
if (value > 0 && value < 8)
{
_state = value;
RaisePropertyChanged("State");
OnStateChanged();
}
}
}
#endregion
@ -262,7 +301,7 @@ namespace NTwain
/// <param name="windowHandle">The window handle if modal.</param>
/// <param name="context">The
/// <see cref="SynchronizationContext" /> that is required for certain operations.
/// It is recommended you call this method in a UI thread and pass in
/// It is recommended you call this method in an UI thread and pass in
/// <see cref="SynchronizationContext.Current" />
/// if you do not have a custom one setup.</param>
/// <returns></returns>
@ -319,7 +358,7 @@ namespace NTwain
/// Disables the source to end data acquisition.
/// </summary>
/// <returns></returns>
ReturnCode DisableSource()
protected ReturnCode DisableSource()
{
Debug.WriteLine(string.Format("Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
@ -553,13 +592,14 @@ namespace NTwain
}
// final method that handles stuff from the source, whether it's from wndproc or callbacks
void HandleSourceMsg(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
protected virtual void HandleSourceMsg(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
{
if (msg != Message.Null)
{
Debug.WriteLine(string.Format("Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}.", Thread.CurrentThread.ManagedThreadId, State, dg, dat, msg));
}
throw new NotImplementedException();
}

View File

@ -8,7 +8,7 @@ using System.Text;
namespace NTwain
{
/// <summary>
/// Defines common methods on <see cref="TwainSession"/> using the raw
/// Defines common methods on <see cref="TwainSessionOld"/> using the raw
/// TWAIN triplet api.
/// </summary>
public static class TwainSessionExtensions
@ -18,7 +18,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static TWStatus GetManagerStatus(this TwainSession session)
public static TWStatus GetManagerStatus(this TwainSessionBase session)
{
TWStatus stat;
session.DGControl.Status.GetManager(out stat);
@ -29,7 +29,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static TWStatus GetSourceStatus(this TwainSession session)
public static TWStatus GetSourceStatus(this TwainSessionBase session)
{
TWStatus stat;
session.DGControl.Status.GetSource(out stat);
@ -42,7 +42,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<TWIdentity> GetSources(this TwainSession session)
public static IList<TWIdentity> GetSources(this TwainSessionBase session)
{
List<TWIdentity> list = new List<TWIdentity>();
@ -93,7 +93,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="capId">The cap id.</param>
/// <returns></returns>
public static T GetCurrentCap<T>(this TwainSession session, CapabilityId capId) where T : struct,IConvertible
public static T GetCurrentCap<T>(this TwainSessionBase session, CapabilityId capId) where T : struct,IConvertible
{
using (TWCapability cap = new TWCapability(capId))
{
@ -230,14 +230,14 @@ namespace NTwain
}
/// <summary>
/// A generic method that tries to get capability values from current <see cref="TwainSession" />.
/// A generic method that tries to get capability values from current <see cref="TwainSessionOld" />.
/// </summary>
/// <typeparam name="TCapVal">The expected capability value type.</typeparam>
/// <param name="session">The session.</param>
/// <param name="capabilityId">The capability unique identifier.</param>
/// <param name="tryUpperWord">if set to <c>true</c> then apply to workaround for certain bad sources.</param>
/// <returns></returns>
public static IList<TCapVal> GetCapabilityValues<TCapVal>(this TwainSession session, CapabilityId capabilityId, bool tryUpperWord) where TCapVal : struct,IConvertible
public static IList<TCapVal> GetCapabilityValues<TCapVal>(this TwainSessionBase session, CapabilityId capabilityId, bool tryUpperWord) where TCapVal : struct,IConvertible
{
var list = new List<TCapVal>();
using (TWCapability cap = new TWCapability(capabilityId))
@ -257,7 +257,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
internal static IList<CapabilityId> GetCapabilities(this TwainSession session)
internal static IList<CapabilityId> GetCapabilities(this TwainSessionBase session)
{
return session.GetCapabilityValues<CapabilityId>(CapabilityId.CapSupportedCaps, false);
}
@ -270,7 +270,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<XferMech> CapGetImageXferMech(this TwainSession session)
public static IList<XferMech> CapGetImageXferMech(this TwainSessionBase session)
{
return session.GetCapabilityValues<XferMech>(CapabilityId.ICapXferMech, true);
}
@ -285,7 +285,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<Compression> CapGetCompression(this TwainSession session)
public static IList<Compression> CapGetCompression(this TwainSessionBase session)
{
return session.GetCapabilityValues<Compression>(CapabilityId.ICapCompression, true);
}
@ -296,7 +296,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="compression">The compression.</param>
/// <returns></returns>
public static ReturnCode CapSetImageCompression(this TwainSession session, Compression compression)
public static ReturnCode CapSetImageCompression(this TwainSessionBase session, Compression compression)
{
using (TWCapability compressCap = new TWCapability(CapabilityId.ICapCompression, new TWOneValue { Item = (uint)compression, ItemType = Values.ItemType.UInt16 }))
{
@ -314,7 +314,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<FileFormat> CapGetImageFileFormat(this TwainSession session)
public static IList<FileFormat> CapGetImageFileFormat(this TwainSessionBase session)
{
return session.GetCapabilityValues<FileFormat>(CapabilityId.ICapImageFileFormat, true);
}
@ -325,7 +325,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="format">The format.</param>
/// <returns></returns>
public static ReturnCode CapSetImageFormat(this TwainSession session, FileFormat format)
public static ReturnCode CapSetImageFormat(this TwainSessionBase session, FileFormat format)
{
using (TWCapability formatCap = new TWCapability(CapabilityId.ICapImageFileFormat, new TWOneValue { Item = (uint)format, ItemType = Values.ItemType.UInt16 }))
{
@ -343,7 +343,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<PixelType> CapGetPixelTypes(this TwainSession session)
public static IList<PixelType> CapGetPixelTypes(this TwainSessionBase session)
{
return session.GetCapabilityValues<PixelType>(CapabilityId.ICapPixelType, true);
}
@ -354,7 +354,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="type">The type.</param>
/// <returns></returns>
public static ReturnCode CapSetPixelType(this TwainSession session, PixelType type)
public static ReturnCode CapSetPixelType(this TwainSessionBase session, PixelType type)
{
var one = new TWOneValue();
one.Item = (uint)type;
@ -375,7 +375,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<int> CapGetDPIs(this TwainSession session)
public static IList<int> CapGetDPIs(this TwainSessionBase session)
{
return session.GetCapabilityValues<int>(CapabilityId.ICapXResolution, true);
}
@ -386,7 +386,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="dpi">The DPI.</param>
/// <returns></returns>
public static ReturnCode CapSetDPI(this TwainSession session, int dpi)
public static ReturnCode CapSetDPI(this TwainSessionBase session, int dpi)
{
return CapSetDPI(session, dpi, dpi);
}
@ -398,7 +398,7 @@ namespace NTwain
/// <param name="xDPI">The x DPI.</param>
/// <param name="yDPI">The y DPI.</param>
/// <returns></returns>
public static ReturnCode CapSetDPI(this TwainSession session, int xDPI, int yDPI)
public static ReturnCode CapSetDPI(this TwainSessionBase session, int xDPI, int yDPI)
{
TWOneValue one = new TWOneValue();
one.Item = (uint)xDPI;// ((uint)dpi) << 16;
@ -429,7 +429,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<SupportedSize> CapGetSupportedSizes(this TwainSession session)
public static IList<SupportedSize> CapGetSupportedSizes(this TwainSessionBase session)
{
return session.GetCapabilityValues<SupportedSize>(CapabilityId.ICapSupportedSizes, true);
}
@ -440,7 +440,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="size">The size.</param>
/// <returns></returns>
public static ReturnCode CapSetSupportedSize(this TwainSession session, SupportedSize size)
public static ReturnCode CapSetSupportedSize(this TwainSessionBase session, SupportedSize size)
{
var one = new TWOneValue();
one.Item = (uint)size;
@ -463,7 +463,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="useIt">if set to <c>true</c> [use it].</param>
/// <returns></returns>
public static ReturnCode CapSetAutoDeskew(this TwainSession session, bool useIt)
public static ReturnCode CapSetAutoDeskew(this TwainSessionBase session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticDeskew))
@ -503,7 +503,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="useIt">if set to <c>true</c> [use it].</param>
/// <returns></returns>
public static ReturnCode CapSetAutoRotate(this TwainSession session, bool useIt)
public static ReturnCode CapSetAutoRotate(this TwainSessionBase session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticRotate))
@ -541,7 +541,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="useIt">if set to <c>true</c> [use it].</param>
/// <returns></returns>
public static ReturnCode CapSetBorderDetection(this TwainSession session, bool useIt)
public static ReturnCode CapSetBorderDetection(this TwainSessionBase session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticBorderDetection))
@ -589,7 +589,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="useIt">if set to <c>true</c> [use it].</param>
/// <returns></returns>
public static ReturnCode CapSetDuplex(this TwainSession session, bool useIt)
public static ReturnCode CapSetDuplex(this TwainSessionBase session, bool useIt)
{
if (session.SourceId.ProtocolMajor >= 2)
{
@ -623,7 +623,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="useIt">if set to <c>true</c> [use it].</param>
/// <returns></returns>
public static ReturnCode CapSetFeeder(this TwainSession session, bool useIt)
public static ReturnCode CapSetFeeder(this TwainSessionBase session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.CapFeederEnabled))

284
NTwain/TwainSessionOld.cs Normal file
View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NTwain.Triplets;
using NTwain.Data;
using NTwain.Values;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Diagnostics;
using System.Security.Permissions;
using System.IO;
using System.ComponentModel;
using System.Threading;
namespace NTwain
{
/// <summary>
/// Provides a session for working with TWAIN api in an application.
/// This is the old implementation for reference purposes only.
/// </summary>
[Obsolete("For reference purposes only.")]
public class TwainSessionOld : TwainSessionBase, IMessageFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSessionOld" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TwainSessionOld(TWIdentity appId) : base(appId) { }
private void DoTransferRoutine()
{
TWPendingXfers pending = new TWPendingXfers();
var rc = ReturnCode.Success;
do
{
IList<FileFormat> formats = Enumerable.Empty<FileFormat>().ToList();
IList<Compression> compressions = Enumerable.Empty<Compression>().ToList();
bool canDoFileXfer = this.CapGetImageXferMech().Contains(XferMech.File);
var curFormat = this.GetCurrentCap<FileFormat>(CapabilityId.ICapImageFileFormat);
var curComp = this.GetCurrentCap<Compression>(CapabilityId.ICapCompression);
TWImageInfo imgInfo;
bool skip = false;
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
// bad!
skip = true;
}
try
{
formats = this.CapGetImageFileFormat();
}
catch { }
try
{
compressions = this.CapGetCompression();
}
catch { }
// ask consumer for cancel in case of non-ui multi-page transfers
TransferReadyEventArgs args = new TransferReadyEventArgs(pending, formats, curFormat, compressions,
curComp, canDoFileXfer, imgInfo);
args.CancelCurrent = skip;
OnTransferReady(args);
if (!args.CancelAll && !args.CancelCurrent)
{
Values.XferMech mech = this.GetCurrentCap<XferMech>(CapabilityId.ICapXferMech);
if (args.CanDoFileXfer && !string.IsNullOrEmpty(args.OutputFile))
{
var setXferRC = DGControl.SetupFileXfer.Set(new TWSetupFileXfer
{
FileName = args.OutputFile,
Format = args.ImageFormat
});
if (setXferRC == ReturnCode.Success)
{
mech = XferMech.File;
}
}
// I don't know how this is supposed to work so it probably doesn't
//this.CapSetImageFormat(args.ImageFormat);
//this.CapSetImageCompression(args.ImageCompression);
#region do xfer
// TODO: expose all swallowed exceptions somehow later
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
string file = null;
try
{
ReturnCode xrc = ReturnCode.Cancel;
switch (mech)
{
case Values.XferMech.Native:
xrc = DGImage.ImageNativeXfer.Get(ref dataPtr);
break;
case Values.XferMech.File:
xrc = DGImage.ImageFileXfer.Get();
if (File.Exists(args.OutputFile))
{
file = args.OutputFile;
}
break;
case Values.XferMech.MemFile:
// not supported yet
//TWImageMemXfer memxfer = new TWImageMemXfer();
//xrc = DGImage.ImageMemXfer.Get(memxfer);
break;
}
if (xrc == ReturnCode.XferDone)
{
State = 7;
if (dataPtr != IntPtr.Zero)
{
lockedPtr = MemoryManager.Instance.Lock(dataPtr);
}
OnDataTransferred(new DataTransferredEventArgs(lockedPtr, file));
}
//}
//else if (group == DataGroups.Audio)
//{
// var xrc = DGAudio.AudioNativeXfer.Get(ref dataPtr);
// if (xrc == ReturnCode.XferDone)
// {
// State = 7;
// try
// {
// var dtHand = DataTransferred;
// if (dtHand != null)
// {
// lockedPtr = MemoryManager.Instance.MemLock(dataPtr);
// dtHand(this, new DataTransferredEventArgs(lockedPtr));
// }
// }
// catch { }
// }
//}
}
finally
{
State = 6;
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
MemoryManager.Instance.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
MemoryManager.Instance.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
#endregion
}
if (args.CancelAll)
{
rc = DGControl.PendingXfers.Reset(pending);
if (rc == ReturnCode.Success)
{
// if audio exit here
//if (group == DataGroups.Audio)
//{
// //???
// return;
//}
}
}
else
{
rc = DGControl.PendingXfers.EndXfer(pending);
}
} while (rc == ReturnCode.Success && pending.Count != 0);
State = 5;
DisableSource();
}
#region messaging use
protected override void HandleSourceMsg(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Values.Message msg, IntPtr data)
{
if (msg != Values.Message.Null)
{
Debug.WriteLine(string.Format("Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}.", Thread.CurrentThread.ManagedThreadId, State, dg, dat, msg));
}
switch (msg)
{
case Values.Message.XferReady:
if (State < 6)
State = 6;
// this is the meat of all twain stuff
DoTransferRoutine();
break;
case Values.Message.DeviceEvent:
TWDeviceEvent de;
var rc = DGControl.DeviceEvent.Get(out de);
if (rc == ReturnCode.Success)
{
OnDeviceEvent(new DeviceEventArgs(de));
}
break;
case Values.Message.CloseDSReq:
case Values.Message.CloseDSOK:
// even though it says closeDS it's really disable.
// dsok is sent if source is enabled with uionly
// some sources send this at other states so do a step down
if (State > 5)
{
ForceStepDown(4);
}
else if (State == 5)
{
// needs this state check since some source sends this more than once
DisableSource();
}
break;
}
}
/// <summary>
/// Message loop processor for winform.
/// Use this by adding the <see cref="TwainSessionOld"/> as an <see cref="IMessageFilter "/>.
/// </summary>
/// <param name="m">The message to be dispatched. You cannot modify this message.</param>
/// <returns>
/// true to filter the message and stop it from being dispatched; false to allow the message to continue to the next filter or control.
/// </returns>
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
bool IMessageFilter.PreFilterMessage(ref System.Windows.Forms.Message m)
{
MSG winmsg = default(MSG);
winmsg.hwnd = m.HWnd;
winmsg.lParam = m.LParam;
winmsg.message = m.Msg;
winmsg.wParam = m.WParam;
return HandleWndProcMessage(ref winmsg);
}
/// <summary>
/// Message loop processor for WPF.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="msg">The message ID.</param>
/// <param name="wParam">The message's wParam value.</param>
/// <param name="lParam">The message's lParam value.</param>
/// <param name="handled">A value that indicates whether the message was handled. Set the value to true if the message was handled; otherwise, false.</param>
/// <returns></returns>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public IntPtr PreFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
MSG winmsg = default(MSG);
winmsg.hwnd = hwnd;
winmsg.lParam = lParam;
winmsg.message = msg;
winmsg.wParam = wParam;
handled = base.HandleWndProcMessage(ref winmsg);
return IntPtr.Zero;
}
#endregion
}
}

View File

@ -3,11 +3,12 @@ TWAIN Application-Side Library
Info
--------------------------------------
This is a dotnet library created to work with [TWAIN](http://twain.org/) interface on Windows.
This is a dotnet library created to work with [TWAIN](http://twain.org/) interface.
This project follows these general goals:
* Targets latest TWAIN version (2.3 at the moment)
* Supports all the TWAIN commands in the spec (or those that are possible)
* Eventally work on platforms other than Windows
The solution contains sample projects in both winforms and wpf. A nuget package is also
[available here](https://www.nuget.org/packages/ntwain)

View File

@ -12,7 +12,7 @@ namespace NTwain.Tests
[ExpectedException(typeof(TwainStateException), "State check failed to throw.")]
public void VerifyState_Throws_When_State_Is_Enforced()
{
ITwainStateInternal session = new TwainSession(TWIdentity.Create(DataGroups.Image, new Version(1, 0), "test", "test", "test", "test"));
ITwainStateInternal session = new TwainSessionOld(TWIdentity.Create(DataGroups.Image, new Version(1, 0), "test", "test", "test", "test"));
session.EnforceState = true;
session.ChangeState(4, false);
@ -22,7 +22,7 @@ namespace NTwain.Tests
[TestMethod]
public void VerifyState_No_Throws_When_State_Is_Not_Enforced()
{
ITwainStateInternal session = new TwainSession(TWIdentity.Create(DataGroups.Image, new Version(1, 0), "test", "test", "test", "test"));
ITwainStateInternal session = new TwainSessionOld(TWIdentity.Create(DataGroups.Image, new Version(1, 0), "test", "test", "test", "test"));
session.EnforceState = false;
session.ChangeState(4, false);

View File

@ -17,6 +17,7 @@ using NTwain.Values;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using CommonWin32;
using System.Threading;
namespace Tester.WPF
{
@ -25,7 +26,7 @@ namespace Tester.WPF
/// </summary>
public partial class MainWindow : Window
{
TwainSession twain;
TwainSessionOld twain;
public MainWindow()
{
InitializeComponent();
@ -58,7 +59,7 @@ namespace Tester.WPF
private void SetupTwain()
{
TWIdentity appId = TWIdentity.Create(DataGroups.Image, new Version(1, 0), "My Company", "Test Family", "Tester", null);
twain = new TwainSession(appId);
twain = new TwainSessionOld(appId);
twain.DataTransferred += (s, e) =>
{
if (e.Data != IntPtr.Zero)
@ -117,7 +118,7 @@ namespace Tester.WPF
if (rc == ReturnCode.Success)
{
step = "Open DS";
rc = twain.OpenSource(dsId);
rc = twain.OpenSource(dsId.ProductName);
//rc = DGControl.Status.Get(dsId, ref stat);
if (rc == ReturnCode.Success)
{
@ -137,7 +138,7 @@ namespace Tester.WPF
}
step = "Enable DS";
rc = twain.EnableSource(SourceEnableMode.NoUI, false, hand);
rc = twain.EnableSource(SourceEnableMode.NoUI, false, hand, SynchronizationContext.Current);
return;
}
else

View File

@ -13,13 +13,14 @@ using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Reflection;
using CommonWin32;
using System.Threading;
namespace Tester.Winform
{
sealed partial class TestForm : Form
{
ImageCodecInfo _tiffCodecInfo;
TwainSession _twain;
TwainSessionOld _twain;
bool _stopScan;
bool _loadingCaps;
@ -63,7 +64,7 @@ namespace Tester.Winform
TWIdentity appId = TWIdentity.Create(DataGroups.Image, new Version(appVer.ProductMajorPart, appVer.ProductMinorPart),
"My Company", "Test Family", "Tester", "A TWAIN testing app.");
_twain = new TwainSession(appId);
_twain = new TwainSessionOld(appId);
_twain.DataTransferred += (s, e) =>
{
if (pictureBox1.Image != null)
@ -167,7 +168,7 @@ namespace Tester.Winform
if (_twain.SupportedCaps.Contains(CapabilityId.CapUIControllable))
{
// hide scanner ui if possible
if (_twain.EnableSource(SourceEnableMode.NoUI, false, hand) == ReturnCode.Success)
if (_twain.EnableSource(SourceEnableMode.NoUI, false, hand, SynchronizationContext.Current) == ReturnCode.Success)
{
btnStopScan.Enabled = true;
btnStartCapture.Enabled = false;
@ -176,7 +177,7 @@ namespace Tester.Winform
}
else
{
if (_twain.EnableSource(SourceEnableMode.ShowUI, false, hand) == ReturnCode.Success)
if (_twain.EnableSource(SourceEnableMode.ShowUI, false, hand, SynchronizationContext.Current) == ReturnCode.Success)
{
btnStopScan.Enabled = true;
btnStartCapture.Enabled = false;
@ -360,7 +361,7 @@ namespace Tester.Winform
private void btnAllSettings_Click(object sender, EventArgs e)
{
var hand = new HandleRef(this, this.Handle);
_twain.EnableSource(SourceEnableMode.ShowUIOnly, true, hand);
_twain.EnableSource(SourceEnableMode.ShowUIOnly, true, hand, SynchronizationContext.Current);
}
#endregion