C 在Windows中打印文件

C 在Windows中打印文件,c,winapi,printing,C,Winapi,Printing,假设我有一个ASCII编码的文本文件mytextfile.txt,我想打印它 我在MSDN上找到了各种来源,但似乎没有一个有用: 发件人: 定义从Windows窗体应用程序打印时将输出发送到打印机的可重用对象。 公共引用类PrintDocument:组件 但是这是C++的意思,而不是c 我还发现,它定义了几个函数,但实际上似乎没有一个函数能够打印: IPrintDocumentPackageStatusEvent:表示打印作业的进度 IPrintDocumentPackageTarget:允许用

假设我有一个ASCII编码的文本文件mytextfile.txt,我想打印它

我在MSDN上找到了各种来源,但似乎没有一个有用:

发件人:

定义从Windows窗体应用程序打印时将输出发送到打印机的可重用对象。 公共引用类PrintDocument:组件

但是这是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调用它