如何从dll中将图片发送到delphi中的应用程序

时间:2013-09-06 18:33:26

标签: delphi

我从DLL文件中获取图像有问题,调用图像的函数是:

  

SNAPI_SnapShot主机到扫描程序命令

     

如果扫描仪支持成像,则会向扫描仪发送命令以进入图像捕获模式。扫描仪保持在图像捕捉模式,直到拉动扳机(物理上或通过呼叫)   SNAPI_PullTrigger)并捕获图像,或者直到触发器拉出超时。然后扫描仪返回解码模式。

     

如果触发了触发器,则图像数据将被发送到DLL。 WM_XFERSTATUS将发送到主机应用程序,其中包含有关图像大小的信息。

     

当收到第一个传输状态消息时,主机应用程序应通过调用SNAPI_SetImageBuffer为映像提供目标缓冲区。将整个图像从扫描仪传输到DLL后,应用程序会收到一条Windows消息,指示已存储图像数据。

图像数据被发送到DLL。我需要知道如何在Delphi中获取图像。

SnapiDll.h:

/*****************************************************************************
*   FILENAME:       SNAPIdll.h

*   COPYRIGHT(c) 2002-2007 Motorola, Inc.  All rights reserved
*
*   DESCRIPTION     :   SNAPI.dll API Functions - Entry points into the dll and error codes
*   
*   File Version    :   3.0.0.4
*****************************************************************************/

#ifndef __SNAPIDLL_API_H__
#define __SNAPIDLL_API_H__
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the SNAPIDLL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// SNAPIDLL_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef SNAPIDLL_EXPORTS
#define SNAPIDLL_API extern "C" __declspec(dllexport)
#else
#ifdef __cplusplus
#define SNAPIDLL_API extern "C" __declspec(dllimport)
#else
#define SNAPIDLL_API __declspec(dllimport)
#endif
#endif



/****************************************************************************/
/*  Defines, typedefs, etc. *************************************************/
#define MAX_COM_PORTS 16
///////////////////////////////////////////////////////////////////////////////
//
//  Library Error reporting
//
// ALL library function calls return 0 if successful, o/w an error code is returned.
// If the error code is a fatal error, SNAPIDisconnect() should be called.
// The list of error codes below describes the errors that can be reported.
//
// In addition to the failure status returned by library function and error codes
// the library can also send or post a WM_ERROR message to the application.
// The application should handle that message and respond appropriately
// The list of Windows messages includes a description of the WM_ERROR message.
//
///////////////////////////////////////////////////////////////////////////////

// Error codes - return values for API CALLS

#define SSICOMM_NOERROR             0     // No error code is set - this will be the value when an API call returns success
#define ERR_SSI_NOOBJECT            -1      // Not a valid device object(handle)
#define ERR_SSI_HWND                    -2    // The hwnd parameter to the SNAPI_Init function was NULL
#define SSICOMM_BAD_SETSTATE        -3    // The library was unable to set the state of the com port - no connection established
#define SSICOMM_BAD_SETTIMEOUTS -4    // The library was unable to set the com timeouts - no connection established   
#define SSICOMM_BAD_GETTIMEOUTS -5    // The library was unable to get the curent com timeouts - no connection established   
#define SSICOMM_BAD_GETCOMSTATE -6    // The library was unable to get the curent com state - no connection established   
#define SSICOMM_ALREADY_CLOSED  -7      // Call to close com port was made when the com port is not open.  There is no connection. 
#define SSICOMM_UNABLE_PURGE        -8      // Call to purge the com port before closing it was not successful.
#define SSICOMM_THREADS_BADEXIT -9      // fatal error - the threads didn't exit properly
#define SSICOMM_ERROR_CLRDTR        -10 // unable to lower DTR when closing com port 
#define SSICOMM_BAD_CREATEFILE  -11 // unable to open the com port
#define SSICOMM_BAD_READTHREAD      -12 // unable to create the read/status thread - no connection
#define SSICOMM_BAD_WRITETHREAD -13 // unable to create the writer thread - no connection
#define SSICOMM_BAD_CREATEEVENT -14  // call to CreateEvent failed - fatal error
#define SSICOMM_BUSY             -15  // not fatal - try your request again later
#define SSICMD_UNIMPLEMENTED     -16  // not fatal - this command is not implemented in the library
#define SSICOMM_ALREADYCONNECTED -17  // if already connected, can't connect without a call to disconnect
#define ERR_SSI_MISMATCHHWND     -18  // the hwnd paramter for the function does not match the stored hwnd for the connection
#define SSICOMM_TOOMUCHDATA      -19   //the maximum allowable input data length was exceeded 
#define SSICOMM_ERRVERSION       -20   // Can't run on this version of windows
#define SSI_INPUTQ_FULL             -21   // unable to add new user request to input queue for transmitting to scanner
                                                    // user should re-try request
#define SSICOMM_BADDATA             -22 // param data is in incorrect format


// these generate WM_ERROR messages to the app - all are either fatal/nonfatal errors occurring during program execution,
// ... or are the return value of an api function indicating a fatal error

#define SSICOMM_WAITMOWRITER        -23 // Wait for Multiple Objects gave WAIT_FAILED in writer proc - if not fatal, protocol retry may recover
#define SSITHREAD_CREATEWEVENT  -24 // Failure to create write event - fatal error
#define SSITHREAD_OLRESW            -25 // Get overlapped result failed - fatal error 
#define SSITHREAD_WRITEERR          -26 // Number of bytes written is not the number requested to be written -if not fatal, retry may recover   
#define SSITHREAD_WMOW              -27 // Wait multiple objects failure in overlapped write - fatal error
#define SSITHREAD_WRITEFILEFAIL -28 // Call to Write failed, but isn't just delayed - fatal error
#define SSITHREAD_BADSETEV          -29 // write thread returned error on set event
#define SSIRTHREAD_ORESULT          -30 // read thread bad overlapped result - fatal error
#define SSIRTHREAD_SETMASK          -31 // read thread bad set mask return - fatal error
#define SSIRTHREAD_BADREAD          -32 // read thread bad read - fatal error
#define SSIRTHREAD_CREATEREVENT -33 // read thread bad create read event -  error code set, AIP call will return false
#define SSIRTHREAD_CREATESEVENT -34 // read thread bad create status event-  error code set, AIP call will return false
#define SSIRTHREAD_WAITCEVENT       -35 // read thread wait com event bad return - fatal error


// One of these error codes is place in the WPARAM of a WM_ERROR messges during SNAPI protocol handling of scanner msgs

#define COMMAND_NOTHANDLED       -36   // command was not processed successfully by decoder
#define ERR_UNSUPPORTED_COMMAND  -37   // command was not processed successfully by decoder
#define SSI_DATAFORMAT_ERR      -38   // scanner data packet was not of correct format from decoder 
#define ERR_UNEXPECTEDDATA       -39   // state machine has received data that was unexpected for the current state


//USB error messages
#define ERR_USB_DEV_NOTFOUND       -40   // No  USB device found with mentioned GUID
#define ERR_USB_OUT_OF_MEMORY      -41   //No memory
#define ERR_INVALID_DEVICE_HANDLE   -42  //Inavlid Device Handle
#define IMAGE_FRAME_CRC_ERROR       -43  // CRC Error
#define SSITHREAD_WMOW_TIMEDOUT   -44   //  // Wait multiple objects  timed out  in overlapped write - fatal error

//Firmware update process messages and error codes
#define SSICOMM_FWUPDATE_ABORTED        -45 // Firmware update process aborted

// One of these OPCODES are placed  in the WPARAM of a WM_TIMEOUT msg
#define DECODE_DATA_TIMEOUT  0xF3
#define IMAGE_DATA_TIMEOUT    0xB1
#define VIDEO_DATA_TIMEOUT   0xB4


// Response Codes
#define COMMAND_SUCCESS         0x01
#define COMMAND_FAIL            0x02
#define COMMAND_NOT_SUPPORTED   0x03
#define COMMAND_SUPPORTED_NOT_COMPLETED 0x04

//Extended Response Codes
#define ALL_PARAMETERS_STORED   0x01
#define NO_PARAMETERS_STORED    0x02
#define SOME_PARAMETERS_STORED  0x03


//Event Codes
#define EVENT_POWER_UP              0x01
#define EVENT_DECODE                0x02
#define EVENT_PARAM_DFLTS           0x03
#define EVENT_PARAM_ENTRY_ERR       0x04
#define EVENT_PARAM_NUM_EXPECTED    0x05
#define EVENT_PARAM_STORED          0x06
#define EVENT_DECODE_MODE           0x10
#define EVENT_IMAGE_MODE            0x11
#define EVENT_VIDEO_MODE            0x12


// These are the beep codes for the beep functions

#define ONESHORTHI  0x00
#define TWOSHORTHI  0x01
#define THREESHORTHI 0x02
#define FOURSHORTHI 0x03
#define FIVESHORTHI 0x04

#define ONESHORTLO  0x05
#define TWOSHORTLO  0x06
#define THREESHORTLO    0x07
#define FOURSHORTLO 0x08
#define FIVESHORTLO 0x09

#define ONELONGHI       0x0A
#define TWOLONGHI       0x0B
#define THREELONGHI 0x0C
#define FOURLONGHI  0x0D
#define FIVELONGHI  0x0E

#define ONELONGLO       0x0F
#define TWOLONGLO       0x10
#define THREELONGLO 0x11
#define FOURLONGLO  0x12
#define FIVELONGLO  0x13


#define FASTHILOHILO 0x14
#define SLOWHILOHILO 0x15
#define HILO            0x16
#define LOHI            0x17
#define HILOHI          0x18
#define LOHILO          0x19



//for windows message handling
typedef struct
{
    DWORD high;
    DWORD low;
} DWPARAM;


#define HIDWORD(wParam)  (wParam->high)
#define LODWORD(wParam)  (wParam->low)



/****************************************************************************/
/*  Extern Function Prototypes **********************************************/




SNAPIDLL_API int __stdcall SNAPI_Connect(HANDLE   DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_Init(HWND hwnd, HANDLE  * DeviceHandles, int * NumDevices);
SNAPIDLL_API int __stdcall SNAPI_Disconnect(HANDLE   DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_SnapShot(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_TransmitVersion(HANDLE DeviceHandle);
/*SNAPIDLL_API int __stdcall AbortImageXfer(HANDLE DeviceHandle);*/
SNAPIDLL_API int __stdcall SNAPI_PullTrigger(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_ReleaseTrigger(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_SetParameters(WORD *Params, int ParamWords, HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_TransmitVideo(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_RequestParameters(WORD *Params, int ParamWords, HANDLE DeviceHandle);

SNAPIDLL_API int __stdcall SNAPI_AimOn(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_AimOff(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_LedOn(HANDLE DeviceHandle, unsigned char nLEDselection);
SNAPIDLL_API int __stdcall SNAPI_LedOff(HANDLE DeviceHandle, unsigned char nLEDselection);
SNAPIDLL_API int __stdcall SNAPI_ScanEnable(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_ScanDisable(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_SoundBeeper(HANDLE DeviceHandle, unsigned char nBeepCode);

SNAPIDLL_API int __stdcall SNAPI_RequestScannerCapabilities(HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_SetParamPersistance(HANDLE DeviceHandle, int bPersist);
SNAPIDLL_API int __stdcall SNAPI_AbortMacroPdf(HANDLE DeviceHandle);  // aborts macro session and throws out any data
SNAPIDLL_API int __stdcall SNAPI_FlushMacroPdf(HANDLE DeviceHandle);  // aborts macro session and sends any data
SNAPIDLL_API int __stdcall SNAPI_SetParameterDefaults(HANDLE DeviceHandle);

SNAPIDLL_API unsigned int __stdcall SNAPI_ReturnDLLVersion(void);
SNAPIDLL_API int __stdcall SNAPI_GetSerialNumber(HANDLE DeviceHandle,unsigned char * SerialNo);

SNAPIDLL_API int __stdcall SNAPI_UpdateFirmware(char* frmFilePath, HANDLE DeviceHandle);
SNAPIDLL_API int __stdcall SNAPI_AbortFirmwareUpdate(HANDLE DeviceHandle);

/* Once filled by the DLL, the app is sent a WM_XXX messagge with the number of bytes of data that were (or should have
** been stored) in lParam and if the buffer wasn't big enough to hold all the data, wparam will have it's last 2 bits set
** to zero.  If no buffer was given to the DLL for the data to be stored into, the last 2 bits of wparam will be 01.
*  If the data was stored correctly, the last 2 bits of wparam will be 11.
**
** After the message is sent, the buffer is marked by the dll as
** NULL indicating no user buffer available for storage - the buffer should be reset as soon as possible
*  after a WM_xxx message is processed.  A second call to set the data buffer will cause the new buffer to be used for any incoming data.
**
**
** An ample video buffer size would be 5000 bytes
*
** Decode data depends on the type of barcode.  If MacroPDF is buffered, large amounts of data are possible.
*  The first byte is the SNAPI codetype id, followed by the decode data sent from the scanner.
**
** The image data buffer can be set when the first image transfer status message is sent to the application which holds
** the length information for the entire image.
**
** The parameter data buffer can be set immediately before the call to RequestParameters is made.  A size of 4000 bytes
** should be ample to hold all the parameter number/value pairs.  A call for a single parameter only requires a small
** buffer - 10 bytes is more than enough.
**
** The version data buffer can be set immediately before the call to TransmitVersion.  The amount of data returned is
** variable but a buffer of length 256 should be ample.
**
** Capabilities data buffer can be set immediately before the call to RequestScannerCapabilities.  The amount of data returned is
** variable but a buffer of length 256 should be ample.
**
*/

#define BUFFERSIZE_MASK 0x0003
#define BUFFERSIZE_GOOD 0x0003
#define BUFFERSIZE_ERROR 0x0000
#define NOBUFFER_ERROR 0x0001


SNAPIDLL_API int __stdcall SNAPI_SetVideoBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);
SNAPIDLL_API int __stdcall SNAPI_SetImageBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);
SNAPIDLL_API int __stdcall SNAPI_SetDecodeBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);
SNAPIDLL_API int __stdcall SNAPI_SetParameterBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);
SNAPIDLL_API int __stdcall SNAPI_SetVersionBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);
SNAPIDLL_API int __stdcall SNAPI_SetCapabilitiesBuffer(HANDLE DeviceHandle, unsigned char *pData, long max_length);

/**********************************************************************************************/
/**   Windows Messages sent to calling process ************************************************/



#define WM_DECODE                   WM_APP+1        // Sent if there is decode data available from the scanner
                                                    //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                    //LODWORD (wparam ) is  the buffer status of the data stored
                                                    //HIDWORD(wparam)  is the length of the data in bytes
                                                    //lparam  is the handle to the device for which the message was posted


#define WM_IMAGE                    WM_APP+2        // Sent if there is image data available from the scanner
                                                    //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        //LODWORD (wparam ) is  the buffer status of the data stored
                                                        //HIDWORD(wparam)  is the length of the data in bytes
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_VIDEOIMAGE           WM_APP+3        // Sent if there is a video frame available from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is  the buffer status of the data stored
                                                        // HIDWORD(wparam)  is the length of the data in bytes
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_ERROR                    WM_APP+4        // Sent if an error occurred.
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is the error code (cast to signed short) (see WM_ERROR codes list )
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_TIMEOUT              WM_APP+5        // Sent if the scanner does not respond to a request from the library within
                                                        // ...the timeout for the request.
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam )  is set to zero (reserved for future use)
                                                        // HIDWORD (wparam )  is the request code (int) that did not receive the response
                                                        // ...(see WM_TIMEOUT codes list)
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_CMDCOMPLETEMSG       WM_APP+6        // Sent when an ACK is received from the scanner in response to a handled
                                          // user command.
                                            //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                            //LODWORD (wparam ) is the command status.
                                            //HIDWORD (wparam ) is extended command status.
                                            //lparam  is the handle to the device for which the message was posted

#define WM_XFERSTATUS           WM_APP+7        // Sent during the transfer of image data from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *).
                                                        // LODWORD (wparam ) is the total number of bytes received so far (cast to uint)
                                                        // HIDWORD (wparam ) is the total number of bytes expected (cast to uint)
                                                        //lparam  is the handle to the device for which the message was posted


#define WM_SWVERSION                WM_APP+8    // Sent when the software version information is available from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        //  LODWORD (wparam ) is the buffer status code
                                                        // HIDWORD (wparam ) is the length of the data in bytes (cast to int).
                                                        // version data is device dependent
                                                        //lparam  is the handle to the device for which the message was posted


#define WM_PARAMS                   WM_APP+9    // Sent when parameter information is available from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is the buffer status,
                                                        // HIDWORD (wparam ) is the length of the parameter data as number of words(cast to int).
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_CAPABILITIES         WM_APP+10       // Sent when capabilities data is available from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam )  is the buffer status
                                                        // HIDWORD (wparam ) is the length of the data in bytes (cast to int).
                                                        //lparam  is the handle to the device for which the message was posted


#define WM_EVENT                    WM_APP+11  // Sent when event data is available from the scanner
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is the event data
                                                        // HIDWORD (wparam ) is the length of the data in bytes (always 1 byte).
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_DEVICE_NOTIFICATION      WM_APP+12  // Sent when a SNAPI compatible device is detected or
                                                        // removed from the system.
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is either of
                                                        // - DEVICE_ARRIVE or DEVICE_REMOVE
                                                        //lparam  is the handle to the device for which the message was posted


#define WM_MGMT_CMD_RESP            WM_APP+13   // Sent when a response is available from the scanner to a management command
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam )  is the buffer status
                                                        // HIDWORD (wparam ) is the length of the data in bytes (cast to int).
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_FU_STARTED               WM_APP+14   //Sent when firmware update process starts
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is the number steps in firmware update process
                                                        //lparam  is the handle to the device for which the message was posted

#define WM_FU_PROGRESS              WM_APP+15   //Sent when each firmware update step finished
                                                        //wparam is a pointer to DWPARAM structure (cast to DWPARAM *)
                                                        // LODWORD (wparam ) is the current step number
                                                        //lparam  is the handle to the device for which the message was posted


// for WM_DEVICE_NOTIFICATION event
#define DEVICE_ARRIVE   0   // The system has detected a new device
#define DEVICE_REMOVE   1   // The device has been removed from the system.



#endif // ifdef __SNAPIDLL_API_H__

1 个答案:

答案 0 :(得分:5)

Read the documentation再次更加谨慎。您引用的部分会告诉您完全如何获取图像数据。

当您收到WM_XFERSTATUS窗口消息时,它会告诉您图像大小。分配足够大的数据缓冲区以容纳该大小,然后将缓冲区传递给SNAPI_SetImageBuffer()。然后,DLL将使用图像数据填充该缓冲区。完成后,您将收到一条WM_IMAGE消息,告诉您缓冲区已准备就绪。

documentation在第1章的由DLL返回的数据发送给调用过程的Windows消息部分中更详细地解释了这一点。

更新:例如:

type
  TScanner = class
  public
    Device: THandle;
    Connected: Boolean;
    ImageData: array of Byte;
    Pending: Boolean;

    procedure Connect;
    procedure Disconnect;
    procedure RequestSnapshot;
    procedure SetImageBuffer(Size: Integer);
  end;

const
  SNAPI_DLL = 'snapi.dll';

function SNAPI_Init(wnd: HWND; var DeviceHandles: THandle, var NumDevices: Integer): Integer; stdcall; external SNAPI_DLL;
function SNAPI_Connect(DeviceHandle: THandle): Integer; stdcall; external SNAPI_DLL;
function SNAPI_Disconnect(DeviceHandle: THandle): Integer; stdcall; external SNAPI_DLL;
function SNAPI_SetImageBuffer(DeviceHandle: THandle; Data: Pointer; MaxLength: Longint): Integer; stdcall; external SNAPI_DLL;
function SNAPI_SnapShot(DeviceHandle: THandle): Integer; stdcall; external SNAPI_DLL;

const
  MAX_SCANNER = ...; // you will have to look this up

  BUFFERSIZE_ERROR = $0000;
  NOBUFFER_ERROR = $0001;
  BUFFERSIZE_GOOD = $0003;
  BUFFERSIZE_MASK = $0003;

  WM_IMAGE = WM_APP+2;
  WM_ERROR = WM_APP+4;
  WM_TIMEOUT = WM_APP+5;
  WM_XFERSTATUS = WM_APP+7;
  WM_DEVICENOTIFICATION := WM_APP+12;

  IMAGE_DATA_TIMEOUT = $B1;

type
  PDWPARAM = ^DWPARAM;
  DWPARAM = record
    dwHigh: DWORD;
    dwLow: DWORD;
  end;

function HIDWORD(wParam: PDWPARAM): DWORD;
begin
  Result := wParam^.dwHigh;
end;

function LODWORD(wParam: PDWPARAM): DWORD;
begin
  Result := wParam^.dwLow;
end;

type
  ESNAPIError = class(Exception)
  public
    Status: Integer;
    constructor CreateErr(AStatus: Integer);
  end;

constructor ESNAPIError.CreateErr(AStatus: Integer);
begin
  inherited CreateFmt('SNAPI Error %d', [AStatus]);
  Status := AStatus;
end;

procedure CheckSNAPIStatus(AStatus: Integer);
begin
  if AStatus <> 0 then
    raise ESNAPIError.CreateErr(AStatus);
end;

procedure TScanner.Connect;
begin
  if not Connected then
  begin
    CheckSNAPIStatus(SNAPI_Connect(Device));
    Connected := True;
  end;
end;

procedure TScanner.Disconnect;
begin
  if Connected then
  begin
    SNAPI_Disconnect(Device);
    Connected := False;
  end;
end;

procedure TScanner.RequestSnapshot;
begin
  Connect;

  if Pending then
    raise Exception.Create('Scanner is busy, try again later');

  SetLength(ImageData, 0);
  CheckSNAPIStatus(SNAPI_SnapShot(Device));

  Pending := True;
end;

procedure TScanner.SetImageBuffer(Size: Integer);
begin
  SetLength(ImageData, Size);
  CheckSNAPIStatus(SNAPI_SetImageBuffer(Device, Pointer(ImageData), Size));
end;

type
  TMyForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    ScannerWnd: HWND;
    Scanners: TObjectList;
    function FindScanner(Device: THandle): TScanner;
    procedure ConnectScanner(int Index);
    procedure DisconnectScanner(int Index);
    procedure RequestSnapshot(int Index);
    procedure ScannerWndProc(var Message: TMessage);
  end;

procedure TMyForm.FormCreate(Sender: TObject);
var
  Devices: array[0..MAX_SCANNER-1] of THandle;
  NumScanners, I: Integer;
  Scanner: TScanner;
begin
  Scanners := TObjectList.Create(True);
  ScannerWnd := AllocateHWnd(ScannerWndProc);

  CheckSNAPIStatus(SNAPI_Init(ScannerWnd, Devices[0], NumScanners));
  for I := 0 to NumScanners-1 do
  begin
    Scanner := TScanner.Create;
    Scanner.Device := Devices[I];
    Scanners.Add(Scanner);
    // add it to the UI somewhere...
  end;
end;

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  DeallocateHWnd(ScannerWnd);
  Scanners.Free;
end;

function TMyForm.FindScanner(Device: THandle): TScanner;
var
  I: Integer;
begin
  for I := 0 to Scanners.Count-1 do
  begin
    Result := TScanner(Scanners[I]);
    if Scanner.Device = Device then
      Exit;
  end;
  Result := nil;
end;

procedure TMyForm.ConnectScanner(int Index);
begin
  TScanner(Scanners[Index]).Connect;
end;

procedure TMyForm.DisconnectScanner(int Index);
begin
  TScanner(Scanners[Index]).Disconnect;
end;

procedure TMyForm.RequestSnapshot(int Index);
begin
  TScanner(Scanners[Index]).RequestSnapshot;
end;

procedure TMyForm.ScannerWndProc(var Message: TMessage);
var
  Scanner: TScanner;
  Param: PDWPARAM;
begin
  case Message.Msg of

    WM_XFERSTATUS:
    begin
      Scanner := FindScanner(THandle(Message.LParam));
      if Scanner = nil then Exit;

      Param := PDWPARAM(Message.WParam);
      //BytesRecv := LODWORD(Param)
      //BytesTotal := HIDWORD(Param)

      if Scanner.ImageData = nil then
        Scanner.SetImageBuffer(Integer(HIDWORD(Param)));
    end;

    WM_IMAGE:
    begin
      Scanner := FindScanner(THandle(Message.LParam));
      if Scanner = nil then Exit;

      Scanner.Pending := False;

      Param := PDWPARAM(Message.WParam);
      // Status := LODWORD(Param)
      // BytesTotal := HIDWORD(Param)

      if (LODWORD(Param) and BUFFERSIZE_MASK) <> BUFFERSIZE_GOOD then
        raise Exception.Create('Image buffer error');

      // use Scanner.ImageData as needed...

      SetLength(Scanner.ImageData, 0);
    end;

    WM_DEVICENOTIFICATION:
    begin
      Param := PDWPARAM(Message.WParam);
      // NotifyCode := LODWORD(Param)

      case LODWORD(Param) of

        DEVICE_ARRIVE:
        begin
          Scanner := TScanner.Create;
          Scanner.Device := THandle(Message.LParam);
          Scanners.Add(Scanner);
          // add it to the UI somewhere...
        end;

        DEVICE_REMOVE:
        begin
          Scanner := FindScanner(THandle(Message.LParam));
          if Scanner <> nil then
          begin
            Scanners.Remove(Scanner);
            // remove it from the UI...
          end;
        end;
      end;
    end;

    WM_TIMEOUT:
    begin
      Scanner := FindScanner(THandle(Message.LParam));
      if Scanner = nil then Exit;

      Scanner.Pending := False;

      // NotifyCode := HIDWORD(Param)

      case HIDWORD(Param) of

        IMAGE_DATA_TIMEOUT:
        begin
          raise Exception.Create('Image timeout');
        end;

      end;
    end;

    WM_ERROR:
    begin
      Scanner := FindScanner(THandle(Message.LParam));
      if Scanner = nil then Exit;

      Scanner.Pending := False;

      Param := PDWPARAM(Message.WParam);
      // Status := LODWORD(Param)

      CheckSNAPIStatus(Integer(LODWORD(Param)));
    end;

  else
    Message.Result := DefWindowProc(ScannerWnd, Message.Msg, Message.WParam, Message.LParam);
end;