Image Acquisition Using TWAIN

The TWAIN software specification provides a uniform interface between graphics-supporting software and image-capturing hardware. Craig presents a C++ class, implemented as a Windows DLL, which can be used to add image acquisition to any Windows application.


September 01, 1994
URL:http://www.drdobbs.com/cpp/image-acquisition-using-twain/184409313

Figure 1


Copyright © 1994, Dr. Dobb's Journal

Figure 2


Copyright © 1994, Dr. Dobb's Journal

Figure 1


Copyright © 1994, Dr. Dobb's Journal

Figure 2


Copyright © 1994, Dr. Dobb's Journal

SEP94: Image Acquisition Using TWAIN

Image Acquisition Using TWAIN

Understanding container data structures is the key

Craig A. Lindley

Craig is the founder and an officer of Enhanced Data Technology of Colorado Springs, CO. He is the author of Practical Image Processing in C and Practical Ray Tracing in C, both published by John Wiley & Sons. Craig can be contacted on CompuServe at 73552,3375.


The TWAIN software specification is designed to provide a uniform interface between graphics-supporting software and image-capturing hardware, making it possible for you to build image-acquisition capabilities directly into your application. This means that you can spend more time writing the application and less time worrying about low-level device drivers for scanners, digitizer boards, digital cameras, and the like. I will discuss TWAIN in this article by presenting a C++ class, implemented as a Windows DLL, which can be used to add image acquisition to any Windows application. I've also included an example application to drive the DLL to show how the interface works. Although the focus here is on Windows apps, this code, along with that provided in the TWAIN toolkit, can be implemented for the Macintosh with very little trouble. Of course, using this code requires access to a scanner or other raster-image generating device along with its TWAIN-compliant device driver (referred to as a "Source"). If you have an older device without a TWAIN Source, contact the manufacturer to see if there is now a TWAIN Source available for it.

TWAIN has its roots in the 1990 Macintosh Scanner Roundtable, the forerunner of the TWAIN working group formed by representatives from imaging companies such as Aldus, Caere, Kodak, Hewlett Packard, and Logitech. The goal of the working group was to create an easy-to-use image-acquisition protocol and API that was useful to both image producers (hardware manufacturers) and image consumers (application developers).

In February 1992, the working group released the TWAIN Toolkit 1.0 (1.51 is the current version, available on CompuServe in the HP Peripheral Forum Library 15), which defined "the protocol and API for generalized acquisition of raster data." Figure 1 is a high-level diagram of the TWAIN Release 1.0 architecture. Most TWAIN transactions involve three entities:

You are responsible for developing the TWAIN Code, but you can use code in the TWAIN toolkit. The Source Manager was developed by the TWAIN working group and is distributed free of charge. The Source is provided by the hardware vendor in support of its TWAIN-compliant device.

The application program controls the acquisition process by making calls to DSM_Entry, the single entry point of the Source Manager. Parameters used in conjunction with these calls control the process. An application never calls a Source directly. As requests for service are made to the Source Manager, it acts on some directly and passes others to the selected Source as required. Image data is returned by the Source to the application under the supervision of the Source Manager. Because the application knows nothing about the hardware specifics concerning the Source with which it is communicating, the Source can be a local device, such as a SCSI-connected scanner, or a remote device connected via a network. Only the developer of the Source must (or should) be aware of the hardware specifics. An application simply requests connection to a specific Source, not caring how the connection is made.

The Source must provide a user interface (UI) for controlling its device. This releases you from having to develop a UI specific to each device your application supports. For instance, the Polaroid CS-500i TWAIN-compliant scanner UI is shown in Figure 2.

TWAIN Operational Overview

The TWAIN toolkit recommends two menu selections for control of TWAIN transactions: Select Source_ and Acquire_, both preferably located in the File menu. The Select Source_ operation allows the user to determine which Source (if more than one are available in the system) is to be used for image acquisition. Once the user selects a Source, it is used for all subsequent image acquisitions until another Source is selected. The Acquire_ operation typically brings up the UI of the selected Source for control of its corresponding device. Using the controls provided within the UI, the user decides how images are acquired for incorporation into the application.

A Source's UI can be treated as modal or nonmodal under Windows, although it is inherently nonmodal in nature. A modal interface (like that presented here) restricts the user to dealing with the scanner until the UI is closed down; typically, after an image is acquired and transferred into the application. At that time, control is returned to the application program. Other applications, however, use the Source's UI in a nonmodal way, bringing up the UI and keeping it up as just another window of the application. This allows images to be acquired for as long as the UI is active. Which method to use is dictated entirely by the application.

Except for error detection and recovery, the steps required to support the Select Source_ operation are:

  1. Open the Source Manager (OpenDSM). This brings the Source Manager into memory and extracts the DSM_Entry point for all subsequent TWAIN operations.
  2. Select the data Source (SelectDS). Executing this function causes the Source Manager to locate all Sources on the system and display a dialog box containing a list box for selecting which Source to utilize. While the dialog box is visible, F1 can be pressed to get a description of the highlighted Source for your inspection. Similarly, pressing Alt+W+G will bring up the list of members of the working group. Making a Source selection dismisses the dialog box.
  3. Close the Source Manager (CloseDSM). This may or may not be appropriate for a given application. In the code presented here, the Source Manager is unloaded after each operation, including this one. In other applications, the Source Manager might be brought up during program initialization and shut down when the application terminates.
Again, except for error detection and recovery, the steps required to support the image-acquisition operation are:

As straightforward as this seems, this doesn't mean the TWAIN interface is easy to understand and use. On the contrary, the specification will take time to fully understand and appreciate. A glance at DC.H (available electronically) makes this apparent. The specification document is full of information on controlling image transfers (native, through file, through memory, formatted/not), capability negotiation, state-transition diagrams, JPEG-compression issues, detailed message descriptions, and more. Anyone interested in understanding TWAIN should get the toolkit and read the documentation. After the second or third pass, the specifications will begin to make sense. (To get the $35.00 toolkit, call 800-722-0379 or download it from CompuServe--GO HPPERIPH--and access the TWAIN library, #15.)

Containers

Containers are data structures, used to hold structured information, that are passed between an application and a Source. Specifically, containers are used for information exchange during "capability negotiation." The four container types are:

In all, there are 13 TWAIN-defined data types that can show up in containers; see
Table 1. CONTAINR.HPP (Listing One) and CONTAINR.CPP (Listing Two) include the functions for manipulating containers of all types. Note that it is always the application program's responsibility to release any memory occupied by containers when it is finished with them. This is true whether or not the container memory was originally allocated by the Source or by the application.

Capability Negotiations

It is via capability negotiations that an application program informs a Source about the type of image(s) that it desires or that it can deal with. For example, if an application only wants to handle color images but is connected to a Source capable of handling both color and gray-scale images, the application would negotiate the ICAP_PIXELTYPE capability with the Source (see RestrictToRGB in TWAIN.CPP, available electronically). A successful negotiation of this capability results in the Source's UI not allowing the selection of gray-scale images. Therefore, the Source could only acquire color images for the application.

Most negotiations between an application and a Source are of a similar form. The application first asks the Source its capabilities, and the Source returns a container describing them. The application then chooses from these capabilitites and requests the Source to limit itself to a certain subset of its capability. The Source will either agree or disagree with the application's request. Capability negotiations are tricky because Sources are not required to negotiate on every conceivable capability. Consequently, the application program must be ready for a refusal to negotiate at any point in the process. This is demonstrated by SetResolution (in TWAIN.CPP), which accepts as a parameter a specification in dots per inch for the maximum resolution for image acquisition.

To begin the SetResolution negotiation, you must inquire of the Source which resolutions it supports by sending a GET message on the Source's ICAP_XRESOLUTION capability. The messaging is done by forming the parameters and calling the DSM_Entry point. The TWAIN specification says that a container of One-Value, Range, or Enumeration type can be used to return the XResolution capability data. Consequently, the code must be prepared to parse any of these returned container types. The returned data type for the XResolution capability is specified as DCTY_FIX32, a fixed-point representation of a floating-point number.

The various container types are processed using a switch statement on the returned container type. If a One-Value container type is returned, the Source is capable of only a single resolution and no negotiations are possible. If the Source returns a Range container, all values less than or equal to the requested resolution are stored for later use. The same is true of all values returned in an Enumeration container. Once all of the data in the returned container is parsed, container memory is freed.

At this point, all resolutions supported by the Source that meet the specification have been saved. The application must then tell the Source which resolutions it is free to use by forming an Enumeration container containing the allowed values in FIX32 format. This is passed to the Source via a SET message on the ICAP_XRESOLUTION capability. If the Source accepts the request, it will limit the resolution selections provided in its UI to just those specified values. It may, however, still refuse the negotiation.

Negotiation must take place within certain states of the TWAIN protocol. For example, to negotiate with a source, you must have first loaded and executed the Source Manager, then opened a Source. Only then will the Source be available for the negotiation of capabilities. Most capabilities must be negotiated in this state (state 4, according to the specification), although a mechanism exists within the specification for extended negotiation in other states.

A Sample Application

The code for the sample application is shown in Listing Three . In addition to the code in Listing Three, the application requires a Windows DLL called MYTWAIN.DLL that supports TWAIN. The files required to produce the DLL and sample application are available electronically; see "Availability," page 3.

The sample app has a File menu with Select Source_, Acquire_, and Exit entities. When the Select Source_ operation is executed, the Source Manager is instructed to put up the Source-selection dialog box so the user can select the Source to acquire from. When the Acquire_ operation is selected, the UI provided by the Source is brought up so the user can acquire an image. When an image is acquired, it is written as a TIFF file to the filename hardcoded into the application program. Admittedly, this isn't very flexible, but it's fine for illustration.

The important thing to notice about the app in Listing Three is that when the application window (TAppWindow) is created, an instance of the Twain class is instantiated. When the application window is closed, that reference to the Twain class is deleted. When the Select Source_ message is detected, the Twain class member function SelectSource is called with the handle to the application's window. This puts up the selection dialog box. When the Acquire_ message is detected, the Twain class member function ScanImage is called, passing the path and filename in which to store the scanned image. For simplicity, the scanned image is not displayed within the application's window; it is only written to the file.

Figure 1 The TWAIN architecture.

Figure 2 Typical UI for a TWAIN-compliant device.

Table 1: TWAIN data types.

    Data Type     Description

    DCTY_INT8     8-bit signed value
    DCTY_INT16    16-bit signed value
    DCTY_INT32    32-bit signed value
    DCTY_UINT8    8-bit unsigned value
    DCTY_UINT16   16-bit unsigned value
    DCTY_UINT32   32-bit unsigned value
    DCTY_BOOL     Boolean value
    DCTY_FIX32    Fixed-point description of a
                  floating-point number
    DCTY_FRAME    Data structure defining an
                  area. Includes Left, Top,
                  Right, and Bottom.
    DCTY_STR32    A string 32 bytes in length
    DCTY_STR64    A string 64 bytes in length
    DCTY_STR128   A string 128 bytes in length
    DCTY_STR255   A string 255 bytes in length

Listing One


/***************************************************************************/
/*** containr.hpp -- interface class for TWAIN containers.               ***/
/*** adapted by Craig A. Lindley -- Revision: 1.0  Last Update: 12/11/93 ***/
/****************************************************************************/
// See the file containr.cpp for the revision history
// Check to see if this file already included
#ifndef CONTAINR_HPP
#define CONTAINR_HPP

#include "dc.h"
class huge Containr {
  private:
    void GetItem(DC_UINT16 Type, LPVOID lpSource, LPVOID lpDest,
                                               int SourceIndex, int DestIndex);
  public:
   DC_FIX32 FloatToFIX32(float AFloat);
   float FIX32ToFloat(DC_FIX32 Fix32);
   BOOL BuildUpOneValue(pDC_CAPABILITY pCap,DC_UINT16 ItemType,DC_UINT32 Item);
   BOOL ExtractOneValue(pDC_CAPABILITY pCap, LPVOID pVoid);
   BOOL BuildUpEnumerationType(pDC_CAPABILITY pCap,pDC_ENUMERATION pE,
                                                                LPVOID lpList);
   BOOL ExtractEnumerationValue(pDC_CAPABILITY pCap, LPVOID pVoid, int Index);
   BOOL BuildUpArrayType(pDC_CAPABILITY pCap, pDC_ARRAY pA, LPVOID lpList);
   BOOL ExtractArrayValue(pDC_CAPABILITY pCap, LPVOID pVoid, int Index);
   BOOL BuildUpRangeType(pDC_CAPABILITY pCap, pDC_RANGE lpRange);
   BOOL ExtractRange(pDC_CAPABILITY pCap, pDC_RANGE lpRange);
};
#endif


Listing Two


/****************************************************************************/
/*** containr.cpp -- interface class for TWAIN containers.               ***/
/*** adapted by Craig A. Lindley -- Revision: 1.0 Last Update: 12/11/93  ***/
/***************************************************************************/

#include "containr.hpp"

// Array of type sizes in bytes
DCItemSize[] = {
  sizeof(DC_INT8),
  sizeof(DC_INT16),
  sizeof(DC_INT32),
  sizeof(DC_UINT8),
  sizeof(DC_UINT16),
  sizeof(DC_UINT32),
  sizeof(DC_BOOL),
  sizeof(DC_FIX32),
  sizeof(DC_FRAME),
  sizeof(DC_STR32),
  sizeof(DC_STR64),
  sizeof(DC_STR128),
  sizeof(DC_STR255),
};
/*** FloatToFIX32 -- Convert a floating point value into a FIX32. ***/
DC_FIX32 Containr::FloatToFIX32(float AFloat) {
  DC_FIX32 Fix32_value;
  DC_INT32 Value = (DC_INT32) (AFloat * 65536.0 + 0.5);
  Fix32_value.Whole = Value >> 16;
  Fix32_value.Frac = Value & 0x0000ffffL;
  return(Fix32_value);
}
/*** FIX32ToFloat -- Convert a FIX32 value into a floating point value ***/
float Containr::FIX32ToFloat(DC_FIX32 Fix32) {
  float AFloat;
  AFloat = (float) Fix32.Whole + (float) Fix32.Frac / 65536.0;
  return(AFloat);
}
/*** GetItem -- Gets data item at lpSource[SIndex] of datatype Type and stores
 *** it at lpDest[DIndex]. ***/
void Containr::GetItem(DC_UINT16 Type, LPVOID lpSource,
                        LPVOID lpDest, int SIndex, int DIndex) {
  switch (Type) {
    case DCTY_INT8:
      *((pDC_INT8)lpDest + DIndex) = *((pDC_INT8)lpSource + SIndex);
      break;
    case DCTY_UINT8:
      *((pDC_UINT8)lpDest + DIndex) = *((pDC_UINT8)lpSource + SIndex);
      break;
    case DCTY_INT16:
    case 44:                 // DCTY_HANDLE
      *((pDC_INT16)lpDest + DIndex) = *((pDC_INT16)lpSource + SIndex);
      break;
    case DCTY_UINT16:
    case DCTY_BOOL:
      *((pDC_UINT16)lpDest + DIndex) = *((pDC_UINT16)lpSource + SIndex);
      break;
    case DCTY_INT32:
      *((pDC_INT32)lpDest + DIndex) = *((pDC_INT32)lpSource + SIndex);
      break;
    case DCTY_UINT32:
    case 43:                 // DCTY_MEMREF
      *((pDC_UINT32)lpDest + DIndex) = *((pDC_UINT32)lpSource + SIndex);
      break;
    case DCTY_FIX32:
      *((pDC_FIX32)lpDest + DIndex) = *((pDC_FIX32)lpSource + SIndex);
      break;
    case DCTY_STR32:
      lstrcpy((pDC_STR32)lpDest + DIndex, (pDC_STR32)lpSource + SIndex);
      break;
    case DCTY_STR64:
      lstrcpy((pDC_STR64)lpDest + DIndex, (pDC_STR64)lpSource + SIndex);
      break;
    case DCTY_STR128:
      lstrcpy((pDC_STR128)lpDest + DIndex, (pDC_STR128)lpSource + SIndex);
      break;
    case DCTY_STR255:
      lstrcpy((pDC_STR255)lpDest + DIndex, (pDC_STR255)lpSource + SIndex);
      break;
  }
}
/*** FUNCTION: BuildUpOneValue ***/ 
 *** ARGS: pCap, pointer to a capability structure, details about container
 *     ItemType, constant that defines the type of the Item to follow
 *     Item, the data to put into the OneValue container
 * RETURNS: pData->hContainer set to address of the container handle, ptr is 
 *     returned there. A TRUE BOOL is returned from this function if
 *     all is well and FALSE if container memory could not be allocated.
 * NOTES:  This function creates a container of type OneValue and returning 
 *    with the hContainer value (excuse me) "pointing" to container. Container 
 *    is filled with values for ItemType and Item requested by the caller. */
BOOL Containr::BuildUpOneValue(pDC_CAPABILITY pCap, DC_UINT16 ItemType, 
                                                              DC_UINT32 Item) {
  pDC_ONEVALUE pOneValue;
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND, 
                                               sizeof(DC_ONEVALUE))) != NULL) {
    // log the container type
    pCap->ConType = DCON_ONEVALUE;
    if ((pOneValue = (pDC_ONEVALUE)GlobalLock(pCap->hContainer)) != NULL) {
      pOneValue->ItemType = ItemType;    // DCTY_XXXX
      pOneValue->Item     = Item;        // DCPT_XXXX...
      GlobalUnlock(pCap->hContainer);
      return TRUE;
    } else {                       // If lock error, free memory
      GlobalFree(pCap->hContainer);
      pCap->hContainer = 0;
    }
  }
  // Could not allocate or lock memory
  return FALSE;
}
/*** FUNCTION: ExtractOneValue 
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pVoid   ptr will be set to point to the item on return
 * RETURNS: pVoid pts to extracted value.
 * NOTES:   This routine will open a container and extract the Item.  The Item 
 * will be returned to the caller in pVoid.  I will type cast the returned 
 * value to that of ItemType.  */   
BOOL Containr::ExtractOneValue(pDC_CAPABILITY pCap, LPVOID pVoid) {
  pDC_ONEVALUE pOneValue;
  if ((pOneValue = (pDC_ONEVALUE)GlobalLock(pCap->hContainer)) != NULL) {
    // Extract the one value
    GetItem(pOneValue->ItemType, (LPVOID) &(pOneValue->Item), pVoid, 0, 0);
    GlobalUnlock(pCap->hContainer);
    return TRUE;
  }
  return FALSE;
}
/*** FUNCTION: BuildUpEnumerationType 
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pE      ptr to struct that contains the other fields of ENUM struct
 *          *pList  ptr to array of elements to put into the ENUM array
 * RETURNS: pData->hContainer set to address of the container handle, ptr is 
 *          returned here
 * NOTES:   The routine dynamically allocates a chunk of memory large enough 
 * to contain all the struct pDC_ENUMERATION as well as store it's ItemList 
 * array INTERNAL to the struct.  The array itself and it's elements must be
 * type cast to ItemType.  I do not know how to dynamically cast elements
 * of an array to ItemType so it is time for a big static switch.>>>
 * Protocol: Used by MSG_GET.. calls were Source allocates the container and 
 * APP uses and then frees the container. */
BOOL Containr::BuildUpEnumerationType(pDC_CAPABILITY pCap, pDC_ENUMERATION pE,
                                                               LPVOID lpList) {
  pDC_ENUMERATION pEnumeration;   // template for ENUM fields
  int Index;                      // anyone with more than 32K array elements
                                  // should crash.  Could type on NumItems.
  LPVOID pVoid;
  // allocate a block large enough for struct and complete enumeration array
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND,
                          (sizeof(DC_ENUMERATION)-sizeof(DC_UINT8))+
                           pE->NumItems*DCItemSize[pE->ItemType])) == NULL)
    return FALSE;                 // return FALSE if memory error
 if ((pEnumeration = (pDC_ENUMERATION) GlobalLock(pCap->hContainer)) == NULL) {
    GlobalFree(pCap->hContainer); // return FALSE if memory error
    return FALSE;
  }
  pCap->ConType = DCON_ENUMERATION;              // Fill in container type
  pEnumeration->ItemType = pE->ItemType;         // DCTY_XXXX
  pEnumeration->NumItems = pE->NumItems;         // DCPT_XXXX...
  pEnumeration->CurrentIndex = pE->CurrentIndex; // current index setting
  pEnumeration->DefaultIndex = pE->DefaultIndex; // default index setting
  // Assign base address of ItemList array to 'generic' pointer
  // i.e. reposition the struct pointer to overlay the allocated block
  pVoid = (LPVOID)pEnumeration->ItemList;
  // Now store the enumerated items
  for (Index=0; Index < (int)pE->NumItems; Index++)
    GetItem(pE->ItemType, (LPVOID) lpList, (LPVOID) pVoid, Index, Index);
  // Unlock the container
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: ExtractEnumerationValue
*  ARGS:    pCap    pointer to a capability structure, details about container
*           pVoid   ptr will be set to point to the item on return
*           Index   requested index into the enumeration
* RETURNS: pVoid   is set to pointer to itemtype
* NOTES:   This routine will open a container and extract the Item.  The 
* Item will be returned to the caller in pVoid. Returned value will
* be type cast to that of ItemType.
* COMMENTS: only a single value is returned; referred to by indexed value. */
BOOL Containr::ExtractEnumerationValue(pDC_CAPABILITY pCap, LPVOID pVoid, 
                                                                   int Index) {
  pDC_ENUMERATION pEnumeration;
  LPVOID pItemList;
  // Lock the container for access
  if ((pEnumeration = (pDC_ENUMERATION) GlobalLock(pCap->hContainer)) == NULL)
    return FALSE;
  // Check that Index is within range
  if (Index > pEnumeration->NumItems-1)
    return FALSE;
  // Assign base address of ItemList array to 'generic' pointer
  pItemList = (LPVOID) pEnumeration->ItemList;
  GetItem(pEnumeration->ItemType, pItemList, pVoid, Index, 0);
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: BuildUpArrayType
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pA      ptr to struct that contains the other fields of ARRAY struct
 *          *pList  ptr to array of elements to put into the ARRAY struct
 * RETURNS: pData->hContainer set to address of the container handle, ptr is 
 *          returned here
 * NOTES: The routine dynamically allocates a chunk of memory large enough to
 * contain all the struct pDC_ARRAY as well as store it's ItemList array
 * INTERNAL to the struct.  The array itself and it's elements must be
 * type cast to ItemType. */
BOOL Containr::BuildUpArrayType(pDC_CAPABILITY pCap, pDC_ARRAY pA,
                                                               LPVOID lpList) {
  pDC_ARRAY pArray;
  int Index;                      // No more than 32K array elements
  LPVOID pVoid;
  // Allocate a block large enough for struct and complete array
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND,
                          (sizeof(DC_ARRAY)-sizeof(DC_UINT8))+
                           pA->NumItems*DCItemSize[pA->ItemType])) == NULL)
    return FALSE;            // Return FALSE if error
  // Lock the memory
  if ((pArray = (pDC_ARRAY) GlobalLock(pCap->hContainer)) == NULL) {
    GlobalFree(pCap->hContainer);
    return FALSE;            // Return FALSE if error
  }
  pArray->ItemType = pA->ItemType;    // DCTY_XXXX
  pArray->NumItems = pA->NumItems;    // DCPT_XXXX...
  // Assign base address of ItemList array to 'generic' pointer
  // i.e. reposition the struct pointer to overlay the allocated block
  pVoid = (LPVOID)pArray->ItemList;
  // For each item of the array
  for (Index=0; Index < (int)pA->NumItems; Index++)
    GetItem(pA->ItemType, lpList, pVoid, Index, Index);
  // Unlock the memory
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: ExtractArrayValue
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pVoid   ptr will be set to point to the item on return
 *          Index   requested index into the array
 * RETURNS: pVoid   is set to pointer to itemtype
 * NOTES:   This routine will open a container and extract the Item.  The 
 * Item will be returned to the caller in pVoid. Returned value will
 * be type cast to that of ItemType.
 * COMMENTS: only a single value is returned; referred to by indexed value.  */
BOOL Containr::ExtractArrayValue(pDC_CAPABILITY pCap,LPVOID pVoid,int Index) {
  pDC_ARRAY pArray;
  LPVOID pItemList;
  // Lock the container for access
  if ((pArray = (pDC_ARRAY) GlobalLock(pCap->hContainer)) == NULL)
    return FALSE;
  // Check that Index is within range
  if (Index > pArray->NumItems-1)
    return FALSE;
  // Assign base address of ItemList array to 'generic' pointer
  pItemList = (LPVOID) pArray->ItemList;
  GetItem(pArray->ItemType, pItemList, pVoid, Index, 0);
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: BuildUpRangeType
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          lpRange ptr to RANGE struct
 * RETURNS: pCap->hContainer set to address of the container handle, ptr is 
 *          returned here
 * NOTES: The routine dynamically allocates a chunk of memory large enough to
 * contain the RANGE struct.  */
BOOL Containr::BuildUpRangeType(pDC_CAPABILITY pCap, pDC_RANGE lpRange) {
  pDC_RANGE pRange;
  // Allocate a block large enough for RANGE struct
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND, 
                                                    sizeof(DC_RANGE))) == NULL)
    return FALSE;            // Return FALSE if error
  // Lock the memory
  if ((pRange = (pDC_RANGE) GlobalLock(pCap->hContainer)) == NULL) {
    GlobalFree(pCap->hContainer);
    return FALSE;            // Return FALSE if error
  }
  // Copy complete RANGE structure
  *pRange = *lpRange;
  // Unlock the memory
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: ExtractRange
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          lpRange ptr to RANGE struct for return
 * NOTES:   This routine will open a container and extract the RANGE.
 * COMMENTS: the complete RANGE struct is returned at lpRange.  */
BOOL Containr::ExtractRange(pDC_CAPABILITY pCap, pDC_RANGE lpRange) {
  pDC_RANGE pRange;
  // Lock the container for access
  if ((pRange = (pDC_RANGE) GlobalLock(pCap->hContainer)) == NULL)
    return FALSE;
  // Copy the complete structure
  *lpRange = *pRange;
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}


Listing Three


// Sample TWAIN Application Program -- (c) Craig A. Lindley 1993
/* This program exercises the MYTWAIN.DLL. It is written in Borland's OWL. It 
allows the user to select a Source for acquisition and to acquire an image from
the selected Source. Each acquired image is written to the file c:\scanimg.tif
in the root directory of drive C. Images are not displayed. */

#include <owl.h>
#include "app.h"
#include "twain.hpp"

class TSampleTWAINApp : public TApplication {
  public:
    TSampleTWAINApp(LPSTR AName, HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
    : TApplication(AName, hInstance, hPrevInstance,
                   lpCmdLine, nCmdShow) {};
    virtual void InitMainWindow();
};
_CLASSDEF(TAppWindow)
class TAppWindow : public TWindow {
  private:
    virtual BOOL CanClose();
    virtual void CMSelectSource(RTMessage Msg)
      = [CM_FIRST + CMID_SELECTSOURCE];
    virtual void CMAcquire(RTMessage Msg)
      = [CM_FIRST + CMID_ACQUIRE];
    virtual void CMHelp(RTMessage Msg)
      = [CM_FIRST + CMID_HELP];
    Twain *TwainClassPtr;         // Pointer the the Twain object
  public:
    TAppWindow(PTWindowsObject AParent, LPSTR ATitle);
   ~TAppWindow();
};
// Window Class Constructor
TAppWindow::TAppWindow(PTWindowsObject AParent, LPSTR ATitle)
             : TWindow(AParent, ATitle) {
  AssignMenu("CmdMenu");          // Assign the menu to the window
  TwainClassPtr = new Twain;      // Instantiate the Twain class object
}
// Window Class Destructor
TAppWindow::~TAppWindow() {
  delete TwainClassPtr;           // Delete the Twain class object
}
BOOL TAppWindow::CanClose() {
  return TRUE;                    // Allow the window to close
}
// This function is called when the Select Source menu item is clicked
void TAppWindow::CMSelectSource(RTMessage) {
  // Make a call to the DLL to perform the operation
  TwainClassPtr->SelectSource(HWindow);
}
// This function is called when the Acquire menu item is clicked
void TAppWindow::CMAcquire(RTMessage) {
  // Make a call to the DLL to perform the operation
  TwainClassPtr->ScanImage("c:\\scanimg.tif");
}
// This function is called when the Help menu item is clicked
void TAppWindow::CMHelp(RTMessage) {
  MessageBox(HWindow, "(c) Craig A. Lindley, 1993",
                      "Sample TWAIN Application", MB_OK);
}
void TSampleTWAINApp::InitMainWindow() {
  MainWindow = new TAppWindow(NULL, Name);
}
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
  TSampleTWAINApp MyApp("Sample TWAIN Application Program",
                         hInstance, hPrevInstance, lpCmdLine, nCmdShow);
  MyApp.nCmdShow = SW_SHOWMAXIMIZED;
  MyApp.Run();
  return MyApp.Status;
}
  // Assign base address of ItemList array to 'generic' pointer
  pItemList = (LPVOID) pEnumeration->ItemList;
  GetItem(pEnumeration->ItemType, pItemList, pVoid, Index, 0);
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: BuildUpArrayType
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pA      ptr to struct that contains the other fields of ARRAY struct
 *          *pList  ptr to array of elements to put into the ARRAY struct
 * RETURNS: pData->hContainer set to address of the container handle, ptr is 
 *          returned here
 * NOTES: The routine dynamically allocates a chunk of memory large enough to
 * contain all the struct pDC_ARRAY as well as store it's ItemList array
 * INTERNAL to the struct.  The array itself and it's elements must be
 * type cast to ItemType. */
BOOL Containr::BuildUpArrayType(pDC_CAPABILITY pCap, pDC_ARRAY pA,
                                                               LPVOID lpList) {
  pDC_ARRAY pArray;
  int Index;                      // No more than 32K array elements
  LPVOID pVoid;
  // Allocate a block large enough for struct and complete array
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND,
                          (sizeof(DC_ARRAY)-sizeof(DC_UINT8))+
                           pA->NumItems*DCItemSize[pA->ItemType])) == NULL)
    return FALSE;            // Return FALSE if error
  // Lock the memory
  if ((pArray = (pDC_ARRAY) GlobalLock(pCap->hContainer)) == NULL) {
    GlobalFree(pCap->hContainer);
    return FALSE;            // Return FALSE if error
  }
  pArray->ItemType = pA->ItemType;    // DCTY_XXXX
  pArray->NumItems = pA->NumItems;    // DCPT_XXXX...
  // Assign base address of ItemList array to 'generic' pointer
  // i.e. reposition the struct pointer to overlay the allocated block
  pVoid = (LPVOID)pArray->ItemList;
  // For each item of the array
  for (Index=0; Index < (int)pA->NumItems; Index++)
    GetItem(pA->ItemType, lpList, pVoid, Index, Index);
  // Unlock the memory
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: ExtractArrayValue
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          pVoid   ptr will be set to point to the item on return
 *          Index   requested index into the array
 * RETURNS: pVoid   is set to pointer to itemtype
 * NOTES:   This routine will open a container and extract the Item.  The 
 * Item will be returned to the caller in pVoid. Returned value will
 * be type cast to that of ItemType.
 * COMMENTS: only a single value is returned; referred to by indexed value.  */
BOOL Containr::ExtractArrayValue(pDC_CAPABILITY pCap,LPVOID pVoid,int Index) {
  pDC_ARRAY pArray;
  LPVOID pItemList;
  // Lock the container for access
  if ((pArray = (pDC_ARRAY) GlobalLock(pCap->hContainer)) == NULL)
    return FALSE;
  // Check that Index is within range
  if (Index > pArray->NumItems-1)
    return FALSE;
  // Assign base address of ItemList array to 'generic' pointer
  pItemList = (LPVOID) pArray->ItemList;
  GetItem(pArray->ItemType, pItemList, pVoid, Index, 0);
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: BuildUpRangeType
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          lpRange ptr to RANGE struct
 * RETURNS: pCap->hContainer set to address of the container handle, ptr is 
 *          returned here
 * NOTES: The routine dynamically allocates a chunk of memory large enough to
 * contain the RANGE struct.  */
BOOL Containr::BuildUpRangeType(pDC_CAPABILITY pCap, pDC_RANGE lpRange) {
  pDC_RANGE pRange;
  // Allocate a block large enough for RANGE struct
  if ((pCap->hContainer = (DC_HANDLE) GlobalAlloc(GHND, 
                                                    sizeof(DC_RANGE))) == NULL)
    return FALSE;            // Return FALSE if error
  // Lock the memory
  if ((pRange = (pDC_RANGE) GlobalLock(pCap->hContainer)) == NULL) {
    GlobalFree(pCap->hContainer);
    return FALSE;            // Return FALSE if error
  }
  // Copy complete RANGE structure
  *pRange = *lpRange;
  // Unlock the memory
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}
/*** FUNCTION: ExtractRange
 * ARGS:    pCap    pointer to a capability structure, details about container
 *          lpRange ptr to RANGE struct for return
 * NOTES:   This routine will open a container and extract the RANGE.
 * COMMENTS: the complete RANGE struct is returned at lpRange.  */
BOOL Containr::ExtractRange(pDC_CAPABILITY pCap, pDC_RANGE lpRange) {
  pDC_RANGE pRange;
  // Lock the container for access
  if ((pRange = (pDC_RANGE) GlobalLock(pCap->hContainer)) == NULL)
    return FALSE;
  // Copy the complete structure
  *lpRange = *pRange;
  GlobalUnlock(pCap->hContainer);
  return TRUE;
}




Listing Three


// Sample TWAIN Application Program -- (c) Craig A. Lindley 1993
/* This program exercises the MYTWAIN.DLL. It is written in Borland's OWL. It 
allows the user to select a Source for acquisition and to acquire an image from
the selected Source. Each acquired image is written to the file c:\scanimg.tif
in the root directory of drive C. Images are not displayed. */

#include <owl.h>
#include "app.h"
#include "twain.hpp"

class TSampleTWAINApp : public TApplication {
  public:
    TSampleTWAINApp(LPSTR AName, HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
    : TApplication(AName, hInstance, hPrevInstance,
                   lpCmdLine, nCmdShow) {};
    virtual void InitMainWindow();
};
_CLASSDEF(TAppWindow)
class TAppWindow : public TWindow {
  private:
    virtual BOOL CanClose();
    virtual void CMSelectSource(RTMessage Msg)
      = [CM_FIRST + CMID_SELECTSOURCE];
    virtual void CMAcquire(RTMessage Msg)
      = [CM_FIRST + CMID_ACQUIRE];
    virtual void CMHelp(RTMessage Msg)
      = [CM_FIRST + CMID_HELP];
    Twain *TwainClassPtr;         // Pointer the the Twain object
  public:
    TAppWindow(PTWindowsObject AParent, LPSTR ATitle);
   ~TAppWindow();
};
// Window Class Constructor
TAppWindow::TAppWindow(PTWindowsObject AParent, LPSTR ATitle)
             : TWindow(AParent, ATitle) {
  AssignMenu("CmdMenu");          // Assign the menu to the window
  TwainClassPtr = new Twain;      // Instantiate the Twain class object
}
// Window Class Destructor
TAppWindow::~TAppWindow() {
  delete TwainClassPtr;           // Delete the Twain class object
}
BOOL TAppWindow::CanClose() {
  return TRUE;                    // Allow the window to close
}
// This function is called when the Select Source menu item is clicked
void TAppWindow::CMSelectSource(RTMessage) {
  // Make a call to the DLL to perform the operation
  TwainClassPtr->SelectSource(HWindow);
}
// This function is called when the Acquire menu item is clicked
void TAppWindow::CMAcquire(RTMessage) {
  // Make a call to the DLL to perform the operation
  TwainClassPtr->ScanImage("c:\\scanimg.tif");
}
// This function is called when the Help menu item is clicked
void TAppWindow::CMHelp(RTMessage) {
  MessageBox(HWindow, "(c) Craig A. Lindley, 1993",
                      "Sample TWAIN Application", MB_OK);
}
void TSampleTWAINApp::InitMainWindow() {
  MainWindow = new TAppWindow(NULL, Name);
}
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
  TSampleTWAINApp MyApp("Sample TWAIN Application Program",
                         hInstance, hPrevInstance, lpCmdLine, nCmdShow);
  MyApp.nCmdShow = SW_SHOWMAXIMIZED;
  MyApp.Run();
  return MyApp.Status;
}

Copyright © 1994, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.