Skip to main content

Java Tech: Acquire Images with TWAIN and SANE, Part 2

January 25, 2005

{cs.r.title}









Contents
The New JTwain
A Capable JTwain
   Capabilities Quick Study
The Revised JTwainDemo
Conclusion
Resources
Answers to Previous Homework

Last time,
I introduced a three-part series on TWAIN and SANE.
Part one explored TWAIN in terms of the big picture and necessary details,
presented the JTwain API library that makes TWAIN available to Java,
and revealed the JTwainDemo application that interacts with JTwain to
obtain images from scanners and other image-acquisition devices (and present
them to the user).

Part one's JTwain API library has some problems. I begin the second part of this
series by pointing out these problems and introducing a new JTwain API library
that overcomes them. I next explore data source capabilities--a concept I
hinted at in part one--and introduce that portion of the new JTwain API
library dedicated to capabilities. I wrap up part two by introducing an improved
JTwainDemo that demonstrates the new JTwain.

Note: Unlike part one, this article does not present build instructions (for brevity).
However, you'll find a make file for the library's DLL in the sample code file
associated with this article (see Resources).

The New JTwain

Several design decisions are responsible for the simplicity of part one's JTwain
API library: keep the library small (two native methods), allow only a single
image to be transferred in a session, and depend on Windows dialog boxes for
the user interface when acquiring an image or selecting a default data source.
Ironically, these same design decisions also introduce problems, necessitating
a new JTwain API library:

  • Inefficient implementation: Each acquire() method call has
    to open the data source manager, open the default data source, display a data-source-specific dialog box, obtain one image, close the default data source,
    and close the data source manager. Repeatedly opening and closing the data
    source manager and the same default data source (unless changed via a call to
    selectSourceAsDefault()) is not efficient. I feel that a better
    design would place these individual operations into their own API methods.

  • Single-image transfer: The acquire() method only returns a
    single image. This rules out acquiring the multiple images that are made available
    by automatic document feeders. A better approach: acquire() needs
    to return an array of images.

  • Windows dialog box dependence: selectSourceAsDefault() and
    acquire() present Windows dialog boxes to the user. This reliance
    on Windows detracts from whatever non-Windows look and feel we might choose for
    our JTwain applications. It would be better to remove acquire()'s
    dialog box, do away with selectSourceAsDefault(), and provide the
    necessary JTwain methods that help to construct Java-based "Select Source" and
    data-source-specific dialog boxes.

I've created a new JTwain API library that solves the aforementioned problems.
Multiple native API methods (including a streamlined acquire())
result in a more efficient implementation. The acquire() method
now returns an array of images. And the Windows dialog boxes are no more: I've
removed selectSourceAsDefault() (responsible for "Select Source"),
and the remodelled acquire() method doesn't display a data-source-specific Windows dialog box. Except for three methods (that I introduce
later), all methods in the new API are described below:

  • public static native Image [] acquire() acquires an array of
    images from the currently open data source. The length of the array (which is
    allocated by JTwain) determines the number of images that were acquired.

    This method will typically return a single image, unless certain capabilities
    (a concept I discuss later) in regard to an automatic document feeder are set
    to appropriate values. I do not discuss automatic document feeders and how to
    set them up, because I don't have access to those devices. To learn more about
    TWAIN's support for automatic document feeders, I recommend studying the TWAIN
    specification (see Resources).

  • public static native void closeDS() closes the currently open
    data source.

  • public static native void closeDSM() closes the data source
    manager.

  • public static native int getCC(int dest) returns the condition
    code from the most recent operation associated with either the data source
    manager or the currently open data source. If dest is 0, the
    condition code is returned from the data source manager. If dest
    is non-zero, the condition code is returned from the currently open data
    source.

    JTwain declares several constants starting with CC_
    that represent all condition codes. If you need to specify a condition code at
    the source code level, use one of these constants instead of the actual value.

  • public static native String getDefaultDS() returns the name of
    the default data source. An application should acquire images from this data
    source if the user has not chosen to select a data source. Also, this name
    should be highlighted in a Java-based "Select Source" dialog box the first
    time that dialog box is displayed.

  • public static native String getFirstDS() returns the name of the
    first data source. When enumerating all data source names, this method must be
    called before getNextDS().

  • public static native String getNextDS() returns the name of the
    next data source, or an empty string if there are no more data sources. When
    enumerating all data source names, call getNextDS() after calling
    getFirstDS().

  • public static native int getRC() returns the return code from the
    most recent operation.

    JTwain declares several constants starting with RC_
    that represent all return codes. If you need to specify a return code at the
    source code level, use one of these constants instead of the actual value.

  • public static native void openDS(String srcName) opens the data
    source identified by srcName. The open data source is referred to
    as the currently open data source.

  • public static native void openDSM() opens the data source manager.

Except for getRC(), which throws no exceptions, each of the above
methods is capable of throwing JTwainException objects.

Note: The methods getRC() and getCC() return values that
identify failures with TWAIN. They do not return values that identify non-TWAIN
failures. The only places where a non-TWAIN failure might occur are located in the
acquire() method: an image transfer used compression or did not
produce an 8-bit (grayscale) or 24-bit (RGB) image, the transferred image could
not be added to an internal vector data structure (because of limited memory),
or the image could not be converted from Windows' device-independent bitmap
format to a Java-based image (perhaps a Java class or method couldn't be found,
memory was exhausted, or a method could not be called).

To demonstrate some of the methods in the new JTwain API, I've written a short
program that enumerates all data source names. The enumeration technique could
be inserted into dialog box code to create a Java-based "Select Source" dialog
box.

// EnumSources.java
import net.javajeff.jtwain.*;
public class EnumSources
{
   public static void main (String [] args)
   {
      // Initialize JTwain.
    
      if (!JTwain.init ())
      {
          System.out.println ("TWAIN unsupported");
          return;
      }
      try
      {
          // Open data source manager.
          JTwain.openDSM ();
          try
          {
              // Get name of first data source.
              String name = JTwain.getFirstDS ();
              do
              {
                 // Output data source name.
                 System.out.println (name);
                 // Get name of next data source.
                 name = JTwain.getNextDS ();
              }
              while (!name.equals (""));
          }
          catch (JTwainException e)
          {
              // Output return code and condition
              // code from data source manager
              // (reason for failure).
              System.out.println (e + ", RC = " +
                                  JTwain.getRC () +
                                  ", CC = " +
                                  JTwain.getCC (0));
          }
          // Close data source manager.
          JTwain.closeDSM ();
      }
      catch (JTwainException e)
      {
          // Output return code (reason for
          // failure).
          System.out.println (e + ", RC = " +
                              JTwain.getRC ());
      }
   }
}

Because EnumSources.java is commented, I won't bother to describe
the source code. Compile that code and run it to obtain a list of data source
names on your platform. You will need those names to play with the program in
the next section. For example, when I execute java EnumSources on
my platform, I obtain the following list of data source names:

HP PrecisionScan LT 3.0
TWAIN_32 Sample Source

A Capable JTwain

Image-acquisition devices support varying features. For example, some scanners
support automatic document feeders to automate the scanning of multiple
documents, and many digital cameras offer a camera preview mode (not to
mention information on battery life). Support for color images and information
about the author of an image are other examples. Collectively, these and other
features are known as capabilities.

A data source "knows" what capabilities are supported by the image-acquisition
hardware that it represents, and displays the current settings of some (if not
all) of these capabilities to the user in a data-source-specific dialog box.
The user then has the opportunity to modify these settings (such as choosing
whether or not to support an automatic document feeder, or changing the paper
size) prior to acquiring an image. Part one's Figure 3 revealed a
Windows dialog box (on my platform) presenting the current settings of a few
capabilities for the TWAIN_32 Sample Source data source.

Because the responsibility for presenting data-source-specific dialog boxes is
no longer with JTwain, but has migrated to JTwain applications, it's necessary
for the application to determine capability settings and allow users to modify
those settings. To assist the application, I've introduced three methods into
the JTwain API that govern access to a single pixel type capability.
This determines the kind of pixel data that a data source can acquire, such
as black-and-white images, grayscale images, and RGB (red/green/blue) images:

  • public static native int getPixelType() returns the currently
    open data source's current pixel type. Compare that return value with one of
    JTwain's PT_ constants--this makes your source
    code more readable.

  • public static native int [] getPixelTypes() returns an array
    containing the currently open data source's supported pixel types. Compare the
    array contents with JTwain's PT_ constants.

  • public static native void setPixelType(int type) sets the
    currently open data source's current pixel type. Instead of passing an integer
    value, pass one of JTwain's PT_ constants.

Like the previous methods, each method above is capable of throwing objects of
the type JTwainException. But each method can also throw an object of
the type UnsupportedCapabilityException, if the data source that's
currently open does not support the pixel type capability.

Note: Each time a data source is opened via openDS(), the current
values of all supported capabilities are reset to their defaults. As a result,
you must change the current values of whatever capabilities you are supporting
before calling acquire().

I have written a short program that demonstrates the three pixel type methods.
You could place the code that gets this capability's current value and list of
supported values into a Java-based, data-source-specific dialog box.

// PixelTypeDemo.java

import net.javajeff.jtwain.*;

public class PixelTypeDemo
{
   public static void main (String [] args)
   {
      // Ensure exactly one command-line argument is
      // specified.

      if (args.length != 1)
      {
          System.out.println ("usage: java " +
                              "PixelTypeDemo " +
                              "srcname");
          return;
      }

      // Initialize JTwain.
    
      if (!JTwain.init ())
      {
          System.out.println ("TWAIN unsupported");
          return;
      }

      try
      {
          // Open data source manager.

          JTwain.openDSM ();

          try
          {
              // Open specified data source.

              JTwain.openDS (args [0]);

              try
              {
                  // Execute pixel type demo.

                  doDemo ();
              }
              catch (JTwainException e)
              {
                  // Output data source CC code.

                  reportFailure (e.getMessage (),
                                 1);
              }

              // Close currently open data source.

              JTwain.closeDS ();

          }
          catch (JTwainException e)
          {
              // Output data source manager CC code.

              reportFailure (e.getMessage (), 0);
          }

          // Close data source manager.

          JTwain.closeDSM ();
      }
      catch (JTwainException e)
      {
          // Do not output a CC code.

          reportFailure (e.getMessage (), -1);
      }
   }

   // ==========================================
   // Execute the pixel type demo. This method
   // catches UnsupportedCapabilityException but
   // throws JTwainException.
   // ==========================================

   static void doDemo () throws JTwainException
   {
      try
      {

      // Obtain array of supported pixel types.

      int [] pixTypes = JTwain.getPixelTypes ();

      // Output array's contents.

      System.out.println ("Supported pixel types:");
      System.out.println ();

      for (int i = 0; i < pixTypes.length; i++)
           outputPixelTypeName (pixTypes [i]);

      // Obtain current pixel type.

      int pixType = JTwain.getPixelType ();

      // Output current pixel type.

      System.out.println ();
      System.out.print ("Current pixel type: ");
      outputPixelTypeName (pixType);
      System.out.println ();

      // Change current pixel type.

      if (pixType == JTwain.PT_GRAY)
      {
          System.out.println ("Set to RGB");
          JTwain.setPixelType (JTwain.PT_RGB);
      }
      else
      {
          System.out.println ("Set to GRAY");
          JTwain.setPixelType (JTwain.PT_GRAY);
      }

      // Obtain current pixel type.

      pixType = JTwain.getPixelType ();

      // Output current pixel type.

      System.out.println ();
      System.out.print ("Current pixel type: ");
      outputPixelTypeName (pixType);

      }
      catch (UnsupportedCapabilityException e)
      {
         System.out.println ("No ICAP_PIXELTYPE");
      }
   }

   // ==============================================
   // Output a pixel type integer as a string value.
   // ==============================================

   static void outputPixelTypeName (int pixType)
   {
      switch (pixType)
      { 
         case JTwain.PT_BW:
              System.out.println ("Black & White");
              break;

         case JTwain.PT_GRAY:
              System.out.println ("Grayscale");
              break;

         case JTwain.PT_RGB:
              System.out.println ("Red/Green/Blue");
              break;

         case JTwain.PT_PALETTE:
              System.out.println ("Palette");
              break;

         case JTwain.PT_CMY:
              System.out.println ("CMY");
              break;

         case JTwain.PT_CMYK:
              System.out.println ("CMYK");
              break;

         case JTwain.PT_YUV:
              System.out.println ("YUV");
              break;

         case JTwain.PT_YUVK:
              System.out.println ("YUVK");
              break;

         case JTwain.PT_CIEXYZ:
              System.out.println ("CIEXYZ");
              break;

         default:
              System.out.println ("Unknown");
      }
   }

   // ==============================================
   // Output a failure message, a return code, and a
   // condition code based on dest.
   //
   // dest < 0 -- don't output condition code
   // dest = 0 -- output data source manager
   //             condition code
   // dest > 0 -- output data source condition code
   // ==============================================

   static void reportFailure (String msg, int dest)
   {
      System.out.print (msg + ", RC = " +
                        JTwain.getRC ());

      if (dest >= 0)
          try
          {
              System.out.print (", CC = " +
                                JTwain.getCC (dest));
          }
          catch (JTwainException e)
          {
          }
   }
}

Once again, I won't bother to describe the source code, because of the variety
of comments. Compile that code and run it on your platform. For example, when
I execute java PixelTypeDemo "TWAIN_32 Sample Source" (the quotes
must be present because of embedded spaces in the data source name), I observe
the following output:

Supported pixel types:

Black & White
Grayscale
Red/Green/Blue

Current pixel type: Grayscale

Set to RGB

Current pixel type: Red/Green/Blue
Capabilities Quick Study

At some point, you'll most likely want to add your own capabilities methods to
JTwain. Before doing that, you will need to acquire the appropriate knowledge.
Although the TWAIN specification provides extensive capabilities coverage, you
might find wading through all of that information somewhat overwhelming. To help
you quickly come up to speed on implementing your own capabilities in JTwain,
I recommend that you read this section. It introduces you to the concepts of
capability constants, operation triplets needed for getting/setting capability
values, and containers for use in those operations.

The twain.h header file (see Resources) offers
an assortment of constants that describe capabilities recognized by TWAIN.
Constants organize into three main categories: constants beginning with the
CAP_ prefix identify capabilities common to all kinds of
image-acquisition devices, constants beginning with the ACAP_
prefix identify audio-oriented capabilities, and constants that begin with the
ICAP_ prefix identify image-oriented capabilities. As an example,
the constant ICAP_PIXELTYPE identifies the pixel type capability.

TWAIN presents six capability-oriented operation triplets that are passed to
DSM_Entry() when working with capabilities. I only use three of
those operation triplets in JTwain:

  • I use the operation triplet of DG_CONTROL, DAT_CAPABILITY, and MSG_GET to return a range of supported values for pixel type.

  • I use the operation triplet of DG_CONTROL, DAT_CAPABILITY, and MSG_GETCURRENT to return the pixel type's current value.

  • I use operation triplet of DG_CONTROL, DAT_CAPABILITY, and MSG_SET to change the pixel type's current value.

When an application wants to change a capability's current value, it allocates
memory for a specific TWAIN container (data structure), populates that
container's fields with appropriate values, and passes that container to TWAIN
(via the final operation triplet in the list above). When an application wants
to obtain a capability's current value, or obtain a list of supported values,
the data source allocates the container's memory. Regardless of the allocation
source, the application is responsible for releasing that memory before
terminating. For an example of a container and how the memory allocation works
on the application side, examine the C++ code fragment below (which refers to
various constants and types located in twain.h). That source code
changes the current value of the pixel type capability:

TW_CAPABILITY cap;

cap.Cap = ICAP_PIXELTYPE;
cap.ConType = TWON_ONEVALUE;

// Allocate memory for the container.

cap.hContainer =
   GlobalAlloc (GHND, sizeof(TW_ONEVALUE));
if (cap.hContainer == 0)
{
    // Handle lack of memory ...
}

// Lock the container memory.

TW_ONEVALUE *pTWOneValue =
  (TW_ONEVALUE *) GlobalLock (cap.hContainer);

// Populate the container's fields.

pTWOneValue->ItemType = TWTY_UINT16;
pTWOneValue->Item = pixType;

// Unlock the container memory.

GlobalUnlock (cap.hContainer);

// Invoke operation by calling DSM_Entry().

g_rc = (*g_pDSM_Entry) (&g_AppID,
                        &g_SrcID,
                        DG_CONTROL,
                        DAT_CAPABILITY,
                        MSG_SET,
                        (TW_MEMREF) &cap);

// Deal with success or failure, indicated by g_rc.

// Free the container's memory.

GlobalFree (cap.hContainer);

The code fragment above introduces a TW_CAPABILITY data structure
that describes a capability to TWAIN. That data structure's Cap
field is assigned the ICAP_PIXELTYPE constant, identifying the
pixel type image capability. The ConType field is assigned the
TWON_ONEVALUE constant that identifies the kind of container
holding the new pixel type value as a single-value container. Memory for that
container must then be allocated. Under Windows, TWAIN requires that the
application call GlobalAlloc() to perform the allocation. Unlike
TWON_ONEVALUE, TW_ONEVALUE provides the correct size
to GlobalAlloc().

Because GlobalAlloc() returns a memory handle, a call is made to
GlobalLock() to lock the memory and return a pointer to that
memory. Using that pointer, the container's fields are then populated: the
ItemType field is assigned the TWTY_UINT16 constant,
identifying the type of the container's value as a 16-bit unsigned integer,
and the Item field is assigned the new value (stored in the variable
pixType) for the pixel type capability. Following the assignment,
the memory is unlocked by calling GlobalUnlock().

DSM_Entry() is invoked with the appropriate operation triplet for
changing the pixel type capability's value. The address of the capability data
structure is passed as the final argument to this function. Whether or not the
operation succeeds, GlobalFree() is invoked to release the memory
previously allocated for the container.

In addition to TWON_ONEVALUE and TW_ONEVALUE, JTwain
uses the TWON_ENUMERATION/TW_ENUMERATION container
to hold a list of supported pixel type values. As you add new capabilities to
JTwain, you might also work with TWON_ARRAY/TW_ARRAY
and TWON_RANGE/TW_RANGE. The TWAIN specification has
much more to say about these containers (and other TWAIN data structures).

The Revised JTwainDemo

I've created a revised JTwainDemo application that exercises nearly the entire
suite of methods in the new JTwain API. As before, JTwainDemo consists of
the source files ImageArea.java and JTwainDemo.java.
Compile both source files and execute java JTwainDemo to run that
application.

From the File menu, choose the Select Source... menu item. In response, you'll
see an all-Java "Select Source" dialog box that lists the names of all data
sources on your platform. As shown in Figure 1, only two data sources exist on
my platform.

Figure 1
Figure 1: "TWAIN_32 Sample Source" is highlighted in the all-Java "Select
Source" dialog box

Choose Acquire... from the File menu and study the "Config Source" dialog box.
That dialog box lists all values supported by the pixel type capability of the
currently open data source. Furthermore, one of those values should be
highlighted, indicating it's the current value of that capability. As Figure 2
reveals, "Config Source" displays a list of three pixel types (with one of the
pixel types highlighted) when "TWAIN_32 Sample Source" is the currently
open data source.

Figure 2
Figure 2: The all-Java "Config Source" dialog box highlights Grayscale

Now that you have seen JTwainDemo's GUI enhancements, you'll probably want to
study the application's source code. The following code fragment provides you
with a glimpse of that source code.

// Set any selected pixel type.

JTwain.setPixelType (cs.getPixType ());

// Acquire one or more images. Only one image
// should be returned because no capabilities
// involving an automatic document feeder have
// been set.

Image [] im = JTwain.acquire ();

// Update ImageArea panel with the first new
// image, and adjust the scrollbars.

ia.setImage (im [0]);

The code fragment is taken from the action listener attached to the Acquire...
menu item. After displaying the "Config Source" dialog box, opening the data
source manager, and opening the selected data source, the listener executes
the code fragment.

The code fragment's first task is to set the open data source's pixel type. An
image is then acquired from that data source. Usually, one image will be
returned in the array (at position 0) unless an automatic document feeder is
in use and various related capabilities have been appropriately initialized.
Once the image has been acquired, it's displayed in the ImageArea
panel.

Note: When acquiring an image, the sequence of JTwain API calls, from
openDSM() through closeDSM(), must be invoked on the
same thread. Doing that will prevent the "lockup" problem with the Win32
GetMessage() function that I discussed in part one. Also, although
you can execute the image-acquisition sequence on the event-handling thread,
your application GUIs will be more responsive by relegating that sequence to a
background thread.

Conclusion

JTwain has grown up. The new version of this API library solves three problems
present in the previous JTwain. You've been introduced to the expanded API and
have learned about the concept of capabilities, as we dug deeper into TWAIN.
You've also been introduced to a revised JTwainDemo application that exercises
almost the entire suite of methods in the new API.

This is the end of our TWAIN coverage. Although I would have loved to dig even
deeper into TWAIN (such as exploring more capabilities, discussing alternative
image transfer modes, showing how to build a custom data source, and so on),
this article is long enough. I believe you now have acquired sufficient
knowledge to continue exploring TWAIN on your own.

I have some homework for you to accomplish:

  • Examine the capabilities list in Chapter 9 of the TWAIN specification and then
    choose a capability that you might like to add to JTwain. Implement at least
    two native methods that get the current value and set the current value of
    that capability. You might also need to implement a third native method to get
    a list of supported values for the capability. Modify JTwainDemo's source code
    to demonstrate your capability of choice.

Next time, Java Tech wraps up this series by exploring SANE.

Resources

Answers to Previous Homework

The previous Java Tech article presented you with some
challenging homework on JTwain and JTwainDemo. Let's revisit that homework and
investigate solutions.

  1. Modify the JTwain API library to include a
    public static native String getDefaultSource() method, which
    returns the name of the default source as a String object. That
    method should throw a JTwainException object if some kind of
    failure occurs. Think carefully about the operation triplet you need to send
    to DSM_Entry().

  2. Test your getDefaultSource() method by placing the following line
    of code (within an appropriate try/catch construct)
    after the call to JTwain.init() in JTwainDemo's
    main() method:

    System.out.println ("Default source = " + JTwain.getDefaultSource ());
  3. Consult the 1 subdirectory of the answers directory in this
    article's attached code file (see Resources).

  4. Modify JTwainDemo to save an acquired image to a file. Add a Save as... menu
    item to the File menu, and provide some logic to choose a filename and save
    the image to a file (with that filename) in a format of your choice. Feel free
    to use the imageio package, available in J2SE 1.4 and J2SE 5.0.

    Consult the 2 subdirectory of the answers directory in this
    article's attached code file (see Resources).

width="1" height="1" border="0" alt=" " />
Jeff Friesen is a freelance software developer and educator specializing in Java technology. Check out his site at javajeff.mb.ca.
Related Topics >> Programming   |   

Comments

interesting article i want to

interesting article i want to apply it but cannot download the code.zip file

Como eu poderia aplicar uma operação tripla para configurar ...

Como eu poderia aplicar uma operação tripla para configurar a resolução da imagem?
Obrigado!

How I can aply an operation triplet to change the resolution of the aquiring image?
Thank you!

Não sei se é a melhor maneira. Funcionou: JNIEXPORT void ...

Não sei se é a melhor maneira. Funcionou:
JNIEXPORT void JNICALL Java_net_javajeff_jtwain_JTwain_setResolution
(JNIEnv *env, jclass clazz, jint resolution)
{
TW_CAPABILITY cap;
float x_res = resolution;
float y_res = resolution;

//=============
// X_RESOLUTION
//=============
cap.Cap = ICAP_XRESOLUTION;
cap.ConType = TWON_ONEVALUE;
cap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));

if(cap.hContainer)
{
TW_ONEVALUE *pTWOneValue = (TW_ONEVALUE *) GlobalLock (cap.hContainer);
pTWOneValue->ItemType = TWTY_FIX32;

TW_FIX32 fix32_val = FloatToFIX32(x_res);
pTWOneValue->Item = *((pTW_INT32) &fix32_val);
GlobalUnlock(cap.hContainer);

g_rc = (*g_pDSM_Entry)(&g_AppID,
&g_SrcID,
DG_CONTROL,
DAT_CAPABILITY,
MSG_SET,
(TW_MEMREF) &cap);

cout << "CODIGO DE RETORNO X_RESOLUTION: " << g_rc;

GlobalFree(cap.hContainer);
}

//=============
// Y_RESOLUTION
//=============
cap.Cap = ICAP_YRESOLUTION;
cap.ConType = TWON_ONEVALUE;
cap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));
if(cap.hContainer)
{
TW_ONEVALUE *pTWOneValue = (TW_ONEVALUE *) GlobalLock (cap.hContainer);
pTWOneValue->ItemType = TWTY_FIX32;

TW_FIX32 fix32_val = FloatToFIX32(y_res);
pTWOneValue->Item = *((pTW_INT32) &fix32_val);
GlobalUnlock(cap.hContainer);

g_rc = (*g_pDSM_Entry)(&g_AppID,
&g_SrcID,
DG_CONTROL,
DAT_CAPABILITY,
MSG_SET,
(TW_MEMREF) &cap);

cout << "CODIGO DE RETORNO Y_RESOLUTION: " << g_rc;

GlobalFree(cap.hContainer);
}
}

// ================
// HELPER FUNCTION
// ================
static TW_FIX32 FloatToFIX32(float i_float)
{
TW_FIX32 Fix32_value;
TW_INT32 value = (TW_INT32) (i_float * 65536.0 + 0.5);
Fix32_value.Whole = LOWORD(value >> 16);
Fix32_value.Frac = LOWORD(value & 0x0000ffffL);
return Fix32_value;
}