/// <summary> /// Instantiate TWAIN and open the DSM... /// </summary> /// <param name="a_intptrHwnd">Parent window (needed for Windows)</param> /// <param name="a_writeoutputdelegate">Optional text output callback</param> /// <param name="a_reportimagedelegate">Optional report image callback</param> /// <param name="m_setmessagefilterdelegate">Optional message filter callback</param> /// <param name="a_szManufacturer">Application manufacturer</param> /// <param name="a_szProductFamily">Application family</param> /// <param name="a_szProductName">Name of the application</param> /// <param name="a_u16ProtocolMajor">TWAIN protocol major (doesn't have to match TWAINH.CS)</param> /// <param name="a_u16ProtocolMinor">TWAIN protocol minor (doesn't have to match TWAINH.CS)</param> /// <param name="a_aszSupportedGroups">Bitmask of DG_ flags</param> /// <param name="a_szTwcy">Application's country code</param> /// <param name="a_szInfo">Info about the application</param> /// <param name="a_szTwlg">Application's language</param> /// <param name="a_u16MajorNum">Application's major version</param> /// <param name="a_u16MinorNum">Application's minor version</param> /// <param name="a_blUseLegacyDSM">The the legacy DSM (like TWAIN_32.DLL)</param> /// <param name="a_blUseCallbacks">Use callbacks (preferred)</param> public TWAINCSToolkit( IntPtr a_intptrHwnd, WriteOutputDelegate a_writeoutputdelegate, ReportImageDelegate a_reportimagedelegate, SetMessageFilterDelegate m_setmessagefilterdelegate, string a_szManufacturer, string a_szProductFamily, string a_szProductName, ushort a_u16ProtocolMajor, ushort a_u16ProtocolMinor, string[] a_aszSupportedGroups, string a_szTwcy, string a_szInfo, string a_szTwlg, ushort a_u16MajorNum, ushort a_u16MinorNum, bool a_blUseLegacyDSM, bool a_blUseCallbacks ) { TWAIN.STS sts; uint u32SupportedGroups; // Init stuff... m_intptrHwnd = a_intptrHwnd; if (a_writeoutputdelegate == null) { WriteOutput = WriteOutputStub; } else { WriteOutput = a_writeoutputdelegate; } ReportImage = a_reportimagedelegate; SetMessageFilter = m_setmessagefilterdelegate; m_szImagePath = null; m_iImageCount = 0; // Convert the supported groups from strings to flags... u32SupportedGroups = 0; foreach (string szSupportedGroup in a_aszSupportedGroups) { TWAIN.DG dg = (TWAIN.DG)Enum.Parse(typeof(TWAIN.DG), szSupportedGroup.Remove(0, 3)); if (Enum.IsDefined(typeof(TWAIN.DG), dg)) { u32SupportedGroups |= (uint)dg; } } // Instantiate TWAIN, and register ourselves... m_twain = new TWAIN ( a_szManufacturer, a_szProductFamily, a_szProductName, a_u16ProtocolMajor, a_u16ProtocolMinor, u32SupportedGroups, (TWAIN.TWCY)Enum.Parse(typeof(TWAIN.TWCY), a_szTwcy), a_szInfo, (TWAIN.TWLG)Enum.Parse(typeof(TWAIN.TWLG), a_szTwlg), a_u16MajorNum, a_u16MinorNum, a_blUseLegacyDSM, a_blUseCallbacks, DeviceEventCallback, ScanCallback ); // Store some values... m_blUseCallbacks = a_blUseCallbacks; // Our default transfer mechanism... m_twsxXferMech = TWAIN.TWSX.NATIVE; // Our default file transfer info... m_twsetupfilexfer = default(TWAIN.TW_SETUPFILEXFER); m_twsetupfilexfer.Format = TWAIN.TWFF.TIFF; if (TWAIN.GetPlatform() == TWAIN.Platform.WINDOWS) { m_twsetupfilexfer.FileName.Set(Path.GetTempPath() + "img"); } else if (TWAIN.GetPlatform() == TWAIN.Platform.LINUX) { m_twsetupfilexfer.FileName.Set(Path.GetTempPath() + "img"); } else if (TWAIN.GetPlatform() == TWAIN.Platform.MACOSX) { m_twsetupfilexfer.FileName.Set("/var/tmp/img"); } else { Log.Msg(Log.Severity.Throw, "Unsupported platform..." + TWAIN.GetPlatform()); } // We've not been in the scan callback yet... m_blScanStart = true; // Open the DSM... try { sts = m_twain.DatParent(TWAIN.DG.CONTROL, TWAIN.MSG.OPENDSM, ref m_intptrHwnd); } catch (Exception exception) { Log.Msg(Log.Severity.Error, "OpenDSM exception: " + exception.Message); sts = TWAIN.STS.FAILURE; } if (sts != TWAIN.STS.SUCCESS) { Log.Msg(Log.Severity.Error, "OpenDSM failed..."); Cleanup(); throw new Exception("OpenDSM failed..."); } }
/// <summary> /// Our scanning callback function. We appeal directly to the supporting /// TWAIN object. This way we don't have to maintain some kind of a loop /// inside of the application, which is the source of most problems that /// developers run into. /// /// While it looks scary at first, there's really not a lot going on in /// here. We do some sanity checks, we watch for certain kinds of events, /// we support the four methods of transferring images, and we dump out /// some meta-data about the transferred image. However, because it does /// look scary I dropped in some region pragmas to break things up... /// </summary> /// <param name="a_blClosing">We're shutting down</param> /// <returns>TWAIN status</returns> private TWAIN.STS ScanCallback(bool a_blClosing) { bool blXferDone; TWAINCSToolkit.MSG msgPendingxfers = TWAINCSToolkit.MSG.ENDXFER; TWAIN.STS sts; string szFilename = ""; MemoryStream memorystream; TWAIN.TW_IMAGEINFO twimageinfo = default(TWAIN.TW_IMAGEINFO); // Validate... if (m_twain == null) { Log.Msg(Log.Severity.Error, "m_twain is null..."); if (ReportImage != null) ReportImage("ScanCallback: 001", "", "", "", CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.FAILURE); } // We're leaving... if (a_blClosing) { if (ReportImage != null) ReportImage("ScanCallback: 002", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.IDENTITY.ToString(), TWAIN.MSG.CLOSEDS.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Somebody pushed the Cancel or the OK button... if (m_twain.IsMsgCloseDsReq()) { m_twain.Rollback(TWAIN.STATE.S4); ReportImage("ScanCallback: 003", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.NULL.ToString(), TWAIN.MSG.CLOSEDSREQ.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else if (m_twain.IsMsgCloseDsOk()) { m_twain.Rollback(TWAIN.STATE.S4); ReportImage("ScanCallback: 004", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.NULL.ToString(), TWAIN.MSG.CLOSEDSOK.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Init ourselves... if (m_blScanStart) { TWAIN.TW_CAPABILITY twcapability; // Don't come in here again until the start of the next scan batch... m_blScanStart = false; // Make a note of it... WriteOutput(Environment.NewLine + "Entered state 5..." + Environment.NewLine); // Get the current setting for the image transfer... twcapability = default(TWAIN.TW_CAPABILITY); twcapability.Cap = TWAIN.CAP.ICAP_XFERMECH; sts = m_twain.DatCapability(TWAIN.DG.CONTROL, TWAIN.MSG.GETCURRENT, ref twcapability); if (sts == TWAIN.STS.SUCCESS) { try { string[] asz = m_twain.CapabilityToCsv(twcapability).Split(new char[] { ',' }); m_twain.DsmMemFree(ref twcapability.hContainer); m_twsxXferMech = (TWAIN.TWSX)ushort.Parse(asz[3]); } catch { m_twsxXferMech = TWAIN.TWSX.NATIVE; } } else { m_twsxXferMech = TWAIN.TWSX.NATIVE; } } // We're waiting for that first image to show up, if we don't // see it, then return... if (!m_twain.IsMsgXferReady()) { // If we're on Windows we need to send event requests to the driver... if (TWAIN.GetPlatform() == TWAIN.Platform.WINDOWS) { TWAIN.TW_EVENT twevent = default(TWAIN.TW_EVENT); twevent.pEvent = Marshal.AllocHGlobal(256); // over allocate for MSG structure if (twevent.pEvent != IntPtr.Zero) { m_twain.DatEvent(TWAIN.DG.CONTROL, TWAIN.MSG.PROCESSEVENT, ref twevent); Marshal.FreeHGlobal(twevent.pEvent); } } // Scoot... return (TWAIN.STS.SUCCESS); } // Transfer the image, at this point we're showing it on the form. The // application should queue this to some other thread or process as fast // as possible, so that we can get to the next image. // // This is the point where TWAIN tells us about things like jams and // multifeeds. // Init some more stuff... blXferDone = false; /////////////////////////////////////////////////////////////////////////////// // // Beginning of the image transfer section... // // A native transfer gives us a handle to a DIB, which is not // quite what we need to fill in a Bitmap in C#, so there's // some work going on under the hood that requires both processing // power and memory that may not make this the best choice. // However, all drivers support native, and the format is reasonably // easy to process... #region TWAIN.TWSX.NATIVE if (m_twsxXferMech == TWAIN.TWSX.NATIVE) { Bitmap bitmap = null; sts = m_twain.DatImagenativexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref bitmap); if (sts != TWAIN.STS.XFERDONE) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 005", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGENATIVEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else { msgPendingxfers = ReportImage("ScanCallback: 006", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGENATIVEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, null, 0); bitmap = null; blXferDone = true; } } #endregion // File transfers are not supported by all TWAIN drivers (it's an // optional transfer format in the TWAIN Specification)... #region TWAIN.TWSX.FILE else if (m_twsxXferMech == TWAIN.TWSX.FILE) { Bitmap bitmap; TWAIN.TW_SETUPFILEXFER twsetupfilexfer = default(TWAIN.TW_SETUPFILEXFER); // Get a copy of the current setup file transfer info... twsetupfilexfer = m_twsetupfilexfer; // ***WARNING*** // Override the current setting with one that supports the pixeltype and // compression of the current image. The choices are JPEG or TIFF. Note // that this only works for drivers that report final value in state 6... if (m_blAutomaticJpegOrTiff) { // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 007", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Assume TIFF, unless we detect JPEG (JFIF)... twsetupfilexfer.Format = TWAIN.TWFF.TIFF; if (twimageinfo.Compression == (ushort)TWAIN.TWCP.JPEG) { twsetupfilexfer.Format = TWAIN.TWFF.JFIF; } } // If specified, m_szImagePath wins over DAT_SETUPFILEXFER... string szFile = m_twsetupfilexfer.FileName.Get(); if ((m_szImagePath != null) && (m_szImagePath != "")) { szFile = m_szImagePath; } // Build the base... szFile = System.IO.Path.Combine(szFile, m_iImageXferCount.ToString("D6")); // Add the image transfer count and the extension... switch (twsetupfilexfer.Format) { default: twsetupfilexfer.FileName.Set(szFile + ".xxx"); break; case TWAIN.TWFF.BMP: twsetupfilexfer.FileName.Set(szFile + ".bmp"); break; case TWAIN.TWFF.DEJAVU: twsetupfilexfer.FileName.Set(szFile + ".dejavu"); break; case TWAIN.TWFF.EXIF: twsetupfilexfer.FileName.Set(szFile + ".exif"); break; case TWAIN.TWFF.FPX: twsetupfilexfer.FileName.Set(szFile + ".fpx"); break; case TWAIN.TWFF.JFIF: twsetupfilexfer.FileName.Set(szFile + ".jpg"); break; case TWAIN.TWFF.JP2: twsetupfilexfer.FileName.Set(szFile + ".jp2"); break; case TWAIN.TWFF.JPX: twsetupfilexfer.FileName.Set(szFile + ".jpx"); break; case TWAIN.TWFF.PDF: twsetupfilexfer.FileName.Set(szFile + ".pdf"); break; case TWAIN.TWFF.PDFA: twsetupfilexfer.FileName.Set(szFile + ".pdf"); break; case TWAIN.TWFF.PICT: twsetupfilexfer.FileName.Set(szFile + ".pict"); break; case TWAIN.TWFF.PNG: twsetupfilexfer.FileName.Set(szFile + ".png"); break; case TWAIN.TWFF.SPIFF: twsetupfilexfer.FileName.Set(szFile + ".spiff"); break; case TWAIN.TWFF.TIFF: twsetupfilexfer.FileName.Set(szFile + ".tif"); break; case TWAIN.TWFF.TIFFMULTI: twsetupfilexfer.FileName.Set(szFile + ".tif"); break; case TWAIN.TWFF.XBM: twsetupfilexfer.FileName.Set(szFile + ".xbm"); break; } // Setup the file transfer... sts = m_twain.DatSetupfilexfer(TWAIN.DG.CONTROL, TWAIN.MSG.SET, ref twsetupfilexfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 008", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.SETUPFILEXFER.ToString(), TWAIN.MSG.SET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Transfer the image... sts = m_twain.DatImagefilexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET); if (sts != TWAIN.STS.XFERDONE) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 009", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else { try { szFilename = twsetupfilexfer.FileName.Get(); Image image = Image.FromFile(szFilename); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 010", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFilename, m_twain.ImageinfoToCsv(twimageinfo), null, 0); bitmap = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 011", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, szFilename, m_twain.ImageinfoToCsv(twimageinfo), null, 0); return (TWAIN.STS.SUCCESS); } } } #endregion // Memory transfers are supported by all TWAIN drivers, and offer // the fastest and most efficient way of moving image data from // the scanner into the application. However, the data format may // be nothing more than a stream of compressed raster data, which // is impossible to interpret until the meta-data is collected // using DAT_IMAGEINFO or DAT_EXTIMAGEINFO. And under TWAIN the // meta-data cannot be collected until after the image data is // fully transferred, so under C# we have some challenges... // // And because there's a lot going on in here I've tossed in some // more region pragmas to break it up... #region TWAIN.TWSX.MEMORY else if (m_twsxXferMech == TWAIN.TWSX.MEMORY) { Bitmap bitmap; byte[] abImage = null; IntPtr intptrTotalAllocated; IntPtr intptrTotalXfer; IntPtr intptrOffset; TWAIN.TW_SETUPMEMXFER twsetupmemxfer = default(TWAIN.TW_SETUPMEMXFER); TWAIN.TW_IMAGEMEMXFER twimagememxfer = default(TWAIN.TW_IMAGEMEMXFER); TWAIN.TW_MEMORY twmemory = default(TWAIN.TW_MEMORY); const int iSpaceForHeader = 512; // Get the preferred transfer size from the driver... sts = m_twain.DatSetupmemxfer(TWAIN.DG.CONTROL, TWAIN.MSG.GET, ref twsetupmemxfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 012", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.SETUPMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate our unmanaged memory... twmemory.Flags = (uint)TWAIN.TWMF.APPOWNS | (uint)TWAIN.TWMF.POINTER; twmemory.Length = twsetupmemxfer.Preferred; twmemory.TheMem = Marshal.AllocHGlobal((int)twsetupmemxfer.Preferred); if (twmemory.TheMem == IntPtr.Zero) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 013", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Loop through all the strips of data, at the end of this the byte // array abImage has all of the data that we got from the scanner... #region Tranfer the image from the driver... intptrTotalAllocated = (IntPtr)iSpaceForHeader; intptrOffset = (IntPtr)iSpaceForHeader; intptrTotalXfer = IntPtr.Zero; sts = TWAIN.STS.SUCCESS; while (sts == TWAIN.STS.SUCCESS) { byte[] abTmp; // Append the new data to the end of the data we've transferred so far... intptrOffset = (IntPtr)((int)iSpaceForHeader + (int)intptrTotalXfer); // Get a strip of image data... twimagememxfer.Memory.Flags = twmemory.Flags; twimagememxfer.Memory.Length = twmemory.Length; twimagememxfer.Memory.TheMem = twmemory.TheMem; sts = m_twain.DatImagememxfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimagememxfer); if (sts == TWAIN.STS.XFERDONE) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else if (sts == TWAIN.STS.SUCCESS) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 014", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate memory for our new strip... intptrTotalAllocated = (IntPtr)((UInt64)intptrTotalAllocated + (UInt64)twimagememxfer.BytesWritten); abTmp = new byte[(int)intptrTotalAllocated]; if (abTmp == null) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 015", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Copy the existing data, if we have it... if (abImage != null) { Buffer.BlockCopy(abImage, 0, abTmp, 0, (int)intptrTotalAllocated - (int)twimagememxfer.BytesWritten); } // Switch pointers... abImage = abTmp; abTmp = null; // Copy the new strip into place... Marshal.Copy(twimagememxfer.Memory.TheMem, abImage, (int)intptrOffset, (int)twimagememxfer.BytesWritten); } #endregion // Increment our counter... m_iImageCount += 1; // Do this if the data is JPEG compressed... #region JPEG Images (grayscale and color)... if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.JPEG) { string szFile = ""; try { // Write the data to disk... if ((m_szImagePath != null) && Directory.Exists(m_szImagePath)) { // Save the image... szFile = Path.Combine(m_szImagePath, "img" + m_iImageCount.ToString("D6") + ".jpg"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write(abImage, iSpaceForHeader, abImage.Length - iSpaceForHeader); } // Show the image from disk... Image image = Image.FromFile(szFile); Bitmap bitmapFile = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 016", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.XFERDONE), bitmapFile, szFile, null, abImage, iSpaceForHeader); blXferDone = true; } // Display the image from memory... else { // Turn the data into a bitmap... memorystream = new MemoryStream(abImage, iSpaceForHeader, abImage.Length - iSpaceForHeader); Image image = Image.FromStream(memorystream); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 017", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, abImage, iSpaceForHeader); bitmap = null; memorystream = null; blXferDone = true; } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 018", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Do this if the data is GROUP4 compressed, the way to add support for // this is to create a bitonal TIFF header, place it in a memory stream // along with the data, and then let .NET decode it... #region Group4 Images (black and white images)... else if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.GROUP4) { IntPtr intptrTiff; TiffBitonalG4 tiffbitonalg4; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 019", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffbitonalg4 = new TiffBitonalG4((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffbitonalg4)); // Copy the header into the memory... Marshal.StructureToPtr(tiffbitonalg4, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4), Marshal.SizeOf(tiffbitonalg4)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 020", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from disk (to reduce our memory footprint)... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send it off to the application... msgPendingxfers = ReportImage("ScanCallback: 021", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 022", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 023", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // We have no compression, we need to handle the data one raster or // one pixel at a time to make sure we get the alignment right, and that // means doing different stuff for black and white vs gray vs color... #region Uncompressed images (all pixel types)... else if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.NONE) { // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 024", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Handle uncompressed bitonal images... #region Handle uncompressed bitonal images... if (twimageinfo.BitsPerPixel == 1) { try { IntPtr intptrTiff; TiffBitonalUncompressed tiffbitonaluncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 025", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffbitonaluncompressed = new TiffBitonalUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffbitonaluncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffbitonaluncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed), Marshal.SizeOf(tiffbitonaluncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 026", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 027", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 028", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 029", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 030", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Handle uncompressed color images... #region Handle uncompressed color images... else if (twimageinfo.BitsPerPixel == 24) { try { IntPtr intptrTiff; TiffColorUncompressed tiffcoloruncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 031", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffcoloruncompressed = new TiffColorUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffcoloruncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffcoloruncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed), Marshal.SizeOf(tiffcoloruncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 032", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 033", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 034", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 035", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 036", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Handle uncompressed grayscale images... #region Handle uncompressed grayscale images... else if (twimageinfo.BitsPerPixel == 8) { try { IntPtr intptrTiff; TiffGrayscaleUncompressed tiffgrayscaleuncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 037", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffgrayscaleuncompressed = new TiffGrayscaleUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffgrayscaleuncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffgrayscaleuncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed), Marshal.SizeOf(tiffgrayscaleuncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 038", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 039", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 040", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 041", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 042", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Uh-oh... #region Uh-oh... else { WriteOutput("Scanning error: unsupported pixeltype..." + Environment.NewLine); m_iImageCount -= 1; m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 043", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion } #endregion // Uh-oh... #region Uh-oh else { WriteOutput("Scanning error: unsupported compression..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 044", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion } #endregion // Memory file transfers combine the best of file transfers and // memory transgers, however at this time it's not clear that any // TWAIN drivers have ever supported it... #region TWAIN.TWSX.MEMFILE else if (m_twsxXferMech == TWAIN.TWSX.MEMFILE) { Bitmap bitmap; byte[] abImage = null; IntPtr intptrTotalAllocated; IntPtr intptrTotalXfer; IntPtr intptrOffset; TWAIN.TW_SETUPMEMXFER twsetupmemxfer = default(TWAIN.TW_SETUPMEMXFER); TWAIN.TW_IMAGEMEMXFER twimagememxfer = default(TWAIN.TW_IMAGEMEMXFER); // Get the preferred transfer size from the driver... sts = m_twain.DatSetupmemxfer(TWAIN.DG.CONTROL, TWAIN.MSG.GET, ref twsetupmemxfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 045", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.SETUPMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate our unmanaged memory... twimagememxfer.Memory.Flags = (uint)TWAIN.TWMF.APPOWNS | (uint)TWAIN.TWMF.POINTER; twimagememxfer.Memory.Length = twsetupmemxfer.Preferred; twimagememxfer.Memory.TheMem = Marshal.AllocHGlobal((int)twsetupmemxfer.Preferred); if (twimagememxfer.Memory.TheMem == IntPtr.Zero) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 046", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Loop through all the strips of data... intptrTotalAllocated = IntPtr.Zero; intptrTotalXfer = IntPtr.Zero; sts = TWAIN.STS.SUCCESS; while (sts == TWAIN.STS.SUCCESS) { byte[] abTmp; // Append the new data to the end of the data we've transferred so far... intptrOffset = (IntPtr)intptrTotalXfer; // Get a strip of image data... sts = m_twain.DatImagememfilexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimagememxfer); if (sts == TWAIN.STS.XFERDONE) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else if (sts == TWAIN.STS.SUCCESS) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 047", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate memory for our new strip... intptrTotalAllocated = (IntPtr)((UInt64)intptrTotalAllocated + (UInt64)twimagememxfer.BytesWritten); abTmp = new byte[(int)intptrTotalAllocated]; if (abTmp == null) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 048", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Copy the existing data, if we have it... if (abImage != null) { Buffer.BlockCopy(abImage, 0, abTmp, 0, (int)intptrTotalAllocated - (int)twimagememxfer.BytesWritten); } // Switch pointers... abImage = abTmp; abTmp = null; // Copy the new strip into place... Marshal.Copy(twimagememxfer.Memory.TheMem, abImage, (int)intptrOffset, (int)twimagememxfer.BytesWritten); } // Okay, turn the data into a bitmap... memorystream = new MemoryStream(abImage); try { Image image = Image.FromStream(memorystream); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 049", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, null, 0); bitmap = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 050", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } memorystream = null; } #endregion // Uh-oh... #region Uh-oh... else { WriteOutput("Scan: unrecognized ICAP_XFERMECH value..." + m_twsxXferMech + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 051", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion // // End of the image transfer section... // /////////////////////////////////////////////////////////////////////////////// // Give us a blank line... WriteOutput(Environment.NewLine); // If we're doing a file tranfer, then output the file we just did... if (szFilename != "") { WriteOutput("File: " + szFilename + Environment.NewLine); } // Let's get some meta data. TWAIN only guarantees that this data // is accurate in state 7 after TWRC_XFERDONE has been received... if (blXferDone) { if (twimageinfo.BitsPerPixel == 0) { twimageinfo = default(TWAIN.TW_IMAGEINFO); sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 052", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } WriteOutput("ImageInfo: " + m_twain.ImageinfoToCsv(twimageinfo) + Environment.NewLine); } // And let's get some more meta data, this time using extended image // info, which is a little more complex. This is just being done to // show how to make the call, as a general rule an applications should // use one or the other, not both... if (blXferDone) { TWAIN.TW_EXTIMAGEINFO twextimageinfo = default(TWAIN.TW_EXTIMAGEINFO); TWAIN.TW_INFO twinfo = default(TWAIN.TW_INFO); twextimageinfo.NumInfos = 0; twinfo.InfoId = (ushort)TWAIN.TWEI.DOCUMENTNUMBER; twextimageinfo.Set(twextimageinfo.NumInfos++, ref twinfo); twinfo.InfoId = (ushort)TWAIN.TWEI.PAGESIDE; twextimageinfo.Set(twextimageinfo.NumInfos++, ref twinfo); sts = m_twain.DatExtimageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twextimageinfo); if (sts == TWAIN.STS.SUCCESS) { string szResult = "ExtImageInfo: "; twextimageinfo.Get(0, ref twinfo); if (twinfo.ReturnCode == (ushort)TWAIN.STS.SUCCESS) { szResult += "DocumentNumber=" + twinfo.Item + " "; } twextimageinfo.Get(1, ref twinfo); if (twinfo.ReturnCode == (ushort)TWAIN.STS.SUCCESS) { szResult += "PageSide=" + "TWCS_" + (TWAIN.TWCS)twinfo.Item + " "; } WriteOutput(szResult + Environment.NewLine); } } // Increment the image transfer count... if (blXferDone) { m_iImageXferCount += 1; } // Tell TWAIN that we're done with this image, this is the one place // that we go downstate without using the Rollback function, so that // we can examine the TW_PENDINGXFERS structure... TWAIN.TW_PENDINGXFERS twpendingxfers = default(TWAIN.TW_PENDINGXFERS); sts = m_twain.DatPendingxfers(TWAIN.DG.CONTROL, TWAIN.MSG.ENDXFER, ref twpendingxfers); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 053", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.ENDXFER.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // We've been asked to do extra work... switch (msgPendingxfers) { // No work needed here... default: case TWAINCSToolkit.MSG.ENDXFER: break; // Reset, we're exiting from scanning... case TWAINCSToolkit.MSG.RESET: m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 054", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); // Stop the feeder... case TWAINCSToolkit.MSG.STOPFEEDER: twpendingxfers = default(TWAIN.TW_PENDINGXFERS); sts = m_twain.DatPendingxfers(TWAIN.DG.CONTROL, TWAIN.MSG.STOPFEEDER, ref twpendingxfers); if (sts != TWAIN.STS.SUCCESS) { // If we can't stop gracefully, then just abort... m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 055", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } break; } // If count goes to zero, then the session is complete, and the // driver goes to state 5, otherwise it goes to state 6 in // preperation for the next image. We'll also return a value of // zero if the transfer hits an error, like a paper jam. And then, // just to keep it interesting, we also need to pay attention to // whether or not we have a UI running. If we don't, then state 5 // is our target, otherwise we want to go to state 4 (programmatic // mode)... if (twpendingxfers.Count == 0) { WriteOutput(Environment.NewLine + "Scanning done: " + TWAIN.STS.SUCCESS + Environment.NewLine); // Any attempt to scan will look like a new session to us... m_blScanStart = true; // We saved this value for you when MSG_ENABLEDS was called, if the // UI is up, then goto state 5... m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 056", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); } // All done... return (TWAIN.STS.SUCCESS); }