当前位置: 代码网 > it编程>编程语言>Asp.net > C# WinForms实现打印监听组件工具

C# WinForms实现打印监听组件工具

2026年04月13日 Asp.net 我要评论
一、组件简介打印监听组件是一款集成于 windows 桌面环境的打印任务管理与监控工具,适用于企业级应用场景。它不仅支持多打印机任务的实时监控,还能通过 websocket 与外部系统集成,实现自动化

一、组件简介

打印监听组件是一款集成于 windows 桌面环境的打印任务管理与监控工具,适用于企业级应用场景。它不仅支持多打印机任务的实时监控,还能通过 websocket 与外部系统集成,实现自动化打印、任务状态反馈、远程控制等功能。

二、界面功能介绍

1. 主界面与托盘集成

主窗体:采用 winforms 界面,包含多标签页(tabcontrol),每个标签页对应一台本地打印机,便于分组管理。

托盘图标:程序最小化后驻留于系统托盘,双击可快速还原主界面,支持右键菜单操作(如退出、重启、服务设置等)。

2. 打印机管理

打印机列表:自动检测本地所有已安装打印机,支持设置默认打印机、查看打印机属性。

/// <summary>
/// 绑定本地打印机列表到菜单
/// </summary>
internal void bindprinterstomenu()
{
    默认打印机toolstripmenuitem.dropdownitems.clear();
    // 获取当前系统默认打印机
    string defaultprinter = new system.drawing.printing.printersettings().printername;

    // 先添加默认打印机(始终第一行)
    var defaultitem = new toolstripmenuitem(defaultprinter)
    {
        checked = true
    };
    defaultitem.click += (s, e) => setdefaultprinterui(defaultprinter);
    // 添加“首选项”子菜单
    var prefitem = new toolstripmenuitem("首选项");
    prefitem.click += (s, e) => showprinterproperties(defaultprinter);
    defaultitem.dropdownitems.add(prefitem);
    默认打印机toolstripmenuitem.dropdownitems.add(defaultitem);

    // 再添加其他打印机(排除默认打印机)
    foreach (string printer in system.drawing.printing.printersettings.installedprinters)
    {
        if (printer == defaultprinter)
            continue;

        var item = new toolstripmenuitem(printer)
        {
            checked = false
        };
        item.click += (s, e) => setdefaultprinterui(printer);

        var prefitem2 = new toolstripmenuitem("首选项");
        prefitem2.click += (s, e) => showprinterproperties(printer);
        item.dropdownitems.add(prefitem2);

        默认打印机toolstripmenuitem.dropdownitems.add(item);
    }

}
 /// <summary>  
 /// ui和系统都设置默认打印机 
 /// </summary>  
 /// <param name="printername"></param>  
 private void setdefaultprinterui(string printername)
 {
     foreach (toolstripmenuitem item in 默认打印机toolstripmenuitem.dropdownitems)
         item.checked = item.text == printername;

     // 如需设置为系统默认打印机,可调用 win32 api(可选)  
     setsystemdefaultprinter(printername);
 }
 /// <summary>
/// 显示打印机首选项对话框
/// </summary>
/// <param name="printername"></param>
private void showprinterproperties(string printername)
{
    // 使用rundll32调用打印机属性对话框
    //string args = $"printui.dll,printuientry /p /n \"{printername}\"";
    //•	/e 参数表示直接打开“首选项”对话框
    string args = $"printui.dll,printuientry /e /n \"{printername}\"";
    var psi = new system.diagnostics.processstartinfo
    {
        filename = "rundll32.exe",
        arguments = args,
        useshellexecute = false,
        createnowindow = true
    };
    try
    {
        system.diagnostics.process.start(psi);
    }
    catch (exception ex)
    {
        messagebox.show("无法打开打印机首选项窗口:" + ex.message, "错误", messageboxbuttons.ok, messageboxicon.error);
    }
}

tabcontrol:每台打印机一个标签页,便于查看和管理各自的打印任务。

 /// <summary>
 /// 绑定本地打印机列表到tabcontrol
 /// </summary>
 private void bindprinterstotabcontrol()
 {
     tabcontrol1.tabpages.clear();

     string defaultprinter = new system.drawing.printing.printersettings().printername;
     list<string> printers = new list<string>();

     // 先将默认打印机添加到列表首位
     printers.add(defaultprinter);

     // 再添加其他打印机(排除默认打印机)
     foreach (string printer in system.drawing.printing.printersettings.installedprinters)
     {
         if (printer != defaultprinter)
             printers.add(printer);
     }

     foreach (string printer in printers)
     {
         var tabpage = new tabpage(printer);
         // 创建datagridview
         var dgv = new datagridview
         {
             dock = dockstyle.fill,
             readonly = true,
             allowusertoaddrows = false,
             allowusertodeleterows = false,
             rowheadersvisible = false,
             autosizecolumnsmode = datagridviewautosizecolumnsmode.fill
         };

         // 添加列
         dgv.columns.add("clientip", "来源");
         dgv.columns.add("taskid", "任务id");
         dgv.columns.add("taskname", "任务名称");
         dgv.columns.add("realname", "模板");
         dgv.columns.add("requesttime", "开始时间");
         dgv.columns.add("status", "任务状态");

         //绑定菜单
         dgv.contextmenustrip = dgvcontextmenu;
         dgv.mousedown += dgv_mousedown;

         // 创建textbox
         var txtsearch = new textbox
         {
             placeholdertext = "任务id",
             width = 120,
             anchor = anchorstyles.left | anchorstyles.bottom
         };

         // 创建button
         var btnsearch = new button
         {
             text = "查找",
             width = 60,
             anchor = anchorstyles.left | anchorstyles.bottom
         };

         // 查找事件
         btnsearch.click += (s, e) =>
         {
             string searchid = txtsearch.text.trim();
             bool found = false;
             foreach (datagridviewrow row in dgv.rows)
             {
                 if (row.isnewrow) continue;
                 if (row.cells["taskid"].value?.tostring() == searchid)
                 {
                     row.selected = true;
                     dgv.currentcell = row.cells["taskid"];
                     found = true;
                 }
                 else
                 {
                     row.selected = false;
                 }
             }
             if (!found)
             {
                 messagebox.show("未找到对应任务id!", "提示", messageboxbuttons.ok, messageboxicon.information);
             }
         };

         // 使用panel布局
         var panel = new panel
         {
             dock = dockstyle.bottom,
             height = 40
         };
         txtsearch.location = new point(10, 8);
         btnsearch.location = new point(140, 6);
         panel.controls.add(txtsearch);
         panel.controls.add(btnsearch);

         tabpage.controls.add(panel);
         tabpage.controls.add(dgv);
         tabcontrol1.tabpages.add(tabpage);
     }
 }

3. 打印任务监控

任务列表:每个打印机标签页下方为 datagridview,实时显示当前打印任务,包括来源、任务id、任务名称、模板、开始时间、任务状态等信息。

右键菜单:支持对单个任务进行“取消打印”、“重新打印”、“删除记录”等操作。

任务搜索:支持按任务id快速定位任务。

4. 其他功能

  • 服务控制:可一键启动/停止 websocket 服务,支持与外部系统通信。
  • 模板设计与预览:集成 fastreport 设计器和预览器,方便模板维护。

因为fastreport.net 是需要购买授权的,所以我使用的是fastreport.opensource(开源版),开源版功能太少,不能直接从程序内部调用fastreport设计器和预览器,只能通过启动本地安装的.exe来实现。

/// <summary>
/// 设计菜单项点击事件,启动 fastreport 设计器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void 设计toolstripmenuitem_click(object sender, eventargs e)
{
    string designerpath = getconfigvalue("designer_path");
    string templatepath = gettemplatepathfromconfig();
    if (string.isnullorempty(designerpath) || !system.io.file.exists(designerpath))
    {
        messagebox.show("未找到 fastreport 设计器,请检查 config.ini 配置!", "错误", messageboxbuttons.ok, messageboxicon.error);
        return;
    }
    if (!system.io.file.exists(templatepath))
    {
        messagebox.show("未找到模板文件,请检查路径!", "错误", messageboxbuttons.ok, messageboxicon.error);
        return;
    }
    try
    {
        system.diagnostics.process.start(designerpath, $"\"{templatepath}\"");
    }
    catch (exception ex)
    {
        messagebox.show("启动 fastreport 设计器失败:" + ex.message, "错误", messageboxbuttons.ok, messageboxicon.error);
    }
}
private void 预览toolstripmenuitem_click(object sender, eventargs e)
{
    string viewerpath = getconfigvalue("viewer_path");
    string templatepath = gettemplatepathfromconfig();
    if (string.isnullorempty(viewerpath) || !system.io.file.exists(viewerpath))
    {
        messagebox.show("未找到 fastreport 预览器,请检查 config.ini 配置!", "错误", messageboxbuttons.ok, messageboxicon.error);
        return;
    }
    if (!system.io.file.exists(templatepath))
    {
        messagebox.show("未找到模板文件,请检查路径!", "错误", messageboxbuttons.ok, messageboxicon.error);
        return;
    }
    try
    {
        system.diagnostics.process.start(viewerpath, $"\"{templatepath}\"");
    }
    catch (exception ex)
    {
        messagebox.show("启动 fastreport 预览器失败:" + ex.message, "错误", messageboxbuttons.ok, messageboxicon.error);
    }
}

自动更新:支持在线检查和自动更新程序版本。

这里使用的是:autoupdater.net

帮助与支持:内置开发者联系方式,便于用户反馈和技术支持。

三、技术要点

1. 打印任务监听与管理

wmi 打印作业监控:通过 system.management 命名空间,使用 wmi 查询 win32_printjob,实现对打印队列的实时监控。可根据任务id或文档名唯一标识,精确定位和管理打印作业。

using system.management;

 /// <summary>
 /// 打印机监听方法实现
 /// </summary>
 /// <param name="printername"></param>
 /// <param name="taskid"></param>
 private void startmonitorprintjob(string printername, int taskid, string taskname)
 {
     task.run(() =>
     {
         try
         {
             string query = $"select * from win32_printjob where name like '%{printername}%'";
             using (var searcher = new managementobjectsearcher(query))
             {
                 while (true)
                 {
                     var jobs = searcher.get();
                     bool found = false;
                     foreach (managementobject job in jobs)
                     {
                         found = true;
                         int jobid = convert.toint32(job["jobid"]);
                         if (jobid == taskid)
                         {
                             // 匹配到本任务,更新状态
                             string jobstatus = job["jobstatus"]?.tostring() ?? "";
                             string status = job["status"]?.tostring() ?? "";
                             string displaystatus = string.isnullorempty(jobstatus) ? status : jobstatus;
                             updatetaskstatusonui(printername, taskname, displaystatus);
                             if (displaystatus.contains("printed") || displaystatus.contains("completed") || displaystatus.contains("deleted"))
                                 return;
                         }
                     }
                     if (!found)
                     {
                         // 作业已消失,认为已完成
                         updatetaskstatusonui(printername, taskname, "已完成");
                         return;
                     }
                     thread.sleep(1000); // 1秒轮询
                 }
             }
         }
         catch (exception ex)
         {
             updatetaskstatusonui(printername, taskname, "状态监听失败");
         }
     });
 }

任务状态同步:通过轮询方式定时查询打印队列,自动更新任务状态(如“正在打印”、“已完成”、“已取消”等),并在 ui 上实时反馈。

2. 打印任务操作

取消打印:通过 wmi 删除指定打印作业,确保任务被及时从队列中移除,并同步更新界面状态。

 /// <summary>
 /// 取消打印
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void cancelprint_click(object sender, eventargs e)
 {
     var dgv = getcurrentdgv();
     if (dgv == null) return;
     var row = dgv.selectedrows.count > 0 ? dgv.selectedrows[0] : null;
     if (row == null) return;
     int taskid = convert.toint32(row.cells["taskid"].value);
     string printername = tabcontrol1.selectedtab.text;
     // 查询打印队列,找到文档名包含 taskid 的作业
     string query = $"select * from win32_printjob where name like '%{printername}%'";
     using (var searcher = new system.management.managementobjectsearcher(query))
     {
         foreach (system.management.managementobject job in searcher.get())
         {
             int jobid = convert.toint32(job["jobid"]);
             if (jobid == taskid)
             {
                 try
                 {
                     job.delete(); // 删除打印任务
                     row.cells["status"].value = "已取消";
                     messagebox.show($"已取消打印任务:{taskid}", "提示", messageboxbuttons.ok, messageboxicon.information);
                 }
                 catch (exception ex)
                 {
                     messagebox.show("取消打印失败:" + ex.message, "错误", messageboxbuttons.ok, messageboxicon.error);
                 }
                 return;
             }
         }
     }
     messagebox.show("未找到对应的打印任务,可能已完成或被清除。", "提示", messageboxbuttons.ok, messageboxicon.information);
 }

重新打印:首次打印时将所有参数(文件路径、数据、模板名等)保存在 datagridview 行的 tag 属性,重新打印时直接复用原始参数,保证打印一致性。

 /// <summary>
 /// 重新打印
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void reprint_click(object sender, eventargs e)
 {
     var dgv = getcurrentdgv();
     if (dgv == null) return;
     var row = dgv.selectedrows.count > 0 ? dgv.selectedrows[0] : null;
     if (row == null) return;
     if (row.tag is printtaskinfo info)
     {
         // 复用原 taskname,或可选生成新 taskname
         string taskname = row.cells["taskname"].value.tostring();
         string status = row.cells["status"].value.tostring();
         if (status == "已完成")
             printfile(info.filepath, info.data, taskname);
     }
     else
     {
         messagebox.show("未找到原始打印信息,无法重新打印。", "错误", messageboxbuttons.ok, messageboxicon.error);
     }
 }

删除记录:支持在任务未完成时先删除打印队列中的作业,再移除界面记录,防止“假删除”导致队列堆积。

/// <summary>
/// 删除打印记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void deleterecord_click(object sender, eventargs e)
{
    var dgv = getcurrentdgv();
    if (dgv == null) return;
    var row = dgv.selectedrows.count > 0 ? dgv.selectedrows[0] : null;
    if (row == null) return;

    int taskid = convert.toint32(row.cells["taskid"].value);
    string status = row.cells["status"].value?.tostring();
    string printername = tabcontrol1.selectedtab.text;
    // 如果未完成,先删除打印队列中的任务
    if (status != "已完成" && status != "已取消")
    {
        string query = $"select * from win32_printjob where name like '%{printername}%'";
        using (var searcher = new system.management.managementobjectsearcher(query))
        {
            foreach (system.management.managementobject job in searcher.get())
            {
                int jobid = convert.toint32(job["jobid"]);
                if (jobid == taskid)
                {
                    try
                    {
                        job.delete(); // 删除打印任务
                    }
                    catch (exception ex)
                    {
                        messagebox.show("删除打印任务失败:" + ex.message, "错误", messageboxbuttons.ok, messageboxicon.error);
                    }
                    break;
                }
            }
        }
    }
    dgv.rows.remove(row);
}

3. websocket 通信

fleck 组件集成:使用 fleck 实现 websocket 服务端,支持外部系统通过网络下发打印任务、查询状态、远程控制等。

消息协议设计:采用 json 协议,支持多种命令(如 printshowping 等),并能将打印结果、错误信息实时反馈给客户端。

socket.onmessage = message =>
{
    var msg = message?.trim().tolowerinvariant();
    // 处理不同的消息
    if (msg == "ping")
    {
        // 回复 pong
        socket.send("pong");
    }
    else if (msg == "show")
    {
        // 显示主窗体
        this.invoke(() =>
        {
            this.show();
            this.windowstate = formwindowstate.normal;
            this.showintaskbar = true;
            this.activate();
        });
    }
    else if (msg != null && msg.trimstart().startswith("{"))
    {
        // 反序列化为 jsonnode 便于动态访问
        var json = jsonnode.parse(msg);
        var cmd = json?["cmd"]?.tostring();
        string requestid = json?["requestid"]?.tostring();
        //处理打印任务
        if (cmd == "print")
        {
            // 取出 printiniinfo 和 data
            var printiniinfo = json["data"]?["printiniinfo"];
            var data = json["data"]?["data"];
            string filepath = printiniinfo?["filepath"]?.tostring();
            string realname = printiniinfo?["realname"]?.tostring();

            // 获取来源ip和端口
            string clientip = socket.connectioninfo.clientipaddress;
            int clientport = socket.connectioninfo.clientport;
            string requesttime = datetime.now.tostring("yyyy-mm-dd hh:mm:ss");
            string status = "作业正在后台处理";
            // 获取当前系统默认打印机
            string printername = new system.drawing.printing.printersettings().printername;
            int taskid = 0;
            // 任务名称为当前时间
            string taskname = datetime.now.tostring("yyyymmddhhmmssfff");
            // 查找对应tabpage和datagridview
            this.invoke(() =>
            {
                foreach (tabpage tab in tabcontrol1.tabpages)
                {
                    // 支持“(默认)”后缀
                    if (tab.text.startswith(printername))
                    {
                        var dgv = tab.controls.oftype<datagridview>().firstordefault();
                        if (dgv != null)
                        {
                            int rowindex = dgv.rows.add(
                                 $"{clientip}:{clientport}", // 来源
                                 taskid,                     // 任务id
                                 taskname,                   // 任务名称
                                 realname,                   // 模板
                                 requesttime,                // 开始时间
                                 status                      // 任务状态
                             );
                            var row = dgv.rows[rowindex];
                            row.tag = new printtaskinfo
                            {
                                filepath = filepath,
                                data = data
                            };
                            // 添加后排序
                            dgv.sort(dgv.columns["requesttime"], listsortdirection.descending);
                        }
                        break;
                    }
                }
            });
            // 调用实际打印方法
            this.invoke(() => printfile(filepath, data, taskname, socket, requestid));

            //监听打印机状态
            startmonitorprintjob(printername, taskid, taskname);
        }
        else
        {
            // 处理其他cmd
            console.writeline($"收到未知cmd: {cmd}");
        }
    }
    else
    {
        console.writeline($"收到未知消息: {message}");
    }
};

异常处理与反馈:打印过程中如遇异常(如文件不存在、数据格式错误等),会捕获异常并通过 websocket 回复详细错误信息,便于外部系统及时处理。

4. 打印文件类型支持

多格式兼容:支持 txt、图片(jpg/png/bmp/gif)、pdf、fastreport 模板(frx)等多种文件类型的打印。

模板数据绑定:对于报表类打印,支持将 json、datatable、dataset 等多种数据源动态绑定到模板,实现灵活的数据驱动打印。

5. 用户体验优化

  • 界面交互友好:采用右键菜单、弹窗提示、托盘集成等方式,提升用户操作便捷性。
  • 错误提示与日志:所有关键操作均有明确的错误提示,便于用户定位问题;可扩展日志记录功能,方便后期维护。

四、总结

打印监听组件通过对打印队列的实时监控、任务的精细化管理、与外部系统的高效集成,极大提升了企业打印自动化和可控性。其灵活的界面、丰富的功能和健壮的技术架构,适用于多种业务场景,值得在企业信息化建设中推广应用。

以上就是c# winforms实现打印监听组件工具的详细内容,更多关于c# winforms打印监听的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com