一、组件简介
打印监听组件是一款集成于 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 协议,支持多种命令(如 print、show、ping 等),并能将打印结果、错误信息实时反馈给客户端。
- 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. 用户体验优化
- 界面交互友好
采用右键菜单、弹窗提示、托盘集成等方式,提升用户操作便捷性。
- 错误提示与日志
所有关键操作均有明确的错误提示,便于用户定位问题;可扩展日志记录功能,方便后期维护。
四、总结
打印监听组件通过对打印队列的实时监控、任务的精细化管理、与外部系统的高效集成,极大提升了企业打印自动化和可控性。其灵活的界面、丰富的功能和健壮的技术架构,适用于多种业务场景,值得在企业信息化建设中推广应用。
版权声明:本文为作者原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
作者: 码农刚子
原文链接: https://www.codeobservatory.cn/archives/bdd5c491.html
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |