diff --git a/LICENSE.txt b/LICENSE.txt index d7972fd..cbedf08 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,3 +1,5 @@ +# For NTwain + The MIT License (MIT) Copyright (c) 2012 - 2023 Eugene Wang @@ -21,10 +23,9 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# Third Party Code Used - -# for twaincs - +## For twaincs Copyright(C) 2013 - 2021 Kodak Alaris Inc. diff --git a/NTwain.sln b/NTwain.sln index 4e0b0cc..f046a16 100644 --- a/NTwain.sln +++ b/NTwain.sln @@ -24,9 +24,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NTwain", "src\NTwain\NTwain EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NTwain-temp", "src\NTwain-temp\NTwain-temp.csproj", "{A7020B90-5CE4-43EF-A75D-5E1F9B501CAC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console32", "samples\Console32\Console32.csproj", "{AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinForm32", "samples\WinForm32\WinForm32.csproj", "{7792A94E-D0B4-440D-8BD5-CA1CA548782C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinForm32", "samples\WinForm32\WinForm32.csproj", "{7792A94E-D0B4-440D-8BD5-CA1CA548782C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -41,10 +39,6 @@ Global {A7020B90-5CE4-43EF-A75D-5E1F9B501CAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7020B90-5CE4-43EF-A75D-5E1F9B501CAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7020B90-5CE4-43EF-A75D-5E1F9B501CAC}.Release|Any CPU.Build.0 = Release|Any CPU - {AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79}.Release|Any CPU.Build.0 = Release|Any CPU {7792A94E-D0B4-440D-8BD5-CA1CA548782C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7792A94E-D0B4-440D-8BD5-CA1CA548782C}.Debug|Any CPU.Build.0 = Debug|Any CPU {7792A94E-D0B4-440D-8BD5-CA1CA548782C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -54,7 +48,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {AAF9FE1B-B695-4AFD-A014-CA4DFDD06A79} = {707B4313-8EF8-4D0F-A95E-590783422187} {7792A94E-D0B4-440D-8BD5-CA1CA548782C} = {707B4313-8EF8-4D0F-A95E-590783422187} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/NTwain/Data/TWAINH_EXTRAS.cs b/src/NTwain/Data/TWAINH_EXTRAS.cs index 316f10f..1745f52 100644 --- a/src/NTwain/Data/TWAINH_EXTRAS.cs +++ b/src/NTwain/Data/TWAINH_EXTRAS.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Text; namespace NTwain.Data @@ -621,9 +622,15 @@ namespace NTwain.Data try { #if NETFRAMEWORK - var bytes = new byte[Size]; - Marshal.Copy(locked, bytes, 0, bytes.Length); - val = Encoding.UTF8.GetString(bytes); + // safe method but with 2 copies (arr and parsed string) + var bytes = new byte[Size]; + Marshal.Copy(locked, bytes, 0, bytes.Length); + val = Encoding.UTF8.GetString(bytes); + + //// unsafe method with 1 copy (does it work?) + //sbyte* bytes = (sbyte*)locked; + //var str = new string(bytes, 0, length, Encoding.UTF8); + //return str; #else val = Marshal.PtrToStringUTF8(locked, (int)Size); #endif diff --git a/src/NTwain/Native/BITMAP.cs b/src/NTwain/Native/BITMAP.cs new file mode 100644 index 0000000..3ab1d2a --- /dev/null +++ b/src/NTwain/Native/BITMAP.cs @@ -0,0 +1,340 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace NTwain.Native +{ + // this is a good read + // http://atlc.sourceforge.net/bmp.html + + /// + /// Defines the dimensions and color information for a DIB. + /// + [StructLayout(LayoutKind.Sequential)] + struct BITMAPINFO + { + /// + /// Structure that contains information about the dimensions of color format. + /// + public BITMAPINFOHEADER bmiHeader; + /// + /// This contains one of the following: + /// 1. An array of RGBQUAD. The elements of the array that make up the color table. + /// 2. An array of 16-bit unsigned integers that specifies indexes into the currently realized logical palette. This use of bmiColors is allowed for functions that use DIBs. + /// The number of entries in the array depends on the values of the biBitCount and biClrUsed members of the BITMAPINFOHEADER structure. + /// + public IntPtr bmiColors; + + }; + + /// + /// Structure that contains information about the dimensions and color format of a DIB. + /// + [StructLayout(LayoutKind.Sequential)] + struct BITMAPINFOHEADER + { + #region fields + /// + /// The number of bytes required by the structure. + /// + public uint biSize; + /// + /// The width of the bitmap, in pixels. + /// If Compression is JPEG or PNG, the Width member specifies the width of the decompressed + /// JPEG or PNG image file, respectively. + /// + public int biWidth; + /// + /// The height of the bitmap, in pixels. If Height is positive, + /// the bitmap is a bottom-up DIB and its origin is the lower-left corner. + /// If Height is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. + /// If Height is negative, indicating a top-down DIB, Compression must be either RGB or BITFIELDS. Top-down DIBs cannot be compressed. + /// If Compression is JPEG or PNG, the Height member specifies the height of the decompressed JPEG or PNG image file, respectively. + /// + public int biHeight; + /// + /// The number of planes for the target device. This value must be set to 1. + /// + public ushort biPlanes; + /// + /// The number of bits-per-pixel. The BitCount member + /// determines the number of bits that define each pixel and the maximum number of colors in the bitmap. + /// + public ushort biBitCount; + /// + /// The type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed). + /// + public CompressionType biCompression; + /// + /// The size, in bytes, of the image. This may be set to zero for RGB bitmaps. + /// If Compression is JPEG or PNG, SizeImage indicates the size of the JPEG or PNG image buffer, respectively. + /// + public uint biSizeImage; + /// + /// The horizontal resolution, in pixels-per-meter, of the target device for the bitmap. + /// An application can use this value to select a bitmap from a resource group that + /// best matches the characteristics of the current device. + /// + public int biXPelsPerMeter; + /// + /// The vertical resolution, in pixels-per-meter, of the target device for the bitmap. + /// + public int biYPelsPerMeter; + /// + /// The number of color indexes in the color table that are actually used by the bitmap. + /// If this value is zero, the bitmap uses the maximum number of colors corresponding to + /// the value of the BitCount member for the compression mode specified by Compression. + /// + public uint biClrUsed; + /// + /// The number of color indexes that are required for displaying the bitmap. + /// If this value is zero, all colors are required. + /// + public uint biClrImportant; + #endregion + + #region utilities + + const double METER_INCH_RATIO = 39.3700787; + + /// + /// Gets the horizontal dpi of the bitmap. + /// + /// + public float GetXDpi() + { + return (float)Math.Round(biXPelsPerMeter / METER_INCH_RATIO, 0); + } + /// + /// Gets the vertical dpi of the bitmap. + /// + /// + public float GetYDpi() + { + return (float)Math.Round(biYPelsPerMeter / METER_INCH_RATIO, 0); + } + /// + /// Gets the size of the structure. + /// + /// + public static uint GetByteSize() + { + return (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); + } + /// + /// Checks to see if this structure contain valid data. + /// It also fills in any missing pieces if possible. + /// + /// + public bool Validate() + { + if (biHeight != 0 && biWidth != 0 && biBitCount != 0) + { + if (biSize == 0) + { + biSize = GetByteSize(); + } + if (biClrUsed == 0) + { + switch (biBitCount) + { + case 1: + biClrUsed = 2; + break; + case 4: + biClrUsed = 16; + break; + case 8: + biClrUsed = 256; + break; + } + } + if (biSizeImage == 0) + { + biSizeImage = (uint)(((( + biWidth * biBitCount) + 31) & ~31) >> 3) * (uint)Math.Abs(biHeight); + } + + return true; + } + return false; + } + + /// + /// Gets the pointer to scan0 given the header pointer. + /// + /// The header PTR. + /// + public IntPtr GetScan0(IntPtr headerPtr) + { + int p = (int)biClrUsed; + if ((p == 0) && (biBitCount <= 8)) + { + p = 1 << biBitCount; + } + p = (p * 4) + (int)biSize + headerPtr.ToInt32(); + return new IntPtr(p); + } + + /// + /// Gets whether the bitmap is bottom-up or top-down format. + /// + /// + /// true if this instance is bottom up image; otherwise, false. + /// + /// + public bool IsBottomUpImage + { + get + { + return biHeight > 0; + } + } + + + ///// + ///// Gets the System.Drawing pixel format of current structure. + ///// + ///// + //public PixelFormat GetDrawingPixelFormat() + //{ + // switch (biBitCount) + // { + // case 1: + // return PixelFormat.Format1bppIndexed; + // case 4: + // return PixelFormat.Format4bppIndexed; + // case 8: + // return PixelFormat.Format8bppIndexed; + // case 16: + // return PixelFormat.Format16bppRgb565; + // case 24: + // return PixelFormat.Format24bppRgb; + // case 32: + // return PixelFormat.Format32bppRgb; + // case 48: + // return PixelFormat.Format48bppRgb; + // } + // return PixelFormat.DontCare; + //} + + ///// + ///// Gets the color palette that's contained in the header. + ///// Note not all images will have palette, so check if the return value + ///// is null before using it. + ///// + ///// + //public ColorPalette? GetDrawingPalette(IntPtr headerPtr) + //{ + // //if (format == PixelFormat.Format8bppIndexed) + // //{ + // // // update color palette to grayscale version + // // ColorPalette grayPallet = bitmap.Palette; + // // for (int i = 0; i < grayPallet.Entries.Length; i++) + // // { + // // grayPallet.Entries[i] = Color.FromArgb(i, i, i); + // // } + // // bitmap.Palette = grayPallet; // this is what makes the gray pallet take effect + // //} + + // if (biClrUsed > 0) + // { + // byte[] data = new byte[biClrUsed * 4]; + // Marshal.Copy(new IntPtr(headerPtr.ToInt32() + biSize), data, 0, data.Length); + // var dummy = new System.Drawing.Bitmap(1, 1, GetDrawingPixelFormat()); + // ColorPalette pal = dummy.Palette; + // dummy.Dispose(); + // int index = 0; + // int setCount = data.Length / 4; + // for (int i = 0; i < setCount; i++) + // { + // index = i * 4; + // pal.Entries[i] = Color.FromArgb(data[index + 2], data[index + 1], data[index]); + // } + // return pal; + // } + // return null; + //} + + /// + /// Gets the stride size of this bitmap. + /// + /// + public int GetStride() + { + int bitsPerRow = (biBitCount * biWidth); + int strideTest = bitsPerRow / 8 + (bitsPerRow % 8 != 0 ? 1 : 0); + int overage = strideTest % 4; + if (overage > 0) + { + strideTest += (4 - overage); + } + return strideTest; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return new StringBuilder().Append("BitmapInfoHeader:") + .Append("\r\n\tSize = " + biSize) + .Append("\r\n\tWidth = " + biWidth) + .Append("\r\n\tHeight = " + biHeight) + .Append("\r\n\tPlanes = " + biPlanes) + .Append("\r\n\tBitCount = " + biBitCount) + .Append("\r\n\tCompression = " + biCompression) + .Append("\r\n\tSizeImage = " + biSizeImage) + .Append("\r\n\tXPixelsPerMeter = " + biXPelsPerMeter) + .Append("\r\n\tYPixelsPerMeter = " + biYPelsPerMeter) + .Append("\r\n\tColorUsed = " + biClrUsed) + .Append("\r\n\tColorImportant = " + biClrImportant).ToString(); + } + #endregion + + /// + /// Indicates the bitmap compression of . + /// + public enum CompressionType : uint + { + /// + /// An uncompressed format. + /// + BI_RGB = 0, + /// + /// A run-length encoded (RLE) format for bitmaps with 8 bpp. The compression format is a 2-byte format consisting of a count byte followed by a byte containing a color index. For more information, see Bitmap Compression. + /// + BI_RLE8 = 1, + /// + /// An RLE, format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting of a count byte followed by two word-length color indexes. For more information, see Bitmap Compression. + /// + BI_RLE4 = 2, + /// + /// Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components of each pixel. + /// This is valid when used with 16- and 32-bpp bitmaps. + /// + BI_BITFIELDS = 3, + /// + /// Indicates that the image is a JPEG image. + /// + BI_JPEG = 4, + /// + /// Indicates that the image is a PNG image. + /// + BI_PNG = 5 + } + }; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BITMAPFILEHEADER + { + public ushort bfType; + public uint bfSize; + public ushort bfReserved1; + public ushort bfReserved2; + public uint bfOffBits; + } +} diff --git a/src/NTwain/Native/TIFF.cs b/src/NTwain/Native/TIFF.cs new file mode 100644 index 0000000..5e34a42 --- /dev/null +++ b/src/NTwain/Native/TIFF.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// +// TwainWorkingGroup.TWAIN +// +// This is a wrapper class for basic TWAIN functionality. It establishes +// behavior that every application should adhere to. It also hides OS +// specific details, so that toolkits or applications can use one unified +// interface to TWAIN. +// +/////////////////////////////////////////////////////////////////////////////////////// +// Author Date TWAIN Comment +// M.McLaughlin 17-May-2021 2.5.0.0 Updated to latest spec +// M.McLaughlin 13-Mar-2019 2.4.0.3 Add language code page support for strings +// M.McLaughlin 13-Nov-2015 2.4.0.0 Updated to latest spec +// M.McLaughlin 13-Sep-2015 2.3.1.2 DsmMem bug fixes +// M.McLaughlin 26-Aug-2015 2.3.1.1 Log fix and sync with TWAIN Direct +// M.McLaughlin 13-Mar-2015 2.3.1.0 Numerous fixes +// M.McLaughlin 13-Oct-2014 2.3.0.4 Added logging +// M.McLaughlin 24-Jun-2014 2.3.0.3 Stability fixes +// M.McLaughlin 21-May-2014 2.3.0.2 64-Bit Linux +// M.McLaughlin 27-Feb-2014 2.3.0.1 AnyCPU support +// M.McLaughlin 21-Oct-2013 2.3.0.0 Initial Release +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2013-2021 Kodak Alaris Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Runtime.InteropServices; + +namespace NTwain.Native +{ + /// + /// The TIFF file header. + /// Needed for supporting DAT.IMAGENATIVEXFER... + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TIFFHEADER + { + public ushort u8ByteOrder; + public ushort u16Version; + public uint u32OffsetFirstIFD; + public ushort u16u16IFD; + } + + /// + /// An individual TIFF Tag. + /// Needed for supporting DAT.IMAGENATIVEXFER... + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TIFFTAG + { + public ushort u16Tag; + public ushort u16Type; + public uint u32Count; + public uint u32Value; + } + +} diff --git a/src/NTwain/ThreadMarshaller.Windows.cs b/src/NTwain/ThreadMarshaller.Windows.cs index 3f7b6fa..a15f9c9 100644 --- a/src/NTwain/ThreadMarshaller.Windows.cs +++ b/src/NTwain/ThreadMarshaller.Windows.cs @@ -5,7 +5,11 @@ using System.Windows.Threading; namespace NTwain { - + /// + /// An that can be used + /// to integrate with + /// an existing Winforms app. + /// public class WinformMarshaller : IThreadMarshaller { private readonly Control control; @@ -26,6 +30,11 @@ namespace NTwain } } + /// + /// An that can be used + /// to integrate with + /// an existing WPF app. + /// public class WpfMarshaller : IThreadMarshaller { private readonly Dispatcher dispatcher; diff --git a/src/NTwain/ThreadMarshaller.cs b/src/NTwain/ThreadMarshaller.cs index 13eabde..2a1328b 100644 --- a/src/NTwain/ThreadMarshaller.cs +++ b/src/NTwain/ThreadMarshaller.cs @@ -24,7 +24,7 @@ namespace NTwain } /// - /// No marshalling occurs. Invokes happen right in place. + /// No marshalling occurs. Invokes happen right in place synchronously. /// public class InPlaceMarshaller : IThreadMarshaller { diff --git a/src/NTwain/TransferErrorEventArgs.cs b/src/NTwain/TransferErrorEventArgs.cs new file mode 100644 index 0000000..b1adf3f --- /dev/null +++ b/src/NTwain/TransferErrorEventArgs.cs @@ -0,0 +1,47 @@ +using NTwain.Data; +using System; + +namespace NTwain +{ + /// + /// Contains TWAIN codes and source status when an error is encountered during transfer. + /// + public class TransferErrorEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The error. + public TransferErrorEventArgs(Exception error) + { + Exception = error; + } + + /// + /// Initializes a new instance of the class. + /// + /// The code. + /// Additional status info from TWAIN. + public TransferErrorEventArgs(STS code, string? info) + { + Code = code; + Info = info; + } + + /// + /// Gets the return code or condition code from the transfer. + /// + public STS Code { get; private set; } + + /// + /// Gets the additional status info from TWAIN. + /// + public string? Info { get; private set; } + + /// + /// Gets the exception if the error is from some exception + /// and not from TWAIN. + /// + public Exception? Exception { get; private set; } + } +} diff --git a/src/NTwain/TwainSession.PropEvents.cs b/src/NTwain/TwainSession.PropEvents.cs index bd69cf4..f54f9b1 100644 --- a/src/NTwain/TwainSession.PropEvents.cs +++ b/src/NTwain/TwainSession.PropEvents.cs @@ -135,5 +135,10 @@ namespace NTwain /// Fires when the source has some device event happening. /// public event Action? DeviceEvent; + + /// + /// Fires when there's an error during transfer. + /// + public event EventHandler? TransferError; } } diff --git a/src/NTwain/TwainSession.Windows.cs b/src/NTwain/TwainSession.Windows.cs index d41ed08..0e8bced 100644 --- a/src/NTwain/TwainSession.Windows.cs +++ b/src/NTwain/TwainSession.Windows.cs @@ -36,6 +36,8 @@ namespace NTwain /// /// Registers this session for use in a WPF UI thread. + /// This requires the hwnd used in + /// be a valid WPF window handle. /// public void AddWpfHook() { @@ -57,12 +59,11 @@ namespace NTwain } } - bool System.Windows.Forms.IMessageFilter.PreFilterMessage(ref System.Windows.Forms.Message m) + bool IMessageFilter.PreFilterMessage(ref Message m) { return CheckIfTwainMessage(m.HWnd, m.Msg, m.WParam, m.LParam); } - // wpf use with HwndSource.FromHwnd(Handle).AddHook( IntPtr WpfHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { handled = CheckIfTwainMessage(hwnd, msg, wParam, lParam); diff --git a/src/NTwain/TwainSession.Xfers.cs b/src/NTwain/TwainSession.Xfers.cs index 85a7a37..9548686 100644 --- a/src/NTwain/TwainSession.Xfers.cs +++ b/src/NTwain/TwainSession.Xfers.cs @@ -1,6 +1,8 @@ using NTwain.Data; +using NTwain.Triplets; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -10,5 +12,94 @@ namespace NTwain partial class TwainSession { + /// + /// Start the transfer loop. + /// This should be called after receiving + /// in the background thread. + /// + void EnterTransferRoutine() + { + // default options if source don't support them or whatever + bool xferImage = true; + bool xferAudio = false; + var imgXferMech = TWSX.NATIVE; + var audXferMech = TWSX.NATIVE; + if (DGControl.XferGroup.Get(ref _appIdentity, ref _currentDS, out DG xferType) == STS.SUCCESS) + { + xferAudio = (xferType & DG.AUDIO) == DG.AUDIO; + var dsName = _currentDS.ProductName.ToString(); + // check for Plustek OpticSlim 2680H, this scanner returns wrong xferGroup after first scanning + if (dsName.IndexOf("Plustek", StringComparison.OrdinalIgnoreCase) > -1 && + dsName.IndexOf("OpticSlim", StringComparison.OrdinalIgnoreCase) > -1 && + dsName.IndexOf("2680H", StringComparison.OrdinalIgnoreCase) > -1) + { + xferImage = true; + } + else + { + // some DS end up getting none but we will assume it's image + xferImage = xferType == 0 || (xferType & DG.IMAGE) == DG.IMAGE; + } + } + + //if (xferImage) + //{ + // imgXferMech = session.CurrentSource.Capabilities.ICapXferMech.GetCurrent(); + //} + //if (xferAudio) + //{ + // audXferMech = session.CurrentSource.Capabilities.ACapXferMech.GetCurrent(); + //} + + TW_PENDINGXFERS pending = default; + var rc = DGControl.PendingXfers.Get(ref _appIdentity, ref _currentDS, ref pending); + if (rc == STS.SUCCESS) + { + do + { + // cancel for now + //rc = DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending); + + + rc = DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending); + + //if (xferType.HasFlag(DG.AUDIO)) + //{ + // DoTransferAudio(); + //} + //else // just defaults to image + //{ + // DoTransferImage(); + //} + + } while (rc == STS.SUCCESS && pending.Count != 0); + } + else + { + HandleNonSuccessXferCode(rc); + } + _uiThreadMarshaller.BeginInvoke(() => + { + DisableSource(); + }); + } + + private void HandleNonSuccessXferCode(STS rc) + { + switch (rc) + { + case STS.SUCCESS: + case STS.XFERDONE: + case STS.CANCEL: + // ok to keep going + break; + default: + var status = GetLastStatus(false); + var text = GetStatusText(status); + // TODO: raise error event + + break; + } + } } } diff --git a/src/NTwain/TwainSession.cs b/src/NTwain/TwainSession.cs index 133c065..2759fb6 100644 --- a/src/NTwain/TwainSession.cs +++ b/src/NTwain/TwainSession.cs @@ -97,8 +97,8 @@ namespace NTwain } internal IntPtr _hwnd; - internal TW_USERINTERFACE _userInterface; // kept around for disable use - TW_EVENT _procEvent = default; // kept here so the alloc/free only happens once + internal TW_USERINTERFACE _userInterface; // kept around for disable to use + TW_EVENT _procEvent; // kept here so the alloc/free only happens once // test threads a bit readonly BlockingCollection _bgPendingMsgs = new(); private readonly IThreadMarshaller _uiThreadMarshaller; @@ -138,6 +138,11 @@ namespace NTwain } break; case MSG.XFERREADY: + _uiThreadMarshaller.Invoke(() => + { + State = STATE.S6; + }); + EnterTransferRoutine(); break; }