The scope of this tutorial covers how to download dosadi’s
EZTWAIN classic free twain scanner Library, build it, install it
and then use it in an example program.
Go to http://www.dosadi.com/products.htm
click on the EZTWAIN classic icon
and download eztw1.zip which is about 190kb
unzip it and you should see a list including the folder VC
click on that and you should see a RELEASE folder
this is where EZTW32.lib is and where EZTW32.dll is.
If you are using MSVC as your IDE/Compiler you do not
have much to do just place EZTW32.lib in your lib folder of
your compiler.
If you are using Code::Blocks with MinGW as I am we are going to have to build the Library
dont worry this is painless and quite straightforward.
First of all fire up Code::Blocks and do -> File -> Import Project ->
Import MS Visual C++ workspace.
Go to the VC folder of your dowloaded EZTWAIN as mentioned before.
And select VC.dsw
this should bring up two projects in your project workspace
TWERP and EZTW32 close TWERP as we dont need it then click on build to
build the EZTW32 library.
Once you have built the Library go to VC -> RELEASE as mentioned before
where you found EZTW32.dll.
You should now see a MinGW library -> libEZTW32.a
copy it and paste it into the MinGW lib folder.
Your going to have to link to it in our example program.
If you are using MSVC then your going to have to link to EZTW32.lib
Next Create an empty Win32 c++ application
Call it ‘scanner’ or some other imaginative title.
Once thats done add a header file and call it resource.h
also while your at it create a resource file and call it resource.rc.
In your resource.h file copy and paste the following
#ifndef RESOURCE_H_INCLUDED #define RESOURCE_H_INCLUDED #define APP_SCAN 102 #define APP_SAVE 103 #define APP_EXIT 104 #endif // RESOURCE_H_INCLUDED
In your resource.rc file type or copy and paste the following
//MENU
//CREATION
#include "resource.h"
APP_MENU MENU DISCARDABLE
BEGIN
POPUP "&Image"
BEGIN
MENUITEM "&Scan", APP_SCAN
MENUITEM "&Save", APP_SAVE, GRAYED
MENUITEM "&Exit", APP_EXIT
END
END
both resource.h and resource.rc concern themselves with Menu Creation
you will notice that Save is grayed out. This is because we dont want
the Image -> Save Menu function to be activated until we scan something in.
Next we concern ourselves with our main.cpp if you dont already have one
create one.
And put the following lines of code in it.
#include <windows.h> #include <eztwain.h> #include <twain.h> #include "resource.h"
you will notice 2 headers eztwain.h and twain.h
these are located in the VC folder.
Copy and Paste them into your compilers include folder.
The next bit of code creates a window just windows standard stuff
HBITMAP hdib;
BITMAP bmp;
HPALETTE hpal; // logical palette
HDC hdcMem;
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp);
/* Make the class name into a global variable */
char szClassName[ ] = "Scanner App";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_HREDRAW|CS_VREDRAW;
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = "APP_MENU"; /* menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
szClassName, /* Title Text */
WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, /* default window */
CW_USEDEFAULT , /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
600, /* The programs width */
800, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
The next bit of code is our windows callback procedure
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC,hdc,Memhdc;
RECT rc;
HMENU hMenu;
hMenu = GetMenu(hwnd);
switch (message) /* handle the messages */
{
The next bit of code is WM_CREATE
case WM_CREATE:
if (!TWAIN_IsAvailable())
{
MessageBox(hwnd, " No Twain devices Available ","Notice",1);
break;
}
else
if (!TWAIN_SelectImageSource(hwnd))
{
SendMessage(hwnd,WM_DESTROY,0,0);
}
return 0;
The first part of this code tests for whether any TWAIN devices are available
if not a Message Box is displayed informing the user.
The next bit of code is a call to a dialog to select a twain device if one is not selected
the window is destroyed.
The next bit of code is WM_COMMAND this concerns itself with the menu commands.
case WM_COMMAND:
switch (wParam)
{
case APP_SCAN:
TWAIN_AcquireToClipboard(hwnd, TWAIN_RGB);
if (OpenClipboard(hwnd))
{
hdib = (HBITMAP) GetClipboardData(CF_BITMAP);
GetObject(hdib, sizeof(BITMAP), (LPSTR)&bmp);
GetClientRect(hwnd, &rc);
CloseClipboard();
hpal = TWAIN_CreateDibPalette(hdib);
EnableMenuItem(hMenu, APP_SAVE, MF_ENABLED);
}
InvalidateRect(hwnd,&rc,TRUE);
break;
this bit when Scan is selected from the menu
opens a local interface with your scanner driver
and copies it to the clipboard. A new palette
is created and the Save function which was grayed out
is enabled.
InvalidateRect forces a repaint of the window.
The next bit of WM_COMMAND is
case APP_EXIT:
SendMessage(hwnd,WM_DESTROY,0,0);
break;
This exits the application
The next bit of WM_COMMAND saves the scanned bitmap to a bmp file.
First it opens a Save As dialog and allows the user to place
it in any folder and directory they choose.
case APP_SAVE:
char szFileName[MAX_PATH] = "";
Memhdc = GetDC(hwnd);
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize= sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Bitmaps(*.bmp)\0*.bmp\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT;
ofn.lpstrDefExt = "bmp";
if (GetSaveFileName(&ofn))
{
if (szFileName)
{
PBITMAPINFO pbi = CreateBitmapInfoStruct(hwnd, hdib);
CreateBMPFile(hwnd, szFileName, pbi, hdib, Memhdc);
}
}
break;
}
return 0;
The next bit of WM_COMMAND concerns itself with the change in palette
case WM_QUERYNEWPALETTE:
if (!hpal)
return FALSE;
hdc = GetDC(hwnd);
SelectPalette (hdc, hpal, FALSE);
RealizePalette (hdc);
InvalidateRect(hwnd,NULL,TRUE);
ReleaseDC(hwnd,hdc);
return TRUE;
case WM_PALETTECHANGED:
if (!hpal || (HWND)wParam == hwnd)
break;
hdc = GetDC(hwnd);
SelectPalette (hdc, hpal, FALSE);
RealizePalette (hdc);
UpdateColors(hdc);
ReleaseDC(hwnd,hdc);
break;
The next bit of code is our WM_PAINT
this obviously draws our bitmap onto the Window
this is a preview window and I will be using
StretchBlt to compress the image into an area
of 600 wide and 800 height. There is a slight loss of colour
information with this method. But its just for the preview
window and so I feel its ok. The resultant saved image will
have no such information loss.
case WM_PAINT:
{
PAINTSTRUCT ps;
hDC = BeginPaint(hwnd, &ps);
if (hDC)
{
RECT rc;
GetClientRect(hwnd, &rc);
if (hdib)
{
hdcMem = CreateCompatibleDC (hDC);
SetMapMode(hDC, MM_ANISOTROPIC);
SetWindowExtEx(hDC, 600, 800, NULL);
SetViewportExtEx(hDC, rc.right, rc.bottom, NULL);
SelectObject(hdcMem, hdib);
SelectPalette (hDC, hpal, FALSE);
RealizePalette (hDC);
SetStretchBltMode(hDC,COLORONCOLOR);
StretchBlt(hDC,rc.left , rc.top ,rc.right,rc.bottom,hdcMem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
ValidateRect(hwnd,&rc);
}
}
EndPaint(hwnd, &ps);
DeleteDC(hdcMem);
}
return 0;
Our final piece of the windows callback procedure is
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
its pretty self explanantory.
The final (Thank God) piece of code deals with the creation of a BitmapInfoStruct
and the actual saving of a bitmap file.
Sing along if you think you know the tune
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
{
MessageBox(hwnd,"GlobalAlloc","Error", MB_OK );
}
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,DIB_RGB_COLORS))
{
MessageBox(hwnd,"GetDIBits","Error",MB_OK );
}
// Create the .BMP file.
hf = CreateFile(pszFile,GENERIC_READ | GENERIC_WRITE,(DWORD) 0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
{
MessageBox( hwnd,"CreateFile","Error", MB_OK);
}
hdr.bfType = 0x4d42; // File type designator "BM" 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL) )
{
MessageBox(hwnd,"WriteFileHeader","Error",MB_OK );
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, NULL))
{
MessageBox(hwnd,"WriteInfoHeader","Error",MB_OK );
}
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
{
MessageBox(hwnd,"WriteFile","Error",MB_OK );
}
// Close the .BMP file.
if (!CloseHandle(hf))
{
MessageBox(hwnd,"CloseHandle","Error",MB_OK );
}
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
//End of BMP Save
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
{
MessageBox(hwnd,"GetObject","Error",MB_OK );
}
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits != 24)
{
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
}
// There is no RGBQUAD array for the 24-bit-per-pixel format.
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
{
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
}
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// For Windows NT, the width must be DWORD aligned unless
// the bitmap is RLE compressed. This example shows this.
// For Windows 95/98/Me, the width must be WORD aligned unless the
// bitmap is RLE compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi; //return BITMAPINFO
}
and there we have it if you have followed the tutorial exactly you should have
a piece of code that compiles. I have used Code::Blocks for this its free and
thats good for me. If you have used MSVC remember to make your project
to use Multibyte character set. As I have not made it a unicode build.
The final thing you have to do is copy EZTW32.dll into your project directory
where your main.cpp is saved and run the application.
The possibilities for building scanning applications are endless and it is easy to
ammend this program for your specific needs. Enjoy and bon chance.





MultiQuote


|