当前位置: 首页 > news >正文

C# WinForms 实现打印监听组件

一、组件简介

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

二、界面功能介绍

1. 主界面与托盘集成

  • 主窗体:采用 WinForms 界面,包含多标签页(TabControl),每个标签页对应一台本地打印机,便于分组管理。
    C# WinForms 实现打印监听组件
  • 托盘图标:程序最小化后驻留于系统托盘,双击可快速还原主界面,支持右键菜单操作(如退出、重启、服务设置等)。
    C# WinForms 实现打印监听组件

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);// 创建DataGridViewvar 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;// 创建TextBoxvar txtSearch = new TextBox{PlaceholderText = "任务ID",Width = 120,Anchor = AnchorStyles.Left | AnchorStyles.Bottom};// 创建Buttonvar 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、任务名称、模板、开始时间、任务状态等信息。
    C# WinForms 实现打印监听组件
  • 右键菜单:支持对单个任务进行“取消打印”、“重新打印”、“删除记录”等操作。
    C# WinForms 实现打印监听组件
  • 任务搜索:支持按任务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

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

三、技术要点

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,或可选生成新 taskNamestring 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"){// 回复 pongsocket.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 和 datavar 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和DataGridViewthis.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,                     // 任务IDtaskName,                   // 任务名称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{// 处理其他cmdConsole.WriteLine($"收到未知cmd: {cmd}");}}else{Console.WriteLine($"收到未知消息: {message}");}
};
  • 异常处理与反馈
    打印过程中如遇异常(如文件不存在、数据格式错误等),会捕获异常并通过 WebSocket 回复详细错误信息,便于外部系统及时处理。

4. 打印文件类型支持

  • 多格式兼容
    支持 TXT、图片(JPG/PNG/BMP/GIF)、PDF、FastReport 模板(FRX)等多种文件类型的打印。
    C# WinForms 实现打印监听组件
    C# WinForms 实现打印监听组件
  • 模板数据绑定
    对于报表类打印,支持将 JSON、DataTable、DataSet 等多种数据源动态绑定到模板,实现灵活的数据驱动打印。

5. 用户体验优化

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

四、总结

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


版权声明:本文为作者原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
作者: 码农刚子
原文链接: https://www.codeobservatory.cn/archives/bdd5c491.html

相关文章:

  • 使用 Flutter 在 Windows 平台开发 Android 应用
  • 人工智能学习28-BP过拟合
  • 创客匠人视角:知识IP变现的主流模式与创新路径
  • 解决Spark4.0.0依赖问题
  • 算法题:一个数组,找出其中最小连续的子数组,是的这个子数组排序后,整体数组...
  • Spark RDD 及性能调优
  • Kafka源码P1-消息ProducerRecord
  • 【无标题】定制园区专属地图:如何让底图只显示道路和地面?
  • 周末复习1
  • 基于U-Net与可分离卷积的肺部分割技术详解
  • 电脑出问题了,无网络环境下一键快速重装系统
  • 【环境配置】解决linux每次打开终端都需要source .bashrc文件的问题
  • 2025虚幻引擎中的轴映射与操作映射相关
  • MQ选型及RocketMQ架构总览
  • Linux系统安装MongoDB 8.0流程
  • 【无标题[特殊字符]2025华为行程解锁
  • DataX HdfsReader 插件:快速上手与深入解析
  • 简历模板2——数据挖掘工程师5年经验
  • 嵌入式 STM32 开发问题:烧录 STM32CubeMX 创建的 Keil 程序没有反应
  • 人工智能学习16-Numpy
  • 哈尔滨网站建设乙薇/seo项目是什么
  • 网站改名字 收录/免费推广的app有哪些
  • 网站开发用什么语言好/如何用网站模板建站
  • 石家庄专业网站营销/seo排名第一的企业
  • 免费网络电话在线拨打/郑州seo外包顾问
  • 做项目挣钱的网站/太原seo