Skip to content

Instantly share code, notes, and snippets.

@jan-bar
Forked from nathan-osman/win32.go
Last active December 7, 2022 09:49
Show Gist options
  • Save jan-bar/13f2ef1e671049db92768560bb106824 to your computer and use it in GitHub Desktop.
Save jan-bar/13f2ef1e671049db92768560bb106824 to your computer and use it in GitHub Desktop.
Simple Windows GUI application written in Go
package main
import (
"log"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
pGetModuleHandleW = kernel32.NewProc("GetModuleHandleW")
)
func getModuleHandle() (syscall.Handle, error) {
ret, _, err := pGetModuleHandleW.Call(uintptr(0))
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
var (
user32 = syscall.NewLazyDLL("user32.dll")
pCreateWindowExW = user32.NewProc("CreateWindowExW")
pDefWindowProcW = user32.NewProc("DefWindowProcW")
pDestroyWindow = user32.NewProc("DestroyWindow")
pDispatchMessageW = user32.NewProc("DispatchMessageW")
pGetMessageW = user32.NewProc("GetMessageW")
pLoadCursorW = user32.NewProc("LoadCursorW")
pPostQuitMessage = user32.NewProc("PostQuitMessage")
pRegisterClassExW = user32.NewProc("RegisterClassExW")
pTranslateMessage = user32.NewProc("TranslateMessage")
)
const (
cSW_SHOW = 5
cSW_USE_DEFAULT = 0x80000000
)
const (
cWS_MAXIMIZE_BOX = 0x00010000
cWS_MINIMIZEBOX = 0x00020000
cWS_THICKFRAME = 0x00040000
cWS_SYSMENU = 0x00080000
cWS_CAPTION = 0x00C00000
cWS_VISIBLE = 0x10000000
cWS_OVERLAPPEDWINDOW = 0x00CF0000
)
func createWindow(className, windowName string, style uint32, x, y, width, height int64, parent, menu, instance syscall.Handle) (syscall.Handle, error) {
ret, _, err := pCreateWindowExW.Call(
uintptr(0),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(className))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(windowName))),
uintptr(style),
uintptr(x),
uintptr(y),
uintptr(width),
uintptr(height),
uintptr(parent),
uintptr(menu),
uintptr(instance),
uintptr(0),
)
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
const (
cWM_DESTROY = 0x0002
cWM_CLOSE = 0x0010
)
func defWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
ret, _, _ := pDefWindowProcW.Call(
uintptr(hwnd),
uintptr(msg),
uintptr(wparam),
uintptr(lparam),
)
return uintptr(ret)
}
func destroyWindow(hwnd syscall.Handle) error {
ret, _, err := pDestroyWindow.Call(uintptr(hwnd))
if ret == 0 {
return err
}
return nil
}
type tPOINT struct {
x, y int32
}
type tMSG struct {
hwnd syscall.Handle
message uint32
wParam uintptr
lParam uintptr
time uint32
pt tPOINT
}
func dispatchMessage(msg *tMSG) {
pDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
}
func getMessage(msg *tMSG, hwnd syscall.Handle, msgFilterMin, msgFilterMax uint32) (bool, error) {
ret, _, err := pGetMessageW.Call(
uintptr(unsafe.Pointer(msg)),
uintptr(hwnd),
uintptr(msgFilterMin),
uintptr(msgFilterMax),
)
if int32(ret) == -1 {
return false, err
}
return int32(ret) != 0, nil
}
const (
cIDC_ARROW = 32512
)
func loadCursorResource(cursorName uint32) (syscall.Handle, error) {
ret, _, err := pLoadCursorW.Call(
uintptr(0),
uintptr(uint16(cursorName)),
)
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
func postQuitMessage(exitCode int32) {
pPostQuitMessage.Call(uintptr(exitCode))
}
const (
cCOLOR_WINDOW = 5
)
type tWNDCLASSEXW struct {
size uint32
style uint32
wndProc uintptr
clsExtra int32
wndExtra int32
instance syscall.Handle
icon syscall.Handle
cursor syscall.Handle
background syscall.Handle
menuName *uint16
className *uint16
iconSm syscall.Handle
}
func registerClassEx(wcx *tWNDCLASSEXW) (uint16, error) {
ret, _, err := pRegisterClassExW.Call(
uintptr(unsafe.Pointer(wcx)),
)
if ret == 0 {
return 0, err
}
return uint16(ret), nil
}
func translateMessage(msg *tMSG) {
pTranslateMessage.Call(uintptr(unsafe.Pointer(msg)))
}
func main() {
className := "testClass"
instance, err := getModuleHandle()
if err != nil {
log.Println(err)
return
}
cursor, err := loadCursorResource(cIDC_ARROW)
if err != nil {
log.Println(err)
return
}
fn := func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
switch msg {
case cWM_CLOSE:
destroyWindow(hwnd)
case cWM_DESTROY:
postQuitMessage(0)
default:
ret := defWindowProc(hwnd, msg, wparam, lparam)
return ret
}
return 0
}
wcx := tWNDCLASSEXW{
wndProc: syscall.NewCallback(fn),
instance: instance,
cursor: cursor,
background: cCOLOR_WINDOW + 1,
className: syscall.StringToUTF16Ptr(className),
}
wcx.size = uint32(unsafe.Sizeof(wcx))
if _, err = registerClassEx(&wcx); err != nil {
log.Println(err)
return
}
_, err = createWindow(
className,
"Test Window",
cWS_VISIBLE|cWS_OVERLAPPEDWINDOW,
cSW_USE_DEFAULT,
cSW_USE_DEFAULT,
cSW_USE_DEFAULT,
cSW_USE_DEFAULT,
0,
0,
instance,
)
if err != nil {
log.Println(err)
return
}
for {
msg := tMSG{}
gotMessage, err := getMessage(&msg, 0, 0, 0)
if err != nil {
log.Println(err)
return
}
if gotMessage {
translateMessage(&msg)
dispatchMessage(&msg)
} else {
break
}
}
}
package main
import (
"fmt"
"os"
"syscall"
"unsafe"
)
func main() {
err := cc()
if err != nil {
panic(err)
}
}
/*
https://github.com/bbigras/go-windows-session-notifications
gopath\pkg\mod\golang.org\x\exp@vX.X.X\shiny\driver\internal\win32\zsyscall_windows.go
https://learn.microsoft.com/zh-cn/windows/console/registering-a-control-handler-function
*/
func cc() error {
nm, err := syscall.UTF16PtrFromString("janbar_window")
if err != nil {
return err
}
wc := WndClass{
LpSzClassName: nm,
LpFnWndProc: syscall.NewCallback(func(hWnd syscall.Handle, msg uint32, wParam, lParam uintptr) (rc uintptr) {
switch msg {
case 0x11: // WM_QUERY_END_SESSION
case 0x2B1: // WM_WTS_SESSION_CHANGE
default:
return DefWindowProc(hWnd, msg, wParam, lParam)
}
fw, _ := os.OpenFile("win.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
fmt.Fprintf(fw, "%X,%X,%X\n", msg, wParam, lParam)
fw.Close()
return 0
}),
}
_, err = RegisterClass(&wc)
if err != nil {
return err
}
const CW_USEDEFAULT int32 = 0x80000000 - 0x100000000
hw, err := CreateWindowEx(0, nm, nm, 0,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, 0, 0)
if err != nil {
return err
}
_ = hw
var msg Msg
for {
a, err := GetMessage(&msg, 0, 0, 0)
if err != nil {
return err
}
if a > 0 {
TranslateMessage(&msg)
DispatchMessage(&msg)
}
}
return nil
}
var (
modUser32 = syscall.NewLazyDLL("user32.dll")
procRegisterClassW = modUser32.NewProc("RegisterClassW")
procCreateWindowExW = modUser32.NewProc("CreateWindowExW")
procGetMessageW = modUser32.NewProc("GetMessageW")
procTranslateMessage = modUser32.NewProc("TranslateMessage")
procDispatchMessageW = modUser32.NewProc("DispatchMessageW")
procDefWindowProcW = modUser32.NewProc("DefWindowProcW")
)
type WndClass struct {
Style uint32
LpFnWndProc uintptr
CbClsExtra int32
CbWndExtra int32
HInstance syscall.Handle
HIcon syscall.Handle
HCursor syscall.Handle
HbrBackground syscall.Handle
LpSzMenuName *uint16
LpSzClassName *uint16
}
func RegisterClass(wc *WndClass) (atom uint16, err error) {
r0, _, e1 := syscall.SyscallN(procRegisterClassW.Addr(),
uintptr(unsafe.Pointer(wc)))
atom = uint16(r0)
if atom == 0 {
if e1 != 0 {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
func CreateWindowEx(exStyle uint32, className *uint16, windowText *uint16,
style uint32, x int32, y int32, width int32, height int32,
parent syscall.Handle, menu syscall.Handle, hInstance syscall.Handle,
lpParam uintptr) (hWnd syscall.Handle, err error) {
r0, _, e1 := syscall.SyscallN(procCreateWindowExW.Addr(),
uintptr(exStyle), uintptr(unsafe.Pointer(className)),
uintptr(unsafe.Pointer(windowText)), uintptr(style),
uintptr(x), uintptr(y), uintptr(width), uintptr(height),
uintptr(parent), uintptr(menu), uintptr(hInstance), lpParam)
hWnd = syscall.Handle(r0)
if hWnd == 0 {
if e1 != 0 {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
type (
Point struct {
X int32
Y int32
}
Msg struct {
HWnd syscall.Handle
Message uint32
WParam uintptr
LParam uintptr
Time uint32
Pt Point
}
)
func GetMessage(msg *Msg, hWnd syscall.Handle,
msgFilterMin uint32, msgFilterMax uint32) (ret int32, err error) {
r0, _, e1 := syscall.SyscallN(procGetMessageW.Addr(),
uintptr(unsafe.Pointer(msg)), uintptr(hWnd),
uintptr(msgFilterMin), uintptr(msgFilterMax))
ret = int32(r0)
if ret == -1 {
if e1 != 0 {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
func TranslateMessage(msg *Msg) (done bool) {
r0, _, _ := syscall.SyscallN(procTranslateMessage.Addr(),
uintptr(unsafe.Pointer(msg)))
done = r0 != 0
return
}
func DispatchMessage(msg *Msg) (ret int32) {
r0, _, _ := syscall.SyscallN(procDispatchMessageW.Addr(),
uintptr(unsafe.Pointer(msg)))
ret = int32(r0)
return
}
func DefWindowProc(hWnd syscall.Handle, uMsg uint32,
wParam, lParam uintptr) (lResult uintptr) {
lResult, _, _ = syscall.SyscallN(procDefWindowProcW.Addr(),
uintptr(hWnd), uintptr(uMsg), wParam, lParam)
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment