package service import ( "fmt" "os" "os/exec" "syscall" "unsafe" "github.com/tarm/serial" ) // SerialPrinter 串口打印机 type SerialPrinter struct { Port *serial.Port } func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) { if baudRate <= 0 { baudRate = 9600 } if len(portName) < 3 || portName[:3] != "COM" { return nil, fmt.Errorf("非串口设备: %s", portName) } c := &serial.Config{Name: portName, Baud: baudRate} port, err := serial.OpenPort(c) if err != nil { return nil, fmt.Errorf("打开串口失败 %s: %w", portName, err) } return &SerialPrinter{Port: port}, nil } func (p *SerialPrinter) Write(data []byte) error { _, err := p.Port.Write(data) return err } func (p *SerialPrinter) Close() error { return p.Port.Close() } // === Windows RAW 打印机(WritePrinter API)=== var ( winspool = syscall.NewLazyDLL("winspool.drv") procOpenPrinter = winspool.NewProc("OpenPrinterW") procClosePrinter = winspool.NewProc("ClosePrinter") procStartDoc = winspool.NewProc("StartDocPrinterW") procStartPage = winspool.NewProc("StartPagePrinter") procWritePrinter = winspool.NewProc("WritePrinter") procEndPage = winspool.NewProc("EndPagePrinter") procEndDoc = winspool.NewProc("EndDocPrinter") ) type PRINTER_DEFAULTS struct { pDatatype *uint16 pDevMode uintptr DesiredAccess uint32 } type DOC_INFO_1 struct { pDocName *uint16 pOutputFile *uint16 pDataType *uint16 } func PrintRaw(printerName string, data []byte) error { namePtr, _ := syscall.UTF16PtrFromString(printerName) rawType, _ := syscall.UTF16PtrFromString("RAW") pd := PRINTER_DEFAULTS{ pDatatype: rawType, pDevMode: 0, DesiredAccess: 0x000F0000 | 0x00000008, // PRINTER_ACCESS_USE | PRINTER_ACCESS_ADMINISTER } var handle syscall.Handle ret, _, _ := procOpenPrinter.Call(uintptr(unsafe.Pointer(namePtr)), uintptr(unsafe.Pointer(&handle)), uintptr(unsafe.Pointer(&pd))) if ret == 0 { return fmt.Errorf("OpenPrinter 失败: %s", printerName) } defer procClosePrinter.Call(uintptr(handle)) docName, _ := syscall.UTF16PtrFromString("Parking Ticket") di := DOC_INFO_1{pDocName: docName, pDataType: rawType} procStartDoc.Call(uintptr(handle), 1, uintptr(unsafe.Pointer(&di))) procStartPage.Call(uintptr(handle)) var written uint32 procWritePrinter.Call(uintptr(handle), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), uintptr(unsafe.Pointer(&written))) procEndPage.Call(uintptr(handle)) procEndDoc.Call(uintptr(handle)) return nil } // copyToPrinter 通过临时文件 + COPY /B 到打印机共享发送 RAW 数据 func copyToPrinter(printerName string, data []byte) error { tmpFile, err := os.CreateTemp("", "tkt_*.bin") if err != nil { return err } tmpPath := tmpFile.Name() defer os.Remove(tmpPath) if _, err := tmpFile.Write(data); err != nil { tmpFile.Close() return err } tmpFile.Close() // 尝试 COPY /B file \\127.0.0.1\share_name // 先查共享名 shareName := printerName for _, path := range []string{ fmt.Sprintf("\\\\127.0.0.1\\%s", shareName), fmt.Sprintf("\\\\localhost\\%s", shareName), } { cmd := exec.Command("cmd", "/C", "copy", "/B", tmpPath, path) output, err := cmd.CombinedOutput() if err == nil { return nil } // 打印失败,尝试下一个 _ = output } // 最后回退 WritePrinter API return PrintRaw(printerName, data) } var _ = os.Stderr