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;
}