http://www.perlmonks.org?node_id=693348

rpnoble419 has asked for the wisdom of the Perl Monks concerning the following question:

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; }

Replies are listed 'Best First'.
Re: Win32::API passing a parameter by reference issue
by ikegami (Patriarch) on Jun 22, 2008 at 07:57 UTC

    My question to the monks is, why does this my $buffersize= \$wParam; allow my function to work for multiple scans.

    If anything is surprising is that it worked the first time without it. The documentation clearly states the variable must contains the size of the allocated buffer when the function is called.

      My example was not clear. I did try using $wParam as the buffer size as that is what was returned by the message hook from Win32::GUI. That still caused the system to hang on the second pass. It was not until I created the reference to wParam that it worked.

        I was experiencing this same issue of the function call only happening once and then the next time the script would just end. Though it looks like we might have been experiencing it for different reasons figured I would post my solution since this is the first google result I got, maybe someone else will find it helpful :)

        Anywise, I was defining my function like this:

        my $getIndivPages = new Win32::API('c:\WINNT\system32\ALISE_Domain_nav.dll', 'GetPageNum', 'PPPP', 'N');

        And it work work the first time I did $getInivPages->Call(...), however I had this in a loop to be executed n times and it would only be executed once. So after much frustration I tried a random thing and redefined my function like this:

        my $getIndivPages = Win32::API->new('c:\WINNT\system32\ALISE_Domain_nav.dll', 'void __stdcall GetPageNum(long num, char *con, char *tit, char *url)');

        And it worked! (though I couldn't believe that's what was wrong after wasting many hours), anywise going through the loop the function would be executed the intended amount of times. Seems weird for it to be able to work just by changing how the function is defined but whatever.
Re: Win32::API passing a parameter by reference issue
by Anonymous Monk on Jun 22, 2008 at 13:56 UTC
    BTW: Please consider using  <readmore> ... code ... </readmore> tags around huge chunks of code when you include them in your posts.
Re: Win32::API passing a parameter by reference issue
by DrBrain (Initiate) on Jul 13, 2008 at 10:48 UTC
    Hello my friend :-) i have the same Socket Scanner but i did not get the SKD until yet.Can you send me latest the SDK fot the Socket Scanner ? if yes i would be very pleased. my E-Mail is no-address@web.de see you DrBrain