Click here to Skip to main content
12,299,243 members (59,377 online)
Click here to Skip to main content
Add your own
alternative version


78 bookmarked

Twain for WPF Applications - Look Ma, No Handles

, 22 Mar 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
A managed interface allows simple usage of Twain in WPF applications. Demo project provides scanning from cameras and scanners, display of multiple images and upload to a server via web services.


This project provides a clean interface that enables using Twain in WPF projects.

The code includes two parts:

  1. A Twain abstraction layer 
  2. A demo application

The abstraction layer exposes a clean C# interface (no handles, IntPtrs, etc.). It contains a few CS files which deal with the necessary interfaces and activation of Twain.

The application demonstrates the usage of Twain scanning in a typical WPF application:

  • Scanning with and without scanner UI
  • Display of multiple scanned images in WPF
  • Upload of image to server using web services


Twain is a widely used software standard common in acquisition of images from scanners and cameras.

The Twain interface is defined using low-level windows with a vast usage of unmanaged pointers, windows handles and messages.

In 2001, a CodeProject article introduced TwainLib - a C# wrapper around the Twain interface. This code has been a reference to many published works. Unfortunately the TwainLib interface still relies on handles, explicit messages and extensive use of GDI.

It was desirable to hide these implementation details from the WPF application. Abstracting out the low level windows stuff makes the resulting code cleaner and simplifies utilization of WPF features.

The current interface class WpfTwain is built on top of the classic TwainLib interface. It handles the integration of the Twain message loop into the WPF system and uses managed BitmapSource instead of GDI+ used in TwainLib.

Using the Code

The interface class to be used by the WFP is WpfTwain.

Acquiring an image is done in the following steps:

  1. Create the interface object, typically in the MainWindow Load event
  2. Select Twain source (not necessary if only one Twain source is defined in your system)
  3. Initiate acquisition
  4. Process acquisition results event

All these steps are straightforward and intuitive. See the MainWindow.cs code in the demo application for a complete sample.

1. Creating the Interface Object

private void Window_Loaded(object sender, RoutedEventArgs e)
    TwainInterface = new WpfTwain();
    TwainInterface.TwainTransferReady += new TwainTransferReadyHandler


Upon creation, the Twain interface will internally hook message and take care for implementation details.

Notice we register to one event - TwainTransferReady. This will allow us to process acquired images.

It is also possible to hook to additional events, but it is not necessary for a simple acquisition. If you need finer-grain low level control over the process, please see the WpfTwain class implementation. Probably not needed in most cases.

2. Selecting a Source

private void SelecctButton_Click(object sender, RoutedEventArgs e)

This is self explanatory, isn't it? Calling the Select method of the Twain interface will cause the Twain source selection dialog to pop. This is a windows dialog and is controlled by the Twain system.

3. Starting a Scan

private void ScanButton_Click(object sender, RoutedEventArgs e)
    TwainInterface.Acquire(false /*show UI*/);

The only thing to mention is the showUI argument. Setting it to true will cause the acquisition window to show. This window is specific to the device (installed by the device driver) so its appearance and behavior is different from device to device.

In some cases (e.g. where automation is desired), it is preferable to hide this and just run the scan. The demo application provides both options.

4. Processing Scan Results

private void TwainWin_TwainTransferReady(WpfTwain sender, List<ImageSource> imageSources)
    foreach (ImageSource ims in imageSources)
        AddImageThumbnail(ims); // process the image by the application

    // alternatively if the program should only support one-image scans
    // you can use imageSources[0]

This event handler receives the list of images acquired and does whatever the application needs.

That's it. This is all that is needed to successfully run scanners and get images from WPF.

The Demo Application

The demo application utilizes some cool WPF features, just to remind us why we wanted to use WPF in the first place.

1. Multiple Image Support

Scan Application

The program displays a list of image thumbnails and a larger selected image. When a new image is acquired, it is added to the list of thumbnails.

Each thumbnail is actually a WPF button. Clicking a button opens up the corresponding image in a full view.

The boundary between the thumbnails and the full-view image can be moved. Doing so changes the width of the thumbnail area. The sizes of the thumbnails and the full image are automatically adjusted. This is all done by the WPF engine - no coding required.

Here is the XAML behind the auto-sizing thumbnails:

<Border BorderThickness="1" BorderBrush="#FF6E789A" Margin="12,70,12,12" >
<Grid  Name="imageGrid"><Grid.ColumnDefinitions><ColumnDefinition Width="75" />
<ColumnDefinition Width="5" /><ColumnDefinition Width="*" />
</Grid.ColumnDefinitions><GridSplitter HorizontalAlignment="Stretch"
                  Grid.Column="1" ResizeBehavior="PreviousAndNext"
                  Width="Auto" Background="#FF787896" Height="Auto" />
                  <ScrollViewer VerticalScrollBarVisibility="Auto">
                  <StackPanel Margin="0,0,0,0" Name="ThumbnailStackPanel" >
    height="Auto" width="Auto"><Button.Content>
                                <Image HorizontalAlignment="Left" Stretch="Uniform"
				free-drink-pictures-espresso-coffee.jpg" />
                <Image  Grid.Column="2" HorizontalAlignment="Left" Name="image1"
                Stretch="Uniform" VerticalAlignment="Top"
			free-drink-pictures-espresso-coffee.jpg" />

Scanning adds images, pressing clear clears all thumbnails.

The following is a screen capture with the slider moved to the right. Note that the thumbnail buttons are stretched to fit the width, and in addition, a vertical scroller is automatically added.

App - wider thumbnails

Note: Images for the above were acquired using Teac mx-10 webcam.

2. Upload an Image to a Web Server

In many applications, it is required to provide scanner automation where scanning and uploading to a web server is done in one click.

Code for uploading an image and the corresponding server side are provided to complete this sample. Note that C# 4 has changed the service proxy, here the client side uses a C# 2 style web service proxy for wider compatibility.

Client side is as follows:

public void UploadImage()
    MemoryStream stream = new MemoryStream();
    try {
        JpegBitmapEncoder encoder = new JpegBitmapEncoder();
        // TBD: encoding parameters (quality etc.)

        TextBlock myTextBlock = new TextBlock();
        BitmapSource bs = image1.Source as BitmapSource;
        BitmapFrame bf = BitmapFrame.Create(bs);

        // upload
        scan2web.ScanServer.Scanner scanerServerProxy =
        			new scan2web.ScanServer.Scanner();
        string result = scanerServerProxy.UploadScan
        		(stream.GetBuffer(), "test 1");
        UploadResultLabel.Content = result;
    } catch (Exception ex) {
    UploadResultLabel.Content = "Error: " + ex.Message;
    // This will come handy if we want to annotate the image
    //RenderTargetBitmap rendered = new RenderTargetBitmap
    ( (int)bs.Width, (int)bs.Height, bs.DpiX, bs.DpiY, bs.Format);

The code uses web method to upload the current image as an array of bytes. Additional arguments can be sent to complete the information - as relevant to your application.

And the corresponding server side is:

public string UploadScan(byte[] data, string scanKey)
    Guid fileID = Guid.NewGuid();
    string path = Server.MapPath("~/Documents");
    string filePath = path + "/" + fileID.ToString() + ".jpg";
    try {
        // TBD: folder by date of upload to prevent too many files in the uploads folder
        FileStream traget = new FileStream
        	(filePath, FileMode.Create); // the jpg extension is for debug
        traget.Write(data, 0, data.Length);
        // TBD: register in the DB (fileID, scanKey, person, date etc)
    } catch (Exception ex) {
    // TBD: cleanup - delete file, clear DB atc.
    // TBD: register the error and alert operators
    return "Error: " + ex.Message;
    return "Saved";

Points of Interest

This project was a practical exercise in abstraction, particularly meant to simplify usage of Twain by removing low level details from the client code.

Most of the effort did not go to the application but rather to figuring out ways to activate the legacy code, translate bitmaps, etc. I hope this can be saved from developers interested in Twain by using this code.

This code was tested on a few computers with 32bit and 64bit OS and a few types of sources:

  1. Teac MX-10 webcam (using twain interface)
  2. HP LaserJet 3055 multi purpose device (using both Twain and Twain over WIA)
  3. Brother MFC-6490W multi purpose scanner
  4. Canon Lide 100

We are not aware of any problems, however testing was limited.


The current code does not utilize many of the more advanced Twain capabilities. Using such capabilities (for example multi-page scan) was out of the scope of this project.


Any feedback, requests, problems, suggestions, fixes and improvements will be highly welcomed..

Please contact Baruch at for requests and comments.


  • 21st March, 2011: Initial version


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Chief Technology Officer IBN Labs Ltd
Israel Israel
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionHow to release memory which the images consuming. Pin
huotuotuofly27-Apr-16 16:15
memberhuotuotuofly27-Apr-16 16:15 
QuestionError while running the demo application Pin
Member 116486857-Jul-15 20:06
memberMember 116486857-Jul-15 20:06 
QuestionFantastic, but stops after one page has scanned. Pin
bontempi223-Apr-15 3:44
memberbontempi223-Apr-15 3:44 
AnswerRe: Fantastic, but stops after one page has scanned. Pin
bontempi223-Apr-15 4:18
memberbontempi223-Apr-15 4:18 
QuestionTwainLib PassMessage Problem Pin
my_gino20-Oct-14 11:48
membermy_gino20-Oct-14 11:48 
Questionerror when i run my app Pin
Member 107273885-Apr-14 13:03
memberMember 107273885-Apr-14 13:03 
AnswerRe: error when i run my app Pin
Baruch236-Apr-14 3:57
memberBaruch236-Apr-14 3:57 
GeneralRe: error when i run my app Pin
Member 107273886-Apr-14 11:17
memberMember 107273886-Apr-14 11:17 
GeneralRe: error when i run my app Pin
Baruch236-Apr-14 12:42
memberBaruch236-Apr-14 12:42 
QuestionHow can i scan multi pages Pin
Member 397326719-May-13 17:45
memberMember 397326719-May-13 17:45 
AnswerRe: How can i scan multi pages Pin
Baruch2320-Nov-13 21:56
memberBaruch2320-Nov-13 21:56 
GeneralRe: How can i scan multi pages Pin
z1h2u3q426-Jun-14 20:35
memberz1h2u3q426-Jun-14 20:35 
GeneralMy vote of 5 Pin
Prasad Khandekar17-Apr-13 9:19
memberPrasad Khandekar17-Apr-13 9:19 
GeneralRe: My vote of 5 Pin
Baruch2317-Apr-13 20:58
memberBaruch2317-Apr-13 20:58 
Questionrunning sharpdevelop 4.X: error System.Runtime.InteropServices.SEHException: Pin
e_matrix16-Mar-13 17:04
membere_matrix16-Mar-13 17:04 
AnswerRe: running sharpdevelop 4.X: error System.Runtime.InteropServices.SEHException: Pin
e_matrix16-Mar-13 19:09
membere_matrix16-Mar-13 19:09 
GeneralMy vote of 5 Pin
abdusalam.benhaj1-Jan-13 21:42
memberabdusalam.benhaj1-Jan-13 21:42 
GeneralRe: My vote of 5 Pin
Baruch232-Jan-13 1:41
memberBaruch232-Jan-13 1:41 
QuestionClose Scanner GUI does not work Pin
kada1235-Nov-12 22:01
memberkada1235-Nov-12 22:01 
AnswerRe: Close Scanner GUI does not work Pin
Baruch236-Nov-12 4:09
memberBaruch236-Nov-12 4:09 
GeneralRe: Close Scanner GUI does not work Pin
kada1238-Nov-12 22:35
memberkada1238-Nov-12 22:35 
GeneralMy vote of 5 Pin
Mario Majčica21-Oct-12 5:42
memberMario Majčica21-Oct-12 5:42 
GeneralRe: My vote of 5 Pin
Baruch2322-Oct-12 8:20
memberBaruch2322-Oct-12 8:20 
QuestionNeed to set size setting in twain Pin
Parekh Vishal31-Jul-12 19:12
memberParekh Vishal31-Jul-12 19:12 
QuestionHide progress bar window Pin
Member 46759534-Jun-12 21:15
memberMember 46759534-Jun-12 21:15 
QuestionNeed to set default parameter in twain UI Pin
Parekh Vishal25-May-12 23:18
memberParekh Vishal25-May-12 23:18 
QuestionHide progress indicator Pin
Member 310245210-Apr-12 8:08
memberMember 310245210-Apr-12 8:08 
GeneralMy vote of 5 Pin
Steve Maier5-Apr-12 4:43
memberSteve Maier5-Apr-12 4:43 
Questionsilverlight Pin
sridharsr25-Mar-12 4:44
membersridharsr25-Mar-12 4:44 
QuestionAttempted to divide by zero Pin
appalanaidu Aug20117-Feb-12 19:30
memberappalanaidu Aug20117-Feb-12 19:30 
Questionreat functionality but something is not working for me... Pin
mosquets16-Aug-11 21:45
membermosquets16-Aug-11 21:45 
AnswerRe: reat functionality but something is not working for me... Pin
Baruch2317-Aug-11 12:16
memberBaruch2317-Aug-11 12:16 
QuestionQuality control Pin
bzden5018-Jul-11 23:42
memberbzden5018-Jul-11 23:42 
AnswerRe: Quality control Pin
Baruch2319-Jul-11 0:30
memberBaruch2319-Jul-11 0:30 
GeneralCapture TWAIN button event Pin
Nigel Stratton23-May-11 5:30
memberNigel Stratton23-May-11 5:30 
GeneralRe: Capture TWAIN button event Pin
Baruch2323-May-11 8:08
memberBaruch2323-May-11 8:08 
Hello Nigel,

I assume you refer to the 'scan' button on the scanner itself (or equivalently to 'scan to file' etc.).
The device buttons are handled by the specific driver of the device, which then opens the relevant application.
I would assume this is proprietary and device specific.

As far as I know the Twain standard does not cover this scenario. Please try consulting with the Twain standard and let me know if there is a standard interface.

The only advice I can offer here is to try checking the specific behavior of your device. For example, it is possible that upon sensing the button event the driver executes some program and the program is defined in the registry. You can tap to this process with your own application. Its a bit of a hacking, and is very device/version specific but it may work for you.

Best regards
GeneralGreat piece of software Pin
axwack14-May-11 13:12
memberaxwack14-May-11 13:12 
GeneralRe: Great piece of software Pin
Baruch2315-May-11 4:29
memberBaruch2315-May-11 4:29 
GeneralDetect missing scanner Pin
Papy2145-May-11 22:38
memberPapy2145-May-11 22:38 
GeneralRe: Detect missing scanner Pin
Baruch236-May-11 12:31
memberBaruch236-May-11 12:31 
GeneralRe: Detect missing scanner Pin
Papy2148-May-11 21:50
memberPapy2148-May-11 21:50 
GeneralMy vote of 5 Pin
Milind R Chavan12-Apr-11 0:52
memberMilind R Chavan12-Apr-11 0:52 
GeneralMy vote of 5 Pin
Tiago Freitas Leal5-Apr-11 18:46
memberTiago Freitas Leal5-Apr-11 18:46 
GeneralA 5 and a request Pin
Tiago Freitas Leal5-Apr-11 18:44
memberTiago Freitas Leal5-Apr-11 18:44 
GeneralRe: A 5 and a request Pin
Baruch236-Apr-11 3:13
memberBaruch236-Apr-11 3:13 
GeneralMy Vote of 5 Pin
RaviRanjankr5-Apr-11 1:52
memberRaviRanjankr5-Apr-11 1:52 
GeneralMy vote of 5 Pin
Robin.C2-Apr-11 5:43
memberRobin.C2-Apr-11 5:43 
GeneralMy vote of 5 Pin
aalfifi28-Mar-11 20:27
memberaalfifi28-Mar-11 20:27 
GeneralMy vote of 5 Pin
martintr28-Mar-11 9:51
membermartintr28-Mar-11 9:51 
GeneralMy vote of 5 Pin
Marcelo Ricardo de Oliveira23-Mar-11 5:49
mvpMarcelo Ricardo de Oliveira23-Mar-11 5:49 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160525.2 | Last Updated 22 Mar 2011
Article Copyright 2011 by Baruch23
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid