在网上搜索了几天关于如何将任意字符串打印到 Windows 上的任意打印机之后,我终于想出了这段代码。
LPBYTE pPrinterEnum;
DWORD pcbNeeded, pcbReturned;
PRINTER_INFO_2 *piTwo = NULL;
HDC printer;
EnumPrinters(PRINTER_ENUM_LOCAL,NULL,2,NULL,0,&pcbNeeded,&pcbReturned);
pPrinterEnum = new BYTE[pcbNeeded];
if (!EnumPrinters(PRINTER_ENUM_LOCAL,NULL,2,pPrinterEnum,pcbNeeded,&pcbNeeded,&pcbReturned)) {
qDebug() << "In Print, could not enumerate printers";
} else {
piTwo = ((PRINTER_INFO_2*)pPrinterEnum);
for (int i = 0; i < pcbReturned; i++) {
QString name = QString::fromWCharArray(piTwo[i].pPrinterName);
if (this->m_printer_path == name) {
const WCHAR * driver = L"WINSPOOL\0";
printer = CreateDC(NULL,piTwo[i].pPrinterName,NULL,NULL);
}
}
}
if (printer == 0) {
qDebug() << "No Printer HDC";
return;
} else {
qDebug() << "Printer seems okay!";
}
qDebug() << "Starting Document";
DOCINFO di;
memset( &di, 0, sizeof( di ) );
di.cbSize = sizeof( di );
WCHAR * text = new WCHAR[ba.length()];
QString(ba).toWCharArray(text);
StartDoc(printer,&di);
qDebug() << "Writing text";
TextOut(printer,0, 0, text, ba.length());
qDebug() << "Text Written";
EndPage(printer);
qDebug() << "Page ended";
DeleteDC(printer);
qDebug() << "DC Deleted";
一些基本的警告:
1) 我不能使用 QPrinter。我需要写原始文本,没有后记。2)在用户设置之前我不知道打印机的名称,并且在用户创建它之前我不知道要打印的字符串的大小。
附加信息:
a) 打印机工作正常,我可以从记事本、Chrome 以及几乎所有我想要的打印机打印。b)我愿意实施任何黑客攻击。将其写入文本文件并发出复制命令似乎不起作用,也就是说,我得到了一个初始化设备错误的失败。
这有效:notepad /P Documents/test_print.txt 这无效:复制 Documents\test_print.txt /D:EPSON_TM_T20 copy Documents\test_print.txt /D \MYCOMPUTER\epson_tm_t20(导致访问被拒绝,打印机被共享)打印文档\test_print.txt(无法初始化设备)
我已经尝试了几乎所有推荐的从命令行打印文本文件的方法,但都不起作用。我已经安装,重新安装了驱动程序,添加了打印机,弄乱了端口并再次完成了这一切。
显然,由于缺乏经验,我缺少一些关于 Windows 打印的简单内容。
我想要完成的是:
1)最佳方案(直接将文本写入打印机) 2)次佳方案(将文本写入文件,然后执行一些程序为我打印)记事本在打印输出的底部增加了令人讨厌的空间浪费纸张。
由于该程序是针对最终用户的,我必须找到一种自动为他们执行此操作的方法,因此我不能指望他们在从 powershell 运行命令 obscure_configuration 后单击选项卡 36 中的复选框 a。
任何帮助将不胜感激。
/杰森
更新
这是工作代码,在我稍微整理一下之前,它将 QByteArray 的内容打印到热敏打印机。
qDebug() << "Executing windows code";
BOOL bStatus = FALSE;
DOC_INFO_1 DocInfo;
DWORD dwJob = 0L;
DWORD dwBytesWritten = 0L;
HANDLE hPrinter;
wchar_t * name = new wchar_t[this->m_printer_path.length()+1];
this->m_printer_path.toWCharArray(name);
name[this->m_printer_path.length() + 1] = 0;
qDebug() << "opening printer";
bStatus = OpenPrinter(name,&hPrinter, NULL);
if (bStatus) {
qDebug() << "Printer opened";
DocInfo.pDocName = L"My Document";
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = L"RAW";
dwJob = StartDocPrinter( hPrinter, 1, (LPBYTE)&DocInfo );
if (dwJob > 0) {
qDebug() << "Job is set.";
bStatus = StartPagePrinter(hPrinter);
if (bStatus) {
qDebug() << "Writing text to printer";
bStatus = WritePrinter(hPrinter,ba.data(),ba.length(),&dwBytesWritten);
EndPagePrinter(hPrinter);
} else {
qDebug() << "could not start printer";
}
EndDocPrinter(hPrinter);
qDebug() << "closing doc";
} else {
qDebug() << "Couldn't create job";
}
ClosePrinter(hPrinter);
qDebug() << "closing printer";
} else {
qDebug() << "Could not open printer";
}
if (dwBytesWritten != ba.length()) {
qDebug() << "Wrong number of bytes";
} else {
qDebug() << "bytes written is correct " << QString::number(ba.length()) ;
}
注意:我确实应该向 Skizz 道歉,他写的内容实际上有助于调试基本问题。QByteArray 中的字符是专门为打印机预先格式化的,问题是,它们包含几个 NULL 字节。当试图将它们发送到打印机时,这会导致 TextOut 截断文本,只打印前几行。正如答案中所建议的,使用 WritePrinter 会忽略空字节并接受 void * 和长度,然后将其全部放在那里。
此外,他推荐使用 PrintDlg 的回复确实可以找到正确的打印机 HDC,问题是,用户首先选择打印机一次,然后不需要每次打印时都选择它,因为他们将打印很多(这是一个销售点)。
从字符串名称获取打印机 HDC 的问题是由于没有将所有重要的 NULL 字节添加到 wchar_* ,这是通过这种方式解决的:
wchar_t * name = new wchar_t[this->m_printer_path.length()+1];
this->m_printer_path.toWCharArray(name);
name[this->m_printer_path.length() + 1] = 0;
在上面,m_printer_path 是从 Print Manager 中获取的打印机名称的字符串表示形式。
因为字符串具有打印机所需的所有格式,所以无需担心换行或任何格式。
这个问题的所有三个答案实际上对实施最终的工作解决方案非常有帮助,我对每个答案都投了赞成票,我感谢每个人花时间做出回应。