Last active
December 6, 2024 16:41
-
-
Save nathan-osman/18c2e227ad00a223b61c0b3c16d452c3 to your computer and use it in GitHub Desktop.
Simple Windows GUI application written in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
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
I confirmed that both of them had worked well.
Anyway this is just what I want to know, Cool.