I am writing an application that requires scanning in .net ( c# version 4.0 , Visual Studio 2010). I am using the TWAIN API to perform a scan, and I am having a problem with the layout function. The following code works fine on the Microtek i800 , a CanoScan 9000F and a Microtek Artix Scan F2 , but when I run it with the Epson Perfection V700 something is really strange.
Even if I set the left margin of the layout to 0 , the left edge of the image is cropped. I tried to set it to negative values, but that didn't make any difference. There seems to be some kind of weirdness, and that makes it be the size of a movie (maybe because I turn on the light). If I use the tool that came with the scanner, it allows me to select an area that turns on both edges (and turn on the light) so that it is possible. In addition, the top and bottom coordinates work fine.
So my question is ...
Does anyone know how I can make it scan the entire width? Are there other settings in TWAIN that I can set to make it forget about paper sizes? (I also tried setting PaperDetectable to false, but that didn't matter).
One more thing: if I do not set the layout, it still turns off the image at the edges (only not from the top and bottom), but if I also do not turn on the light (or I do not set the light on but I set the size), it does what I expect: specifically scans the whole picture from the very left edge (but the problem is that I really need light and the entire width of the scan - of course, this is not much to ask ...).
Here is the code (this is the code for applying a form form from 1 form with one button on it):
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; namespace TwainLayoutWindowsFormsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { TwainIdentity appid = null; TwainIdentity scanner = null; bool enabled = false; try { appid = InitializeTwain(Handle); scanner = GetSource(appid, "EPSON Perfection V700/V750"); Open(appid, scanner); SetLightOn(appid, scanner); SetLayout(appid, scanner); Enable(appid, scanner, Handle); enabled = true; var bmps = Scan(appid, scanner); Disable(appid, scanner); enabled = false; bmps.First().Save(@"c:\users\public\scan.bmp", ImageFormat.Bmp); } catch(Exception ex) { MessageBox.Show(ex.Message); } finally { if (appid != null && scanner != null) { if (enabled) { try { Disable(appid, scanner); } catch(InvalidOperationException) { } } Close(appid, scanner); } } } private static void SetLayout(TwainIdentity appid, TwainIdentity scanner) { TwainImageLayout layout = new TwainImageLayout(); var rc = NativeMethods.DSilayout(appid, scanner, TwainDataGroups.Image, TwainDataArgumentType.ImageLayout, TwainMessage.Get, layout);
For completeness, here is the code I use to convert HBITMAP to System.Drawing.Bitmap :
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Globalization; namespace TwainLayoutWindowsFormsApplication { internal static class TwainBitmapConvertor { [StructLayout(LayoutKind.Sequential, Pack = 2)] private class BitmapInfoHeader { public int Size; public int Width; public int Height; public short Planes; public short BitCount; public int Compression; public int SizeImage; public int XPelsPerMeter; public int YPelsPerMeter; public int ClrUsed; public int ClrImportant; } internal static Bitmap ToBitmap(IntPtr dibHandle) { var bitmapPointer = NativeMethods.GlobalLock(dibHandle); var bitmapInfo = new BitmapInfoHeader(); Marshal.PtrToStructure(bitmapPointer, bitmapInfo); var rectangle = new Rectangle(); rectangle.X = rectangle.Y = 0; rectangle.Width = bitmapInfo.Width; rectangle.Height = bitmapInfo.Height; if (bitmapInfo.SizeImage == 0) { bitmapInfo.SizeImage = ((((bitmapInfo.Width * bitmapInfo.BitCount) + 31) & ~31) >> 3) * bitmapInfo.Height; }
and here p / calls:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace TwainLayoutWindowsFormsApplication { internal static class NativeMethods { [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GlobalAlloc(int flags, int size); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GlobalLock(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern bool GlobalUnlock(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GlobalFree(IntPtr handle); [DllImport("gdi32.dll", ExactSpelling = true)] public static extern int SetDIBitsToDevice(IntPtr hdc, int xdst, int ydst, int width, int height, int xsrc, int ysrc, int start, int lines, IntPtr bitsptr, IntPtr bmiptr, int color); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMparent([In, Out] TwainIdentity origin, IntPtr zeroptr, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, ref IntPtr refptr); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMident([In, Out] TwainIdentity origin, IntPtr zeroptr, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainIdentity idds); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMentry([In, Out] TwainIdentity origin, IntPtr zeroptr, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainIdentity idds); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMstatus([In, Out] TwainIdentity origin, IntPtr zeroptr, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainStatus dsmstat); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSuserif([In, Out] TwainIdentity origin, [In, Out] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, TwainUserInterface guif); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSevent([In, Out] TwainIdentity origin, [In, Out] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, ref TwainEvent evt); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSstatus([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainStatus dsmstat); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DScap([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainCapability capa); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSiinf([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainImageInfo imginf); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSixfer([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, ref IntPtr hbitmap); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMemixfer([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainImageMemXfer memxfr); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSpxfer([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainPendingXfers pxfr); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSilayout([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainImageLayout layout); [DllImport("twain_32.dll", EntryPoint = "#1")] internal static extern TwainReturnCode DSMEntry([In, Out] TwainIdentity origin, [In] TwainIdentity dest, TwainDataGroups dg, TwainDataArgumentType dat, TwainMessage msg, [In, Out] TwainSetupFileXfer fileXf); } }
and finally, the rest of the material (structure and much more) that should work:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace TwainLayoutWindowsFormsApplication { [StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Ansi)] internal class TwainIdentity { public IntPtr Id; public TwainVersion Version; public short ProtocolMajor; public short ProtocolMinor; public int SupportedGroups; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)] public string Manufacturer; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)] public string ProductFamily; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)] public string ProductName; } internal enum TwainReturnCode : short { Success = 0x0000, Failure = 0x0001, CheckStatus = 0x0002, Cancel = 0x0003, DSEvent = 0x0004, NotDSEvent = 0x0005, XferDone = 0x0006, EndOfList = 0x0007, InfoNotSupported = 0x0008, DataNotAvailable = 0x0009 } [StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Ansi)] internal struct TwainVersion { public short MajorNum; public short MinorNum; public short Language; public short Country; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)] public string Info; } [Flags] internal enum TwainDataGroups : short { Control = 0x0001, Image = 0x0002, Audio = 0x0004 } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal struct TwainEvent { public IntPtr EventPtr; public short Message; } internal enum TwainDataArgumentType : short { Null = 0x0000, Capability = 0x0001, Event = 0x0002, Identity = 0x0003, Parent = 0x0004, PendingXfers = 0x0005, SetupMemXfer = 0x0006, SetupFileXfer = 0x0007, Status = 0x0008, UserInterface = 0x0009, XferGroup = 0x000a, TwunkIdentity = 0x000b, CustomDSData = 0x000c, DeviceEvent = 0x000d, FileSystem = 0x000e, PassThru = 0x000f, ImageInfo = 0x0101, ImageLayout = 0x0102, ImageMemXfer = 0x0103, ImageNativeXfer = 0x0104, ImageFileXfer = 0x0105, CieColor = 0x0106, GrayResponse = 0x0107, RGBResponse = 0x0108, JpegCompression = 0x0109, Palette8 = 0x010a, ExtImageInfo = 0x010b, SetupFileXfer2 = 0x0301 } internal enum TwainMessage : short { Null = 0x0000, Get = 0x0001, GetCurrent = 0x0002, GetDefault = 0x0003, GetFirst = 0x0004, GetNext = 0x0005, Set = 0x0006, Reset = 0x0007, QuerySupport = 0x0008, XFerReady = 0x0101, CloseDSReq = 0x0102, CloseDSOK = 0x0103, DeviceEvent = 0x0104, CheckStatus = 0x0201, OpenDSM = 0x0301, CloseDSM = 0x0302, OpenDS = 0x0401, CloseDS = 0x0402, UserSelect = 0x0403, DisableDS = 0x0501, EnableDS = 0x0502, EnableDSUIOnly = 0x0503, ProcessEvent = 0x0601, EndXfer = 0x0701, StopFeeder = 0x0702, ChangeDirectory = 0x0801, CreateDirectory = 0x0802, Delete = 0x0803, FormatMedia = 0x0804, GetClose = 0x0805, GetFirstFile = 0x0806, GetInfo = 0x0807, GetNextFile = 0x0808, Rename = 0x0809, Copy = 0x080A, AutoCaptureDir = 0x080B, PassThru = 0x0901 } [StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct TwainWindowMessage { public IntPtr hwnd; public int message; public IntPtr wParam; public IntPtr lParam; public int time; public int x; public int y; } internal enum TwainOn : short { Array = 0x0003, Enum = 0x0004, One = 0x0005, Range = 0x0006, DontCare = -1 } internal enum TwainCapabilityType : short { XferCount = 0x0001, ICompression = 0x0100, IPixelType = 0x0101, IUnits = 0x0102, IXferMech = 0x0103, Author = 0x1000, Caption = 0x1001, FeederEnabled = 0x1002, FeederLoaded = 0x1003, Timedate = 0x1004, SupportedCapabilities = 0x1005, Extendedcaps = 0x1006, AutoFeed = 0x1007, ClearPage = 0x1008, FeedPage = 0x1009, RewindPage = 0x100a, Indicators = 0x100b, SupportedCapsExt = 0x100c, PaperDetectable = 0x100d, UIControllable = 0x100e, DeviceOnline = 0x100f, AutoScan = 0x1010, ThumbnailsEnabled = 0x1011, Duplex = 0x1012, DuplexEnabled = 0x1013, Enabledsuionly = 0x1014, CustomdsData = 0x1015, Endorser = 0x1016, JobControl = 0x1017, Alarms = 0x1018, AlarmVolume = 0x1019, AutomaticCapture = 0x101a, TimeBeforeFirstCapture = 0x101b, TimeBetweenCaptures = 0x101c, ClearBuffers = 0x101d, MaxBatchBuffers = 0x101e, DeviceTimeDate = 0x101f, PowerSupply = 0x1020, CameraPreviewUI = 0x1021, DeviceEvent = 0x1022, SerialNumber = 0x1024, Printer = 0x1026, PrinterEnabled = 0x1027, PrinterIndex = 0x1028, PrinterMode = 0x1029, PrinterString = 0x102a, PrinterSuffix = 0x102b, Language = 0x102c, FeederAlignment = 0x102d, FeederOrder = 0x102e, ReAcquireAllowed = 0x1030, BatteryMinutes = 0x1032, BatteryPercentage = 0x1033, CameraSide = 0x1034, Segmented = 0x1035, CameraEnabled = 0x1036, CameraOrder = 0x1037, MicrEnabled = 0x1038, FeederPrep = 0x1039, Feederpocket = 0x103a, Autobright = 0x1100, Brightness = 0x1101, Contrast = 0x1103, CustHalftone = 0x1104, ExposureTime = 0x1105, Filter = 0x1106, Flashused = 0x1107, Gamma = 0x1108, Halftones = 0x1109, Highlight = 0x110a, ImageFileFormat = 0x110c, LampState = 0x110d, LightSource = 0x110e, Orientation = 0x1110, PhysicalWidth = 0x1111, PhysicalHeight = 0x1112, Shadow = 0x1113, Frames = 0x1114, XNativeResolution = 0x1116, YNativeResolution = 0x1117, XResolution = 0x1118, YResolution = 0x1119, MaxFrames = 0x111a, Tiles = 0x111b, Bitorder = 0x111c, Ccittkfactor = 0x111d, Lightpath = 0x111e, Pixelflavor = 0x111f, Planarchunky = 0x1120, Rotation = 0x1121, Supportedsizes = 0x1122, Threshold = 0x1123, Xscaling = 0x1124, Yscaling = 0x1125, Bitordercodes = 0x1126, Pixelflavorcodes = 0x1127, Jpegpixeltype = 0x1128, Timefill = 0x112a, BitDepth = 0x112b, Bitdepthreduction = 0x112c, Undefinedimagesize = 0x112d, Imagedataset = 0x112e, Extimageinfo = 0x112f, Minimumheight = 0x1130, Minimumwidth = 0x1131, Fliprotation = 0x1136, Barcodedetectionenabled = 0x1137, Supportedbarcodetypes = 0x1138, Barcodemaxsearchpriorities = 0x1139, Barcodesearchpriorities = 0x113a, Barcodesearchmode = 0x113b, Barcodemaxretries = 0x113c, Barcodetimeout = 0x113d, Zoomfactor = 0x113e, Patchcodedetectionenabled = 0x113f, Supportedpatchcodetypes = 0x1140, Patchcodemaxsearchpriorities = 0x1141, Patchcodesearchpriorities = 0x1142, Patchcodesearchmode = 0x1143, Patchcodemaxretries = 0x1144, Patchcodetimeout = 0x1145, Flashused2 = 0x1146, Imagefilter = 0x1147, Noisefilter = 0x1148, Overscan = 0x1149, Automaticborderdetection = 0x1150, Automaticdeskew = 0x1151, Automaticrotate = 0x1152, Jpegquality = 0x1153, Feedertype = 0x1154, Iccprofile = 0x1155, Autosize = 0x1156, AutomaticCropUsesFrame = 0x1157, AutomaticLengthDetection = 0x1158, AutomaticColorEnabled = 0x1159, AutomaticColorNonColorPixelType = 0x115a, ColorManagementEnabled = 0x115b, ImageMerge = 0x115c, ImageMergeHeightThreshold = 0x115d, SupoortedExtImageInfo = 0x115e, Audiofileformat = 0x1201, Xfermech = 0x1202 } internal enum TwainType : short { Int8 = 0x0000, Int16 = 0x0001, Int32 = 0x0002, UInt8 = 0x0003, UInt16 = 0x0004, UInt32 = 0x0005, Bool = 0x0006, Fix32 = 0x0007, Frame = 0x0008, Str32 = 0x0009, Str64 = 0x000a, Str128 = 0x000b, Str255 = 0x000c, Str1024 = 0x000d, Str512 = 0x000e } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainStatus { public short ConditionCode; public short Reserved; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainUserInterface { public short ShowUI; public short ModalUI; public IntPtr ParentHand; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainImageInfo { public int XResolution; public int YResolution; public int ImageWidth; public int ImageLength; public short SamplesPerPixel; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public short[] BitsPerSample; public short BitsPerPixel; public short Planar; public short PixelType; public short Compression; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwMemory { public uint Flags; public uint Length; IntPtr TheMem; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainImageMemXfer { public ushort Compression; public uint BytesPerRow; public uint Columns; public uint Rows; public uint XOffset; public uint YOffset; public uint BytesWritten; [MarshalAs(UnmanagedType.Struct)] TwMemory Memory; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal struct TwainFix32 { public short Whole; public ushort Frac; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainFrame { public TwainFix32 Left; public TwainFix32 Top; public TwainFix32 Right; public TwainFix32 Bottom; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainImageLayout { public TwainFrame Frame; public int DocumentNumber; public int PageNumber; public int FrameNumber; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainPendingXfers { public short Count; public int EOJ; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainSetupFileXfer { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)] public string FileName; public ushort Format; public short VRefNum; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class TwainCapability : IDisposable { internal TwainCapability(TwainCapabilityType cap) { Cap = (short)cap; ConType = -1; } internal TwainCapability(TwainCapabilityType cap, short sval) { Cap = (short)cap; ConType = (short)TwainOn.One; Handle = NativeMethods.GlobalAlloc(0x42, 6); IntPtr pv = NativeMethods.GlobalLock(Handle); Marshal.WriteInt16(pv, 0, (short)TwainType.Int16); Marshal.WriteInt32(pv, 2, (int)sval); NativeMethods.GlobalUnlock(Handle); } ~TwainCapability() { Dispose(false); } public short Cap; public short ConType; public IntPtr Handle; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (Handle != IntPtr.Zero) NativeMethods.GlobalFree(Handle); } } }