VB.NET无按钮自动检测打印机数据更新并触发打印方案问询
解决方案:VB.NET自动监听打印机文件并无冲突打印
核心问题拆解
- 自动文件监听:替代按钮触发,实时检测
C:\vDos\#LPT1.asc的更新 - 文件占用冲突:原代码以独占模式打开文件,导致与vDos进程的写入操作冲突
- 可靠打印执行:处理文件写入未完成、临时占用等异常情况
实现方案
1. 用FileSystemWatcher实现自动监听
通过FileSystemWatcher监听目标文件的修改事件,同时添加防抖逻辑避免重复触发(vDos写入可能多次触发修改事件)。
2. 修改文件读取模式避免占用冲突
打开文件时指定FileShare.ReadWrite,允许其他进程同时读写该文件,解决占用报错问题。
3. 添加重试机制保证打印可靠性
当文件临时无法读取时,自动重试几次,避免单次失败导致打印丢失。
完整代码示例
Imports System.IO Imports System.Runtime.InteropServices Imports System.Timers Public Class MainForm ' 防抖计时器,避免文件多次修改重复触发打印 Private WithEvents debounceTimer As New Timer With {.Interval = 500, .Enabled = False} ' 文件监听对象 Private WithEvents fileWatcher As FileSystemWatcher ' 打印机名称 Private Const PrinterName As String = "Generic / Text Only" ' 目标文件路径 Private Const TargetFilePath As String = "C:\vDos\#LPT1.asc" Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 初始化文件监听 fileWatcher = New FileSystemWatcher With { .Path = Path.GetDirectoryName(TargetFilePath), .Filter = Path.GetFileName(TargetFilePath), .NotifyFilter = NotifyFilters.LastWrite Or NotifyFilters.Size, .EnableRaisingEvents = True } End Sub ' 文件修改事件触发 Private Sub FileWatcher_Changed(sender As Object, e As FileSystemEventArgs) Handles fileWatcher.Changed ' 重置防抖计时器 debounceTimer.Stop() debounceTimer.Start() End Sub ' 防抖结束后执行打印 Private Sub DebounceTimer_Elapsed(sender As Object, e As ElapsedEventArgs) Handles debounceTimer.Elapsed debounceTimer.Stop() ' 异步执行打印,避免阻塞UI Task.Run(AddressOf TryPrintTargetFile) End Sub ' 带重试的打印逻辑 Private Sub TryPrintTargetFile() Dim retryCount = 3 Dim success = False While retryCount > 0 AndAlso Not success Try success = SendFileToPrinter(PrinterName, TargetFilePath) If success Then ' 可选:打印成功后清空文件,避免重复打印(根据需求调整) File.WriteAllText(TargetFilePath, String.Empty) End If Catch ex As IOException ' 文件被占用,等待1秒后重试 Threading.Thread.Sleep(1000) retryCount -= 1 Catch ex As Exception ' 其他异常直接终止重试 retryCount = 0 MessageBox.Show($"打印失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End If If Not success Then MessageBox.Show("重试多次后仍无法打印文件,请检查文件状态", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error) End If End Sub ' 修改后的文件打印函数,解决占用问题 Public Shared Function SendFileToPrinter(ByVal szPrinterName As String, ByVal szFileName As String) As Boolean Dim bSuccess As Boolean = False ' 以共享读写模式打开文件,避免占用冲突 Using fs As New FileStream(szFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) Using br As New BinaryReader(fs) Dim nLength As Integer = Convert.ToInt32(fs.Length) If nLength = 0 Then Return False ' 文件为空无需打印 Dim bytes(nLength - 1) As Byte bytes = br.ReadBytes(nLength) Dim pUnmanagedBytes As IntPtr = Marshal.AllocCoTaskMem(nLength) Try Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength) bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength) Finally Marshal.FreeCoTaskMem(pUnmanagedBytes) End Try End Using End Using Return bSuccess End Function ' 原有的SendBytesToPrinter和SendStringToPrinter函数(需保留) <DllImport("winspool.drv", CharSet:=CharSet.Auto, SetLastError:=True)> Private Shared Function SendBytesToPrinter(ByVal szPrinterName As String, ByVal pBytes As IntPtr, ByVal dwCount As Integer) As Boolean End Function Public Shared Function SendStringToPrinter(ByVal szPrinterName As String, ByVal szString As String) As Boolean Dim pBytes As IntPtr Dim dwCount As Int32 = (szString.Length + 1) * Marshal.SystemMaxDBCSCharSize pBytes = Marshal.StringToCoTaskMemAnsi(szString) Try SendBytesToPrinter(szPrinterName, pBytes, dwCount) Finally Marshal.FreeCoTaskMem(pBytes) End Try Return True End Function End Class
关键说明
- FileSystemWatcher防抖:设置500ms延迟是因为vDos写入文件可能分多次完成,避免短时间内重复触发打印。
- FileShare.ReadWrite:这是解决文件占用问题的核心,允许当前进程读取文件的同时,vDos进程仍可写入。
- 重试机制:遇到IO异常(文件占用)时等待1秒重试,最多3次,平衡可靠性和效率。
- 异步打印:用
Task.Run执行打印操作,避免阻塞UI线程,保证界面响应。 - 文件清空(可选):打印成功后清空文件,防止下次文件修改时重复打印旧内容,可根据实际需求调整。
内容的提问来源于stack exchange,提问作者roy
相关产品推荐
相关产品推荐

