I bow to the monks
I'm writing an interface to a barcode scanner from Socket mobile. I have the latest SDK and all seamed to work just fine untill I needed to get the scanned data from the scanner.
Using Win32::API, I created the following link to the scanner's dll
my $ScanGetData = Win32::API->new('ScanAPI.dll','long ScanGetData(HANDLE hScanner, LPSTR lpBuff, LPINT BufSize)');
When I called the $ScanGetData function it caused perl to crash. I spent the next 9 hours debugging my function calls and parameters. What I found to work was:
sub WMSCANNERDATA
{
print "Scanner Has data waiting\n";
my ($object, $wParam, $lParam, $type, $msgcode) = @_;
return unless $type == 0;
return unless $msgcode == WM_SCANNERDATA;
print "Data size: $wParam\n";
my $buffer= 0 x $wParam;
my $ret=$ScanGetData->Call($ScannerHandle, $buffer, \$buffersi
+ze);
print "Return code: $ret";
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print $buffer."\n\n";
return 1;
}
This code would work for the first scan and then cause perl to crash on the second scan. I figured out the the \$buffersize was not being dereferenced. After much searching I found that this code would work:
sub WMSCANNERDATA
{
print "Scanner Has data waiting\n";
my ($object, $wParam, $lParam, $type, $msgcode) = @_;
return unless $type == 0;
return unless $msgcode == WM_SCANNERDATA;
print "Data size: $wParam\n";
my $buffer= 0 x $wParam;
# added this line to make function call work
my $buffersize= \$wParam;
my $ret=$ScanGetData->Call($ScannerHandle, $buffer, $buffersiz
+e); # Inform scanApi.dll this application does nor handle call bac
+ks
print "Return code: $ret";
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print $buffer."\n\n";
return 1;
}
Here is the actual function description from the scanner docs
SCANGETDATA()
Prototype:
SCAN_RESULT ScanGetData(HANDLE hScanner, TCHAR * lpBuff, LPINT BufSize
+);
Purpose:
Returns scanned data after a successful read operation on the scanner
+device.
Arguments:
[in] hScanner is the value received by the client application in lPara
+m of the WM_INSERTION message specified when ScanInit() was called.
If the application program doesn’t support callbacks, use 1 for the HA
+NDLE (if multi-scanner disabled) .
[out] lpBuff points to the buffer to receive the scanned data.
[in/out] BufSize points to the size of the buffer in bytes (not charac
+ters). Upon return, BufSize will be set to the number of bytes writte
+n into lpBuff. This can be converted to a character count by dividing
+ BufSize by sizeof(TCHAR). Typically the buffer size will be equal to
+ the character count on Win32 Desktop systems.
My question to the monks is, why does this my $buffersize= \$wParam; allow my function to work for multiple scans. If I understand the Win32::API docs, all perl variables are passed as reference so \$buffersize should not have been needed, and creating a var that is a reference to another var should not be needed. Does it have to due with the BuffSize being rewritten to the $buffersize parameter by the scanner dll? I'm confused. Happy I got it to work but confused. Here is the complete test script.
#!perl -w
use strict;
use warnings;
use Win32::GUI();
use Win32::API;
use Win32::API::Prototype;
use Carp qw(croak);
use Data::Dumper;
# Preloaded methods go here.
###################################################################
+########
# Supported C types
###################################################################
+########
# ATOM s HWINSTA L
# BOOL L HWND L
# BOOLEAN c INT i
# BYTE C INT32 i
# CHAR c INT64 q
# COLORREF L LANGID s
# DWORD L LCID L
# DWORD32 L LCSCSTYPE L
# DWORD64 Q LCSGAMUTMATCH L
# FLOAT f LCTYPE L
# HACCEL L LONG l
# HANDLE L LONG32 l
# HBITMAP L LONG64 q
# HBRUSH L LONGLONG q
# HCOLORSPACE L LPARAM L
# HCONV L LRESULT L
# HCONVLIST L REGSAM L
# HCURSOR L SC_HANDLE L
# HDC L SC_LOCK L
# HDDEDATA L SERVICE_STATUS_HANDLE L
# HDESK L SHORT s
# HDROP L SIZE_T L
# HDWP L SSIZE_T L
# HENHMETAFILE L TBYTE c
# HFILE L TCHAR C
# HFONT L UCHAR C
# HGDIOBJ L UINT I
# HGLOBAL L UINT_PTR L
# HHOOK L UINT32 I
# HICON L UINT64 Q
# HIMC L ULONG L
# HINSTANCE L ULONG32 L
# HKEY L ULONG64 Q
# HKL L ULONGLONG Q
# HLOCAL L USHORT S
# HMENU L WCHAR S
# HMETAFILE L WORD S
# HMODULE L WPARAM L
# HPALETTE L VOID c
# HPEN L int i
# HRGN L long l
# HRSRC L float f
# HSZ L double d
# char c
###################################################################
+########
# DLL Function call setup
###################################################################
+########
my $ScanInit = Win32::API->new('ScanAPI.dll','long ScanInit(HWND hW
+nd, long wminseriton, long wmRemoval)');
# my $ScanErrToText = Win32::API->new('ScanAPI.dll','long ScanErrToT
+ext(int Err, TCHAR lpBuff, int BuffSize)');
my $ScanOpenDevice = Win32::API->new('ScanAPI.dll','long ScanOpenDe
+vice(HANDLE hScanner)');
my $ScanCloseDevice = Win32::API->new('ScanAPI.dll','long ScanClose
+Device(HANDLE hScanner)');
my $ScanRequestDataEvents = Win32::API->new('ScanAPI.dll','long Sca
+nRequestDataEvents(HANDLE hScanner, HWND hWnd, long wmScannerData)');
my $ScanTrigger = Win32::API->new('ScanAPI.dll','long ScanTrigger(H
+ANDLE hScanner)');
my $ScanGetData = Win32::API->new('ScanAPI.dll','long ScanGetData(H
+ANDLE hScanner, LPSTR lpBuff, LPINT BufSize)');
# my $ScanGetData = Win32::API->new('ScanAPI.dll','ScanGetData',['N'
+,'P','N'],'N');
# sub hWnd(){0}; # tell the ScanAPI function that no ca
+ll back is needed
sub WM_USER() {1024};
sub WM_INSERTION(){WM_USER + 1}; # the message we want on device
+ insertions
sub WM_REMOVAL(){WM_USER + 2}; # the message we want on device r
+emovals
sub WM_SCANNERDATA(){WM_USER + 3}; # the message we want when da
+ta is available
sub WM_CHS_STATUS(){WM_USER + 4}; # the message we want when CHS
+ status changes
sub NOCALLBACK(){1};
# SCANAPI retunr values
use constant SR_SUCCESS => 0;
use constant SR_INVALID_WMINSERTION => 1;
use constant SR_INVALID_WMREMOVAL => 2;
use constant SR_PLUG_THREAD_FAILURE => 3;
use constant SR_DEVICE_THREAD_FAILURE => 4;
use constant SR_INVALID_SCANNER_HANDLE => 5;
use constant SR_OPEN_FAILURE => 6;
use constant SR_INVALID_WMSCANNERDATA => 7;
use constant SR_NO_DATA => 8;
use constant SR_BUFFER_TOO_SMALL => 9;
use constant SR_SCANNER_NOT_OPEN => 10;
use constant SR_INVALID_SOUND_TYPE => 11;
use constant SR_WAVFILE_NOT_FOUND => 12;
use constant SR_MEMORY_FAILURE => 13;
use constant SR_INVALID_ERR => 14;
use constant SR_TOO_MANY_USERS => 15;
use constant SR_NOT_INITIALIZED => 16;
use constant SR_DEVICE_FAILURE => 17;
use constant SR_INTERNAL_FAILURE => 18;
use constant SR_INVALID_STRUCTURE => 19;
use constant SR_HOTSWAP_ERROR => 20;
use constant SR_SCANNER_REMOVED => 21;
use constant SR_INVALID_WMCHSSTATUS => 22;
our $ret;
our $ScannerHandle;
sub Buffersize(){1024};
# Create a window, saving it in variable $main
my $main = Win32::GUI::Window->new(
-name => 'Main',
-width => 100,
-height => 100,
);
# Add a label to the window (by default a label
# has size big enough for its text and is positioned
# in the top left of its containing window)
$main->AddLabel(
-text => $main->{-handle},
);
$main->AddButton(
-name => 'button1',
-text => "click",
);
$main->AddButton(
-name => 'button2',
-text => "scan",
-left => 50,
);
$main->Hook(WM_INSERTION, \&WMINSERTION);
$main->Hook(WM_REMOVAL, \&WMREMOVAL);
$main->Hook(WM_SCANNERDATA, \&WMSCANNERDATA);
# Show our main window
$main->Show();
# Enter the windows message loop, often referred
# to as the "dialog phase".
Win32::GUI::Dialog();
# When the message loopreturns control to our
# perl program, then the interaction with the
# GUI is complete, so we exit.
exit(0);
###################### ######################
# The Terminate event handler for a window
# named 'Main'. Returning -1 causes the
# windows message loop to exit and return
# control to our perl program.
sub Main_Terminate {
return -1;
}
sub button1_Click
{
$ret=$ScanInit->Call($main->{-handle},WM_INSERTION,WM_REMOVAL);
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print "Scanner Interface Started\n";
}
1;
sub button2_Click
{
$ret=$ScanTrigger->Call($ScannerHandle);
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print "Scanner Triggered\n";
}
1;
sub WMINSERTION
{
my ($object, $wParam, $lParam, $type, $msgcode) = @_;
return unless $type == 0;
return unless $msgcode == WM_INSERTION;
$ScannerHandle=$lParam;
print "Scanner inserted\n\nscanner handle: $lParam\n";
my $ret=$ScanOpenDevice->Call($lParam); # Inform scanApi.dl
+l this application does nor handle call backs
print "Return code: $ret\n";
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
return -1;
}
print "Scanner opened\n";
$ret=$ScanRequestDataEvents->Call($lParam, $main->{-handle}, W
+M_SCANNERDATA);
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
return -1;
}
print "Scanner Events Monitoring started\n";
return;
}
sub WMREMOVAL
{
my ($object, $wParam, $lParam, $type, $msgcode) = @_;
return unless $type == 0;
return unless $msgcode == WM_REMOVAL;
print "Scanner Removed\n\nscanner handle: $lParam\n";
my $ret=$ScanCloseDevice->Call($lParam); print "Return
+ code: $ret\n";
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print "Scanner Closed\n";
return;
}
sub WMSCANNERDATA
{
print "Scanner Has data waiting\n";
my ($object, $wParam, $lParam, $type, $msgcode) = @_;
return unless $type == 0;
return unless $msgcode == WM_SCANNERDATA;
print "Data size: $wParam\n";
my $buffer= 0 x $wParam;
my $buffersize= \$wParam;
my $ret=$ScanGetData->Call($ScannerHandle, $buffer, $buffersiz
+e); # Inform scanApi.dll this application does nor handle call bac
+ks
print "Return code: $ret";
if ($ret != SR_SUCCESS)
{
&DisplayScanError($ret);
exit;
}
print $buffer."\n\n";
# undef @_;
# undef $object;
# undef $wParam;
# undef $lParam;
# undef $type;
# undef $msgcode;
return 1;
}
sub DisplayScanError()
{
my $ErrorCode=$_[0];
my $ErrorText='';
if ($ErrorCode == SR_INVALID_WMINSERTION)
{
$ErrorText= "ScanAPI Error: Invalid WM_INSERTION Value";
}
if ($ErrorCode == SR_INVALID_WMREMOVAL)
{
$ErrorText= "ScanAPI Error: Invalid WM_REMOVAL Value";
}
if ($ErrorCode == SR_PLUG_THREAD_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_PLUG_THREAD_FAILURE";
}
if ($ErrorCode == SR_DEVICE_THREAD_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_DEVICE_THREAD_FAILURE";
}
if ($ErrorCode == SR_INVALID_SCANNER_HANDLE)
{
$ErrorText= "ScanAPI Error: SR_INVALID_SCANNER_HANDLE";
}
if ($ErrorCode == SR_OPEN_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_OPEN_FAILURE";
}
if ($ErrorCode == SR_INVALID_WMSCANNERDATA)
{
$ErrorText= "ScanAPI Error: SR_INVALID_WMSCANNERDATA";
}
if ($ErrorCode == SR_NO_DATA)
{
$ErrorText= "ScanAPI Error: SR_NO_DATA";
}
if ($ErrorCode == SR_BUFFER_TOO_SMALL)
{
$ErrorText= "ScanAPI Error: SR_BUFFER_TOO_SMALL";
}
if ($ErrorCode == SR_SCANNER_NOT_OPEN)
{
$ErrorText= "ScanAPI Error: SR_SCANNER_NOT_OPEN";
}
if ($ErrorCode == SR_INVALID_SOUND_TYPE)
{
$ErrorText= "ScanAPI Error: SR_INVALID_SOUND_TYPE";
}
if ($ErrorCode == SR_WAVFILE_NOT_FOUND)
{
$ErrorText= "ScanAPI Error: SR_WAVFILE_NOT_FOUND";
}
if ($ErrorCode == SR_MEMORY_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_MEMORY_FAILURE";
}
if ($ErrorCode == SR_INVALID_ERR)
{
$ErrorText= "ScanAPI Error: SR_INVALID_ERR";
}
if ($ErrorCode == SR_TOO_MANY_USERS)
{
$ErrorText= "ScanAPI Error: SR_TOO_MANY_USERS";
}
if ($ErrorCode == SR_NOT_INITIALIZED)
{
$ErrorText= "ScanAPI Error: SR_NOT_INITIALIZED";
}
if ($ErrorCode == SR_DEVICE_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_DEVICE_FAILURE";
}
if ($ErrorCode == SR_INTERNAL_FAILURE)
{
$ErrorText= "ScanAPI Error: SR_INTERNAL_FAILURE";
}
if ($ErrorCode == SR_INVALID_STRUCTURE)
{
$ErrorText= "ScanAPI Error: SR_INVALID_STRUCTURE";
}
if ($ErrorCode == SR_HOTSWAP_ERROR)
{
$ErrorText= "ScanAPI Error: SR_HOTSWAP_ERROR";
}
if ($ErrorCode == SR_SCANNER_REMOVED)
{
$ErrorText= "ScanAPI Error: SR_SCANNER_REMOVED";
}
if ($ErrorCode == SR_INVALID_WMCHSSTATUS)
{
$ErrorText= "ScanAPI Error: SR_INVALID_WMCHSSTATUS";
}
print "Error Message: $ErrorText\n";
return 1;
}