win32 C ++打印字符串到打印机

时间:2012-09-11 14:31:43

标签: c++ qt winapi

经过几天网上搜索我是如何准确地将任意字符串打印到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)我愿意实施任何黑客攻击。有人喜欢将它写入文本文件并发出复制命令似乎不起作用,也就是说,我初始化设备错误失败。

这有效: 记事本/ P Documents / test_print.txt 这不起作用: 复制Documents \ test_print.txt / D:EPSON_TM_T20 复制Documents \ test_print.txt / D \ MYCOMPUTER \ epson_tm_t20(导致拒绝访问,共享打印机) print Documents \ 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中获取的打印机名称的字符串表示。

由于字符串具有打印机所需的所有格式,因此无需担心新行或任何格式。

这个问题的所有三个答案实际上对实施最终工作解决方案非常有帮助,我已经对每个答案进行了投票,并且感谢每个人回答的时间。

5 个答案:

答案 0 :(得分:5)

大多数现代打印机不对其给出的数据执行任何形式的布局处理。因此,将一系列字符发送到打印机最多只能以某种默认字体打印在页面一侧运行的一行文本。回车也可能有效。

现代打印机通常使用打印机理解的预处理数据打印页面,并定义打印的位置和打印方式。所有这些预处理都在主机PC上完成,结果发送到打印机。这就是您通常安装打印机驱动程序的原因 - 这些驱动程序获取用户数据(无论是简单的文本文件还是DTP页面)并将其转换为打印机能够理解的语言。

这样做的结果是将原始文本发送到打印机可能无法正常工作。

然后你遇到了拥有不同属性和语言的多台打印机的问题。

因此,在Windows中,所有这些都被抽象到打印机设备上下文对象中。它具有与图形设备上下文相同的界面,但您以不同方式创建它。

Win32 API有一个通用对话框,让用户选择打印机。使用PrintDlgEx功能允许用户选择打印机。然后使用返回的DC将文本绘制到页面。

答案 1 :(得分:2)

有几篇MSDN文章描述了如何将原始数据(打印机控制代码等)发送到打印机。

答案 2 :(得分:1)

您有正确的想法(尽管您应该StartPageEndDoc来匹配)。问题是TextOut只绘制了一行文字。它不会将长字符串分成多行等等。您需要这样做(或找到代码来执行此操作)。

如果你知道文字总是适合单个页面,你可以用TextOut来代替DrawTextEx,这可以做基本的换行,标签扩展等。

答案 3 :(得分:1)

为什么不试试QPrint ..它使用Generic Text Only驱动程序打印原始文本

QString prn("^XA^FO121,41^A0N,19,15^FDABC DEFGHIJKLMNOPQRSTUVWXYZ^FS^XZ");
QPrinter printer(QPrinterInfo::defaultPrinter());  //the default printer is "Generic / Text Only"

QTextDocument doc(prn);
doc.print(&printer);

答案 4 :(得分:0)

M在C ++中尝试以下代码:

 #include<fstream>
Class PrinterDriver{
Private:
fstream print("PRN")
Public:
Void Print(char a[]){
print >>a;}
Char GetPrinterStatus[](){
char c[];
print<<c;
return c;}};

了解它(关键)