Source Code Downloads
TWAINSharp was developed to fulfill the needs of Virtual Charting,
software which provides a wireless, and paperless practice management
solution for medical offices. The software required a scanning component
in order to provide maximum compatibility with the scanning hardware
used by medical offices. While a WIA (Windows Image Acquisition) component
was originally developed to meet this need, WIA hardware support was
found to be seriously lacking. This left a TWAIN implementation as
the only other viable solution.
This TWAIN component had to meet a few key requirements:
- It needed to be managed code, or at least be able to interact
well with managed code.
- It needed to support multi-page image transfers.
- It needed to be able to make use of the .NET Image system.
- It needed to be low-cost.
There are a few .NET TWAIN components out there. First, there is
LEAD TWAIN, developed by LEADTOOLS. The .NET components (including
a TWAIN component) are included as part of their Raster Imaging
Pro toolkit, which sports an impressive list of features. Unfortunately,
Virtual Charting did not require the vast majority of the functionality
implemented by the LEADTOOLS SDK’s (and many of the other
products out there), and thus the price of these packages could
not be justified. Virtual Charting decided to develop its own C#
component. This article explains how to use TWAINSharp to acquire
multiple images from a TWAIN-compliant data source.
The TWAIN API has its roots back in the days of 16-bit computing.
Under Microsoft Windows, TWAIN uses the Windows Message Pump in order
to communicate with the application. Because of this, the API is not
particularly intuitive to modern-day developers. Searching for TWAIN
examples on the Internet yields many results, primarily written in
C/C++ or Delphi. TWAINSharp was developed in a style similar to that
used in these examples, in order to assist the porting of some example
code. At the same time, it presents an easy-to-use interface to the
programmer.
TWAIN is a state-based API, and applications communicating with a
TWAIN Data Source or the TWAIN Data Source Manager follow a fairly
strict order of operations, detailed in Figure 1, below. Please refer
to the current TWAIN specification (1.9 at the time of this writing)
for more details on how TWAIN works.
In order to illustrate the steps required to initialze TWAIN for use,
the sample application uses a system whereby buttons are enabled and
disabled according to the current TWAIN state, as defined by the TWAIN
State Diagram (see Figure 1, below).
Figure 1
First things first -- to add a TWAINSharp TWAINComponent to a form,
you can simply drag and drop inside of the Windows Forms Designer
from the Toolbox onto a form. There is one more step which needs to
be performed before the TWAINComponent can be used inside the form.
The TWAIN Data Source Manager uses a window handle to uniquely identify
the application, so it is necessary to give the TWAINComponent object
the window handle of the main form using the 'Handle' property of
the form. The constructor implementation for the main application
form is shown below:
public MainForm() {
// initialize our form components:
InitializeComponent();
// VERY IMPORTANT!! We MUST set the 'ContainerHandle' property
// of the TWAIN component, or nothing will work:
this.twainComponent.ContainerHandle = this.Handle;
// TODO: Add any other constructor code here.
}
Next, the form's message pump must be modified in order to communicate
with TWAIN. This is done by implementing the IMessageFilter interface,
and adding the IMessageFilter.PreFilterMessage method (illustrated
below) to the form. This is also where the application will perform
any data acquisition, close out the TWAIN Data Source and uninstall
the application message filter. Note that the TWAINComponent object
returns images as .NET Bitmap objects.
bool IMessageFilter.PreFilterMessage( ref Message msg )
{
// pass this message off to the TWAIN Data Source (or Data Source Manager):
TWAIN.MSG returnCode = twainComponent.PassMessage( ref msg );
// If this is a normal Windows message, we pass it on
// to the Windows default message handler:
if(TWAIN.MSG.YOUPROCESS == returnCode)
return false;
// This seems to be a message which TWAIN wants our application to process.
// According to the TWAIN specification, there are only four messages that
// the Application should care about with regards to Data Source events.
// Refer to Chapter 3 (pgs. 28 and 29) of the TWAIN 1.9 specification for more details.
switch( returnCode ) {
case TWAIN.MSG.CLOSEDSREQ:
{
// The user has clicked the "Close" button of the Acquisition
// user interface, so TWAIN is asking us to close the currently
// opened Data Source. We also uninstall our message filter here.
this.twainComponent.CloseDataSource();
// uninstall ourselves as a message filter:
this.RemoveMessageFilter();
// re-enable the "Close Data Source Manager" button:
this.closeDSMButton.Enabled
= true;
// disable the "Image Acquisition" button:
this.showAcquireButton.Enabled = false;
// re-enable the main form:
this.Enabled = true;
this.Focus();
}
break;
case TWAIN.MSG.CLOSEDSOK:
{
// The user has clicked the "OK" button of the Acquisition user
// interface, so TWAIN is asking us to close the currently
// opened Data Source. We also uninstall our message filter here.
// This is just a special case of TWAIN.MSG.CLOSEDSREQ.
this.twainComponent.CloseDataSource();
// TODO: Get custom data here.
// uninstall ourselves as a message filter:
this.RemoveMessageFilter();
// re-enable the "Close Data Source Manager" button:
this.closeDSMButton.Enabled
= true;
// disable the "Image Acquisition" button:
this.showAcquireButton.Enabled = false;
// re-enable the main form:
this.Enabled = true;
this.Focus();
}
break;
case TWAIN.MSG.DEVICEEVENT:
{
// This notification is sent to the Application by the Data Source
// when a specific event has occurred, but only if the Application
// gave the Data Source prior instructions to pass along such events.
// We don't need to worry about this message right now.
}
break;
case TWAIN.MSG.XFERREADY:
{
// This notification is sent when the Data Source has prepared data
// it wishes to transfer to the Application. This is where we acquire
// all of our images (there will be one or more) from the Data Source.
if(
this.twainComponent != null
) {
Bitmap newImage
= null;
int
numPending = 0;
int
imageIndex = 1;
do {
// acquire a single image:
newImage = this.twainComponent.AcquireSingleImage( ref numPending, false );
// display it if acquisition was successful:
if(
newImage != null ) {
// display it in a new PictureForm:
PictureForm picForm = new PictureForm();
if(
picForm != null
) {
// set the form text:
picForm.Text
= "TWAINSharp Image " + imageIndex.ToString();
// set the form image:
picForm.Picture = newImage;
picForm.Visible = true;
}
// increment the image index:
++imageIndex;
}
} while( numPending != 0 );
}
}
break;
}
return true;
}
It is now time to start transitioning up in TWAIN state. Because TWAINSharp
takes care of the transition through TWAIN states 1 and 2 automatically,
the first step is to open the TWAIN Data Source Manager. This process
is illustrated in the event handler 'openDSMButton_Click', below.
When this event handler returns, the application is ready to execute
the transition into TWAIN state 4, and the "Select Data Source"
and "Open Data Source" buttons are enabled. Please note
that selecting a data source is an optional step, because a TWAIN
always maintains a "default" data source.
private void openDSMButton_Click( object sender, System.EventArgs e ) {
// open the Data Source Manager, if possible:
if
( this.twainComponent != null && this.openDSMButton.Enabled
) {
// attempt to open the TWAIN Data Source Manager:
if
( this.twainComponent.OpenDataSourceManager()
) {
// disable the "Open Data Source Manager" button:
this.openDSMButton.Enabled
= false;
// enable the "Select Data Source" and "Open Data Source" buttons:
this.selectDSButton.Enabled
= true;
this.openDSButton.Enabled
= true;
// disable the "Show Image Acquisition Interface" button:
this.showAcquireButton.Enabled = false;
// enable the "Close Data Source Manager" button:
this.closeDSMButton.Enabled = true;
}
}
}
If there is more than one TWAIN Data Source installed on the system,
the TWAIN Data Source Manager implements code which presents a dialog
box to the end user that allows them to select the Data Source they
wish to use. If the user selects a data source, it becomes the new
"default" TWAIN data source. This step is optional, and
does not cause a change in TWAIN state. In the sample application,
this dialog is displayed if the user clicks the "Select Data
Source" button, the event handler for which is shown below.
private void selectDSButton_Click( object sender, System.EventArgs e ) {
// present the standard Data Source selection dialog box, if possible:
if(twainComponent != null && this.selectDSButton.Enabled) {
// display the "Select TWAIN Data Source" dialog box.
// if the function returns 'true', the user selected a Data Source,
// which becomes the new "default" Data Source. Otherwise, the
// default Data Source is determined by the Data Source Manager.
this.twainComponent.SelectDataSource();
// because this doesn't cause the TWAIN Application state to
// change, we don't enable/disable any of our buttons here.
}
}
It is now time to open a connection to the current data source. Before
we do this, however, we can optionally perform capability negotiation
with the Data Source. In the sample application, capability negotiation
is used to enable the document feeder (if one is present) and enable
the Data Source to automatically feed documents. As a final step before
opening the device, the form must install its message filter. Once
these steps have been performed, the application may open the TWAIN
Data Source for image acquisition. In the sample application, all
of these steps are performed in the event handler for the "Open
Data Source" button, which is shown below.
private void openDSButton_Click( object sender, System.EventArgs e ) {
// attempt to open the current "default" Data Source. there will
// ALWAYS be a default Data Source, as long as the DSM was opened.
if(
this.twainComponent != null && this.openDSButton.Enabled
) {
// attempt to open the Data Source:
if(
this.twainComponent.OpenDataSource()
) {
TWAIN.CAPABILITY capXFerCount
= null;
TWAIN.CAPABILITY capFeederEnable= null;
TWAIN.CAPABILITY capAutoFeed
= null;
ushort xferCount
= ((ushort)TWAIN.CAP.XFERCOUNT);
ushort feederEnable
= ((ushort)TWAIN.CAP.FEEDERENABLED);
ushort autoFeedEnable = ((ushort)TWAIN.CAP.AUTOFEED);
// Install our message filter, in order to communicate with the Data Source:
this.InstallMessageFilter();
// Perform capability negotiation to enable the document feeder
// and other such things necessary for multi-image acquisition:
capXFerCount
= new TWAIN.CAPABILITY();
capFeederEnable = new TWAIN.CAPABILITY();
capAutoFeed
= new TWAIN.CAPABILITY();
// we need to let the device know we can accept multiple images:
capXFerCount.FromONEVALUE( xferCount, TWAIN.TWTY.INT32, -1 );
// we need to enable the document feeder, if one is present:
capFeederEnable.FromONEVALUE( feederEnable, TWAIN.TWTY.BOOL, true );
// we need to enable the automatic feeding capability as well:
capAutoFeed.FromONEVALUE( autoFeedEnable, TWAIN.TWTY.BOOL, true );
// set the capabilities to the device. normally, you'd check for
// success (as defined according to the TWAIN specification). we
// won't do that here.
this.twainComponent.SetCapability( ref capXFerCount );
this.twainComponent.SetCapability( ref capFeederEnable );
this.twainComponent.SetCapability( ref capAutoFeed );
// TODO: Perform other capability negotiation here.
// disable the "Select Data Source" and "Open Data Source" buttons:
this.openDSButton.Enabled
= false;
this.selectDSButton.Enabled = false;
// enable the "Show Image Acquisition Interface" button:
this.showAcquireButton.Enabled = true;
// disable the "Close Data Source Manager" button:
this.closeDSMButton.Enabled = false;
}
}
Now that the application has successfully opened the TWAIN Data Source,
it is ready to acquire data. The device's TWAIN driver implements
a dialog box which usually allows the user to specify things like
acquisition rectangles, and image color depth. While this dialog is
displayed, it is important to disable access to the parent form to
prevent the user from prematurely closing the Data Source. When data
is ready to be acquired from the Source, the application will receive
a TWAIN.MSG.XFERREADY message. If the user chooses to cancel data
acquisition, the application will receive a TWAIN.MSG.CLOSEDSREQ message.
These messages are processed in the application's message filter.
To display the data acquisition dialog box, call TWAINComponent.ShowAcquireUI(),
as illustrated below in the following event handler:
private void showAcquireButton_Click( object sender, System.EventArgs e ) {
// attempt to display the image acquisition interface:
if(
this.twainComponent.ShowAcquireUI()
) {
// we need to disable access to the main form until
// the image acquisition dialog box is dismissed:
this.Enabled = false;
}
}
Finally, it is time to close the Data Source Manager (the Data Source
should have already been closed in the message filter). Because it
is possible (through capability negotiation) to bypass the display
of the data acquisition interface, the Data Source is closed and message
filter uninstalled during this phase as well. Once the Data Source
Manager has been closed, the process is ready to begin again.
private void closeDSMButton_Click( object sender, System.EventArgs e ) {
// close the Data Source Manager, if possible:
if(
this.twainComponent != null && this.closeDSMButton.Enabled
) {
// attempt to close the TWAIN Data Source:
if(
this.twainComponent.CloseDataSource()
) {
// uninstall our message filter:
this.RemoveMessageFilter();
}
// attempt to close the TWAIN Data Source Manager.
if(
this.twainComponent.CloseDataSourceManager()
) {
// enable the "Open Data Source Manager" button:
this.openDSMButton.Enabled
= true;
// disable the rest of the buttons:
this.selectDSButton.Enabled = false;
this.openDSButton.Enabled
= false;
this.showAcquireButton.Enabled = false;
this.closeDSMButton.Enabled = false;
}
}
}
By
now you should have a general idea of how TWAIN works, as well as
how TWAINSharp can help you add TWAIN functionality to your applications
quickly and easily. The example code for this article is available
on the TWAINSharp website, in the "Download
TWAINSharp" section. Buildable example code is included;
however, it will not run due to licensing restrictions. If you would
like to run the example application without a TWAIN capture source,
the TWAIN Tookit is available from the main TWAIN web site. This includes
a sample TWAIN Data Source driver. Some of the documentation was removed
from the code samples posted with this article to shorten the length.
The full documentation is available with the sample download.
TWAIN CSharpToHTML
TWAIN.net
CodeProject Article Virtual
Charting |