C 在Windows中打印文件
假设我有一个ASCII编码的文本文件mytextfile.txt,我想打印它 我在MSDN上找到了各种来源,但似乎没有一个有用: 发件人: 定义从Windows窗体应用程序打印时将输出发送到打印机的可重用对象。 公共引用类PrintDocument:组件C 在Windows中打印文件,c,winapi,printing,C,Winapi,Printing,假设我有一个ASCII编码的文本文件mytextfile.txt,我想打印它 我在MSDN上找到了各种来源,但似乎没有一个有用: 发件人: 定义从Windows窗体应用程序打印时将输出发送到打印机的可重用对象。 公共引用类PrintDocument:组件 但是这是C++的意思,而不是c 我还发现,它定义了几个函数,但实际上似乎没有一个函数能够打印: IPrintDocumentPackageStatusEvent:表示打印作业的进度 IPrintDocumentPackageTarget:允许用
但是这是C++的意思,而不是c
我还发现,它定义了几个函数,但实际上似乎没有一个函数能够打印: IPrintDocumentPackageStatusEvent:表示打印作业的进度 IPrintDocumentPackageTarget:允许用户枚举支持的包目标类型,并使用给定的类型ID创建一个包目标类型。IPrintDocumentPackageTarget还支持跟踪包打印进度和取消 IPrintDocumentPackageTargetFactory:与IPrintDocumentPackageTarget一起用于启动打印作业 啊!!看起来是这样!事实证明,这也适用于C++:IPrintDocumentPackageTargetFactory::CreateDocumentPackageTargetForPrintJob
我认为尝试和模仿C语言的课程既不恰当又困难
我可以使用print命令:
但这似乎有点像黑客,有没有更好的方法使用函数打印文件
澄清一下:我希望打印在纸上,而不是在终端上。您可以在循环中读取一行,打印一行,直到EOF 编辑以下内容以输出到打印机
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUF_SIZE 1024
int main( void )
{
char buffer[ MAX_BUF_SIZE ];
FILE *fin = fopen( "mytextfile.txt", "f" );
if( ! fin )
{
perror( "fopen failed for reading file" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
FILE *printer = fopen("LPT1", "w");
if( !printer )
{
perror( "fopen failed for printing" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
while( fgets( buffer, sizeof buffer, fin ) )
{
fprintf( printer, "%s", buffer );
}
fprintf( printer, "%s", "\n" );
fclose( fin );
fclose( printer );
return 0;
}
你可以读一行,打印一行,循环,直到EOF 编辑以下内容以输出到打印机
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUF_SIZE 1024
int main( void )
{
char buffer[ MAX_BUF_SIZE ];
FILE *fin = fopen( "mytextfile.txt", "f" );
if( ! fin )
{
perror( "fopen failed for reading file" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
FILE *printer = fopen("LPT1", "w");
if( !printer )
{
perror( "fopen failed for printing" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
while( fgets( buffer, sizeof buffer, fin ) )
{
fprintf( printer, "%s", buffer );
}
fprintf( printer, "%s", "\n" );
fclose( fin );
fclose( printer );
return 0;
}
下面是一些示例打印代码,您应该能够正确地完成这项工作。这是一个最小的、自包含的示例,您应该能够编译并运行它。基于此,您应该能够根据您的具体需求进行调整。首先,这里是代码,然后是一些注释 请注意,我写这个是作为一个控制台应用程序,但这可能不是你真正想要的。此外,完成所有工作print_文件的函数现在可以根据请求从C调用。好的,下面是代码:
#include <windows.h>
#include <conio.h>
#include <string>
#include <fstream>
#include <iostream>
#define SCALE_FACTOR 100 // percent
inline static int MM_TO_PIXELS (int mm, int dpi)
{
return MulDiv (mm * 100, dpi, 2540);
}
// Calculate the wrapped height of a string
static int calculate_wrapped_string_height (HDC hDC, int width, const std::string& s)
{
RECT r = { 0, 0, width, 16384 };
DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK);
return (r.bottom == 16384) ? calculate_wrapped_string_height (hDC, width, " ") : r.bottom;
}
// Print a string in the width provided.
static void print_string (HDC hDC, int x, int y, int width, const std::string& s)
{
RECT r = { x, y, x + width, 16384 };
DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_NOPREFIX | DT_WORDBREAK);
}
// Print page number. Returns (y + vertical space consumed)
static int print_pagenum (HDC hDC, int x, int y, int width, int& pagenum)
{
std::string hdr = "Page: " + std::to_string (++pagenum) + "\n";
int space_needed = calculate_wrapped_string_height (hDC, width, hdr);
print_string (hDC, x, y, width, hdr);
std::cout << "Printing page: " << pagenum << "\n";
return space_needed;
}
extern "C" bool print_file (const char *filename)
{
std::ifstream f;
f.open ("g:\\temp\\print_me.txt", std::ios_base::in);
if (!f)
{
std::cout << "Cannot open input file, error " << GetLastError () << "\n";
return false;
}
// Display print dialog
PRINTDLGEX pdex = { sizeof (pdex) };
PRINTPAGERANGE pr [10] = { };
HDC hDC;
pdex.hwndOwner = GetDesktopWindow ();
pdex.Flags = PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOSELECTION;
pdex.nMaxPageRanges = _countof (pr);
pdex.nPageRanges = 1;
pr [0].nFromPage = pr [0].nToPage = 1;
pdex.lpPageRanges = pr;
pdex.nMinPage = 1;
pdex.nMaxPage = 999999;
pdex.nCopies = 1;
pdex.nStartPage = START_PAGE_GENERAL;
HRESULT hr = PrintDlgEx (&pdex);
if (hr != S_OK)
{
std::cout << "PrintDlgEx failed, error " << GetLastError () << "\n";
return false;
}
if (pdex.dwResultAction == PD_RESULT_CANCEL)
return false;
hDC = pdex.hDC;
if (pdex.dwResultAction != PD_RESULT_PRINT)
{
DeleteDC (hDC);
return false;
}
// Only print what we need to
int max_page = 0x7fffffff;
if (pdex.Flags & PD_PAGENUMS)
{
max_page = 0;
for (int i = 0; i < (int) pdex.nPageRanges; ++i)
{
if ((int) pdex.lpPageRanges [i].nToPage > max_page)
max_page = pdex.lpPageRanges [i].nToPage;
}
}
constexpr int dpi = 96 * 100 / SCALE_FACTOR;
int lpx = GetDeviceCaps (hDC, LOGPIXELSX);
int lpy = GetDeviceCaps (hDC, LOGPIXELSX);
int res_x = GetDeviceCaps (hDC, HORZRES);
int res_y = GetDeviceCaps (hDC, VERTRES);
// margins
int left_margin = MM_TO_PIXELS (10, dpi);
int top_margin = MM_TO_PIXELS (20, dpi);
int right_margin = MM_TO_PIXELS (20, dpi);
int bottom_margin = MM_TO_PIXELS (20, dpi);
int width = MulDiv (res_x, dpi, lpx) - (left_margin + right_margin);
int y_max = MulDiv (res_y, dpi, lpy) - bottom_margin;
// Set up for SCALE_FACTOR
SetMapMode (hDC, MM_ANISOTROPIC);
SetWindowExtEx (hDC, dpi, dpi, NULL);
SetViewportExtEx (hDC, lpx, lpy, NULL);
SetStretchBltMode (hDC, HALFTONE);
DOCINFO di = { 0 };
di.cbSize = sizeof (di);
di.lpszDocName = "Stack Overflow";
int job_id = StartDoc (hDC, &di);
if (job_id <= 0)
{
std::cout << "StartDoc failed, error " << GetLastError () << "\n";
DeleteDC (hDC);
return false;
}
SetBkMode (hDC, TRANSPARENT);
LOGFONT lf = { 0 };
lf.lfWeight = FW_NORMAL;
lf.lfHeight = -12;
HFONT hTextFont = CreateFontIndirect (&lf);
HFONT hOldFont = (HFONT) GetCurrentObject (hDC, OBJ_FONT);
SelectObject (hDC, hTextFont);
int x = left_margin;
int y = top_margin;
int pagenum = 0;
int err = StartPage (hDC);
if (err <= 0)
{
std::cout << "StartPage failed, error " << GetLastError () << "\n";
DeleteDC (hDC);
return false;
}
y += print_pagenum (hDC, x, y, width, pagenum);
// Printing loop, per line
for ( ; ; )
{
if (_kbhit ())
{
AbortDoc (hDC);
break;
}
std::string line;
std::getline (f, line);
if (!f)
break;
int space_needed = calculate_wrapped_string_height (hDC, width, line);
if (space_needed > y_max - y)
{
if (pagenum >= max_page)
break;
if (EndPage (hDC) < 0 || StartPage (hDC) < 0)
break;
y = top_margin;
y += print_pagenum (hDC, x, y, width, pagenum);
}
print_string (hDC, x, y, width, line);
y += space_needed;
}
EndPage (hDC);
EndDoc (hDC);
SelectObject (hDC, hOldFont);
DeleteObject (hTextFont);
DeleteDC (hDC);
return true;
}
// main
int main ()
{
bool ok = print_file ("g:\\temp\\print_me.txt");
return !ok;
}
注:
代码显示了如何正确分页输出。我在打印输出中包括页码,只是为了好玩
输入文件名是硬编码的。请根据您的需要调整这个
正如我所说,这是作为一个控制台应用程序编写的。如果要在Windows应用程序中包含此项,则需要使用不同的父窗口句柄和不同的机制—无模式对话框,通常用于报告进度并允许用户取消打印作业
编写的代码希望编译为ANSI纯粹是为了方便。我相信你能解决这个问题
此代码无法正确处理用户在Windows标准打印对话框中输入的页面范围。我把它留给读者作为练习
要使此代码可以从C调用,请将其编译为一个单独的.cpp文件,当然不包括main文件,然后在
将.h文件分离为:
外部C布尔打印文件常量字符*文件名
然后将此文件同时包含在.cpp文件和.c文件中
请注意,bool是C99及更高版本中的预定义类型(排序),请参见:
下面是一些示例打印代码,您应该能够正确地完成这项工作。这是一个最小的、自包含的示例,您应该能够编译并运行它。基于此,您应该能够根据您的具体需求进行调整。首先,这里是代码,然后是一些注释 请注意,我写这个是作为一个控制台应用程序,但这可能不是你真正想要的。此外,完成所有工作print_文件的函数现在可以根据请求从C调用。好的,下面是代码:
#include <windows.h>
#include <conio.h>
#include <string>
#include <fstream>
#include <iostream>
#define SCALE_FACTOR 100 // percent
inline static int MM_TO_PIXELS (int mm, int dpi)
{
return MulDiv (mm * 100, dpi, 2540);
}
// Calculate the wrapped height of a string
static int calculate_wrapped_string_height (HDC hDC, int width, const std::string& s)
{
RECT r = { 0, 0, width, 16384 };
DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK);
return (r.bottom == 16384) ? calculate_wrapped_string_height (hDC, width, " ") : r.bottom;
}
// Print a string in the width provided.
static void print_string (HDC hDC, int x, int y, int width, const std::string& s)
{
RECT r = { x, y, x + width, 16384 };
DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_NOPREFIX | DT_WORDBREAK);
}
// Print page number. Returns (y + vertical space consumed)
static int print_pagenum (HDC hDC, int x, int y, int width, int& pagenum)
{
std::string hdr = "Page: " + std::to_string (++pagenum) + "\n";
int space_needed = calculate_wrapped_string_height (hDC, width, hdr);
print_string (hDC, x, y, width, hdr);
std::cout << "Printing page: " << pagenum << "\n";
return space_needed;
}
extern "C" bool print_file (const char *filename)
{
std::ifstream f;
f.open ("g:\\temp\\print_me.txt", std::ios_base::in);
if (!f)
{
std::cout << "Cannot open input file, error " << GetLastError () << "\n";
return false;
}
// Display print dialog
PRINTDLGEX pdex = { sizeof (pdex) };
PRINTPAGERANGE pr [10] = { };
HDC hDC;
pdex.hwndOwner = GetDesktopWindow ();
pdex.Flags = PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOSELECTION;
pdex.nMaxPageRanges = _countof (pr);
pdex.nPageRanges = 1;
pr [0].nFromPage = pr [0].nToPage = 1;
pdex.lpPageRanges = pr;
pdex.nMinPage = 1;
pdex.nMaxPage = 999999;
pdex.nCopies = 1;
pdex.nStartPage = START_PAGE_GENERAL;
HRESULT hr = PrintDlgEx (&pdex);
if (hr != S_OK)
{
std::cout << "PrintDlgEx failed, error " << GetLastError () << "\n";
return false;
}
if (pdex.dwResultAction == PD_RESULT_CANCEL)
return false;
hDC = pdex.hDC;
if (pdex.dwResultAction != PD_RESULT_PRINT)
{
DeleteDC (hDC);
return false;
}
// Only print what we need to
int max_page = 0x7fffffff;
if (pdex.Flags & PD_PAGENUMS)
{
max_page = 0;
for (int i = 0; i < (int) pdex.nPageRanges; ++i)
{
if ((int) pdex.lpPageRanges [i].nToPage > max_page)
max_page = pdex.lpPageRanges [i].nToPage;
}
}
constexpr int dpi = 96 * 100 / SCALE_FACTOR;
int lpx = GetDeviceCaps (hDC, LOGPIXELSX);
int lpy = GetDeviceCaps (hDC, LOGPIXELSX);
int res_x = GetDeviceCaps (hDC, HORZRES);
int res_y = GetDeviceCaps (hDC, VERTRES);
// margins
int left_margin = MM_TO_PIXELS (10, dpi);
int top_margin = MM_TO_PIXELS (20, dpi);
int right_margin = MM_TO_PIXELS (20, dpi);
int bottom_margin = MM_TO_PIXELS (20, dpi);
int width = MulDiv (res_x, dpi, lpx) - (left_margin + right_margin);
int y_max = MulDiv (res_y, dpi, lpy) - bottom_margin;
// Set up for SCALE_FACTOR
SetMapMode (hDC, MM_ANISOTROPIC);
SetWindowExtEx (hDC, dpi, dpi, NULL);
SetViewportExtEx (hDC, lpx, lpy, NULL);
SetStretchBltMode (hDC, HALFTONE);
DOCINFO di = { 0 };
di.cbSize = sizeof (di);
di.lpszDocName = "Stack Overflow";
int job_id = StartDoc (hDC, &di);
if (job_id <= 0)
{
std::cout << "StartDoc failed, error " << GetLastError () << "\n";
DeleteDC (hDC);
return false;
}
SetBkMode (hDC, TRANSPARENT);
LOGFONT lf = { 0 };
lf.lfWeight = FW_NORMAL;
lf.lfHeight = -12;
HFONT hTextFont = CreateFontIndirect (&lf);
HFONT hOldFont = (HFONT) GetCurrentObject (hDC, OBJ_FONT);
SelectObject (hDC, hTextFont);
int x = left_margin;
int y = top_margin;
int pagenum = 0;
int err = StartPage (hDC);
if (err <= 0)
{
std::cout << "StartPage failed, error " << GetLastError () << "\n";
DeleteDC (hDC);
return false;
}
y += print_pagenum (hDC, x, y, width, pagenum);
// Printing loop, per line
for ( ; ; )
{
if (_kbhit ())
{
AbortDoc (hDC);
break;
}
std::string line;
std::getline (f, line);
if (!f)
break;
int space_needed = calculate_wrapped_string_height (hDC, width, line);
if (space_needed > y_max - y)
{
if (pagenum >= max_page)
break;
if (EndPage (hDC) < 0 || StartPage (hDC) < 0)
break;
y = top_margin;
y += print_pagenum (hDC, x, y, width, pagenum);
}
print_string (hDC, x, y, width, line);
y += space_needed;
}
EndPage (hDC);
EndDoc (hDC);
SelectObject (hDC, hOldFont);
DeleteObject (hTextFont);
DeleteDC (hDC);
return true;
}
// main
int main ()
{
bool ok = print_file ("g:\\temp\\print_me.txt");
return !ok;
}
注:
代码显示了如何正确分页输出。我在打印输出中包括页码,只是为了好玩
输入文件名是硬编码的。请根据您的需要调整这个
正如我所说,这是作为一个控制台应用程序编写的。如果要在Windows应用程序中包含此项,则需要使用不同的父窗口句柄和不同的机制—无模式对话框,通常用于报告进度并允许用户取消打印作业
编写的代码希望编译为ANSI纯粹是为了方便。我相信你能解决这个问题
此代码无法正确处理用户在Windows标准打印对话框中输入的页面范围。我把它留给读者作为练习
要使此代码可以从C调用,请将其编译为一个单独的.cpp文件,当然不包括main文件,然后在
将.h文件分离为:
外部C布尔打印文件常量字符*文件名
然后将此文件同时包含在.cpp文件和.c文件中
请注意,bool是C99及更高版本中的预定义类型(排序),请参见:
但是那会怎么打印呢?在你说它不清楚之前,我已经给它加上了打印机的标签,所有的函数都提到了打印。@Simon,while循环正在调用fgets将文本读入缓冲区[]
如果成功读取了任何文本,那么对printf的调用会将该文本输出到终端,但我需要它写在纸上!很抱歉,如果您误解了我的意思,我将编辑您需要的是纸面文档,好的,假设可执行文件名为“printfile”,运行程序的命令行将是:printfile>lpr或“printfile”pr@IInspectable,我发布的答案不会创建其他流程。它确实完成了OP要求的任务,只使用C库函数,但如何打印任何内容?在你说它不清楚之前,我已经给它加上了打印机的标签,所有函数都提到打印。@Simon,while循环调用fgets将文本读入缓冲区[],如果成功读入任何文本,则调用printf将文本输出到终端,但我需要它写在纸上!很抱歉,如果您误解了我的意思,我将编辑您需要的是纸面文档,好的,假设可执行文件名为“printfile”,运行程序的命令行将是:printfile>lpr或“printfile”pr@IInspectable,我发布的答案不会创建其他流程。它确实完成了OP要求的工作,只使用C库函数使用fopen和fprintf打开lpt1?Windows不提供如此高级别的函数。您必须编写代码才能作为GDI设备与打印机进行通信。不是C++的意思。它是一个COM接口,因此与语言无关。你可以很好地使用C中的COM。它可能有点冗长,但不比使用C++更困难。只需确保在包含DocumentTarget.h之前定义COBJMACROS,即可定义C helper宏。要打印ASCII文件,您可能只需打开打印机并传递打印机名称,例如LPT1,然后写入句柄。它可能会或不会满足您的需要。我无法证实这一点。想必,您可以这样做,因此,验证适用性对您来说更有意义。您可以使用Windows API调用,也可以使用Jonathan Potter中概述的CRT调用。使用fopen和fprintf打开lpt1?Windows不提供如此高级别的函数。您必须编写代码才能作为GDI设备与打印机进行通信。不是C++的意思。它是一个COM接口,因此与语言无关。你可以很好地使用C中的COM。它可能有点冗长,但不比使用C++更困难。只需确保在包含DocumentTarget.h之前定义COBJMACROS,即可定义C helper宏。要打印ASCII文件,您可能只需打开打印机并传递打印机名称,例如LPT1,然后写入句柄。它可能会或不会满足您的需要。我无法证实这一点。想必,您可以这样做,因此,验证适用性对您来说更有意义。您可以使用Windows API调用,也可以使用Jonathan Potter中概述的CRT调用。我接受您的回答,尽管这不是一个纯C解决方案,但如果您提到可以通过将其放入运行时从C调用它。通过这种方式,仍然可以从CI调用它。如果您提到它可以通过将其放入运行时从C调用,那么它将接受您的答案,尽管它不是纯C解决方案。这样,仍然可以从C调用它