MFC uses RawInput to obtain code scanning gun data without focus

        Recently, when the code scanning gun is used in the company's projects, it may encounter the situation that the focus is not in the program. As a keyboard device, the code scanning gun will also output the data where the focus is. It has been found on the Internet for a long time. Basically, there are many hooks, but they are not satisfactory. The data of the keyboard and the code scanning gun cannot be accurately distinguished, After integrating the solutions given by several netizens and consulting the relevant information, this problem was finally solved relatively perfectly.

        First, according to Primary school dogs meow All VID devices in the system are obtained in the article, and the specified device can be selected when using, https://blog.csdn.net/qq_39554698/article/details/99586564#comments_13018985 . The implementation code is as follows:

.h

#pragma once

#include "afxdialogex.h"
#include <iostream>  
#include <string>  
#include <fstream>
#include <setupapi.h>
#include <devguid.h>
#include <winioctl.h>
#include <regstr.h>
#include <conio.h>
#include <vector>

#pragma comment(lib,"setupapi.lib")
extern "C" {
#include "setupapi.h"
#include <hidsdi.h> 
};

using namespace::std;

struct _VID_PID_
{
	USHORT V_ID;
	USHORT P_ID;
	_VID_PID_( USHORT vid, USHORT pid )
	{
		V_ID = vid;
		P_ID = pid;
	}
	bool operator==( const _VID_PID_ &other )
	{
		if (V_ID == other.V_ID && P_ID == other.P_ID)
			return true;
		return false;
	}
};


class Scanner
{

public:
	vector<_VID_PID_> GetDeviceVector( );
private:
	void HID_FindAllDevices( unsigned short *FIFO_Length );

private:
	vector<_VID_PID_> vecVidPid;
};

.cpp

#include "pch.h"
#include "Scanner.h"


vector<_VID_PID_> Scanner::GetDeviceVector( )
{
	unsigned short* a;

	a = new unsigned short[100];
	memset( a, 0x00, 100 );
	vecVidPid.clear( );

	HID_FindAllDevices( a );

	delete[] a;

	a = NULL;
	return vecVidPid;
}

void Scanner::HID_FindAllDevices( unsigned short * FIFO_Length )
{
	GUID                             HidGuid;
	HDEVINFO                         DevInfo;
	HIDD_ATTRIBUTES					 DevAttributes;
	SP_DEVICE_INTERFACE_DATA         DevData;
	PSP_DEVICE_INTERFACE_DETAIL_DATA DevDetail;
	ULONG                            Length;
	int                              Index;
	BOOL                             ok;

	HANDLE DevHandle;
	int DevCount = 0;
	vecVidPid.clear( );
	/* Get GUID for all System HIDs */
	HidD_GetHidGuid( &HidGuid );
	/* Get Device Information for all present devices */
	DevInfo = SetupDiGetClassDevs( &HidGuid, NULL, NULL, ( DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ) );
	DevData.cbSize = sizeof( DevData );
	DevDetail = NULL;
	Index = -1;
	*FIFO_Length = 0;
	/* Scan all Devices */
	do {
		Index++;
		/* Device Interface Element of a Device Information set */
		ok = SetupDiEnumDeviceInterfaces( DevInfo, 0, &HidGuid, Index, &DevData );
		if (!ok) break;
		/* Get Device Interface Details - Get Length */
		ok = SetupDiGetDeviceInterfaceDetail( DevInfo, &DevData, NULL, 0, &Length, NULL );

		/* Allocate memory for Device Detailed Data */
		DevDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc( Length );
		/* Set cbSize in the DevDetail structure */
		DevDetail->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA );
		/* Get Device Interface Details */
		ok = SetupDiGetDeviceInterfaceDetail( DevInfo, &DevData, DevDetail, Length, NULL, NULL );
		if (!ok)
		{
			free( DevDetail );
			DevDetail = NULL;
			continue;
		}

		/* Create File for Device Read/Write */
		DevHandle = CreateFile( DevDetail->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL );
		if (DevHandle == INVALID_HANDLE_VALUE)
		{
			free( DevDetail );
			DevDetail = NULL;
			continue;
		}

		DevAttributes.Size = sizeof( DevAttributes );
		ok = HidD_GetAttributes( DevHandle, &DevAttributes );
		if (!ok)
		{
			free( DevDetail );
			CloseHandle( DevHandle );
			DevDetail = NULL;
			continue;
		}

		if (find( vecVidPid.begin( ), vecVidPid.end( ), _VID_PID_( DevAttributes.VendorID, DevAttributes.ProductID ) ) == vecVidPid.end( ) )
		{
			vecVidPid.push_back( _VID_PID_( DevAttributes.VendorID, DevAttributes.ProductID ) );
			free( DevDetail );
			CloseHandle( DevHandle );
			DevDetail = NULL;
		}

		if ( DevDetail )
		{
			free( DevDetail );

			DevDetail = NULL;
		}
	} while (DevCount < 20);
	SetupDiDestroyDeviceInfoList( DevInfo );
	return;

}

        Again based on qq_39554698 blog_ Primary school dogs meow_ CSDN blog of This article , you know that you need to get data from RawInput, but the PreTranslateMessage function should only receive messages from this program, so you need to register the RawInput device and receive the message ON_WM_INPUT.

        To register a RawInput device:

bool ***********::RegisterRawInput( )
{
	// TODO: add implementation code here
	RAWINPUTDEVICE rawinputdevice[1];
	rawinputdevice[0].usUsagePage = 0x01;
	rawinputdevice[0].usUsage = 0x06;
	rawinputdevice[0].dwFlags = RIDEV_INPUTSINK;
	rawinputdevice[0].hwndTarget = AfxGetMainWnd( )->m_hWnd;

	BOOL ret = RegisterRawInputDevices( &rawinputdevice[0], 1, sizeof( RAWINPUTDEVICE ) );
	if ( ret == FALSE )
	{
		MessageBox( _T( "Device registration failed!") );
	}

	return ret;
}

        according to MSDN statement Usagepage = 0x01, usagepage = 0x06, that is, the keyboard is registered or can be configured as other devices.

         Respond on in program_ WM_ Input message with the following code:

void *******::OnRawInput( UINT nInputcode, HRAWINPUT hRawInput )
{
	// This feature requires Windows XP or later.
	// Symbols_ WIN32_WINNT must be > = 0x0501.
	// TODO: add message handler code here and / or call default values

	UINT dwsize = 0;
	GetRawInputData( (HRAWINPUT)hRawInput, RID_INPUT, NULL, &dwsize, sizeof( RAWINPUTHEADER ) );

	LPBYTE lpb = new BYTE[dwsize];


	if (GetRawInputData( (HRAWINPUT)hRawInput, RID_INPUT, lpb, &dwsize, sizeof( RAWINPUTHEADER ) ) != dwsize) //Get message information
	{
		MessageBox( _T( "Error getting input information!" ) );
	}
	RAWINPUT *raw = (RAWINPUT*)lpb;

	if (raw->header.dwType == RIM_TYPEKEYBOARD) //The annotated part is used to judge whether the key is pressed
	{

		UINT dwSize = 0;
		//Prepare buffer size for keyboard device name
		GetRawInputDeviceInfo( raw->header.hDevice, RIDI_DEVICENAME, NULL, &dwSize );
		WCHAR stringBuffer[1024];
		//Read device name into buffer stringBuffer
		GetRawInputDeviceInfo( raw->header.hDevice, RIDI_DEVICENAME, stringBuffer, &dwSize );

		DWORD dwNum = WideCharToMultiByte( CP_OEMCP, NULL, stringBuffer, -1, NULL, 0, NULL, FALSE );//Application of WideCharToMultiByte
		char *psText;  // psText is a temporary array of char * as an intermediate variable assigned to std::string
		psText = new char[dwNum];
		WideCharToMultiByte( CP_OEMCP, NULL, stringBuffer, -1, psText, dwNum, NULL, FALSE );//Reuse of WideCharToMultiByte
		string szDst = psText;// std::string assignment
		delete[] psText;// Clear psText
		psText = NULL;

		if (szDst.find( m_scannerstring ) != string::npos)
		{
			if (raw->data.keyboard.Message == WM_KEYUP)
			{
				if (raw->data.keyboard.VKey == 0x10)
				{
					m_ShiftDown = FALSE;
				}
			}

			if (raw->data.keyboard.Message == WM_KEYDOWN)
			{
				char keytext[50] = { 0 };

				BYTE state[256] = { 0 };
				if (m_ShiftDown)
				{
					state[0x10] = 0x80;
				}

				//Get the name through the virtual keyboard code
				int length = ToAscii( raw->data.keyboard.VKey, raw->data.keyboard.MakeCode, state, (LPWORD)keytext, 0 );

				if (raw->data.keyboard.VKey == 0x10)
				{
					m_ShiftDown = TRUE;
				}

				if (raw->data.keyboard.VKey == '\r')
				{
					SendMessage( WM_COMMAND, MAKELONG( IDC_BARCODE, EN_UPDATE ), (LPARAM)(GetDlgItem( IDC_BARCODE )->GetSafeHwnd( )) );
				}

				m_barcodestringRawInput.append( keytext );
			}
		}
	}

	delete[] lpb;
	lpb = nullptr;
	raw = nullptr;

	CDialogEx::OnRawInput( nInputcode, hRawInput );
}

         m_scannerstring is the saved VID-PID flag of the scanning gun, which only processes the information obtained from the scanning gun, WM_KEYUP is used to check whether the Shift key has been released. If the released key is Shift, m_ShiftDown = FALSE, VKey = 0x10, that is, Shift key, WM_KEYDOWN is the key press flag. If the key pressed is Shift, m_ShiftDown = TRUE. When the Shift key is pressed, the corresponding bit of state in the ToAscii function must be changed. Otherwise, it cannot be converted correctly. For example, if Shift has been pressed, but the bit in state is not marked, when a is entered, a is converted, which is not the result we want.

         m_barcodestringRawInput saves the key information obtained each time. When it is completed, the code scanning gun will send '\ r'. At this time, it can judge that the reception has been completed and send a message to other functions for processing.

Keywords: C++ MFC microsoft

Added by mburkwit on Fri, 29 Oct 2021 04:43:22 +0300