Skip to content

Instantly share code, notes, and snippets.

@nathan-osman
Last active August 31, 2023 22:01
Show Gist options
  • Save nathan-osman/18c2e227ad00a223b61c0b3c16d452c3 to your computer and use it in GitHub Desktop.
Save nathan-osman/18c2e227ad00a223b61c0b3c16d452c3 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 int32, 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
}
}
}
@i-e-b
Copy link

i-e-b commented Aug 25, 2021

Changing int32to int64in line 53 solves this (might not be the best solution):

x, y, width, height int32 --> x, y, width, height int64

x, y, width, height int32 --> x, y, width, height uint32 looks like it works too

@maito1201
Copy link

I confirmed that both of them had worked well.

x, y, width, height int32 --> x, y, width, height uint32
x, y, width, height int32 --> x, y, width, height int64

Anyway this is just what I want to know, Cool.

@pankajby
Copy link

Awesome! Thanks for posting this. It's working fine. The only change required was updating int32 to int64 for parameter within CreateWindow function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment