Click here to Skip to main content
Click here to Skip to main content

.NET TWAIN image scanner

By , 12 May 2002
 

Sample Screenshot

Abstract

In Windows imaging applications, the most used API for scanning is TWAIN www.twain.org. Unfortunately, the new .NET Framework has no built-in support for TWAIN. So we have to work with the interop methods of .NET to access this API. This article doesn't explain this interop techniques, and good knowledge of the TWAIN 1.9 specifications is assumed! The sample code included doesn't present a finished library, only some essential steps for a minimal TWAIN adaption to .NET applications.

Details

First step was to port the most important parts of TWAIN.H, these are found in TwainDefs.cs. The real logic for calling TWAIN is coded in the class Twain, in file TwainLib.cs.. As the TWAIN API is exposed by the Windows DLL, twain_32.dll, we have to use the .NET DllImport mechanism for interop with legacy code. This DLL has the central DSM_Entry(), ordinal #1 function exported as the entry point to TWAIN. This call has numerous parameters, and the last one is of variable type! It was found to be best if we declare multiple variants of the call like:

[DllImport("twain_32.dll", EntryPoint="#1")]
private static extern TwRC DSMparent(
    [In, Out] TwIdentity origin,
    IntPtr zeroptr,
    TwDG dg, TwDAT dat, TwMSG msg,
    ref IntPtr refptr );

The Twain class has a simple 5-step interface:

class Twain
{
    Init();
    Select();
    Acquire();
    PassMessage();
    TransferPictures();
}

For some sort of 'callbacks', TWAIN uses special Windows messages, and these must be caught from the application-message-loop. In .NET, the only way found was IMessageFilter.PreFilterMessage(), and this filter has to be activated with a call like Application.AddMessageFilter(). Within the filter method, we have to forward each message to Twain.PassMessage(), and we get a hint (enum TwainCommand) back for how we have to react.

Sample App

The sample is a Windows Forms MDI-style application. It has the two TWAIN-related menu items Select Source... and Acquire... Once an image is scanned in, we can save it to a file in any of the GDI+ supported file formats (BMP, GIF, TIFF, JPEG...)

Limitations

All code was only tested on Windows 2000SP2, with an Epson Perfection USB scanner and an Olympus digital photo camera. The scanned picture is (by TWAIN spec) a Windows DIB, and the sample code has VERY little checking against error return codes and bitmap formats. Unfortunately, no direct method is available in .NET to convert a DIB to the managed Bitmap class... Some known problems may show up with color palettes and menus.

Note, TWAIN has it's root in 16-Bit Windows! For a more modern API supported on Windows ME/XP, have a look at Windows Image Acquisition (WIA).

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

About the Author

NETMaster
Web Developer
Switzerland Switzerland
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionImage Resolution Problem.memberrush2rajiv29 Jun '12 - 0:06 
Hi,
 
Image always stored at 96dpi. I am not able to find the solution for that.
QuestionOut of Memory ExceptionmemberJaydeep Jadav15 Jun '12 - 20:56 
Hello guys, i am using this lib with vb.net winform application. it works fine when i try to scan pages using my scanner. But after that when i try to scan images using this lib it throws the Out of memory exception. It works when i close my app then run again. I came on conclusion that this lib stores the scanned images in memory and not free the memory after transfering pictures. i study the code and find that there is a function of
GlobleFree
which is not run after
acquire
or
transferpictures
I tried to change some code but no luck. Is anyone having same issues or handled the same situation then please help me.
 
Thanking You,
Jaydeep Jadav
QuestionRe: Out of Memory Exceptionmembernileshkakade26 Feb '13 - 19:17 
Dear Jaydeep,
I am Suffering from the same issue but little Diffident.
Have you reach to some Solution for OUT of memory Exception?
Because when I am scanning 100-110 pages it works Smoothly but Some time it gets Out of Memory Exception when I restart app it Again run Smoothly
Can you please Suggest
AnswerRe: Out of Memory ExceptionmemberJaydeep Jadav1 Mar '13 - 23:18 
I fixed it by forcing GC to collect garbage after every transfer of picture.
 
I used
GC.Finalize() 
 
Thanks and Regards,
Jaydeep Jadav
QuestionLoader Lock Error Problemmemberleenak8 Jun '12 - 2:11 
Please help on this .. when I run the program I am able to select the scanner but when I click Acquire image it gives me exception as below
 

LoaderLock was detected
Message: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.
 
I am using Visual Studio 2008, is it causing the problem..
 
Thanks & Regards
 
Leena
GeneralRe: Loader Lock Error Problem [modified]membersmile869110 Jul '12 - 11:59 
I have the same problem. Did you find any solutions? Thanks.

modified 11 Jul '12 - 8:22.

QuestionDuplex ScanningmemberIsuru Alahakoon8 Jun '12 - 1:14 
Great article. Thank you very much.
But I want to enable duplex scan using this code.
Can anyone help me.
Thank you.
Questionhide progress bar window.memberMember 46759534 Jun '12 - 21:32 
I tried many ways to hide progress indicator when ShowuI=False; But not working. Please help me.
QuestionError on 64 bits machinememberdavid bong1 Jun '12 - 6:09 
Hi,
 
When I downloaded the source code and run debug mode on the 64 bits machine, it throws an error when calling the Init() method. it's complaining invalid format ... please advise
asda

AnswerRe: Error on 64 bits machinememberboblogan13 Jun '12 - 6:00 
You might check your project output - and make sure it is specify 32 bit code only...
GeneralRe: Error on 64 bits machinememberAbdullah Çetinkaya22 Jan '13 - 23:33 
where we check 32 bit code in vs ?
Questionset Flatbed or FeadermemberAndrew8231 May '12 - 21:22 
hi,
i have a question how set Flatbed of Feader.
 
in twaindef
 
    CAPFEnabled = 0x1002
 
 
in twainlib in acquire method
      TwCapability capF = new TwCapability(TwCap.CAPFEnabled, 1);
      rc = DScap(appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Get, capF);
      if (rc != TwRC.Success)
      {
          Console.WriteLine("No feeder");
      }
 
it's correct???
QuestionCan use project as webmemberramysamir29 May '12 - 0:40 
please i want to ask i can user that classes in asp.net webform
QuestionDo you have code for automatically control scannermembergolanw12 May '12 - 22:52 
The code opens a window to select the parameters I want to redefine
the resolution the area of scan etc
 
Can you send the code
golan87@walla.co.il
 
Thanks
QuestionDLLmemberMember 888329025 Apr '12 - 9:57 
Provide the dll used for development, please.
QuestionHow Can I change scaning variables with c# code?memberRadu23717 Apr '12 - 20:46 
It's posible to change scaning variable from code?
AnswerRe: How Can I change scaning variables with c# code?memberMember 100259915 May '13 - 16:36 
TwUserInterface guif = new TwUserInterface();
guif.ShowUI = 0;
guif.ModalUI = 1;
QuestionHow to check scanner is connected befor Acquirememberswapnil7434513 Apr '12 - 21:02 
if scanner is not connected then that freeze whole application so is there any method to check whether any scanner is connected before calling of Acquire?
 
plz help...
Questionconverting?memberdarkalastair19 Mar '12 - 22:15 
Hi, thanks for app really very usefull but how can i show scanned image to picturebox?
AnswerRe: converting?memberMember 667621 Oct '12 - 8:58 
bool IMessageFilter.PreFilterMessage( ref Message m )
		    {
		    TwainCommand cmd = tw.PassMessage( ref m );
		    if( cmd == TwainCommand.Not )
			    return false;
 
		    switch( cmd )
			    {
			    case TwainCommand.CloseRequest:
				    {
				    EndingScan();
				    tw.CloseSrc();
				    break;
				    }
			    case TwainCommand.CloseOk:
				    {
				    EndingScan();
				    tw.CloseSrc();
				    break;
				    }
			    case TwainCommand.DeviceEvent:
				    {
				    break;
				    }
			    case TwainCommand.TransferReady:
				    {
				    ArrayList pics = tw.TransferPictures();
				    EndingScan();
				    tw.CloseSrc();
				    picnumber++;
                    MessageBox.Show("Number of Scanned Docs: " + pics.Count.ToString());
				    for( int i = 0; i < pics.Count; i++ )
					    {
					    IntPtr img = (IntPtr) pics[ i ];
                        //PicForm newpic = new PicForm( img );
                        //newpic.MdiParent = this;
					    //int picnum = i + 1;
                        this.pictureBox1.Image = TwainHelper.ImageFromIntPtr(img);
                        //newpic.Text = "ScanPass" + picnumber.ToString() + "_Pic" + picnum.ToString();
                        //newpic.Show();
					    }
				    break;
				    }
			    }
 
		    return true;
		    }
----------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
 
namespace TwainGui
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class BITMAPINFOHEADER
    {
        public uint biSize;
        public int biWidth;
        public int biHeight;
        public ushort biPlanes;
        public ushort biBitCount;
        public uint biCompression;
        public uint biSizeImage;
        public int biXPelsPerMeter;
        public int biYPelsPerMeter;
        public uint biClrUsed;
        public uint biClrImportant;
        public void Init() { this.biSize = (uint)Marshal.SizeOf(this); }
    }
    class TwainHelper
    {
        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
            for (int j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }
 

        public static Image ImageFromIntPtr(IntPtr imagePtr)
        {
            BITMAPINFOHEADER bmpInfoHeader = new BITMAPINFOHEADER();
            IntPtr bmpptr = GlobalLock(imagePtr);
            IntPtr pixptr = GetPixelInfo(bmpptr, bmpInfoHeader);
 
            Bitmap bitmap = new Bitmap(bmpInfoHeader.biWidth, bmpInfoHeader.biHeight);
            Graphics graphics = Graphics.FromImage(bitmap);
 
            IntPtr hdc = graphics.GetHdc();
            SetDIBitsToDevice(hdc, 0, 0, bmpInfoHeader.biWidth, bmpInfoHeader.biHeight,
                    0, 0, 0, bmpInfoHeader.biHeight, pixptr, bmpptr, 0);
            graphics.ReleaseHdc(hdc);
 
            return (Image)bitmap;
        }
 
        private static IntPtr GetPixelInfo(IntPtr bmpptr, BITMAPINFOHEADER bmpInfoHeader)
        {
            Marshal.PtrToStructure(bmpptr, bmpInfoHeader);
 
            if (bmpInfoHeader.biSizeImage == 0)
                bmpInfoHeader.biSizeImage = (uint)(((((uint)bmpInfoHeader.biWidth * (uint)bmpInfoHeader.biBitCount) + 31) & ~31) >> 3) * (uint)bmpInfoHeader.biHeight;
 
            int p = (int)bmpInfoHeader.biClrUsed;
            if ((p == 0) && (bmpInfoHeader.biBitCount <= 8))
                p = 1 << bmpInfoHeader.biBitCount;
            p = (p * 4) + (int)bmpInfoHeader.biSize + (int)bmpptr;
            return (IntPtr)p;
        }
        [DllImport("gdi32.dll", ExactSpelling = true)]
        internal 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("kernel32.dll", ExactSpelling = true)]
        internal static extern IntPtr GlobalLock(IntPtr handle);
        [DllImport("gdiplus.dll", ExactSpelling = true)]
        internal static extern int GdipCreateBitmapFromGdiDib(IntPtr bminfo, IntPtr pixdat, out IntPtr image);
 

    }
}

QuestionTwainLib Visual Studio 2010 Windows 7 64 bitmembersanvi615 Mar '12 - 3:10 
The binary "TestTwainLib.exe" run Ok, but the source show compilation error "LoaderLock" in the:
rc = DSM_Ident(appid, IntPtr.Zero, TwDG.TwDG_Control, TwDAT.TwDAT_Identity, TwMSG.TwMSG_CloseDS, SelectedDataSource)
 
Whats Happen?
AnswerRe: TwainLib Visual Studio 2010 Windows 7 64 bitmemberUstesGreenridge26 Mar '12 - 3:55 
I was getting that until i went into Debug, Exceptions, Managed Debugging Assistants, and turned off LoaderLock exceptions.
AnswerRe: TwainLib Visual Studio 2010 Windows 7 64 bitmemberDanielMarzan14 May '12 - 8:19 
Change the platform target to x86. That should fix this issue.
GeneralRe: TwainLib Visual Studio 2010 Windows 7 64 bitmembersmile869110 Jul '12 - 12:02 
I changed the target and also unmark the exception for the loader lock. still get the error message. Any ideas? Thanks.
GeneralMy vote of 5membermanoj kumar choubey26 Feb '12 - 18:23 
Nice
Questionproblem ..!!!!!!!!!!memberA7mad_22 Feb '12 - 7:43 
When I press acuqire it opens a window from my scanner
when I press cancel on it, the whole application get freezed. How to solve this???
QuestionEpson V30 scan problem with 16 bit grayscale tiffmembersmarino6021 Feb '12 - 7:52 
Hy,
First of all thanks for the posted code in c # that is perhaps unique in the network. Big Grin | :-D
 
my question:
I have to create an application that scans a document in 16-bit gray scale and saved in TIFF format, uncompressed 600 dpi. Of course I must not use the Epson interface.
With the Epson interface there are no problems but when I try to do the same thing programmatically, I can not set the skills in the right way and I always get an error. Using C # visual studio 2010. Is there any good Samaritan who can help me?
The steps are:
1) Select Source (not required)
2) Scan the document in A4 format such as TIFF 16-bit grayscale
3) Save to file, or manage it as an array in memory.
 
... Before I committed suicide. Thank you very much and please excuse the language! D'Oh! | :doh: Dead | X|
QuestionTransferPictures() method returns pics count 1 alwaysmemberappalanaidu Aug20117 Feb '12 - 22:29 
HI all ,
 
I tried to fix this issue from last 1 month onwards,TransferPictures() method returns pics count 1 alway ,when scanning multiple pages.previously it was working before changed Acquire() method code to set the user selected scanner.I have written the method setscanner() in Acquire().after wards it this code scan's multiple pages ,but TransferPictures() method returns pics count 1.One day i commented the method setscanner() in Acquire() method , then it works fine.after 2 day's it is not working.I dont know how it changes time to time.
 
please help me , this issue struggles me like anything...........
Question+1membermiwalter4 Feb '12 - 22:06 
well done, great help for getting started!
GeneralMy vote of 1membernavidsoft10 Jan '12 - 22:39 
it doen't work
QuestionThis get freezed the whole applicationmemberMember 333762221 Dec '11 - 2:24 
Hi ,
I am realatively new for programming, Please help me.
When I press acuqire it opens a window from my scanner
when I press cancel on it
the whole application get freezed. How to get rid from this.
 
thanks
AnswerRe: This get freezed the whole applicationmemberA7mad_21 Feb '12 - 9:26 
do you solve the problem????
plz help
QuestionScanning area parameter through applicationmemberRajendrarajsri28 Nov '11 - 15:42 
How to pass scanning area parameter in the application. Like I want to scann only 10 centimeter from the top of the scanner. Thanks in advance.
QuestionTWAIN A3 Papersize change i.e. TwCapability capPaperSize = new TwCapability(TwCap.ICAP_SUPPORTEDSIZES, 1)memberWaynePorter18 Nov '11 - 2:55 
I am trying to use API calls in c# to do A3 scanning. I am using the following code, but the result in the rc variable comes back as failure. I have used TwCap.
 
TwCapability capPaperSize = new TwCapability(TwCap.ICAP_SUPPORTEDSIZES, 1);
rc = DScap(appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Set, capPaperSize);
 
if (rc != TwRC.Success)
{
CloseSrc();
return;
}
 
I have also used the following TwCap enum values in the TwCapability parameter, but the result are the same apart from TWSS_A4. A4 work but nothing else does. Am I doing something wrong. This really urgent I am using XEROX DocuMate 752 scanner.
 
TWSS_NONE = 0x0000,
TWSS_A4 = 0x0001,
TWSS_JISB5 = 0x0002,
TWSS_USLETTER = 0x0003,
TWSS_USLEGAL = 0x0004,
/* Added 1.5 */
TWSS_A5 = 0x0005,
TWSS_ISOB4 = 0x0006,
TWSS_ISOB6 = 0x0007,
/* Added 1.7 */
TWSS_USLEDGER = 0x0009,
TWSS_USEXECUTIVE = 0x000A,
TWSS_A3 = 0x000B,
Questioncan any one give this whole project in vb.netmembernsk_saravana15 Nov '11 - 21:13 
can any one give this whole project in vb.net (windows application)
AnswerRe: can any one give this whole project in vb.netmemberwellhi18 Nov '11 - 16:43 
you can convert it yourself by the website bellow,it works fine.
 
http://www.developerfusion.co.uk/utilities/convertcsharptovb.aspx[^]
QuestionAutocropmembernsk_saravana13 Nov '11 - 22:21 
How to acchieve the auto crop in twainlib.vb. where i have change the code.
QuestionRemoving Scanner UImembernsk_saravana13 Nov '11 - 17:40 
How can i remove scanner UI and i would like to pass Auto Crop or Full Page and Color or Blackand white from our vb.net interface. How can i pass these parameters from the vb.net to twainlib.vb.
kindly help me.it is urgent.
Questionremoving the UImembermist99911 Nov '11 - 4:45 
The problem i am facing while running this code is, when i click acquire it throws up a UI for selecting an option of grayscale, color, black&white.

Is there any way i can get rid of the UI. I tried a lot analyzing it, but somehow the UI keeps pop up for me.

Is there any way i can manually enter the value for grayscale. So that it always scans in gray scale.

 
Plzzzz need help on this
QuestionADF Question.....Help Mememberbeyond111717 Oct '11 - 16:59 
Dear All,
The first come to here. I use the NET TWAIN image scanner.
But I Selet ADF Option, [Document In ADF] set more than 1 value,
then Scan button click.The TWAIN DS window display all long,
When Scan button click,I want to close the TWAIN window.
What Can I Do?Help Me..Thank you.
QuestionCapture color image using twainmemberakul12321 Sep '11 - 0:43 
I could hide the user interface by commenting the line
//this.ShowUI;
But after commenting the line, only black and white image can be captured.
I want to capture color images.How can i solve this?
QuestionCould not compilememberditoroin21 Sep '11 - 0:33 
Could not compile the source code (VS 2010, Windows 7)
 
I receive the following error:
"An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)"
SuggestionRe: Could not compilememberMrPetan21 Sep '11 - 5:29 
"BadImageFormatException" is thrown when the application loads a DLL having incompatible bitness
(a 64 bit exe tried to load a 32bit DLL or a 32 bit exe tried to load a 64 bit DLL).

Make sure you compile your exe file as 32 bit (x86) !!!!.

GeneralRe: Could not compilememberMember 831300223 Apr '12 - 22:59 
In your project, go to Build tab, Change PlatForm target to x86
QuestionCAP_DEVICEONLINE always returns falsememberbennett cummins13 Sep '11 - 6:26 
The code below always returns false even if the scanner is online and ready. Can anyone see any problems with the code? I appreciate any help.
 
Public Function IsDeviceOnline() As Boolean
Try
Dim rc As TwRC
 
Dim capDeviceOnline As New TwCapability(TwCap.CAP_DEVICEONLINE)
 
rc = DScap(appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.DeviceEvent, capDeviceOnline)
If rc <> TwRC.Success Then
CloseSrc()
Return False
End If
Return True
Catch ex As Exception
Return False
End Try
 
End Function
 

 
Below is the TwainLib constructor.
 
Public Sub New()
appid = New TwIdentity()
appid.Id = IntPtr.Zero
appid.Version.MajorNum = 1
appid.Version.MinorNum = 1
appid.Version.Language = LanguageUSA
appid.Version.Country = CountryUSA
appid.Version.Info = "" ' TODO
appid.ProtocolMajor = TwProtocol.Major
appid.ProtocolMinor = TwProtocol.Minor
appid.SupportedGroups = CInt(TwDG.Image Or TwDG.Control)
appid.Manufacturer = ""
appid.ProductFamily = ""
appid.ProductName = ""
 
srcds = New TwIdentity()
srcds.Id = IntPtr.Zero
 
evtmsg.EventPtr = Marshal.AllocHGlobal(Marshal.SizeOf(_winmsg))
End Sub
Questionconverting this sample in asp.netmemberakul12323 Aug '11 - 22:33 
I want to convert this sample application in asp.net. I have quoted here a portion from the source code. If you go through the source code then you will get the following portion in the MainFrame.cs.
public MainFrame()
    {
    InitializeComponent();
    tw = new Twain();
    tw.Init( this.Handle );
    }
what will be the equivalent of the parameter
this.Handle
in asp.net.
AnswerRe: converting this sample in asp.netmembermahamahmaha22 Jan '12 - 21:22 
use the native functions to get the ptr value for the active window as follows
 
tw.Init( GetForegroundWindow());
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
QuestionImage sizememberVBCoder6818 Aug '11 - 5:14 
I used this code to rewrite an old application that used the Pegasus library for image scanning. The old application produced tiff images that were 2544 X 3296 and about 40kb in size. The Twain application produces tiff images that are 1700 X 2200 but are more than 2.5 mb in size.
 
I'm new to image processing. But, I'm guessing this has something to do with compression. How do turn compression on?
 
Thanks,
 
Mike
QuestionDont want dialog boxesmemberNeha Ameen16 Aug '11 - 1:22 
I am providing source internally and does not want to show the dialog box for selecting scanner type .please help
GeneralMy vote of 5memberShahin Khorshidnia12 Aug '11 - 19:50 
Excellent. Thank you.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 13 May 2002
Article Copyright 2001 by NETMaster
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid