C++ win32 USB打印机开发
本文地址:http://tongxinmao.com/Article/Detail/id/258
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 宽度 )
复制代码
上一篇:打印机语言PostScript和PCL的比较
下一篇:Delphi打印机编程