diff --git a/NTwain.Net35/NTwain.Net35.csproj b/NTwain.Net35/NTwain.Net35.csproj index 53fdfc2..e0670e3 100644 --- a/NTwain.Net35/NTwain.Net35.csproj +++ b/NTwain.Net35/NTwain.Net35.csproj @@ -79,8 +79,8 @@ Internals\ICommittable.cs - - Internals\ITwainStateInternal.cs + + Internals\ITwainSessionInternal.cs Internals\MessageLoop.cs @@ -91,6 +91,9 @@ Internals\TentativeStateCommitable.cs + + Internals\TransferLogic.cs + Internals\WindowsHook.cs @@ -103,6 +106,9 @@ ITwainOperation.cs + + ITwainSession.cs + ITwainState.cs diff --git a/NTwain/Data/CapReadOut.cs b/NTwain/Data/CapReadOut.cs index 0cb1ab5..833e760 100644 --- a/NTwain/Data/CapReadOut.cs +++ b/NTwain/Data/CapReadOut.cs @@ -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 diff --git a/NTwain/Data/TwainTypesExtended.cs b/NTwain/Data/TwainTypesExtended.cs index 273faca..2bb0896 100644 --- a/NTwain/Data/TwainTypesExtended.cs +++ b/NTwain/Data/TwainTypesExtended.cs @@ -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 /// /// The capability. /// The value. - [EnvironmentPermissionAttribute(SecurityAction.LinkDemand)] public TWCapability(CapabilityId capability, TWOneValue value) { Capability = capability; @@ -644,7 +643,6 @@ namespace NTwain.Data /// /// The capability. /// The value. - [EnvironmentPermissionAttribute(SecurityAction.LinkDemand)] public TWCapability(CapabilityId capability, TWEnumeration value) { Capability = capability; @@ -656,7 +654,6 @@ namespace NTwain.Data /// /// The capability. /// The value. - [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 /// /// /// - [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 /// public partial class TWStatus { + public TWStatus() + { + + } + internal TWStatus(ushort code, ushort data) + { + _conditionCode = code; + _data = data; + } /// /// Condition Code describing the status. /// - public ConditionCode ConditionCode { get { return (ConditionCode)_conditionCode; } internal set { _conditionCode = (ushort)value; } } + public ConditionCode ConditionCode { get { return (ConditionCode)_conditionCode; } } /// /// 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. /// - public ushort Data { get { return _data; } internal set { _data = Data; } } + public ushort Data { get { return _data; } } } /// @@ -2210,7 +2212,7 @@ namespace NTwain.Data /// public TWStatus Status { - get { return new TWStatus { ConditionCode = (ConditionCode)_conditionCode, Data = _data }; } + get { return new TWStatus(_conditionCode, _data); } } /// diff --git a/NTwain/Data/TwainValues.cs b/NTwain/Data/TwainValues.cs index 15d7a34..b8c4a68 100644 --- a/NTwain/Data/TwainValues.cs +++ b/NTwain/Data/TwainValues.cs @@ -1467,7 +1467,7 @@ namespace NTwain.Data /// /// Corresponds to DG_*. /// - [Flags] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags"), Flags] public enum DataGroups : uint { None = 0, diff --git a/NTwain/Data/ValueConverter.cs b/NTwain/Data/ValueConverter.cs index 1bef797..f73a6dc 100644 --- a/NTwain/Data/ValueConverter.cs +++ b/NTwain/Data/ValueConverter.cs @@ -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); } } } diff --git a/NTwain/ITwainSession.cs b/NTwain/ITwainSession.cs new file mode 100644 index 0000000..0819682 --- /dev/null +++ b/NTwain/ITwainSession.cs @@ -0,0 +1,22 @@ +using NTwain.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NTwain +{ + /// + /// General interface for a TWAIN session. + /// + public interface ITwainSession : ITwainState, ITwainOperation + { + /// + /// Gets the supported caps for the currently open source. + /// + /// + /// The supported caps. + /// + IList SupportedCaps { get; } + } +} diff --git a/NTwain/Internals/Extensions.cs b/NTwain/Internals/Extensions.cs index 8d18db6..bb506db 100644 --- a/NTwain/Internals/Extensions.cs +++ b/NTwain/Internals/Extensions.cs @@ -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 /// The triplet data argument type. /// The triplet message. /// - 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; + } + } } diff --git a/NTwain/Internals/ITwainStateInternal.cs b/NTwain/Internals/ITwainSessionInternal.cs similarity index 80% rename from NTwain/Internals/ITwainStateInternal.cs rename to NTwain/Internals/ITwainSessionInternal.cs index eaa3c6e..0f69319 100644 --- a/NTwain/Internals/ITwainStateInternal.cs +++ b/NTwain/Internals/ITwainSessionInternal.cs @@ -1,11 +1,12 @@ using NTwain.Data; +using System.Collections.Generic; namespace NTwain.Internals { /// /// Internal interface for state management. /// - interface ITwainStateInternal : ITwainState + interface ITwainSessionInternal : ITwainSession { /// /// 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); } } diff --git a/NTwain/Internals/NativeMethods.cs b/NTwain/Internals/NativeMethods.cs index 0ac99d2..652ca20 100644 --- a/NTwain/Internals/NativeMethods.cs +++ b/NTwain/Internals/NativeMethods.cs @@ -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 } diff --git a/NTwain/Internals/TentativeStateCommitable.cs b/NTwain/Internals/TentativeStateCommitable.cs index 16411f1..266e9e9 100644 --- a/NTwain/Internals/TentativeStateCommitable.cs +++ b/NTwain/Internals/TentativeStateCommitable.cs @@ -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; diff --git a/NTwain/Internals/TransferLogic.cs b/NTwain/Internals/TransferLogic.cs new file mode 100644 index 0000000..aa00d16 --- /dev/null +++ b/NTwain/Internals/TransferLogic.cs @@ -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 +{ + /// + /// Contains the actual data transfer logic since TwainSession is getting too large. + /// + static class TransferLogic + { + /// + /// Performs the TWAIN transfer routine at state 6. + /// + 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(); + 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(); + 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 + } +} diff --git a/NTwain/Internals/WinMemoryManager.cs b/NTwain/Internals/WinMemoryManager.cs index 0dd7ba6..4dd741b 100644 --- a/NTwain/Internals/WinMemoryManager.cs +++ b/NTwain/Internals/WinMemoryManager.cs @@ -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; } diff --git a/NTwain/NTwain.csproj b/NTwain/NTwain.csproj index 68200de..dfe3a5d 100644 --- a/NTwain/NTwain.csproj +++ b/NTwain/NTwain.csproj @@ -27,7 +27,7 @@ true - MinimumRecommendedRules.ruleset + AllRules.ruleset pdbonly @@ -60,10 +60,12 @@ - + + + diff --git a/NTwain/Triplets/DGAudio/DGAudio.AudioFileXfer.cs b/NTwain/Triplets/DGAudio/DGAudio.AudioFileXfer.cs index d103dad..abf2259 100644 --- a/NTwain/Triplets/DGAudio/DGAudio.AudioFileXfer.cs +++ b/NTwain/Triplets/DGAudio/DGAudio.AudioFileXfer.cs @@ -9,7 +9,7 @@ namespace NTwain.Triplets /// sealed class AudioFileXfer : OpBase { - internal AudioFileXfer(ITwainStateInternal session) : base(session) { } + internal AudioFileXfer(ITwainSessionInternal session) : base(session) { } /// /// 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. diff --git a/NTwain/Triplets/DGAudio/DGAudio.AudioInfo.cs b/NTwain/Triplets/DGAudio/DGAudio.AudioInfo.cs index 04fc25a..004099a 100644 --- a/NTwain/Triplets/DGAudio/DGAudio.AudioInfo.cs +++ b/NTwain/Triplets/DGAudio/DGAudio.AudioInfo.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class AudioInfo : OpBase { - internal AudioInfo(ITwainStateInternal session) : base(session) { } + internal AudioInfo(ITwainSessionInternal session) : base(session) { } /// /// Used to get the information of the current audio data ready to transfer. /// diff --git a/NTwain/Triplets/DGAudio/DGAudio.AudioNativeXfer.cs b/NTwain/Triplets/DGAudio/DGAudio.AudioNativeXfer.cs index c8ca621..7dea5ec 100644 --- a/NTwain/Triplets/DGAudio/DGAudio.AudioNativeXfer.cs +++ b/NTwain/Triplets/DGAudio/DGAudio.AudioNativeXfer.cs @@ -9,7 +9,7 @@ namespace NTwain.Triplets /// sealed class AudioNativeXfer : OpBase { - internal AudioNativeXfer(ITwainStateInternal session) : base(session) { } + internal AudioNativeXfer(ITwainSessionInternal session) : base(session) { } /// /// 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. diff --git a/NTwain/Triplets/DGAudio/DGAudio.cs b/NTwain/Triplets/DGAudio/DGAudio.cs index aab77cf..8f65423 100644 --- a/NTwain/Triplets/DGAudio/DGAudio.cs +++ b/NTwain/Triplets/DGAudio/DGAudio.cs @@ -8,8 +8,8 @@ namespace NTwain.Triplets /// 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; diff --git a/NTwain/Triplets/DGControl/DGControl.Callback.cs b/NTwain/Triplets/DGControl/DGControl.Callback.cs index 50fc862..4e40396 100644 --- a/NTwain/Triplets/DGControl/DGControl.Callback.cs +++ b/NTwain/Triplets/DGControl/DGControl.Callback.cs @@ -5,7 +5,7 @@ namespace NTwain.Triplets { sealed class Callback : OpBase { - internal Callback(ITwainStateInternal session) : base(session) { } + internal Callback(ITwainSessionInternal session) : base(session) { } /// /// This triplet is sent to the DSM by the Application to register the application’s entry point with /// the DSM, so that the DSM can use callbacks to inform the application of events generated by the diff --git a/NTwain/Triplets/DGControl/DGControl.Callback2.cs b/NTwain/Triplets/DGControl/DGControl.Callback2.cs index ffadf47..b6a336e 100644 --- a/NTwain/Triplets/DGControl/DGControl.Callback2.cs +++ b/NTwain/Triplets/DGControl/DGControl.Callback2.cs @@ -5,7 +5,7 @@ namespace NTwain.Triplets { sealed class Callback2 : OpBase { - internal Callback2(ITwainStateInternal session) : base(session) { } + internal Callback2(ITwainSessionInternal session) : base(session) { } /// /// This triplet is sent to the DSM by the Application to register the application’s entry point with /// the DSM, so that the DSM can use callbacks to inform the application of events generated by the diff --git a/NTwain/Triplets/DGControl/DGControl.Capability.cs b/NTwain/Triplets/DGControl/DGControl.Capability.cs index 9dfcab3..d24dc04 100644 --- a/NTwain/Triplets/DGControl/DGControl.Capability.cs +++ b/NTwain/Triplets/DGControl/DGControl.Capability.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class Capability : OpBase { - internal Capability(ITwainStateInternal session) : base(session) { } + internal Capability(ITwainSessionInternal session) : base(session) { } /// /// Returns the Source’s Current, Default and Available Values for a specified capability. /// diff --git a/NTwain/Triplets/DGControl/DGControl.CustomDSData.cs b/NTwain/Triplets/DGControl/DGControl.CustomDSData.cs index 7f7ae6a..3829c4c 100644 --- a/NTwain/Triplets/DGControl/DGControl.CustomDSData.cs +++ b/NTwain/Triplets/DGControl/DGControl.CustomDSData.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class CustomDSData : OpBase { - internal CustomDSData(ITwainStateInternal session) : base(session) { } + internal CustomDSData(ITwainSessionInternal session) : base(session) { } /// /// 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 diff --git a/NTwain/Triplets/DGControl/DGControl.DeviceEvent.cs b/NTwain/Triplets/DGControl/DGControl.DeviceEvent.cs index 3a524d7..1b20ab2 100644 --- a/NTwain/Triplets/DGControl/DGControl.DeviceEvent.cs +++ b/NTwain/Triplets/DGControl/DGControl.DeviceEvent.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// 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); diff --git a/NTwain/Triplets/DGControl/DGControl.EntryPoint.cs b/NTwain/Triplets/DGControl/DGControl.EntryPoint.cs index 58b4db3..c2282ff 100644 --- a/NTwain/Triplets/DGControl/DGControl.EntryPoint.cs +++ b/NTwain/Triplets/DGControl/DGControl.EntryPoint.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// sealed class EntryPoint : OpBase { - internal EntryPoint(ITwainStateInternal session) : base(session) { } + internal EntryPoint(ITwainSessionInternal session) : base(session) { } /// /// Gets the function entry points for twain 2.0 or higher. /// diff --git a/NTwain/Triplets/DGControl/DGControl.Event.cs b/NTwain/Triplets/DGControl/DGControl.Event.cs index aa3cbc5..e9cb86f 100644 --- a/NTwain/Triplets/DGControl/DGControl.Event.cs +++ b/NTwain/Triplets/DGControl/DGControl.Event.cs @@ -5,7 +5,7 @@ namespace NTwain.Triplets { sealed class Event : OpBase { - internal Event(ITwainStateInternal session) : base(session) { } + internal Event(ITwainSessionInternal session) : base(session) { } /// /// This operation supports the distribution of events from the application to Sources so that the diff --git a/NTwain/Triplets/DGControl/DGControl.FileSystem.cs b/NTwain/Triplets/DGControl/DGControl.FileSystem.cs index ba2ca99..a45fd0c 100644 --- a/NTwain/Triplets/DGControl/DGControl.FileSystem.cs +++ b/NTwain/Triplets/DGControl/DGControl.FileSystem.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class FileSystem : OpBase { - internal FileSystem(ITwainStateInternal session) : base(session) { } + internal FileSystem(ITwainSessionInternal session) : base(session) { } /// /// This operation selects the destination directory within the Source (camera, storage, etc), where /// images captured using CapAutomaticCapture will be stored. This command only selects diff --git a/NTwain/Triplets/DGControl/DGControl.Identity.cs b/NTwain/Triplets/DGControl/DGControl.Identity.cs index b984955..d2b749e 100644 --- a/NTwain/Triplets/DGControl/DGControl.Identity.cs +++ b/NTwain/Triplets/DGControl/DGControl.Identity.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class Identity : OpBase { - internal Identity(ITwainStateInternal session) : base(session) { } + internal Identity(ITwainSessionInternal session) : base(session) { } /// /// 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 diff --git a/NTwain/Triplets/DGControl/DGControl.Parent.cs b/NTwain/Triplets/DGControl/DGControl.Parent.cs index 7be5cd7..c330011 100644 --- a/NTwain/Triplets/DGControl/DGControl.Parent.cs +++ b/NTwain/Triplets/DGControl/DGControl.Parent.cs @@ -9,7 +9,7 @@ namespace NTwain.Triplets /// sealed class Parent : OpBase { - internal Parent(ITwainStateInternal session) : base(session) { } + internal Parent(ITwainSessionInternal session) : base(session) { } /// /// When the application has closed all the Sources it had previously opened, and is finished with diff --git a/NTwain/Triplets/DGControl/DGControl.PassThru.cs b/NTwain/Triplets/DGControl/DGControl.PassThru.cs index cc612d8..89f5ca5 100644 --- a/NTwain/Triplets/DGControl/DGControl.PassThru.cs +++ b/NTwain/Triplets/DGControl/DGControl.PassThru.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class PassThru : OpBase { - internal PassThru(ITwainStateInternal session) : base(session) { } + internal PassThru(ITwainSessionInternal session) : base(session) { } /// /// PASSTHRU is intended for the use of Source writers writing diagnostic applications. It allows /// raw communication with the currently selected device in the Source. diff --git a/NTwain/Triplets/DGControl/DGControl.PendingXfers.cs b/NTwain/Triplets/DGControl/DGControl.PendingXfers.cs index cdc1848..ce34c83 100644 --- a/NTwain/Triplets/DGControl/DGControl.PendingXfers.cs +++ b/NTwain/Triplets/DGControl/DGControl.PendingXfers.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// sealed class PendingXfers : OpBase { - internal PendingXfers(ITwainStateInternal session) : base(session) { } + internal PendingXfers(ITwainSessionInternal session) : base(session) { } /// /// 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 diff --git a/NTwain/Triplets/DGControl/DGControl.SetupFileXfer.cs b/NTwain/Triplets/DGControl/DGControl.SetupFileXfer.cs index d74d3db..5b3f460 100644 --- a/NTwain/Triplets/DGControl/DGControl.SetupFileXfer.cs +++ b/NTwain/Triplets/DGControl/DGControl.SetupFileXfer.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class SetupFileXfer : OpBase { - internal SetupFileXfer(ITwainStateInternal session) : base(session) { } + internal SetupFileXfer(ITwainSessionInternal session) : base(session) { } /// /// Returns information about the file into which the Source has or will put the acquired image /// or audio data. diff --git a/NTwain/Triplets/DGControl/DGControl.SetupMemXfer.cs b/NTwain/Triplets/DGControl/DGControl.SetupMemXfer.cs index 5d2ca33..ab30b1c 100644 --- a/NTwain/Triplets/DGControl/DGControl.SetupMemXfer.cs +++ b/NTwain/Triplets/DGControl/DGControl.SetupMemXfer.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class SetupMemXfer : OpBase { - internal SetupMemXfer(ITwainStateInternal session) : base(session) { } + internal SetupMemXfer(ITwainSessionInternal session) : base(session) { } /// /// Returns the Source’s preferred, minimum, and maximum allocation sizes for transfer memory /// buffers. diff --git a/NTwain/Triplets/DGControl/DGControl.Status.cs b/NTwain/Triplets/DGControl/DGControl.Status.cs index 5db7401..2fe7af4 100644 --- a/NTwain/Triplets/DGControl/DGControl.Status.cs +++ b/NTwain/Triplets/DGControl/DGControl.Status.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class Status : OpBase { - internal Status(ITwainStateInternal session) : base(session) { } + internal Status(ITwainSessionInternal session) : base(session) { } /// /// Returns the current Condition Code for the Source Manager. /// diff --git a/NTwain/Triplets/DGControl/DGControl.StatusUtf8.cs b/NTwain/Triplets/DGControl/DGControl.StatusUtf8.cs index faf978b..259a753 100644 --- a/NTwain/Triplets/DGControl/DGControl.StatusUtf8.cs +++ b/NTwain/Triplets/DGControl/DGControl.StatusUtf8.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class StatusUtf8 : OpBase { - internal StatusUtf8(ITwainStateInternal session) : base(session) { } + internal StatusUtf8(ITwainSessionInternal session) : base(session) { } /// /// Translate the contents of a TW_STATUS structure received from a Source into a localized UTF-8 /// encoded string. diff --git a/NTwain/Triplets/DGControl/DGControl.UserInterface.cs b/NTwain/Triplets/DGControl/DGControl.UserInterface.cs index 97c0771..703847e 100644 --- a/NTwain/Triplets/DGControl/DGControl.UserInterface.cs +++ b/NTwain/Triplets/DGControl/DGControl.UserInterface.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// sealed class UserInterface : OpBase { - internal UserInterface(ITwainStateInternal session) : base(session) { } + internal UserInterface(ITwainSessionInternal session) : base(session) { } /// /// This operation causes the Source’s user interface, if displayed during the /// EnableDS operation, to be lowered. The Source is returned to diff --git a/NTwain/Triplets/DGControl/DGControl.XferGroup.cs b/NTwain/Triplets/DGControl/DGControl.XferGroup.cs index 2151754..e22d89b 100644 --- a/NTwain/Triplets/DGControl/DGControl.XferGroup.cs +++ b/NTwain/Triplets/DGControl/DGControl.XferGroup.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class XferGroup : OpBase { - internal XferGroup(ITwainStateInternal session) : base(session) { } + internal XferGroup(ITwainSessionInternal session) : base(session) { } /// /// Returns the Data Group (the type of data) for the upcoming transfer. The Source is required to diff --git a/NTwain/Triplets/DGControl/DGControl.cs b/NTwain/Triplets/DGControl/DGControl.cs index f9317f5..9a7364b 100644 --- a/NTwain/Triplets/DGControl/DGControl.cs +++ b/NTwain/Triplets/DGControl/DGControl.cs @@ -8,8 +8,8 @@ namespace NTwain.Triplets /// 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; diff --git a/NTwain/Triplets/DGImage/DGImage.CieColor.cs b/NTwain/Triplets/DGImage/DGImage.CieColor.cs index 8174f56..0698c38 100644 --- a/NTwain/Triplets/DGImage/DGImage.CieColor.cs +++ b/NTwain/Triplets/DGImage/DGImage.CieColor.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class CieColor : OpBase { - internal CieColor(ITwainStateInternal session) : base(session) { } + internal CieColor(ITwainSessionInternal session) : base(session) { } /// /// This operation causes the Source to report the currently active parameters to be used in diff --git a/NTwain/Triplets/DGImage/DGImage.ExtImageInfo.cs b/NTwain/Triplets/DGImage/DGImage.ExtImageInfo.cs index f2f3e30..ad8207c 100644 --- a/NTwain/Triplets/DGImage/DGImage.ExtImageInfo.cs +++ b/NTwain/Triplets/DGImage/DGImage.ExtImageInfo.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class ExtImageInfo : OpBase { - internal ExtImageInfo(ITwainStateInternal session) : base(session) { } + internal ExtImageInfo(ITwainSessionInternal session) : base(session) { } public ReturnCode Get(out TWExtImageInfo info) { diff --git a/NTwain/Triplets/DGImage/DGImage.Filter.cs b/NTwain/Triplets/DGImage/DGImage.Filter.cs index cd6df89..433f720 100644 --- a/NTwain/Triplets/DGImage/DGImage.Filter.cs +++ b/NTwain/Triplets/DGImage/DGImage.Filter.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class Filter : OpBase { - internal Filter(ITwainStateInternal session) : base(session) { } + internal Filter(ITwainSessionInternal session) : base(session) { } /// diff --git a/NTwain/Triplets/DGImage/DGImage.GrayResponse.cs b/NTwain/Triplets/DGImage/DGImage.GrayResponse.cs index 49baf4f..171980f 100644 --- a/NTwain/Triplets/DGImage/DGImage.GrayResponse.cs +++ b/NTwain/Triplets/DGImage/DGImage.GrayResponse.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class GrayResponse : OpBase { - internal GrayResponse(ITwainStateInternal session) : base(session) { } + internal GrayResponse(ITwainSessionInternal session) : base(session) { } /// /// The Reset operation causes the Source to use its "identity response curve." The identity diff --git a/NTwain/Triplets/DGImage/DGImage.IccProfile.cs b/NTwain/Triplets/DGImage/DGImage.IccProfile.cs index 2a4eb2f..30d196c 100644 --- a/NTwain/Triplets/DGImage/DGImage.IccProfile.cs +++ b/NTwain/Triplets/DGImage/DGImage.IccProfile.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class IccProfile : OpBase { - internal IccProfile(ITwainStateInternal session) : base(session) { } + internal IccProfile(ITwainSessionInternal session) : base(session) { } /// /// This operation provides the application with the ICC profile associated with the image which is diff --git a/NTwain/Triplets/DGImage/DGImage.ImageFileXfer.cs b/NTwain/Triplets/DGImage/DGImage.ImageFileXfer.cs index 61b44fb..24ae3a8 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageFileXfer.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageFileXfer.cs @@ -9,7 +9,7 @@ namespace NTwain.Triplets /// sealed class ImageFileXfer : OpBase { - internal ImageFileXfer(ITwainStateInternal session) : base(session) { } + internal ImageFileXfer(ITwainSessionInternal session) : base(session) { } /// /// This operation is used to initiate the transfer of an image from the Source to the application via diff --git a/NTwain/Triplets/DGImage/DGImage.ImageInfo.cs b/NTwain/Triplets/DGImage/DGImage.ImageInfo.cs index 0ee0272..e10811c 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageInfo.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageInfo.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class ImageInfo : OpBase { - internal ImageInfo(ITwainStateInternal session) : base(session) { } + internal ImageInfo(ITwainSessionInternal session) : base(session) { } public ReturnCode Get(out TWImageInfo info) { diff --git a/NTwain/Triplets/DGImage/DGImage.ImageLayout.cs b/NTwain/Triplets/DGImage/DGImage.ImageLayout.cs index 435d078..126cc01 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageLayout.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageLayout.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class ImageLayout : OpBase { - internal ImageLayout(ITwainStateInternal session) : base(session) { } + internal ImageLayout(ITwainSessionInternal session) : base(session) { } public ReturnCode Get(out TWImageLayout layout) { diff --git a/NTwain/Triplets/DGImage/DGImage.ImageMemFileXfer.cs b/NTwain/Triplets/DGImage/DGImage.ImageMemFileXfer.cs index 2c4268d..3e3856b 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageMemFileXfer.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageMemFileXfer.cs @@ -5,7 +5,7 @@ namespace NTwain.Triplets { sealed class ImageMemFileXfer : OpBase { - internal ImageMemFileXfer(ITwainStateInternal session) : base(session) { } + internal ImageMemFileXfer(ITwainSessionInternal session) : base(session) { } /// /// This operation is used to initiate the transfer of an image from the Source to the application via diff --git a/NTwain/Triplets/DGImage/DGImage.ImageMemXfer.cs b/NTwain/Triplets/DGImage/DGImage.ImageMemXfer.cs index b4dc7b9..8e56c6f 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageMemXfer.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageMemXfer.cs @@ -5,7 +5,7 @@ namespace NTwain.Triplets { sealed class ImageMemXfer : OpBase { - internal ImageMemXfer(ITwainStateInternal session) : base(session) { } + internal ImageMemXfer(ITwainSessionInternal session) : base(session) { } /// /// This operation is used to initiate the transfer of an image from the Source to the application via diff --git a/NTwain/Triplets/DGImage/DGImage.ImageNativeXfer.cs b/NTwain/Triplets/DGImage/DGImage.ImageNativeXfer.cs index 1a600c6..97e7a5b 100644 --- a/NTwain/Triplets/DGImage/DGImage.ImageNativeXfer.cs +++ b/NTwain/Triplets/DGImage/DGImage.ImageNativeXfer.cs @@ -6,7 +6,7 @@ namespace NTwain.Triplets { sealed class ImageNativeXfer : OpBase { - internal ImageNativeXfer(ITwainStateInternal session) : base(session) { } + internal ImageNativeXfer(ITwainSessionInternal session) : base(session) { } /// /// Causes the transfer of an image’s data from the Source to the application, via the Native transfer diff --git a/NTwain/Triplets/DGImage/DGImage.JpegCompression.cs b/NTwain/Triplets/DGImage/DGImage.JpegCompression.cs index 7e947ec..e0aef8f 100644 --- a/NTwain/Triplets/DGImage/DGImage.JpegCompression.cs +++ b/NTwain/Triplets/DGImage/DGImage.JpegCompression.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class JpegCompression : OpBase { - internal JpegCompression(ITwainStateInternal session) : base(session) { } + internal JpegCompression(ITwainSessionInternal session) : base(session) { } /// /// Causes the Source to return the parameters that will be used during the compression of data diff --git a/NTwain/Triplets/DGImage/DGImage.Palette8.cs b/NTwain/Triplets/DGImage/DGImage.Palette8.cs index ef66883..440d40e 100644 --- a/NTwain/Triplets/DGImage/DGImage.Palette8.cs +++ b/NTwain/Triplets/DGImage/DGImage.Palette8.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class Palette8 : OpBase { - internal Palette8(ITwainStateInternal session) : base(session) { } + internal Palette8(ITwainSessionInternal session) : base(session) { } /// /// This operation causes the Source to report its current palette information. diff --git a/NTwain/Triplets/DGImage/DGImage.RgbResponse.cs b/NTwain/Triplets/DGImage/DGImage.RgbResponse.cs index 566fa91..d0e0767 100644 --- a/NTwain/Triplets/DGImage/DGImage.RgbResponse.cs +++ b/NTwain/Triplets/DGImage/DGImage.RgbResponse.cs @@ -8,7 +8,7 @@ namespace NTwain.Triplets /// public sealed class RgbResponse : OpBase { - internal RgbResponse(ITwainStateInternal session) : base(session) { } + internal RgbResponse(ITwainSessionInternal session) : base(session) { } /// /// Causes the Source to use its "identity" response curves for future RGB transfers. The identity diff --git a/NTwain/Triplets/DGImage/DGImage.cs b/NTwain/Triplets/DGImage/DGImage.cs index be6770b..172d8a0 100644 --- a/NTwain/Triplets/DGImage/DGImage.cs +++ b/NTwain/Triplets/DGImage/DGImage.cs @@ -8,8 +8,8 @@ namespace NTwain.Triplets /// 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; diff --git a/NTwain/Triplets/OpBase.cs b/NTwain/Triplets/OpBase.cs index f8f73cb..b361941 100644 --- a/NTwain/Triplets/OpBase.cs +++ b/NTwain/Triplets/OpBase.cs @@ -13,7 +13,7 @@ namespace NTwain.Triplets /// /// The session. /// - internal OpBase(ITwainStateInternal session) + internal OpBase(ITwainSessionInternal session) { if (session == null) { throw new ArgumentNullException("session"); } Session = session; @@ -25,6 +25,6 @@ namespace NTwain.Triplets /// /// The session. /// - internal ITwainStateInternal Session { get; private set; } + internal ITwainSessionInternal Session { get; private set; } } } diff --git a/NTwain/TwainSession.cs b/NTwain/TwainSession.cs index ede135e..faecbad 100644 --- a/NTwain/TwainSession.cs +++ b/NTwain/TwainSession.cs @@ -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 /// /// Basic class for interfacing with TWAIN. You should only have one of this per application process. /// - public class TwainSession : ITwainStateInternal, ITwainOperation + public class TwainSession : ITwainSessionInternal { /// /// Initializes a new instance of the 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 _supportedCaps; /// - /// Gets the supported caps for the current source. + /// Gets the supported caps for the currently open source. /// /// /// The supported caps. @@ -64,7 +66,7 @@ namespace NTwain } /// - /// 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. /// @@ -74,13 +76,13 @@ namespace NTwain public SynchronizationContext SynchronizationContext { get; set; } - #region ITwainStateInternal Members + #region ITwainSessionInternal Members /// /// Gets the app id used for the session. /// /// The app id. - TWIdentity ITwainStateInternal.AppId { get { return _appId; } } + TWIdentity ITwainSessionInternal.AppId { get { return _appId; } } /// /// Gets or sets a value indicating whether calls to triplets will verify the current twain session state. @@ -90,7 +92,7 @@ namespace NTwain /// 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; } - /// - /// Disables the source to end data acquisition. - /// - /// - 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 /// State of the target. 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 /// public event EventHandler TransferError; + + /// + /// Raises event and if applicable marshal it asynchronously to the thread. + /// + /// The on event function. + /// The handler. + 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); + } + } + /// /// Raises event and if applicable marshal it synchronously to the thread. /// @@ -529,37 +572,6 @@ namespace NTwain } } - /// - /// Raises event and if applicable marshal it asynchronously to the thread. - /// - /// The on event function. - /// The handler. - 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); - } - } - /// /// Called when changed. /// @@ -585,25 +597,24 @@ namespace NTwain /// Called when a data transfer is ready. /// /// The instance containing the event data. - protected virtual void OnTransferReady(TransferReadyEventArgs e) { } + internal protected virtual void OnTransferReady(TransferReadyEventArgs e) { } /// /// Called when data has been transferred. /// /// The instance containing the event data. - protected virtual void OnDataTransferred(DataTransferredEventArgs e) { } + internal protected virtual void OnDataTransferred(DataTransferredEventArgs e) { } /// /// Called when an error has been encountered during transfer. /// /// The instance containing the event data. - 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; } } - /// - /// Performs the TWAIN transfer routine at state 6. - /// - 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(); - 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(); - 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 } } diff --git a/NTwain/TwainSessionExtensions.cs b/NTwain/TwainSessionExtensions.cs index c7429bd..a692b1f 100644 --- a/NTwain/TwainSessionExtensions.cs +++ b/NTwain/TwainSessionExtensions.cs @@ -15,7 +15,7 @@ namespace NTwain /// /// The session. /// - 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 /// /// The session. /// - 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 /// /// The session. /// - public static IList GetSources(this TwainSession session) + public static IList GetSources(this ITwainOperation session) { List list = new List(); @@ -67,7 +67,7 @@ namespace NTwain /// The session. /// The cap id. /// - 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 /// The session. /// The capability unique identifier. /// - public static IList GetCapabilityValues(this TwainSession session, CapabilityId capabilityId) + public static IList GetCapabilityValues(this ITwainOperation session, CapabilityId capabilityId) { var list = new List(); using (TWCapability cap = new TWCapability(capabilityId)) @@ -167,7 +167,7 @@ namespace NTwain /// /// The session. /// - internal static IList GetCapabilities(this TwainSession session) + internal static IList GetCapabilities(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.CapSupportedCaps).CastToEnum(false); } @@ -180,7 +180,7 @@ namespace NTwain /// /// The session. /// - public static IList CapGetImageXferMech(this TwainSession session) + public static IList CapGetImageXferMech(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapXferMech).CastToEnum(true); } @@ -195,7 +195,7 @@ namespace NTwain /// /// The session. /// - public static IList CapGetCompression(this TwainSession session) + public static IList CapGetCompression(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapCompression).CastToEnum(true); } @@ -206,7 +206,7 @@ namespace NTwain /// The session. /// The compression. /// - 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 /// /// The session. /// - public static IList CapGetImageFileFormat(this TwainSession session) + public static IList CapGetImageFileFormat(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapImageFileFormat).CastToEnum(true); } @@ -235,7 +235,7 @@ namespace NTwain /// The session. /// The format. /// - 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 /// /// The session. /// - public static IList CapGetPixelTypes(this TwainSession session) + public static IList CapGetPixelTypes(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapPixelType).CastToEnum(true); } @@ -264,7 +264,7 @@ namespace NTwain /// The session. /// The type. /// - 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 /// /// The session. /// - public static IList CapGetImageXferMechs(this TwainSession session) + public static IList CapGetImageXferMechs(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapXferMech).CastToEnum(true); } @@ -296,7 +296,7 @@ namespace NTwain /// /// The session. /// - public static IList CapGetAudioXferMechs(this TwainSession session) + public static IList CapGetAudioXferMechs(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ACapXferMech).CastToEnum(true); } @@ -307,7 +307,7 @@ namespace NTwain /// The session. /// The type. /// - 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 /// The session. /// The type. /// - 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 /// /// The session. /// - public static IList CapGetDPIs(this TwainSession session) + public static IList CapGetDPIs(this ITwainOperation session) { var list = session.GetCapabilityValues(CapabilityId.ICapXResolution); return list.Select(o => o.ConvertToFix32()).ToList(); @@ -357,7 +357,7 @@ namespace NTwain /// The session. /// The DPI. /// - 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 /// The x DPI. /// The y DPI. /// - 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 /// /// The session. /// - public static IList CapGetSupportedSizes(this TwainSession session) + public static IList CapGetSupportedSizes(this ITwainOperation session) { return session.GetCapabilityValues(CapabilityId.ICapSupportedSizes).CastToEnum(true); } @@ -411,7 +411,7 @@ namespace NTwain /// The session. /// The size. /// - 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 /// The session. /// if set to true [use it]. /// - 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 /// The session. /// if set to true [use it]. /// - 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 /// The session. /// if set to true [use it]. /// - 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 /// The session. /// if set to true [use it]. /// - 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)) diff --git a/Tests/NTwain.Tests/TwainSessionTests.cs b/Tests/NTwain.Tests/TwainSessionTests.cs index ae46b9c..a7a7dc0 100644 --- a/Tests/NTwain.Tests/TwainSessionTests.cs +++ b/Tests/NTwain.Tests/TwainSessionTests.cs @@ -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);