C++ win32 USB打印机开发
前言
Windows系统 打印机连接电脑,windows会自动更新硬件驱动来识别硬件; 正规的打印机打印机一般都有自己的官方驱动;打印机一般都支持ESC/POS指令。所以小票打印一般可以采取两种模式:1、基于硬件驱动;2、基于打印机驱动。
本文只做一个实现上的抛砖引玉。
基于打印驱动
流程:应用软件=>打印驱动=>设备驱动=>打印设备
开发:
1 : 获得本地打印驱动
struct PrintInfo { BOOL m_bisPrintPage; string sDeviceName; string sPortName; string sShareName; string sLoction; string sPaperType; int nWidthMin; bool bisPrint; PrintInfo() { m_bisPrintPage = FALSE; sDeviceName = ""; sPortName = ""; sShareName = ""; sLoction = ""; sPaperType = ""; nWidthMin = 0; bisPrint = FALSE; } }; typedef vector<PrintInfo> PrintInfo_Ary; int GetLocalPrinterDrivers(PrintInfo_Ary &Drivers) { DWORD Flags = PRINTER_ENUM_FAVORITE | PRINTER_ENUM_LOCAL; //local printers DWORD cbBuf; DWORD pcReturned; DWORD index; DWORD Level = 2; CHAR Name[500]; LPPRINTER_INFO_2A pPrinterEnum = NULL; memset(Name, 0, sizeof(CHAR)* 500); ::EnumPrintersA(Flags, Name, Level, NULL, 0, &cbBuf, &pcReturned); pPrinterEnum = (LPPRINTER_INFO_2A)LocalAlloc(LPTR, cbBuf + 4); if (!pPrinterEnum) { goto clean_up; } if (!EnumPrintersA( Flags, // DWORD Flags, printer object types Name, // LPTSTR Name, name of printer object Level, // DWORD Level, information level (LPBYTE)pPrinterEnum, // LPBYTE pPrinterEnum, printer information buffer cbBuf, // DWORD cbBuf, size of printer information buffer &cbBuf, // LPDWORD pcbNeeded, bytes received or required &pcReturned) // LPDWORD pcReturned number of printers enumerated ) { goto clean_up; } if (pcReturned > 0) { for (index = 0; index < pcReturned; index++) { PrintInfo DeviceDate; if ((pPrinterEnum + index)->pPrinterName != NULL) DeviceDate.sDeviceName = (pPrinterEnum + index)->pPrinterName; else DeviceDate.sDeviceName =""; if ((pPrinterEnum + index)->pPortName != NULL) DeviceDate.sPortName = (pPrinterEnum + index)->pPortName; else DeviceDate.sPortName = ""; if ((pPrinterEnum + index)->pShareName != NULL) DeviceDate.sShareName = (pPrinterEnum + index)->pShareName; else DeviceDate.sShareName = ""; if ((pPrinterEnum + index)->pLocation != NULL) DeviceDate.sLoction = (pPrinterEnum + index)->pLocation; else DeviceDate.sLoction = ""; if ((pPrinterEnum + index)->pDevMode) { DeviceDate.nWidthMin = (pPrinterEnum + index)->pDevMode->dmPaperWidth; DeviceDate.sPaperType = (char*)((pPrinterEnum + index)->pDevMode->dmFormName); } //DeviceDate.nWidthMin = (pPrinterEnum + index)->pDevMode->dmPaperWidth; //DeviceDate.sPaperType = (char*)((pPrinterEnum + index)->pDevMode->dmFormName); DWORD nstuse = (pPrinterEnum + index)->Status; DWORD Attributes = (pPrinterEnum + index)->Attributes; DeviceDate.m_bisPrintPage = BisConnectedA(nstuse, Attributes); Drivers.push_back(DeviceDate); } } clean_up: LocalFree(LocalHandle(pPrinterEnum)); return (int)Drivers.size(); }
2: 打开设备进行打印打开一个打印机驱动DriverName 是驱动名称
{ HDC hdcPrint = CreateDC(NULL, DriverName, NULL, NULL);
HFONT Font = CreateFont(32, 0, 0, 0, fontWeight, 0, 0, 0,
DEFAULT_CHARSET, OUT_TT_PRECIS,
CLIP_TT_ALWAYS, PROOF_QUALITY,VARIABLE_PITCH | FF_MODERN | 0x04,L"宋体");
//设置打印梯子
SelectObject(hdcPaint,Font);
LPCWSTR Lp = L"task";
DOCINFO div={sizeof (DOCINFO),Lp};
//编辑一个文档 StartDoc(hdcPrint, &m_di); //编辑一个文档的一页 StartPage(hdcPrint); RECT rect; rect.top = 0; rect.bottom = 200; rect.left = 0; rect.right = 380; DrawTextA(hdcPrint, "helleo world",13, &rect, DT_LEFT | DT_TOP | DT_EXPANDTABS | DT_NOPREFIX | DT_EDITCONTROL | DT_WORDBREAK | DT_EXTERNALLEADI NG | DT_CALCRECT); DrawTextA(hdcPrint,"helleo world",13,&rect, DT_LEFT | DT_TOP | DT_EXPANDTABS | DT_NOPREFIX | DT_EDITCONTROL | DT_WORDBREAK | DT_EXTERNALLEADING ); //.... //结束编辑页 EndPage(hdcPrint); //结束整个文本编辑 EndDoc(hdcPrint); CloseHandle(hdcPrint); }
3: 总结,以上代码只做抛砖引玉,由于驱动都是GDI驱动,所以文字按照GDI绘制即可。
基于硬件驱动开发
流程: 应用软件=>设备驱动=>打印设备
简述: 很明显基于设备驱动开发,跳过了打印驱动层,这个优点是不需要关心打印驱动是否正常,是否需要配置端口。劣势,直接和打印机通信,基于(ESC)指令,这样增加了开发难度。
开发:
1: 获得打印设备信息
#pragma once #include <iostream> #include <fstream> #include <string> #include <sstream> #include <windows.h> #include <devguid.h> #include <setupapi.h> #include <vector> #pragma comment (lib, "setupapi.lib") const char DRIVER_NAME_USBPRINT[] = "USB 打印支持"; const char DRIVER_NAME_USBPRINT_EN[] = "USB Printing Support"; struct deviceinfo { std::string sdeviceid; std::string sdesc; std::string sdevicepath; std::string sdeviceshow; deviceinfo() { sdeviceid = ""; sdesc = ""; sdevicepath = ""; sdeviceshow = ""; } }; std::vector<deviceinfo > g_printerdevices; const GUID GUID_DEVINTERFACE_USB_HUB = { 0xf18a0e88L, 0xc30c, 0x11d0, { 0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8 } }; const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3abf6f2dL, 0x71c4, 0x462a, { 0x8a, 0x92, 0x1e, 0x68, 0x61, 0xe6, 0xaf, 0x27 } }; const GUID GUID_USB_WMI_STD_DATA = { 0x4E623B20L, 0xCB14, 0x11D1, { 0xB3, 0x31, 0x00, 0xA0, 0xC9, 0x59, 0xBB, 0xD2 } }; const GUID GUID_USB_WMI_STD_NOTIFICATION = { 0x4E623B20L, 0xCB14, 0x11D1, { 0xB3, 0x31, 0x00, 0xA0, 0xC9, 0x59, 0xBB, 0xD2 } }; const GUID USBPrinterGUID = { 0x4d36e979L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } }; //GUID__DEVINTERFACE_USB_DEVICE int GetUSBPrintDevices(const GUID guid) { const GUID GUID_DEVINTERFACE_USB_DEVICE = guid; const GUID* InterfaceGuid = &GUID_DEVINTERFACE_USB_DEVICE; // that contains all devices of a specified class HDEVINFO hDevInfo = SetupDiGetClassDevsA(InterfaceGuid, NULL, // Enumerator NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); if (hDevInfo == INVALID_HANDLE_VALUE) { return 1; } SP_DEVINFO_DATA DeviceInfoData; DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); PSP_DEVINFO_DATA pDeviceInfoData = &DeviceInfoData; DWORD MemberIndex; bool found_usb_printer_flag = false; for (MemberIndex = 0; SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, pDeviceInfoData) == TRUE; MemberIndex++) { SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); PSP_DEVICE_INTERFACE_DATA pDeviceInterfaceData = &DeviceInterfaceData; DWORD RequiredSize; PDWORD pRequiredSize = &RequiredSize; DWORD InnerMemberIndex; for (InnerMemberIndex = 0; SetupDiEnumDeviceInterfaces(hDevInfo, pDeviceInfoData, InterfaceGuid, InnerMemberIndex, pDeviceInterfaceData) == TRUE; InnerMemberIndex++) { DWORD DeviceInterfaceDetailDataSize = 0; BOOL first_call_result = SetupDiGetDeviceInterfaceDetailA(hDevInfo, pDeviceInterfaceData, NULL, // NULL DeviceInterfaceDetailData pointer 0, // DeviceInterfaceDetailDataSize of zero pRequiredSize, pDeviceInfoData); if (first_call_result == TRUE) { continue; } else { DWORD first_call_error = GetLastError(); if (first_call_error != ERROR_INSUFFICIENT_BUFFER) { continue; } else { CHAR* buffer = new CHAR[RequiredSize]; PSP_DEVICE_INTERFACE_DETAIL_DATA_A pDeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)buffer; pDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); DWORD DeviceInterfaceDetailDataSize = RequiredSize; PDWORD pDeviceInterfaceDetailDataSize = &DeviceInterfaceDetailDataSize; BOOL second_call_result = SetupDiGetDeviceInterfaceDetailA(hDevInfo, pDeviceInterfaceData, pDeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, &RequiredSize, pDeviceInfoData); if (second_call_result == FALSE) { DWORD last_error_number_second_call = GetLastError(); CHAR last_error_message_second_call[512]; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, last_error_number_second_call, 0, last_error_message_second_call, 1024, NULL); } else { DWORD dwNumberOfBytesWritten = 0; CHAR szDescription[MAX_PATH]; memset(szDescription, 0, MAX_PATH); const GUID* ClassGuid = &(pDeviceInfoData->ClassGuid); SetupDiGetClassDescriptionA(ClassGuid, szDescription, MAX_PATH, &RequiredSize); memset(szDescription, 0, MAX_PATH); SetupDiGetDeviceInstanceIdA(hDevInfo, pDeviceInfoData, szDescription, MAX_PATH, 0); char szName[64] = {0}; if (SetupDiGetDeviceRegistryProperty(hDevInfo, pDeviceInfoData, SPDRP_FRIENDLYNAME, 0L, (PBYTE)szName, 63, 0)) { } else if (SetupDiGetDeviceRegistryProperty(hDevInfo, pDeviceInfoData, SPDRP_DEVICEDESC, 0L, (PBYTE)szName, 63, 0)) { }; std::string sdrivername = szName; if(sdrivername.find(DRIVER_NAME_USBPRINT) != std::string::npos || sdrivername.find(DRIVER_NAME_USBPRINT_EN) != std::string::npos) { deviceinfo device; device.sdeviceid = szDescription; device.sdesc = szName; device.sdevicepath = pDeviceInterfaceDetailData->DevicePath; GetShowNames(device.sdeviceid ,device.sdeviceshow); g_printerdevices.push_back(device); } } // end if second call returned true // now free the buffer delete[] buffer; } // end if first call was successful } // end if on second call } // end iterate through device interfacess } // end iterate through device info SetupDiDestroyDeviceInfoList(hDevInfo); return 0; }
2. 选取一个设备打印
const char initialize_printer_command[] = {ESCAPE_CODE, AT_CODE, NULL_CODE}; const char ESC_WIDTHANDHEGHIT2[] = {0x1c,0x21,12}; int WiterFileInfo(HANDLE handle,const char* sdata) { DWORD dwInitializePrinterNumberOfBytesToWrite = 0; DWORD dwInitializePrinterNumberOfBytesWritten = 0; dwInitializePrinterNumberOfBytesToWrite = (DWORD)strlen(sdata); BOOL ret = WriteFile( handle, sdata, dwInitializePrinterNumberOfBytesToWrite, &(dwInitializePrinterNumberOfBytesWritten), NULL); return dwInitializePrinterNumberOfBytesWritten; } int USBPrintData(deviceinfo &driver) { HANDLE handle = CreateFileA(driver.sdevicepath.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) { CloseHandle(handle); return 0; } else { int i = 0; } DWORD dwInitializePrinterNumberOfBytesToWrite = 0; DWORD dwInitializePrinterNumberOfBytesWritten = 0; //ESC 指令打印 WiterFileInfo(handle,initialize_printer_command); sprintf(write,"%s两倍大小字体\n",ESC_WIDTHANDHEGHIT2); WiterFileInfo(handle,write); //空白纸走3行 WiterFileInfo(handle," \n \n \n"); CloseHandle(handle); return 0; }
3: 以上代码只是为了帮助新手成功连接打印机并且打印出字。细节需要自己研究
备注
一点ESC指令测试
const char ESC_INIT_RWX[] = {0x1B,0x40}; //初始化; const char ESC_LINE_TAB[] = {0x0A}; //换行 const char ESC_LINE_ADD[] = {0x1B,0x21,0x28}; //加粗 //const char ESC_LINE_H2[] = {0x1D,0x21,0x04}; //倍高 垂直( 三位符(倍高度) 1 2 4 8 1-7 倍) //const char ESC_LINE_W2[] = {0x1D,0x21,0x20}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_H1[] = {0x1D,0x21,0x01}; //倍高 垂直( 三位符(倍高度) 1 2 4 8 1-7 倍) const char ESC_LINE_H2[] = {0x1D,0x21,0x02}; //倍高 垂直( 三位符(倍高度) 1 2 4 8 1-7 倍) const char ESC_LINE_H3[] = {0x1D,0x21,0x04}; //倍高 垂直( 三位符(倍高度) 1 2 4 8 1-7 倍) const char ESC_LINE_W1[] = {0x1D,0x21,0x10}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_W2[] = {0x1D,0x21,0x20}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_W3[] = {0x1D,0x21,0x30}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_T1[] = {0x1B,0x21,0x02}; //原始高度 const char ESC_LINE_T2[] = {0x1D,0x21,0x11}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_T3[] = {0x1D,0x21,0x22}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_T4[] = {0x1D,0x21,0x34}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 ) const char ESC_LINE_T5[] = {0x1D,0x21,0x48}; //倍高 垂直( 三位符(倍高度) 10 20 30 40 宽度 )
浙公网安备 33010602011771号