|
|
@@ -3,18 +3,13 @@ package service
|
|
|
import (
|
|
|
"fmt"
|
|
|
"os"
|
|
|
- "os/exec"
|
|
|
"syscall"
|
|
|
+ "unsafe"
|
|
|
|
|
|
"github.com/tarm/serial"
|
|
|
)
|
|
|
|
|
|
-// SerialPrinter 通用打印机接口(串口或 Windows USB)
|
|
|
-type Printer interface {
|
|
|
- Write(data []byte) error
|
|
|
- Close() error
|
|
|
-}
|
|
|
-
|
|
|
+// SerialPrinter 串口打印机
|
|
|
type SerialPrinter struct {
|
|
|
Port *serial.Port
|
|
|
}
|
|
|
@@ -23,9 +18,8 @@ func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) {
|
|
|
if baudRate <= 0 {
|
|
|
baudRate = 9600
|
|
|
}
|
|
|
- // 非 COM 端口 → 尝试 Windows 打印机路径
|
|
|
- if len(portName) >= 3 && portName[:3] != "COM" {
|
|
|
- return nil, fmt.Errorf("非串口设备,请使用 USB 驱动安装虚拟 COM 口,或连接真实 COM 口")
|
|
|
+ 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)
|
|
|
@@ -44,60 +38,51 @@ func (p *SerialPrinter) Close() error {
|
|
|
return p.Port.Close()
|
|
|
}
|
|
|
|
|
|
-// USBPrinter Windows USB 打印机(原生 API 发送 RAW 数据)
|
|
|
-type USBPrinter struct {
|
|
|
- handle syscall.Handle
|
|
|
+// === 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 DOC_INFO_1 struct {
|
|
|
+ pDocName *uint16
|
|
|
+ pOutputFile *uint16
|
|
|
+ pDataType *uint16
|
|
|
}
|
|
|
|
|
|
-func NewUSBPrinter(printerName string) (*USBPrinter, error) {
|
|
|
- paths := []string{
|
|
|
- fmt.Sprintf("\\\\.\\%s", printerName), // Win32 device
|
|
|
- fmt.Sprintf("\\\\127.0.0.1\\%s", printerName), // UNC share
|
|
|
- }
|
|
|
+func PrintRaw(printerName string, data []byte) error {
|
|
|
+ namePtr, _ := syscall.UTF16PtrFromString(printerName)
|
|
|
|
|
|
var handle syscall.Handle
|
|
|
- var lastErr error
|
|
|
- for _, path := range paths {
|
|
|
- pathPtr, _ := syscall.UTF16PtrFromString(path)
|
|
|
- handle, lastErr = syscall.CreateFile(pathPtr, syscall.GENERIC_WRITE,
|
|
|
- 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
|
|
|
- if lastErr == nil {
|
|
|
- return &USBPrinter{handle: handle}, nil
|
|
|
- }
|
|
|
+ ret, _, _ := procOpenPrinter.Call(uintptr(unsafe.Pointer(namePtr)),
|
|
|
+ uintptr(unsafe.Pointer(&handle)), 0)
|
|
|
+ if ret == 0 {
|
|
|
+ return fmt.Errorf("OpenPrinter 失败: %s", printerName)
|
|
|
}
|
|
|
+ defer procClosePrinter.Call(uintptr(handle))
|
|
|
|
|
|
- // 所有路径都失败 → 回退 PowerShell Out-Printer
|
|
|
- return nil, fmt.Errorf("CREATE_FILE_FAILED:%s", printerName)
|
|
|
-}
|
|
|
+ docName, _ := syscall.UTF16PtrFromString("Parking Ticket")
|
|
|
+ dataType, _ := syscall.UTF16PtrFromString("RAW")
|
|
|
+ di := DOC_INFO_1{pDocName: docName, pDataType: dataType}
|
|
|
|
|
|
-func (p *USBPrinter) Write(data []byte) error {
|
|
|
- var written uint32
|
|
|
- return syscall.WriteFile(p.handle, data, &written, nil)
|
|
|
-}
|
|
|
+ procStartDoc.Call(uintptr(handle), 1, uintptr(unsafe.Pointer(&di)))
|
|
|
+ procStartPage.Call(uintptr(handle))
|
|
|
|
|
|
-func (p *USBPrinter) Close() error {
|
|
|
- return syscall.CloseHandle(p.handle)
|
|
|
-}
|
|
|
+ var written uint32
|
|
|
+ procWritePrinter.Call(uintptr(handle), uintptr(unsafe.Pointer(&data[0])),
|
|
|
+ uintptr(len(data)), uintptr(unsafe.Pointer(&written)))
|
|
|
|
|
|
-// PrintRawFile 通过临时文件 + PowerShell Out-Printer 打印
|
|
|
-func PrintRawFile(printerName string, data []byte) error {
|
|
|
- tmpFile, err := os.CreateTemp("", "ticket_*.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()
|
|
|
+ procEndPage.Call(uintptr(handle))
|
|
|
+ procEndDoc.Call(uintptr(handle))
|
|
|
|
|
|
- // PowerShell: Get-Content <file> -Encoding Byte | Out-Printer -Name "<printer>"
|
|
|
- cmd := exec.Command("powershell", "-Command",
|
|
|
- fmt.Sprintf("Get-Content -Path '%s' -Encoding Byte | Out-Printer -Name '%s'", tmpPath, printerName))
|
|
|
- return cmd.Run()
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-var _ = os.Stderr // keep os import
|
|
|
-var _ = exec.ErrNotFound // keep exec import
|
|
|
+var _ = os.Stderr // keep import
|