Refactoring xfer logic from twainsession out.

This commit is contained in:
soukoku 2014-04-20 18:42:51 -04:00
parent 1743b8379b
commit 4b08d3bc29
55 changed files with 717 additions and 688 deletions

View File

@ -79,8 +79,8 @@
<Compile Include="..\NTwain\Internals\ICommittable.cs">
<Link>Internals\ICommittable.cs</Link>
</Compile>
<Compile Include="..\NTwain\Internals\ITwainStateInternal.cs">
<Link>Internals\ITwainStateInternal.cs</Link>
<Compile Include="..\NTwain\Internals\ITwainSessionInternal.cs">
<Link>Internals\ITwainSessionInternal.cs</Link>
</Compile>
<Compile Include="..\NTwain\Internals\MessageLoop.cs">
<Link>Internals\MessageLoop.cs</Link>
@ -91,6 +91,9 @@
<Compile Include="..\NTwain\Internals\TentativeStateCommitable.cs">
<Link>Internals\TentativeStateCommitable.cs</Link>
</Compile>
<Compile Include="..\NTwain\Internals\TransferLogic.cs">
<Link>Internals\TransferLogic.cs</Link>
</Compile>
<Compile Include="..\NTwain\Internals\WindowsHook.cs">
<Link>Internals\WindowsHook.cs</Link>
</Compile>
@ -103,6 +106,9 @@
<Compile Include="..\NTwain\ITwainOperation.cs">
<Link>ITwainOperation.cs</Link>
</Compile>
<Compile Include="..\NTwain\ITwainSession.cs">
<Link>ITwainSession.cs</Link>
</Compile>
<Compile Include="..\NTwain\ITwainState.cs">
<Link>ITwainState.cs</Link>
</Compile>

View File

@ -1,6 +1,7 @@
using NTwain.Properties;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
namespace NTwain.Data
@ -55,7 +56,7 @@ namespace NTwain.Data
ContainerType = capability.ContainerType,
}.ReadRangeValue(baseAddr);
default:
throw new ArgumentException(string.Format(Resources.CapHasBadContainer, capability.ContainerType), "capability");
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.CapHasBadContainer, capability.ContainerType), "capability");
}
}
finally

View File

@ -234,7 +234,7 @@ namespace NTwain.Data
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
return Convert.ChangeType((float)this, conversionType);
return Convert.ChangeType((float)this, conversionType, CultureInfo.InvariantCulture);
}
ushort IConvertible.ToUInt16(IFormatProvider provider)
@ -632,7 +632,6 @@ namespace NTwain.Data
/// </summary>
/// <param name="capability">The capability.</param>
/// <param name="value">The value.</param>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public TWCapability(CapabilityId capability, TWOneValue value)
{
Capability = capability;
@ -644,7 +643,6 @@ namespace NTwain.Data
/// </summary>
/// <param name="capability">The capability.</param>
/// <param name="value">The value.</param>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public TWCapability(CapabilityId capability, TWEnumeration value)
{
Capability = capability;
@ -656,7 +654,6 @@ namespace NTwain.Data
/// </summary>
/// <param name="capability">The capability.</param>
/// <param name="value">The value.</param>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public TWCapability(CapabilityId capability, TWRange value)
{
Capability = capability;
@ -684,14 +681,13 @@ namespace NTwain.Data
#region value functions
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
void SetOneValue(TWOneValue value)
{
if (value == null) { throw new ArgumentNullException("value"); }
ContainerType = ContainerType.OneValue;
// since one value can only house UInt32 we will not allow type size > 4
if (TypeReader.GetItemTypeSize(value.ItemType) > 4) { throw new ArgumentException(string.Format(Resources.BadValueType, "TWOneValue")); }
if (TypeReader.GetItemTypeSize(value.ItemType) > 4) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.BadValueType, "TWOneValue")); }
_hContainer = Platform.MemoryManager.Allocate((uint)Marshal.SizeOf(value));
if (_hContainer != IntPtr.Zero)
@ -700,7 +696,6 @@ namespace NTwain.Data
}
}
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
void SetEnumValue(TWEnumeration value)
{
if (value == null) { throw new ArgumentNullException("value"); }
@ -726,14 +721,13 @@ namespace NTwain.Data
}
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
void SetRangeValue(TWRange value)
{
if (value == null) { throw new ArgumentNullException("value"); }
ContainerType = ContainerType.Range;
// since range value can only house UInt32 we will not allow type size > 4
if (TypeReader.GetItemTypeSize(value.ItemType) > 4) { throw new ArgumentException(string.Format(Resources.BadValueType, "TWRange")); }
if (TypeReader.GetItemTypeSize(value.ItemType) > 4) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.BadValueType, "TWRange")); }
_hContainer = Platform.MemoryManager.Allocate((uint)Marshal.SizeOf(value));
if (_hContainer != IntPtr.Zero)
@ -752,23 +746,22 @@ namespace NTwain.Data
/// <param name="offset"></param>
/// <param name="type"></param>
/// <param name="value"></param>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
void WriteValue(IntPtr baseAddr, ref int offset, ItemType type, object value)
{
switch (type)
{
case ItemType.Int8:
case ItemType.UInt8:
Marshal.WriteByte(baseAddr, offset, Convert.ToByte(value));// (byte)value);
Marshal.WriteByte(baseAddr, offset, Convert.ToByte(value, CultureInfo.InvariantCulture));// (byte)value);
break;
case ItemType.Bool:
case ItemType.Int16:
case ItemType.UInt16:
Marshal.WriteInt16(baseAddr, offset, Convert.ToInt16(value));//(short)value);
Marshal.WriteInt16(baseAddr, offset, Convert.ToInt16(value, CultureInfo.InvariantCulture));//(short)value);
break;
case ItemType.UInt32:
case ItemType.Int32:
Marshal.WriteInt32(baseAddr, offset, Convert.ToInt32(value));//(int)value);
Marshal.WriteInt32(baseAddr, offset, Convert.ToInt32(value, CultureInfo.InvariantCulture));//(int)value);
break;
case ItemType.Fix32:
TWFix32 f32 = (TWFix32)value;
@ -2188,15 +2181,24 @@ namespace NTwain.Data
/// </summary>
public partial class TWStatus
{
public TWStatus()
{
}
internal TWStatus(ushort code, ushort data)
{
_conditionCode = code;
_data = data;
}
/// <summary>
/// Condition Code describing the status.
/// </summary>
public ConditionCode ConditionCode { get { return (ConditionCode)_conditionCode; } internal set { _conditionCode = (ushort)value; } }
public ConditionCode ConditionCode { get { return (ConditionCode)_conditionCode; } }
/// <summary>
/// Valid for TWAIN 2.1 and later. This field contains additional
/// scanner-specific data. If there is no data, then this value must be zero.
/// </summary>
public ushort Data { get { return _data; } internal set { _data = Data; } }
public ushort Data { get { return _data; } }
}
/// <summary>
@ -2210,7 +2212,7 @@ namespace NTwain.Data
/// </summary>
public TWStatus Status
{
get { return new TWStatus { ConditionCode = (ConditionCode)_conditionCode, Data = _data }; }
get { return new TWStatus(_conditionCode, _data); }
}
/// <summary>

View File

@ -1467,7 +1467,7 @@ namespace NTwain.Data
/// <summary>
/// Corresponds to DG_*.
/// </summary>
[Flags]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags"), Flags]
public enum DataGroups : uint
{
None = 0,

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace NTwain.Data
@ -36,7 +37,7 @@ namespace NTwain.Data
var rawType = Enum.GetUnderlyingType(returnType);
if (typeof(ushort).IsAssignableFrom(rawType))
{
var intVal = Convert.ToUInt32(value);
var intVal = Convert.ToUInt32(value, CultureInfo.InvariantCulture);
var enumVal = GetLowerWord(intVal);
if (!Enum.IsDefined(returnType, enumVal))
{
@ -52,7 +53,7 @@ namespace NTwain.Data
else if (typeof(IConvertible).IsAssignableFrom(returnType))
{
// for regular integers and whatnot
return (T)Convert.ChangeType(value, returnType);
return (T)Convert.ChangeType(value, returnType, CultureInfo.InvariantCulture);
}
// return as-is from cap. if caller made a mistake then there should be exceptions
return (T)value;
@ -78,7 +79,7 @@ namespace NTwain.Data
{
return (TWFix32)value;
}
return (TWFix32)Convert.ToSingle(value);
return (TWFix32)Convert.ToSingle(value, CultureInfo.InvariantCulture);
}
}
}

22
NTwain/ITwainSession.cs Normal file
View File

@ -0,0 +1,22 @@
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NTwain
{
/// <summary>
/// General interface for a TWAIN session.
/// </summary>
public interface ITwainSession : ITwainState, ITwainOperation
{
/// <summary>
/// Gets the supported caps for the currently open source.
/// </summary>
/// <value>
/// The supported caps.
/// </value>
IList<CapabilityId> SupportedCaps { get; }
}
}

View File

@ -1,6 +1,8 @@
using NTwain.Data;
using NTwain.Properties;
using System;
using System.Globalization;
using System.IO;
namespace NTwain.Internals
{
@ -30,14 +32,70 @@ namespace NTwain.Internals
/// <param name="dataArgumentType">The triplet data argument type.</param>
/// <param name="message">The triplet message.</param>
/// <exception cref="TwainStateException"></exception>
public static void VerifyState(this ITwainStateInternal session, int allowedMinimum, int allowedMaximum, DataGroups group, DataArgumentType dataArgumentType, Message message)
public static void VerifyState(this ITwainSessionInternal session, int allowedMinimum, int allowedMaximum, DataGroups group, DataArgumentType dataArgumentType, Message message)
{
if (session.EnforceState && (session.State < allowedMinimum || session.State > allowedMaximum))
{
throw new TwainStateException(session.State, allowedMinimum, allowedMaximum, group, dataArgumentType, message,
string.Format("TWAIN state {0} does not match required range {1}-{2} for operation {3}-{4}-{5}.",
string.Format(CultureInfo.InvariantCulture, "TWAIN state {0} does not match required range {1}-{2} for operation {3}-{4}-{5}.",
session.State, allowedMinimum, allowedMaximum, group, dataArgumentType, message));
}
}
public static string ChangeExtensionByFormat(this TWSetupFileXfer fileInfo, string currentFilePath)
{
string finalFile = null;
switch (fileInfo.Format)
{
case FileFormat.Bmp:
finalFile = Path.ChangeExtension(currentFilePath, ".bmp");
break;
case FileFormat.Dejavu:
finalFile = Path.ChangeExtension(currentFilePath, ".dejavu");
break;
case FileFormat.Exif:
finalFile = Path.ChangeExtension(currentFilePath, ".exit");
break;
case FileFormat.Fpx:
finalFile = Path.ChangeExtension(currentFilePath, ".fpx");
break;
case FileFormat.Jfif:
finalFile = Path.ChangeExtension(currentFilePath, ".jpg");
break;
case FileFormat.Jp2:
finalFile = Path.ChangeExtension(currentFilePath, ".jp2");
break;
case FileFormat.Jpx:
finalFile = Path.ChangeExtension(currentFilePath, ".jpx");
break;
case FileFormat.Pdf:
case FileFormat.PdfA:
case FileFormat.PdfA2:
finalFile = Path.ChangeExtension(currentFilePath, ".pdf");
break;
case FileFormat.Pict:
finalFile = Path.ChangeExtension(currentFilePath, ".pict");
break;
case FileFormat.Png:
finalFile = Path.ChangeExtension(currentFilePath, ".png");
break;
case FileFormat.Spiff:
finalFile = Path.ChangeExtension(currentFilePath, ".spiff");
break;
case FileFormat.Tiff:
case FileFormat.TiffMulti:
finalFile = Path.ChangeExtension(currentFilePath, ".tif");
break;
case FileFormat.Xbm:
finalFile = Path.ChangeExtension(currentFilePath, ".xbm");
break;
default:
finalFile = Path.ChangeExtension(currentFilePath, ".unknown");
break;
}
return finalFile;
}
}
}

View File

@ -1,11 +1,12 @@
using NTwain.Data;
using System.Collections.Generic;
namespace NTwain.Internals
{
/// <summary>
/// Internal interface for state management.
/// </summary>
interface ITwainStateInternal : ITwainState
interface ITwainSessionInternal : ITwainSession
{
/// <summary>
/// Gets the app id used for the session.
@ -37,5 +38,11 @@ namespace NTwain.Internals
ICommittable GetPendingStateChanger(int newState);
void ChangeSourceId(TWIdentity sourceId);
ReturnCode DisableSource();
void SafeSyncableRaiseEvent(DataTransferredEventArgs e);
void SafeSyncableRaiseEvent(TransferErrorEventArgs e);
void SafeSyncableRaiseEvent(TransferReadyEventArgs e);
}
}

View File

@ -10,17 +10,17 @@ namespace NTwain.Internals
#region mem stuff for twain 1.x
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalAlloc")]
internal static extern IntPtr WinGlobalAlloc(uint uFlags, UIntPtr dwBytes);
public static extern IntPtr WinGlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalFree")]
internal static extern IntPtr WinGlobalFree(IntPtr hMem);
public static extern IntPtr WinGlobalFree(IntPtr hMem);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalLock")]
internal static extern IntPtr WinGlobalLock(IntPtr handle);
public static extern IntPtr WinGlobalLock(IntPtr handle);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalUnlock")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinGlobalUnlock(IntPtr handle);
public static extern bool WinGlobalUnlock(IntPtr handle);
#endregion
}

View File

@ -3,10 +3,10 @@
class TentativeStateCommitable : ICommittable
{
bool _commit;
ITwainStateInternal _session;
ITwainSessionInternal _session;
int _origState;
int _newState;
public TentativeStateCommitable(ITwainStateInternal session, int newState)
public TentativeStateCommitable(ITwainSessionInternal session, int newState)
{
_session = session;
_origState = session.State;

View File

@ -0,0 +1,436 @@
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace NTwain.Internals
{
/// <summary>
/// Contains the actual data transfer logic since TwainSession is getting too large.
/// </summary>
static class TransferLogic
{
/// <summary>
/// Performs the TWAIN transfer routine at state 6.
/// </summary>
public static void DoTransferRoutine(ITwainSessionInternal session)
{
var pending = new TWPendingXfers();
var rc = ReturnCode.Success;
do
{
#region build and raise xfer ready
TWAudioInfo audInfo;
if (session.DGAudio.AudioInfo.Get(out audInfo) != ReturnCode.Success)
{
audInfo = null;
}
TWImageInfo imgInfo;
if (session.DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
// ask consumer for xfer details
var preXferArgs = new TransferReadyEventArgs
{
AudioInfo = audInfo,
PendingImageInfo = imgInfo,
PendingTransferCount = pending.Count,
EndOfJob = pending.EndOfJob == 0
};
session.SafeSyncableRaiseEvent(preXferArgs);
#endregion
#region actually handle xfer
if (preXferArgs.CancelAll)
{
rc = session.DGControl.PendingXfers.Reset(pending);
}
else if (!preXferArgs.CancelCurrent)
{
DataGroups xferGroup = DataGroups.None;
if (session.DGControl.XferGroup.Get(ref xferGroup) != ReturnCode.Success)
{
xferGroup = DataGroups.None;
}
if ((xferGroup & DataGroups.Image) == DataGroups.Image)
{
var mech = session.GetCurrentCap(CapabilityId.ICapXferMech).ConvertToEnum<XferMech>();
switch (mech)
{
case XferMech.Native:
DoImageNativeXfer(session);
break;
case XferMech.Memory:
DoImageMemoryXfer(session);
break;
case XferMech.File:
DoImageFileXfer(session);
break;
case XferMech.MemFile:
DoImageMemoryFileXfer(session);
break;
}
}
if ((xferGroup & DataGroups.Audio) == DataGroups.Audio)
{
var mech = session.GetCurrentCap(CapabilityId.ACapXferMech).ConvertToEnum<XferMech>();
switch (mech)
{
case XferMech.Native:
DoAudioNativeXfer(session);
break;
case XferMech.File:
DoAudioFileXfer(session);
break;
}
}
}
rc = session.DGControl.PendingXfers.EndXfer(pending);
#endregion
} while (rc == ReturnCode.Success && pending.Count != 0);
session.ChangeState(5, true);
session.DisableSource();
}
#region audio xfers
static void DoAudioNativeXfer(ITwainSessionInternal session)
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
var xrc = session.DGAudio.AudioNativeXfer.Get(ref dataPtr);
if (xrc == ReturnCode.XferDone)
{
session.ChangeState(7, true);
if (dataPtr != IntPtr.Zero)
{
lockedPtr = Platform.MemoryManager.Lock(dataPtr);
}
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs { NativeData = lockedPtr });
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
}
finally
{
session.ChangeState(6, true);
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
Platform.MemoryManager.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
}
static void DoAudioFileXfer(ITwainSessionInternal session)
{
string filePath = null;
TWSetupFileXfer setupInfo;
if (session.DGControl.SetupFileXfer.Get(out setupInfo) == ReturnCode.Success)
{
filePath = setupInfo.FileName;
}
var xrc = session.DGAudio.AudioFileXfer.Get();
if (xrc == ReturnCode.XferDone)
{
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs { FileDataPath = filePath });
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
#endregion
#region image xfers
static void DoImageNativeXfer(ITwainSessionInternal session)
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
var xrc = session.DGImage.ImageNativeXfer.Get(ref dataPtr);
if (xrc == ReturnCode.XferDone)
{
session.ChangeState(7, true);
if (dataPtr != IntPtr.Zero)
{
lockedPtr = Platform.MemoryManager.Lock(dataPtr);
}
DoImageXferredEventRoutine(session, lockedPtr, null, null);
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
}
finally
{
session.ChangeState(6, true);
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
Platform.MemoryManager.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
}
static void DoImageFileXfer(ITwainSessionInternal session)
{
string filePath = null;
TWSetupFileXfer setupInfo;
if (session.DGControl.SetupFileXfer.Get(out setupInfo) == ReturnCode.Success)
{
filePath = setupInfo.FileName;
}
var xrc = session.DGImage.ImageFileXfer.Get();
if (xrc == ReturnCode.XferDone)
{
DoImageXferredEventRoutine(session, IntPtr.Zero, null, filePath);
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
static void DoImageMemoryXfer(ITwainSessionInternal session)
{
TWSetupMemXfer memInfo;
if (session.DGControl.SetupMemXfer.Get(out memInfo) == ReturnCode.Success)
{
TWImageMemXfer xferInfo = new TWImageMemXfer();
try
{
// how to tell if going to xfer in strip vs tile?
// if tile don't allocate memory in app?
xferInfo.Memory = new TWMemory
{
Flags = MemoryFlags.AppOwns | MemoryFlags.Pointer,
Length = memInfo.Preferred,
TheMem = Platform.MemoryManager.Allocate(memInfo.Preferred)
};
// do the unthinkable and keep all xferred batches in memory,
// possibly defeating the purpose of mem xfer
// unless compression is used.
// todo: use array instead of memory stream?
using (MemoryStream xferredData = new MemoryStream())
{
var xrc = ReturnCode.Success;
do
{
xrc = session.DGImage.ImageMemFileXfer.Get(xferInfo);
if (xrc == ReturnCode.Success ||
xrc == ReturnCode.XferDone)
{
session.ChangeState(7, true);
// optimize and allocate buffer only once instead of inside the loop?
byte[] buffer = new byte[(int)xferInfo.BytesWritten];
IntPtr lockPtr = IntPtr.Zero;
try
{
lockPtr = Platform.MemoryManager.Lock(xferInfo.Memory.TheMem);
Marshal.Copy(lockPtr, buffer, 0, buffer.Length);
xferredData.Write(buffer, 0, buffer.Length);
}
finally
{
if (lockPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockPtr);
}
}
}
} while (xrc == ReturnCode.Success);
if (xrc == ReturnCode.XferDone)
{
DoImageXferredEventRoutine(session, IntPtr.Zero, xferredData.ToArray(), null);
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
}
finally
{
session.ChangeState(6, true);
if (xferInfo.Memory.TheMem != IntPtr.Zero)
{
Platform.MemoryManager.Free(xferInfo.Memory.TheMem);
}
}
}
}
static void DoImageMemoryFileXfer(ITwainSessionInternal session)
{
// since it's memory-file xfer need info from both (maybe)
TWSetupMemXfer memInfo;
TWSetupFileXfer fileInfo;
if (session.DGControl.SetupMemXfer.Get(out memInfo) == ReturnCode.Success &&
session.DGControl.SetupFileXfer.Get(out fileInfo) == ReturnCode.Success)
{
TWImageMemXfer xferInfo = new TWImageMemXfer();
var tempFile = Path.GetTempFileName();
string finalFile = null;
try
{
// no strip or tile here, just chunks
xferInfo.Memory = new TWMemory
{
Flags = MemoryFlags.AppOwns | MemoryFlags.Pointer,
Length = memInfo.Preferred,
TheMem = Platform.MemoryManager.Allocate(memInfo.Preferred)
};
var xrc = ReturnCode.Success;
using (var outStream = File.OpenWrite(tempFile))
{
do
{
xrc = session.DGImage.ImageMemFileXfer.Get(xferInfo);
if (xrc == ReturnCode.Success ||
xrc == ReturnCode.XferDone)
{
session.ChangeState(7, true);
byte[] buffer = new byte[(int)xferInfo.BytesWritten];
IntPtr lockPtr = IntPtr.Zero;
try
{
lockPtr = Platform.MemoryManager.Lock(xferInfo.Memory.TheMem);
Marshal.Copy(lockPtr, buffer, 0, buffer.Length);
}
finally
{
if (lockPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockPtr);
}
}
outStream.Write(buffer, 0, buffer.Length);
}
} while (xrc == ReturnCode.Success);
}
if (xrc == ReturnCode.XferDone)
{
finalFile = fileInfo.ChangeExtensionByFormat(tempFile);
File.Move(tempFile, finalFile);
}
else
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = session.GetSourceStatus() });
}
}
catch (Exception ex)
{
session.SafeSyncableRaiseEvent(new TransferErrorEventArgs { Exception = ex });
}
finally
{
session.ChangeState(6, true);
if (xferInfo.Memory.TheMem != IntPtr.Zero)
{
Platform.MemoryManager.Free(xferInfo.Memory.TheMem);
}
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
if (File.Exists(finalFile))
{
DoImageXferredEventRoutine(session, IntPtr.Zero, null, finalFile);
}
}
}
static void DoImageXferredEventRoutine(ITwainSessionInternal session, IntPtr dataPtr, byte[] dataArray, string filePath)
{
TWImageInfo imgInfo;
TWExtImageInfo extInfo = null;
if (session.SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
{
if (session.DGImage.ExtImageInfo.Get(out extInfo) != ReturnCode.Success)
{
extInfo = null;
}
}
if (session.DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
session.SafeSyncableRaiseEvent(new DataTransferredEventArgs
{
NativeData = dataPtr,
MemData = dataArray,
FileDataPath = filePath,
ImageInfo = imgInfo,
ExImageInfo = extInfo
});
if (extInfo != null) { extInfo.Dispose(); }
}
#endregion
}
}

View File

@ -1,6 +1,7 @@
using NTwain.Data;
using NTwain.Internals;
using System;
using System.ComponentModel;
namespace NTwain
{
@ -16,7 +17,7 @@ namespace NTwain
if (retVal == IntPtr.Zero)
{
throw new OutOfMemoryException();
throw new Win32Exception();
}
return retVal;
}

View File

@ -27,7 +27,7 @@
<DocumentationFile>
</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -60,10 +60,12 @@
<Compile Include="DataTransferredEventArgs.cs" />
<Compile Include="IMemoryManager.cs" />
<Compile Include="Internals\ICommittable.cs" />
<Compile Include="Internals\ITwainStateInternal.cs" />
<Compile Include="Internals\ITwainSessionInternal.cs" />
<Compile Include="Internals\TransferLogic.cs" />
<Compile Include="Internals\WindowsHook.cs" />
<Compile Include="Internals\WrappedManualResetEvent.cs" />
<Compile Include="ITwainOperation.cs" />
<Compile Include="ITwainSession.cs" />
<Compile Include="ITwainState.cs" />
<Compile Include="Internals\WinMemoryManager.cs" />
<Compile Include="Internals\MessageLoop.cs" />

View File

@ -9,7 +9,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class AudioFileXfer : OpBase
{
internal AudioFileXfer(ITwainStateInternal session) : base(session) { }
internal AudioFileXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation is used to initiate the transfer of audio from the Source to the application via the
/// disk-file transfer mechanism. It causes the transfer to begin.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class AudioInfo : OpBase
{
internal AudioInfo(ITwainStateInternal session) : base(session) { }
internal AudioInfo(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Used to get the information of the current audio data ready to transfer.
/// </summary>

View File

@ -9,7 +9,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class AudioNativeXfer : OpBase
{
internal AudioNativeXfer(ITwainStateInternal session) : base(session) { }
internal AudioNativeXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Causes the transfer of an audio data from the Source to the application, via the Native
/// transfer mechanism, to begin. The resulting data is stored in main memory in a single block.

View File

@ -8,8 +8,8 @@ namespace NTwain.Triplets
/// </summary>
public sealed class DGAudio
{
ITwainStateInternal _session;
internal DGAudio(ITwainStateInternal session)
ITwainSessionInternal _session;
internal DGAudio(ITwainSessionInternal session)
{
if (session == null) { throw new ArgumentNullException("session"); }
_session = session;

View File

@ -5,7 +5,7 @@ namespace NTwain.Triplets
{
sealed class Callback : OpBase
{
internal Callback(ITwainStateInternal session) : base(session) { }
internal Callback(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This triplet is sent to the DSM by the Application to register the applications entry point with
/// the DSM, so that the DSM can use callbacks to inform the application of events generated by the

View File

@ -5,7 +5,7 @@ namespace NTwain.Triplets
{
sealed class Callback2 : OpBase
{
internal Callback2(ITwainStateInternal session) : base(session) { }
internal Callback2(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This triplet is sent to the DSM by the Application to register the applications entry point with
/// the DSM, so that the DSM can use callbacks to inform the application of events generated by the

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class Capability : OpBase
{
internal Capability(ITwainStateInternal session) : base(session) { }
internal Capability(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Returns the Sources Current, Default and Available Values for a specified capability.
/// </summary>

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class CustomDSData : OpBase
{
internal CustomDSData(ITwainStateInternal session) : base(session) { }
internal CustomDSData(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation is used by the application to query the data source for its current settings, e.g.
/// DPI, paper size, color format. The actual format of the data is data source dependent and not

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class DeviceEvent : OpBase
{
internal DeviceEvent(ITwainStateInternal session) : base(session) { }
internal DeviceEvent(ITwainSessionInternal session) : base(session) { }
public ReturnCode Get(out TWDeviceEvent sourceDeviceEvent)
{
Session.VerifyState(4, 7, DataGroups.Control, DataArgumentType.DeviceEvent, Message.Get);

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class EntryPoint : OpBase
{
internal EntryPoint(ITwainStateInternal session) : base(session) { }
internal EntryPoint(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Gets the function entry points for twain 2.0 or higher.
/// </summary>

View File

@ -5,7 +5,7 @@ namespace NTwain.Triplets
{
sealed class Event : OpBase
{
internal Event(ITwainStateInternal session) : base(session) { }
internal Event(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation supports the distribution of events from the application to Sources so that the

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class FileSystem : OpBase
{
internal FileSystem(ITwainStateInternal session) : base(session) { }
internal FileSystem(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation selects the destination directory within the Source (camera, storage, etc), where
/// images captured using CapAutomaticCapture will be stored. This command only selects

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class Identity : OpBase
{
internal Identity(ITwainStateInternal session) : base(session) { }
internal Identity(ITwainSessionInternal session) : base(session) { }
/// <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

View File

@ -9,7 +9,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class Parent : OpBase
{
internal Parent(ITwainStateInternal session) : base(session) { }
internal Parent(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// When the application has closed all the Sources it had previously opened, and is finished with

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class PassThru : OpBase
{
internal PassThru(ITwainStateInternal session) : base(session) { }
internal PassThru(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// PASSTHRU is intended for the use of Source writers writing diagnostic applications. It allows
/// raw communication with the currently selected device in the Source.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class PendingXfers : OpBase
{
internal PendingXfers(ITwainStateInternal session) : base(session) { }
internal PendingXfers(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This triplet is used to cancel or terminate a transfer. Issued in state 6, this triplet cancels the next
/// pending transfer, discards the transfer data, and decrements the pending transfers count. In

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class SetupFileXfer : OpBase
{
internal SetupFileXfer(ITwainStateInternal session) : base(session) { }
internal SetupFileXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Returns information about the file into which the Source has or will put the acquired image
/// or audio data.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class SetupMemXfer : OpBase
{
internal SetupMemXfer(ITwainStateInternal session) : base(session) { }
internal SetupMemXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Returns the Sources preferred, minimum, and maximum allocation sizes for transfer memory
/// buffers.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class Status : OpBase
{
internal Status(ITwainStateInternal session) : base(session) { }
internal Status(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Returns the current Condition Code for the Source Manager.
/// </summary>

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class StatusUtf8 : OpBase
{
internal StatusUtf8(ITwainStateInternal session) : base(session) { }
internal StatusUtf8(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Translate the contents of a TW_STATUS structure received from a Source into a localized UTF-8
/// encoded string.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class UserInterface : OpBase
{
internal UserInterface(ITwainStateInternal session) : base(session) { }
internal UserInterface(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation causes the Sources user interface, if displayed during the
/// EnableDS operation, to be lowered. The Source is returned to

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class XferGroup : OpBase
{
internal XferGroup(ITwainStateInternal session) : base(session) { }
internal XferGroup(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Returns the Data Group (the type of data) for the upcoming transfer. The Source is required to

View File

@ -8,8 +8,8 @@ namespace NTwain.Triplets
/// </summary>
public sealed class DGControl
{
ITwainStateInternal _session;
internal DGControl(ITwainStateInternal session)
ITwainSessionInternal _session;
internal DGControl(ITwainSessionInternal session)
{
if (session == null) { throw new ArgumentNullException("session"); }
_session = session;

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class CieColor : OpBase
{
internal CieColor(ITwainStateInternal session) : base(session) { }
internal CieColor(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation causes the Source to report the currently active parameters to be used in

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class ExtImageInfo : OpBase
{
internal ExtImageInfo(ITwainStateInternal session) : base(session) { }
internal ExtImageInfo(ITwainSessionInternal session) : base(session) { }
public ReturnCode Get(out TWExtImageInfo info)
{

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class Filter : OpBase
{
internal Filter(ITwainStateInternal session) : base(session) { }
internal Filter(ITwainSessionInternal session) : base(session) { }
/// <summary>

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class GrayResponse : OpBase
{
internal GrayResponse(ITwainStateInternal session) : base(session) { }
internal GrayResponse(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// The Reset operation causes the Source to use its "identity response curve." The identity

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class IccProfile : OpBase
{
internal IccProfile(ITwainStateInternal session) : base(session) { }
internal IccProfile(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation provides the application with the ICC profile associated with the image which is

View File

@ -9,7 +9,7 @@ namespace NTwain.Triplets
/// </summary>
sealed class ImageFileXfer : OpBase
{
internal ImageFileXfer(ITwainStateInternal session) : base(session) { }
internal ImageFileXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation is used to initiate the transfer of an image from the Source to the application via

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class ImageInfo : OpBase
{
internal ImageInfo(ITwainStateInternal session) : base(session) { }
internal ImageInfo(ITwainSessionInternal session) : base(session) { }
public ReturnCode Get(out TWImageInfo info)
{

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class ImageLayout : OpBase
{
internal ImageLayout(ITwainStateInternal session) : base(session) { }
internal ImageLayout(ITwainSessionInternal session) : base(session) { }
public ReturnCode Get(out TWImageLayout layout)
{

View File

@ -5,7 +5,7 @@ namespace NTwain.Triplets
{
sealed class ImageMemFileXfer : OpBase
{
internal ImageMemFileXfer(ITwainStateInternal session) : base(session) { }
internal ImageMemFileXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation is used to initiate the transfer of an image from the Source to the application via

View File

@ -5,7 +5,7 @@ namespace NTwain.Triplets
{
sealed class ImageMemXfer : OpBase
{
internal ImageMemXfer(ITwainStateInternal session) : base(session) { }
internal ImageMemXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation is used to initiate the transfer of an image from the Source to the application via

View File

@ -6,7 +6,7 @@ namespace NTwain.Triplets
{
sealed class ImageNativeXfer : OpBase
{
internal ImageNativeXfer(ITwainStateInternal session) : base(session) { }
internal ImageNativeXfer(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Causes the transfer of an images data from the Source to the application, via the Native transfer

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class JpegCompression : OpBase
{
internal JpegCompression(ITwainStateInternal session) : base(session) { }
internal JpegCompression(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Causes the Source to return the parameters that will be used during the compression of data

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class Palette8 : OpBase
{
internal Palette8(ITwainStateInternal session) : base(session) { }
internal Palette8(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// This operation causes the Source to report its current palette information.

View File

@ -8,7 +8,7 @@ namespace NTwain.Triplets
/// </summary>
public sealed class RgbResponse : OpBase
{
internal RgbResponse(ITwainStateInternal session) : base(session) { }
internal RgbResponse(ITwainSessionInternal session) : base(session) { }
/// <summary>
/// Causes the Source to use its "identity" response curves for future RGB transfers. The identity

View File

@ -8,8 +8,8 @@ namespace NTwain.Triplets
/// </summary>
public sealed class DGImage
{
ITwainStateInternal _session;
internal DGImage(ITwainStateInternal session)
ITwainSessionInternal _session;
internal DGImage(ITwainSessionInternal session)
{
if (session == null) { throw new ArgumentNullException("session"); }
_session = session;

View File

@ -13,7 +13,7 @@ namespace NTwain.Triplets
/// </summary>
/// <param name="session">The session.</param>
/// <exception cref="System.ArgumentNullException"></exception>
internal OpBase(ITwainStateInternal session)
internal OpBase(ITwainSessionInternal session)
{
if (session == null) { throw new ArgumentNullException("session"); }
Session = session;
@ -25,6 +25,6 @@ namespace NTwain.Triplets
/// <value>
/// The session.
/// </value>
internal ITwainStateInternal Session { get; private set; }
internal ITwainSessionInternal Session { get; private set; }
}
}

View File

@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
@ -15,7 +16,7 @@ namespace NTwain
/// <summary>
/// Basic class for interfacing with TWAIN. You should only have one of this per application process.
/// </summary>
public class TwainSession : ITwainStateInternal, ITwainOperation
public class TwainSession : ITwainSessionInternal
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession" /> class.
@ -27,21 +28,22 @@ namespace NTwain
if (appId == null) { throw new ArgumentNullException("appId"); }
_appId = appId;
((ITwainStateInternal)this).ChangeState(1, false);
((ITwainSessionInternal)this).ChangeState(1, false);
EnforceState = true;
MessageLoop.Instance.EnsureStarted(HandleWndProcMessage);
}
TWIdentity _appId;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
object _callbackObj; // kept around so it doesn't get gc'ed
TWIdentity _appId;
TWUserInterface _twui;
static readonly CapabilityId[] _emptyCapList = new CapabilityId[0];
private IList<CapabilityId> _supportedCaps;
/// <summary>
/// Gets the supported caps for the current source.
/// Gets the supported caps for the currently open source.
/// </summary>
/// <value>
/// The supported caps.
@ -64,7 +66,7 @@ namespace NTwain
}
/// <summary>
/// EXPERIMENTAL. Gets or sets the optional synchronization context.
/// Gets or sets the optional synchronization context.
/// This allows events to be raised on the thread
/// associated with the context.
/// </summary>
@ -74,13 +76,13 @@ namespace NTwain
public SynchronizationContext SynchronizationContext { get; set; }
#region ITwainStateInternal Members
#region ITwainSessionInternal Members
/// <summary>
/// Gets the app id used for the session.
/// </summary>
/// <value>The app id.</value>
TWIdentity ITwainStateInternal.AppId { get { return _appId; } }
TWIdentity ITwainSessionInternal.AppId { get { return _appId; } }
/// <summary>
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
@ -90,7 +92,7 @@ namespace NTwain
/// </value>
public bool EnforceState { get; set; }
void ITwainStateInternal.ChangeState(int newState, bool notifyChange)
void ITwainSessionInternal.ChangeState(int newState, bool notifyChange)
{
_state = newState;
if (notifyChange)
@ -100,18 +102,31 @@ namespace NTwain
}
}
ICommittable ITwainStateInternal.GetPendingStateChanger(int newState)
ICommittable ITwainSessionInternal.GetPendingStateChanger(int newState)
{
return new TentativeStateCommitable(this, newState);
}
void ITwainStateInternal.ChangeSourceId(TWIdentity sourceId)
void ITwainSessionInternal.ChangeSourceId(TWIdentity sourceId)
{
SourceId = sourceId;
RaisePropertyChanged("SourceId");
SafeAsyncSyncableRaiseOnEvent(OnSourceChanged, SourceChanged);
}
void ITwainSessionInternal.SafeSyncableRaiseEvent(DataTransferredEventArgs e)
{
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, e);
}
void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferErrorEventArgs e)
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, e);
}
void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferReadyEventArgs e)
{
SafeSyncableRaiseOnEvent(OnTransferReady, TransferReady, e);
}
#endregion
#region ITwainState Members
@ -134,7 +149,7 @@ namespace NTwain
public int State
{
get { return _state; }
protected set
internal protected set
{
if (value > 0 && value < 8)
{
@ -240,7 +255,7 @@ namespace NTwain
var rc = ReturnCode.Failure;
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenManager.", Thread.CurrentThread.ManagedThreadId));
rc = DGControl.Parent.OpenDsm(MessageLoop.Instance.LoopHandle);
if (rc == ReturnCode.Success)
@ -274,7 +289,7 @@ namespace NTwain
var rc = ReturnCode.Failure;
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseManager.", Thread.CurrentThread.ManagedThreadId));
rc = DGControl.Parent.CloseDsm(MessageLoop.Instance.LoopHandle);
});
@ -296,7 +311,7 @@ namespace NTwain
var rc = ReturnCode.Failure;
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
var source = new TWIdentity();
source.ProductName = sourceProductName;
@ -318,7 +333,7 @@ namespace NTwain
var rc = ReturnCode.Failure;
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
rc = DGControl.Identity.CloseDS();
if (rc == ReturnCode.Success)
@ -343,7 +358,7 @@ namespace NTwain
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: EnableSource.", Thread.CurrentThread.ManagedThreadId));
// app v2.2 or higher uses callback2
if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
@ -387,17 +402,13 @@ namespace NTwain
return rc;
}
/// <summary>
/// Disables the source to end data acquisition.
/// </summary>
/// <returns></returns>
protected ReturnCode DisableSource()
ReturnCode ITwainSessionInternal.DisableSource()
{
var rc = ReturnCode.Failure;
MessageLoop.Instance.Invoke(() =>
{
Debug.WriteLine(string.Format("Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
rc = DGControl.UserInterface.DisableDS(_twui);
if (rc == ReturnCode.Success)
@ -416,7 +427,7 @@ namespace NTwain
/// <param name="targetState">State of the target.</param>
public void ForceStepDown(int targetState)
{
Debug.WriteLine(string.Format("Thread {0}: ForceStepDown.", Thread.CurrentThread.ManagedThreadId));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: ForceStepDown.", Thread.CurrentThread.ManagedThreadId));
bool origFlag = EnforceState;
EnforceState = false;
@ -448,7 +459,7 @@ namespace NTwain
}
if (targetState < 5)
{
DisableSource();
((ITwainSessionInternal)this).DisableSource();
}
if (targetState < 4)
{
@ -496,6 +507,38 @@ namespace NTwain
/// </summary>
public event EventHandler<TransferErrorEventArgs> TransferError;
/// <summary>
/// Raises event and if applicable marshal it asynchronously to the <see cref="SynchronizationContext"/> thread.
/// </summary>
/// <param name="onEventFunc">The on event function.</param>
/// <param name="handler">The handler.</param>
void SafeAsyncSyncableRaiseOnEvent(Action onEventFunc, EventHandler handler)
{
var syncer = SynchronizationContext;
if (syncer == null)
{
try
{
onEventFunc();
if (handler != null) { handler(this, EventArgs.Empty); }
}
catch { }
}
else
{
syncer.Post(o =>
{
try
{
onEventFunc();
if (handler != null) { handler(this, EventArgs.Empty); }
}
catch { }
}, null);
}
}
/// <summary>
/// Raises event and if applicable marshal it synchronously to the <see cref="SynchronizationContext" /> thread.
/// </summary>
@ -529,37 +572,6 @@ namespace NTwain
}
}
/// <summary>
/// Raises event and if applicable marshal it asynchronously to the <see cref="SynchronizationContext"/> thread.
/// </summary>
/// <param name="onEventFunc">The on event function.</param>
/// <param name="handler">The handler.</param>
void SafeAsyncSyncableRaiseOnEvent(Action onEventFunc, EventHandler handler)
{
var syncer = SynchronizationContext;
if (syncer == null)
{
try
{
onEventFunc();
if (handler != null) { handler(this, EventArgs.Empty); }
}
catch { }
}
else
{
syncer.Post(o =>
{
try
{
onEventFunc();
if (handler != null) { handler(this, EventArgs.Empty); }
}
catch { }
}, null);
}
}
/// <summary>
/// Called when <see cref="State"/> changed.
/// </summary>
@ -585,25 +597,24 @@ namespace NTwain
/// Called when a data transfer is ready.
/// </summary>
/// <param name="e">The <see cref="TransferReadyEventArgs"/> instance containing the event data.</param>
protected virtual void OnTransferReady(TransferReadyEventArgs e) { }
internal protected virtual void OnTransferReady(TransferReadyEventArgs e) { }
/// <summary>
/// Called when data has been transferred.
/// </summary>
/// <param name="e">The <see cref="DataTransferredEventArgs"/> instance containing the event data.</param>
protected virtual void OnDataTransferred(DataTransferredEventArgs e) { }
internal protected virtual void OnDataTransferred(DataTransferredEventArgs e) { }
/// <summary>
/// Called when an error has been encountered during transfer.
/// </summary>
/// <param name="e">The <see cref="TransferErrorEventArgs"/> instance containing the event data.</param>
protected virtual void OnTransferError(TransferErrorEventArgs e) { }
internal protected virtual void OnTransferError(TransferErrorEventArgs e) { }
#endregion
#region TWAIN logic during xfer work
#region handle twain ds message
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
void HandleWndProcMessage(ref WindowsHook.MESSAGE winMsg, ref bool handled)
{
// this handles the message from a typical WndProc message loop and check if it's from the TWAIN source.
@ -621,7 +632,7 @@ namespace NTwain
evt.pEvent = msgPtr;
if (handled = (DGControl.Event.ProcessEvent(evt) == ReturnCode.DSEvent))
{
Debug.WriteLine(string.Format("Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: HandleWndProcMessage at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, evt.TWMessage));
HandleSourceMsg(evt.TWMessage);
}
@ -637,7 +648,7 @@ namespace NTwain
{
if (origin != null && SourceId != null && origin.Id == SourceId.Id)
{
Debug.WriteLine(string.Format("Thread {0}: CallbackHandler at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, msg));
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CallbackHandler at state {1} with MSG={2}.", Thread.CurrentThread.ManagedThreadId, State, msg));
// spec says we must handle this on the thread that enabled the DS.
// by using the internal dispatcher this will be the case.
@ -660,7 +671,7 @@ namespace NTwain
{
State = 6;
}
DoTransferRoutine();
TransferLogic.DoTransferRoutine(this);
break;
case Message.DeviceEvent:
TWDeviceEvent de;
@ -683,530 +694,12 @@ namespace NTwain
else if (State == 5)
{
// needs this state check since some source sends this more than once
DisableSource();
((ITwainSessionInternal)this).DisableSource();
}
break;
}
}
/// <summary>
/// Performs the TWAIN transfer routine at state 6.
/// </summary>
protected virtual void DoTransferRoutine()
{
var pending = new TWPendingXfers();
var rc = ReturnCode.Success;
do
{
#region build and raise xfer ready
TWAudioInfo audInfo;
if (DGAudio.AudioInfo.Get(out audInfo) != ReturnCode.Success)
{
audInfo = null;
}
TWImageInfo imgInfo;
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
// ask consumer for xfer details
var preXferArgs = new TransferReadyEventArgs
{
AudioInfo = audInfo,
PendingImageInfo = imgInfo,
PendingTransferCount = pending.Count,
EndOfJob = pending.EndOfJob == 0
};
SafeSyncableRaiseOnEvent(OnTransferReady, TransferReady, preXferArgs);
#endregion
#region actually handle xfer
if (preXferArgs.CancelAll)
{
rc = DGControl.PendingXfers.Reset(pending);
}
else if (!preXferArgs.CancelCurrent)
{
DataGroups xferGroup = DataGroups.None;
if (DGControl.XferGroup.Get(ref xferGroup) != ReturnCode.Success)
{
xferGroup = DataGroups.None;
}
if ((xferGroup & DataGroups.Image) == DataGroups.Image)
{
var mech = this.GetCurrentCap(CapabilityId.ICapXferMech).ConvertToEnum<XferMech>();
switch (mech)
{
case XferMech.Native:
DoImageNativeXfer();
break;
case XferMech.Memory:
DoImageMemoryXfer();
break;
case XferMech.File:
DoImageFileXfer();
break;
case XferMech.MemFile:
DoImageMemoryFileXfer();
break;
}
}
if ((xferGroup & DataGroups.Audio) == DataGroups.Audio)
{
var mech = this.GetCurrentCap(CapabilityId.ACapXferMech).ConvertToEnum<XferMech>();
switch (mech)
{
case XferMech.Native:
DoAudioNativeXfer();
break;
case XferMech.File:
DoAudioFileXfer();
break;
}
}
}
rc = DGControl.PendingXfers.EndXfer(pending);
#endregion
} while (rc == ReturnCode.Success && pending.Count != 0);
State = 5;
DisableSource();
}
#region audio xfers
private void DoAudioNativeXfer()
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
var xrc = DGAudio.AudioNativeXfer.Get(ref dataPtr);
if (xrc == ReturnCode.XferDone)
{
State = 7;
if (dataPtr != IntPtr.Zero)
{
lockedPtr = Platform.MemoryManager.Lock(dataPtr);
}
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs { NativeData = lockedPtr });
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
catch (Exception ex)
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { Exception = ex });
}
finally
{
State = 6;
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
Platform.MemoryManager.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
}
private void DoAudioFileXfer()
{
string filePath = null;
TWSetupFileXfer setupInfo;
if (DGControl.SetupFileXfer.Get(out setupInfo) == ReturnCode.Success)
{
filePath = setupInfo.FileName;
}
var xrc = DGAudio.AudioFileXfer.Get();
if (xrc == ReturnCode.XferDone)
{
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs { FileDataPath = filePath });
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
#endregion
#region image xfers
private void DoImageNativeXfer()
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
var xrc = DGImage.ImageNativeXfer.Get(ref dataPtr);
if (xrc == ReturnCode.XferDone)
{
State = 7;
TWImageInfo imgInfo;
TWExtImageInfo extInfo = null;
if (SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
{
if (DGImage.ExtImageInfo.Get(out extInfo) != ReturnCode.Success)
{
extInfo = null;
}
}
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
if (dataPtr != IntPtr.Zero)
{
lockedPtr = Platform.MemoryManager.Lock(dataPtr);
}
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs
{
NativeData = lockedPtr,
ImageInfo = imgInfo,
ExImageInfo = extInfo
});
if (extInfo != null) { extInfo.Dispose(); }
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
catch (Exception ex)
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { Exception = ex });
}
finally
{
State = 6;
// data here is allocated by source so needs to use shared mem calls
if (lockedPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockedPtr);
lockedPtr = IntPtr.Zero;
}
if (dataPtr != IntPtr.Zero)
{
Platform.MemoryManager.Free(dataPtr);
dataPtr = IntPtr.Zero;
}
}
}
private void DoImageFileXfer()
{
string filePath = null;
TWSetupFileXfer setupInfo;
if (DGControl.SetupFileXfer.Get(out setupInfo) == ReturnCode.Success)
{
filePath = setupInfo.FileName;
}
var xrc = DGImage.ImageFileXfer.Get();
if (xrc == ReturnCode.XferDone)
{
TWImageInfo imgInfo;
TWExtImageInfo extInfo = null;
if (SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
{
if (DGImage.ExtImageInfo.Get(out extInfo) != ReturnCode.Success)
{
extInfo = null;
}
}
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs
{
FileDataPath = filePath,
ImageInfo = imgInfo,
ExImageInfo = extInfo
});
if (extInfo != null) { extInfo.Dispose(); }
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
private void DoImageMemoryXfer()
{
TWSetupMemXfer memInfo;
if (DGControl.SetupMemXfer.Get(out memInfo) == ReturnCode.Success)
{
TWImageMemXfer xferInfo = new TWImageMemXfer();
try
{
// how to tell if going to xfer in strip vs tile?
// if tile don't allocate memory in app?
xferInfo.Memory = new TWMemory
{
Flags = MemoryFlags.AppOwns | MemoryFlags.Pointer,
Length = memInfo.Preferred,
TheMem = Platform.MemoryManager.Allocate(memInfo.Preferred)
};
// do the unthinkable and keep all xferred batches in memory,
// possibly defeating the purpose of mem xfer
// unless compression is used.
// todo: use array instead of memory stream?
using (MemoryStream xferredData = new MemoryStream())
{
var xrc = ReturnCode.Success;
do
{
xrc = DGImage.ImageMemFileXfer.Get(xferInfo);
if (xrc == ReturnCode.Success ||
xrc == ReturnCode.XferDone)
{
State = 7;
// optimize and allocate buffer only once instead of inside the loop?
byte[] buffer = new byte[(int)xferInfo.BytesWritten];
IntPtr lockPtr = IntPtr.Zero;
try
{
lockPtr = Platform.MemoryManager.Lock(xferInfo.Memory.TheMem);
Marshal.Copy(lockPtr, buffer, 0, buffer.Length);
xferredData.Write(buffer, 0, buffer.Length);
}
finally
{
if (lockPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockPtr);
}
}
}
} while (xrc == ReturnCode.Success);
if (xrc == ReturnCode.XferDone)
{
TWImageInfo imgInfo;
TWExtImageInfo extInfo = null;
if (SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
{
if (DGImage.ExtImageInfo.Get(out extInfo) != ReturnCode.Success)
{
extInfo = null;
}
}
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs
{
MemData = xferredData.ToArray(),
ImageInfo = imgInfo,
ExImageInfo = extInfo
});
if (extInfo != null) { extInfo.Dispose(); }
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
}
catch (Exception ex)
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { Exception = ex });
}
finally
{
State = 6;
if (xferInfo.Memory.TheMem != IntPtr.Zero)
{
Platform.MemoryManager.Free(xferInfo.Memory.TheMem);
}
}
}
}
private void DoImageMemoryFileXfer()
{
// since it's memory-file xfer need info from both (maybe)
TWSetupMemXfer memInfo;
TWSetupFileXfer fileInfo;
if (DGControl.SetupMemXfer.Get(out memInfo) == ReturnCode.Success &&
DGControl.SetupFileXfer.Get(out fileInfo) == ReturnCode.Success)
{
TWImageMemXfer xferInfo = new TWImageMemXfer();
var tempFile = Path.GetTempFileName();
string finalFile = null;
try
{
// no strip or tile here, just chunks
xferInfo.Memory = new TWMemory
{
Flags = MemoryFlags.AppOwns | MemoryFlags.Pointer,
Length = memInfo.Preferred,
TheMem = Platform.MemoryManager.Allocate(memInfo.Preferred)
};
var xrc = ReturnCode.Success;
using (var outStream = File.OpenWrite(tempFile))
{
do
{
xrc = DGImage.ImageMemFileXfer.Get(xferInfo);
if (xrc == ReturnCode.Success ||
xrc == ReturnCode.XferDone)
{
State = 7;
byte[] buffer = new byte[(int)xferInfo.BytesWritten];
IntPtr lockPtr = IntPtr.Zero;
try
{
lockPtr = Platform.MemoryManager.Lock(xferInfo.Memory.TheMem);
Marshal.Copy(lockPtr, buffer, 0, buffer.Length);
}
finally
{
if (lockPtr != IntPtr.Zero)
{
Platform.MemoryManager.Unlock(lockPtr);
}
}
outStream.Write(buffer, 0, buffer.Length);
}
} while (xrc == ReturnCode.Success);
}
if (xrc == ReturnCode.XferDone)
{
switch (fileInfo.Format)
{
case FileFormat.Bmp:
finalFile = Path.ChangeExtension(tempFile, ".bmp");
break;
case FileFormat.Dejavu:
finalFile = Path.ChangeExtension(tempFile, ".dejavu");
break;
case FileFormat.Exif:
finalFile = Path.ChangeExtension(tempFile, ".exit");
break;
case FileFormat.Fpx:
finalFile = Path.ChangeExtension(tempFile, ".fpx");
break;
case FileFormat.Jfif:
finalFile = Path.ChangeExtension(tempFile, ".jpg");
break;
case FileFormat.Jp2:
finalFile = Path.ChangeExtension(tempFile, ".jp2");
break;
case FileFormat.Jpx:
finalFile = Path.ChangeExtension(tempFile, ".jpx");
break;
case FileFormat.Pdf:
case FileFormat.PdfA:
case FileFormat.PdfA2:
finalFile = Path.ChangeExtension(tempFile, ".pdf");
break;
case FileFormat.Pict:
finalFile = Path.ChangeExtension(tempFile, ".pict");
break;
case FileFormat.Png:
finalFile = Path.ChangeExtension(tempFile, ".png");
break;
case FileFormat.Spiff:
finalFile = Path.ChangeExtension(tempFile, ".spiff");
break;
case FileFormat.Tiff:
case FileFormat.TiffMulti:
finalFile = Path.ChangeExtension(tempFile, ".tif");
break;
case FileFormat.Xbm:
finalFile = Path.ChangeExtension(tempFile, ".xbm");
break;
default:
finalFile = Path.ChangeExtension(tempFile, ".unknown");
break;
}
File.Move(tempFile, finalFile);
}
else
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { ReturnCode = xrc, SourceStatus = this.GetSourceStatus() });
}
}
catch (Exception ex)
{
SafeSyncableRaiseOnEvent(OnTransferError, TransferError, new TransferErrorEventArgs { Exception = ex });
}
finally
{
State = 6;
if (xferInfo.Memory.TheMem != IntPtr.Zero)
{
Platform.MemoryManager.Free(xferInfo.Memory.TheMem);
}
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
if (File.Exists(finalFile))
{
TWImageInfo imgInfo;
TWExtImageInfo extInfo = null;
if (SupportedCaps.Contains(CapabilityId.ICapExtImageInfo))
{
if (DGImage.ExtImageInfo.Get(out extInfo) != ReturnCode.Success)
{
extInfo = null;
}
}
if (DGImage.ImageInfo.Get(out imgInfo) != ReturnCode.Success)
{
imgInfo = null;
}
SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, new DataTransferredEventArgs
{
FileDataPath = finalFile,
ImageInfo = imgInfo,
ExImageInfo = extInfo
});
if (extInfo != null) { extInfo.Dispose(); }
}
}
}
#endregion
#endregion
}
}

View File

@ -15,7 +15,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static TWStatus GetManagerStatus(this TwainSession session)
public static TWStatus GetManagerStatus(this ITwainOperation session)
{
TWStatus stat;
session.DGControl.Status.GetManager(out stat);
@ -26,7 +26,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static TWStatus GetSourceStatus(this TwainSession session)
public static TWStatus GetSourceStatus(this ITwainOperation session)
{
TWStatus stat;
session.DGControl.Status.GetSource(out stat);
@ -39,7 +39,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 ITwainOperation session)
{
List<TWIdentity> list = new List<TWIdentity>();
@ -67,7 +67,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="capId">The cap id.</param>
/// <returns></returns>
public static object GetCurrentCap(this TwainSession session, CapabilityId capId)
public static object GetCurrentCap(this ITwainOperation session, CapabilityId capId)
{
using (TWCapability cap = new TWCapability(capId))
{
@ -147,7 +147,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="capabilityId">The capability unique identifier.</param>
/// <returns></returns>
public static IList<object> GetCapabilityValues(this TwainSession session, CapabilityId capabilityId)
public static IList<object> GetCapabilityValues(this ITwainOperation session, CapabilityId capabilityId)
{
var list = new List<object>();
using (TWCapability cap = new TWCapability(capabilityId))
@ -167,7 +167,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 ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.CapSupportedCaps).CastToEnum<CapabilityId>(false);
}
@ -180,7 +180,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 ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapXferMech).CastToEnum<XferMech>(true);
}
@ -195,7 +195,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<CompressionType> CapGetCompression(this TwainSession session)
public static IList<CompressionType> CapGetCompression(this ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapCompression).CastToEnum<CompressionType>(true);
}
@ -206,7 +206,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="compression">The compression.</param>
/// <returns></returns>
public static ReturnCode CapSetImageCompression(this TwainSession session, CompressionType compression)
public static ReturnCode CapSetImageCompression(this ITwainOperation session, CompressionType compression)
{
using (TWCapability compressCap = new TWCapability(CapabilityId.ICapCompression, new TWOneValue { Item = (uint)compression, ItemType = ItemType.UInt16 }))
{
@ -224,7 +224,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 ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapImageFileFormat).CastToEnum<FileFormat>(true);
}
@ -235,7 +235,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 ITwainOperation session, FileFormat format)
{
using (TWCapability formatCap = new TWCapability(CapabilityId.ICapImageFileFormat, new TWOneValue { Item = (uint)format, ItemType = ItemType.UInt16 }))
{
@ -253,7 +253,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 ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapPixelType).CastToEnum<PixelType>(true);
}
@ -264,7 +264,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 ITwainOperation session, PixelType type)
{
var one = new TWOneValue();
one.Item = (uint)type;
@ -285,7 +285,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<XferMech> CapGetImageXferMechs(this TwainSession session)
public static IList<XferMech> CapGetImageXferMechs(this ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapXferMech).CastToEnum<XferMech>(true);
}
@ -296,7 +296,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<XferMech> CapGetAudioXferMechs(this TwainSession session)
public static IList<XferMech> CapGetAudioXferMechs(this ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ACapXferMech).CastToEnum<XferMech>(true);
}
@ -307,7 +307,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="type">The type.</param>
/// <returns></returns>
public static ReturnCode CapSetImageXferMech(this TwainSession session, XferMech type)
public static ReturnCode CapSetImageXferMech(this ITwainOperation session, XferMech type)
{
var one = new TWOneValue();
one.Item = (uint)type;
@ -324,7 +324,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="type">The type.</param>
/// <returns></returns>
public static ReturnCode CapSetAudioXferMech(this TwainSession session, XferMech type)
public static ReturnCode CapSetAudioXferMech(this ITwainOperation session, XferMech type)
{
var one = new TWOneValue();
one.Item = (uint)type;
@ -345,7 +345,7 @@ namespace NTwain
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public static IList<TWFix32> CapGetDPIs(this TwainSession session)
public static IList<TWFix32> CapGetDPIs(this ITwainOperation session)
{
var list = session.GetCapabilityValues(CapabilityId.ICapXResolution);
return list.Select(o => o.ConvertToFix32()).ToList();
@ -357,7 +357,7 @@ namespace NTwain
/// <param name="session">The session.</param>
/// <param name="dpi">The DPI.</param>
/// <returns></returns>
public static ReturnCode CapSetDPI(this TwainSession session, TWFix32 dpi)
public static ReturnCode CapSetDPI(this ITwainOperation session, TWFix32 dpi)
{
return CapSetDPI(session, dpi, dpi);
}
@ -369,7 +369,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, TWFix32 xDPI, TWFix32 yDPI)
public static ReturnCode CapSetDPI(this ITwainOperation session, TWFix32 xDPI, TWFix32 yDPI)
{
TWOneValue one = new TWOneValue();
one.Item = (uint)xDPI;// ((uint)dpi) << 16;
@ -400,7 +400,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 ITwainOperation session)
{
return session.GetCapabilityValues(CapabilityId.ICapSupportedSizes).CastToEnum<SupportedSize>(true);
}
@ -411,7 +411,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 ITwainOperation session, SupportedSize size)
{
var one = new TWOneValue();
one.Item = (uint)size;
@ -474,7 +474,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 ITwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticRotate))
@ -512,7 +512,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 ITwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticBorderDetection))
@ -560,7 +560,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 ITwainSession session, bool useIt)
{
if (session.SourceId.ProtocolMajor >= 2)
{
@ -594,7 +594,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 ITwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.CapFeederEnabled))

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"));
ITwainSessionInternal session = new TwainSession(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"));
ITwainSessionInternal session = new TwainSession(TWIdentity.Create(DataGroups.Image, new Version(1, 0), "test", "test", "test", "test"));
session.EnforceState = false;
session.ChangeState(4, false);