如何将LPT1重定向至USB001实现ASC文件原始打印
最优解决方案:DOS程序LPT1打印重定向至USB001端口
问题描述
现有仅支持LPT1端口的DOS程序,需将其打印输出重定向至USB001端口,可通过以下两种方式实现:
- 直接对路径为
C:\vDos\#LPT1.asc的ASC文件进行原始打印 - 监控
C:\Users\Admin\AppData\Local\Temp\vDos路径下的tmp格式临时文件并打印
相关文件路径
ASC文件路径:C:\vDos\#LPT1.asc 临时文件目录:C:\Users\Admin\AppData\Local\Temp\vDos
现有VB.NET代码
Public Class Form1 Public Const FILE_ATTRIBUTE_NORMAL As Short = &H80 Public Const INVALID_HANDLE_VALUE As Short = -1 Public Const GENERIC_READ As UInteger = &H80000000UI Public Const GENERIC_WRITE As UInteger = &H40000000 Public Const CREATE_NEW As UInteger = 1 Public Const CREATE_ALWAYS As UInteger = 2 Public Const OPEN_EXISTING As UInteger = 3 <DllImport("kernel32.dll", SetLastError:=True)> Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As UInteger, ByVal dwShareMode As UInteger, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As UInteger, ByVal dwFlagsAndAttributes As UInteger, ByVal hTemplateFile As IntPtr) As SafeFileHandle End Function Public Shared Sub sendTextToLPT1(ByVal receiptText As String) Using ptr As SafeFileHandle = CreateFile("LPT1", GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero) ' 检查是否为无效句柄 If ptr.IsInvalid Then ' 将Win32错误码转换为异常抛出 Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()) Else Using lpt As New FileStream(ptr, FileAccess.ReadWrite) Dim buffer(2047) As Byte ' 根据打印机支持选择编码,此处用ASCII buffer = System.Text.Encoding.ASCII.GetBytes(receiptText) lpt.Write(buffer, 0, buffer.Length) lpt.Close() End Using End If End Using End Sub Private Sub btnPrint_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click Dim s As String = "C:\vDos\#LPT1.ASC" Dim p As New PrintDocument() AddHandler p.PrintPage, Sub(sender1 As Object, e1 As PrintPageEventArgs) e1.Graphics.DrawString(s, New Font("Times New Roman", 12), New SolidBrush(Color.Black), New RectangleF(0, 0, p.DefaultPageSettings.PrintableArea.Width, p.DefaultPageSettings.PrintableArea.Height)) End Sub Try p.Print() Catch ex As Exception Throw New Exception("打印时发生异常", ex) End Try End Sub End Class
最优解决方案实现
方案一:直接打印ASC文件到USB打印机(推荐)
此方案直接读取vDos生成的ASC文件,以原始字节形式发送到USB打印机,完整保留DOS程序的原始打印格式(包括控制码)。
步骤1:添加原始打印辅助类
添加RawPrinterHelper类,用于实现绕过GDI的原始打印功能:
Imports System Imports System.Drawing Imports System.Drawing.Printing Imports System.IO Imports System.Runtime.InteropServices Public Class RawPrinterHelper Structure DOCINFO <MarshalAs(UnmanagedType.LPWStr)> Public pDocName As String <MarshalAs(UnmanagedType.LPWStr)> Public pOutputFile As String <MarshalAs(UnmanagedType.LPWStr)> Public pDataType As String End Structure <DllImport("winspool.drv", EntryPoint:="OpenPrinterW", SetLastError:=True, CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function OpenPrinter(ByVal szPrinter As String, ByRef hPrinter As IntPtr, ByVal pd As IntPtr) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="ClosePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="StartDocPrinterW", SetLastError:=True, CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function StartDocPrinter(ByVal hPrinter As IntPtr, ByVal level As Int32, ByRef pDI As DOCINFO) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="EndDocPrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function EndDocPrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="StartPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function StartPagePrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="EndPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function EndPagePrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.drv", EntryPoint:="WritePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> Public Shared Function WritePrinter(ByVal hPrinter As IntPtr, ByVal pBytes As IntPtr, ByVal dwCount As Int32, ByRef dwWritten As Int32) As Boolean End Function ' 将原始字节发送到打印机 Public Shared Function SendBytesToPrinter(ByVal szPrinterName As String, ByVal pBytes As IntPtr, ByVal dwCount As Int32) As Boolean Dim hPrinter As IntPtr = IntPtr.Zero Dim dwError As Int32 = 0 Dim di As DOCINFO = New DOCINFO() Dim dwWritten As Int32 = 0 Dim bSuccess As Boolean = False di.pDocName = "DOS原始打印文档" di.pDataType = "RAW" If OpenPrinter(szPrinterName, hPrinter, IntPtr.Zero) Then If StartDocPrinter(hPrinter, 1, di) Then If StartPagePrinter(hPrinter) Then bSuccess = WritePrinter(hPrinter, pBytes, dwCount, dwWritten) EndPagePrinter(hPrinter) End If EndDocPrinter(hPrinter) End If ClosePrinter(hPrinter) End If If Not bSuccess Then dwError = Marshal.GetLastWin32Error() Throw New Exception($"打印失败,错误码:{dwError}") End If Return bSuccess End Function ' 将文件内容发送到打印机 Public Shared Function SendFileToPrinter(ByVal szPrinterName As String, ByVal szFileName As String) As Boolean Dim fs As FileStream = New FileStream(szFileName, FileMode.Open) Dim br As BinaryReader = New BinaryReader(fs) Dim bytes() As Byte = New Byte(fs.Length - 1) {} Dim pUnmanagedBytes As IntPtr = IntPtr.Zero Dim nLength As Int32 = 0 nLength = Convert.ToInt32(fs.Length) bytes = br.ReadBytes(nLength) pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength) Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength) SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength) Marshal.FreeCoTaskMem(pUnmanagedBytes) Return True End Function End Class
步骤2:修改打印按钮事件
替换原btnPrint_Click_1方法,实现读取ASC文件并发送到USB打印机:
Private Sub btnPrint_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click Dim ascFilePath As String = "C:\vDos\#LPT1.asc" ' 替换为你的USB打印机名称,可在Windows打印机设置中查看 Dim usbPrinterName As String = "你的USB打印机名称" Try ' 检查文件是否存在 If File.Exists(ascFilePath) Then RawPrinterHelper.SendFileToPrinter(usbPrinterName, ascFilePath) MessageBox.Show("打印成功") Else MessageBox.Show("ASC文件不存在") End If Catch ex As Exception MessageBox.Show($"打印失败:{ex.Message}") End Try End Sub
方案二:监控临时文件目录自动打印
如果需要实时捕获DOS程序的打印输出,可监控指定临时文件目录,当新的tmp文件生成时自动打印:
实现代码
添加FileSystemWatcher组件到窗体,设置监控参数:
Private watcher As FileSystemWatcher Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load watcher = New FileSystemWatcher() watcher.Path = "C:\Users\Admin\AppData\Local\Temp\vDos" watcher.Filter = "*.tmp" ' 监控文件创建事件 AddHandler watcher.Created, AddressOf OnTmpFileCreated watcher.EnableRaisingEvents = True End Sub Private Sub OnTmpFileCreated(source As Object, e As FileSystemEventArgs) ' 等待文件写入完成 Threading.Thread.Sleep(1000) Dim usbPrinterName As String = "你的USB打印机名称" Try RawPrinterHelper.SendFileToPrinter(usbPrinterName, e.FullPath) ' 可选:打印后删除临时文件 File.Delete(e.FullPath) Catch ex As Exception MessageBox.Show($"临时文件打印失败:{ex.Message}") End Try End Sub
注意事项
- 确保USB打印机支持原始打印(大部分票据打印机、针式打印机支持)
- 替换代码中的
你的USB打印机名称为实际打印机名称,可通过PrinterSettings.InstalledPrinters枚举获取所有已安装打印机 - ASC文件包含DOS程序的原始打印控制码,直接发送可保证打印格式与DOS环境完全一致
内容的提问来源于stack exchange,提问作者roy
相关产品推荐
相关产品推荐

