daShangDao_kfz_goods_pricing/cmd/server/gui_windows.go
97694732@qq.com e26d7a027e first commit
2026-06-11 16:06:08 +08:00

278 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build windows
package main
import (
"fmt"
"log"
"os"
"runtime"
"sync"
"syscall"
"unsafe"
)
// Win32 常量
const (
WS_OVERLAPPEDWINDOW = 0x00CF0000
WS_CHILD = 0x40000000
WS_VISIBLE = 0x10000000
WS_VSCROLL = 0x00200000
WS_HSCROLL = 0x00100000
WS_EX_CLIENTEDGE = 0x00000200
ES_MULTILINE = 0x0004
ES_READONLY = 0x0800
ES_AUTOVSCROLL = 0x0040
ES_AUTOHSCROLL = 0x0080
WM_DESTROY = 0x0002
WM_SIZE = 0x0005
WM_SETFONT = 0x0030
WM_CTLCOLOREDIT = 0x0133
EM_SETSEL = 0x00B1
EM_REPLACESEL = 0x00C2
SW_SHOW = 5
IDC_ARROW = 32512
CW_USEDEFAULT = ^0x7FFFFFFF
FIXED_PITCH = 1
FF_MODERN = 48
DEFAULT_CHARSET = 1
FW_NORMAL = 400
MB_ICONERROR = 0x00000010
)
// 暗色主题 (BGR格式)
const (
colorBg = 0x001E1E1E
colorFg = 0x00D4D4D4
)
type tWNDCLASSEX struct {
CbSize uint32
Style uint32
LpfnWndProc uintptr
CbClsExtra int32
CbWndExtra int32
HInstance uintptr
HIcon uintptr
HCursor uintptr
HbrBackground uintptr
LpszMenuName *uint16
LpszClassName *uint16
HIconSm uintptr
}
type tRECT struct {
Left int32
Top int32
Right int32
Bottom int32
}
type tMSG struct {
HWnd uintptr
Message uint32
_ uint32
WParam uintptr
LParam uintptr
Time uint32
PtX int32
PtY int32
}
var (
modUser32 = syscall.NewLazyDLL("user32.dll")
modKernel32 = syscall.NewLazyDLL("kernel32.dll")
modGdi32 = syscall.NewLazyDLL("gdi32.dll")
pGetModuleHandleW = modKernel32.NewProc("GetModuleHandleW")
pLoadCursorW = modUser32.NewProc("LoadCursorW")
pRegisterClassExW = modUser32.NewProc("RegisterClassExW")
pCreateWindowExW = modUser32.NewProc("CreateWindowExW")
pShowWindow = modUser32.NewProc("ShowWindow")
pUpdateWindow = modUser32.NewProc("UpdateWindow")
pGetMessageW = modUser32.NewProc("GetMessageW")
pTranslateMessage = modUser32.NewProc("TranslateMessage")
pDispatchMessageW = modUser32.NewProc("DispatchMessageW")
pDefWindowProcW = modUser32.NewProc("DefWindowProcW")
pPostQuitMessage = modUser32.NewProc("PostQuitMessage")
pSendMessageW = modUser32.NewProc("SendMessageW")
pGetClientRect = modUser32.NewProc("GetClientRect")
pMoveWindow = modUser32.NewProc("MoveWindow")
pMessageBoxW = modUser32.NewProc("MessageBoxW")
pCreateFontW = modGdi32.NewProc("CreateFontW")
pDeleteObject = modGdi32.NewProc("DeleteObject")
pCreateSolidBrush = modGdi32.NewProc("CreateSolidBrush")
pSetTextColor = modGdi32.NewProc("SetTextColor")
pSetBkColor = modGdi32.NewProc("SetBkColor")
)
var (
hWndMain uintptr
hWndEdit uintptr
hFont uintptr
hbrBg uintptr
guiReady bool
guiMu sync.Mutex
pendingLogs []string
)
// guiLogWriter 实现 io.Writer将日志写入编辑控件
type guiLogWriter struct{}
func (w *guiLogWriter) Write(p []byte) (n int, err error) {
text := string(p)
guiMu.Lock()
if !guiReady || hWndEdit == 0 {
pendingLogs = append(pendingLogs, text)
guiMu.Unlock()
return len(p), nil
}
guiMu.Unlock()
appendText(text)
return len(p), nil
}
func appendText(text string) {
if hWndEdit == 0 {
return
}
// 光标移到末尾
pSendMessageW.Call(hWndEdit, EM_SETSEL, ^uintptr(0), ^uintptr(0))
// 插入文本
ptr, _ := syscall.UTF16PtrFromString(text)
pSendMessageW.Call(hWndEdit, EM_REPLACESEL, 1, uintptr(unsafe.Pointer(ptr)))
}
func flushPendingLogs() {
guiMu.Lock()
logs := pendingLogs
pendingLogs = nil
guiReady = true
guiMu.Unlock()
for _, text := range logs {
appendText(text)
}
}
func wndProc(hWnd uintptr, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case WM_SIZE:
if hWndEdit != 0 {
var rect tRECT
pGetClientRect.Call(hWnd, uintptr(unsafe.Pointer(&rect)))
pMoveWindow.Call(hWndEdit, 0, 0, uintptr(rect.Right), uintptr(rect.Bottom), 1)
}
return 0
case WM_CTLCOLOREDIT:
pSetTextColor.Call(wParam, colorFg)
pSetBkColor.Call(wParam, colorBg)
return hbrBg
case WM_DESTROY:
if hFont != 0 {
pDeleteObject.Call(hFont)
}
if hbrBg != 0 {
pDeleteObject.Call(hbrBg)
}
pPostQuitMessage.Call(0)
return 0
}
ret, _, _ := pDefWindowProcW.Call(hWnd, uintptr(msg), wParam, lParam)
return ret
}
// runGUI 创建并运行GUI窗口阻塞直到窗口关闭
func runGUI() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
hInstance, _, _ := pGetModuleHandleW.Call(0)
className, _ := syscall.UTF16PtrFromString("KfzLogWnd")
windowTitle, _ := syscall.UTF16PtrFromString("孔网商品定价 - 日志")
editClass, _ := syscall.UTF16PtrFromString("EDIT")
hCursor, _, _ := pLoadCursorW.Call(0, IDC_ARROW)
// 创建暗色背景画刷
hbrBg, _, _ = pCreateSolidBrush.Call(colorBg)
// 注册窗口类
wc := tWNDCLASSEX{
CbSize: uint32(unsafe.Sizeof(tWNDCLASSEX{})),
LpfnWndProc: syscall.NewCallback(wndProc),
HInstance: hInstance,
HCursor: hCursor,
HbrBackground: hbrBg,
LpszClassName: className,
}
pRegisterClassExW.Call(uintptr(unsafe.Pointer(&wc)))
// 创建主窗口
hWndMain, _, _ = pCreateWindowExW.Call(
0,
uintptr(unsafe.Pointer(className)),
uintptr(unsafe.Pointer(windowTitle)),
WS_OVERLAPPEDWINDOW,
100, 100, 900, 600,
0, 0, hInstance, 0,
)
// 创建编辑控件(多行、只读、滚动条)
hWndEdit, _, _ = pCreateWindowExW.Call(
WS_EX_CLIENTEDGE,
uintptr(unsafe.Pointer(editClass)),
0,
WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_MULTILINE|ES_READONLY|ES_AUTOVSCROLL|ES_AUTOHSCROLL,
0, 0, 0, 0,
hWndMain, 0, hInstance, 0,
)
// 设置等宽字体
fontName, _ := syscall.UTF16PtrFromString("Consolas")
hFont, _, _ = pCreateFontW.Call(
16, 0, 0, 0, FW_NORMAL, 0, 0, 0,
DEFAULT_CHARSET, 0, 0, 0,
FIXED_PITCH|FF_MODERN,
uintptr(unsafe.Pointer(fontName)),
)
pSendMessageW.Call(hWndEdit, WM_SETFONT, hFont, 1)
// 刷新缓冲日志
flushPendingLogs()
// 显示窗口
pShowWindow.Call(hWndMain, SW_SHOW)
pUpdateWindow.Call(hWndMain)
// 消息循环
var msg tMSG
for {
ret, _, _ := pGetMessageW.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0)
if ret == 0 {
break
}
pTranslateMessage.Call(uintptr(unsafe.Pointer(&msg)))
pDispatchMessageW.Call(uintptr(unsafe.Pointer(&msg)))
}
}
// fatalExit 弹出错误对话框并退出
func fatalExit(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
log.Print(msg)
title, _ := syscall.UTF16PtrFromString("错误")
text, _ := syscall.UTF16PtrFromString(msg)
pMessageBoxW.Call(0, uintptr(unsafe.Pointer(text)), uintptr(unsafe.Pointer(title)), MB_ICONERROR)
os.Exit(1)
}