├── example ├── gui │ ├── basic │ │ ├── screenshot.png │ │ ├── README.md │ │ └── main.go │ └── notifyicon │ │ ├── icon.ico │ │ ├── screenshot.png │ │ ├── README.md │ │ ├── notifyicon.go │ │ └── main.go ├── cmdline │ └── main.go ├── syscall │ ├── module │ │ ├── main.go │ │ └── syscall.go │ ├── lasterror │ │ ├── main.go │ │ └── syscall.go │ └── basic │ │ └── main.go └── processlist │ └── main.go ├── README.md └── pkg └── win ├── macro.go ├── util.go ├── type.go ├── constant.go └── win.go /example/gui/basic/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hallazzang/go-windows-programming/HEAD/example/gui/basic/screenshot.png -------------------------------------------------------------------------------- /example/gui/notifyicon/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hallazzang/go-windows-programming/HEAD/example/gui/notifyicon/icon.ico -------------------------------------------------------------------------------- /example/gui/notifyicon/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hallazzang/go-windows-programming/HEAD/example/gui/notifyicon/screenshot.png -------------------------------------------------------------------------------- /example/gui/basic/README.md: -------------------------------------------------------------------------------- 1 | ## Basic GUI Example 2 | 3 | This is an example Windows GUI application. 4 | 5 | Here's a screenshot: 6 | 7 | ![Screenshot](screenshot.png) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Windows Programming 2 | 3 | Go Windows Programming Tutorial 4 | 5 | Please visit [wiki page](https://github.com/hallazzang/go-and-windows-programming/wiki). 6 | -------------------------------------------------------------------------------- /pkg/win/macro.go: -------------------------------------------------------------------------------- 1 | package win 2 | 3 | import "unsafe" 4 | 5 | func MAKEINTRESOURCE(i uintptr) *uint16 { 6 | return (*uint16)(unsafe.Pointer(i)) 7 | } 8 | 9 | func LOWORD(dwValue uint32) uint16 { 10 | return uint16(dwValue) 11 | } 12 | 13 | func HIWORD(dwValue uint32) uint16 { 14 | return uint16((dwValue >> 16) & 0xffff) 15 | } 16 | -------------------------------------------------------------------------------- /example/cmdline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "syscall" 7 | ) 8 | 9 | func system(command string) error { 10 | cmd := exec.Command("cmd") 11 | cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: fmt.Sprintf(`/c "%s"`, command)} 12 | return cmd.Run() 13 | } 14 | 15 | func main() { 16 | if err := system(`notepad "foo.txt"`); err != nil { 17 | panic(err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/syscall/module/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang.org/x/sys/windows" 5 | ) 6 | 7 | func main() { 8 | caption, err := windows.UTF16PtrFromString("Go Windows Programming") 9 | if err != nil { 10 | panic(err) 11 | } 12 | text, err := windows.UTF16PtrFromString("Calling MessageBox from Go!") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | messageBox(0, text, caption, 0) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/win/util.go: -------------------------------------------------------------------------------- 1 | package win 2 | 3 | import ( 4 | "unicode/utf16" 5 | "unsafe" 6 | ) 7 | 8 | func UTF16PtrToString(p *uint16) string { 9 | var s []uint16 10 | for i := 0; i < 65536; i++ { 11 | c := *(*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(unsafe.Sizeof(uint16(0))*uintptr(i)))) 12 | if c == 0 { 13 | break 14 | } 15 | s = append(s, c) 16 | } 17 | return string(utf16.Decode(s)) 18 | } 19 | -------------------------------------------------------------------------------- /example/syscall/module/syscall.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | var ( 10 | libuser32 = windows.NewLazySystemDLL("user32.dll") 11 | 12 | procMessageBoxW = libuser32.NewProc("MessageBoxW") 13 | ) 14 | 15 | func messageBox(hWnd uintptr, lpText *uint16, lpCaption *uint16, uType uint32) int32 { 16 | r1, _, _ := procMessageBoxW.Call(hWnd, uintptr(unsafe.Pointer(lpText)), uintptr(unsafe.Pointer(lpCaption)), uintptr(uType)) 17 | return int32(r1) 18 | } 19 | -------------------------------------------------------------------------------- /example/syscall/lasterror/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | func main() { 10 | caption, err := windows.UTF16PtrFromString("Go Windows Programming") 11 | if err != nil { 12 | panic(err) 13 | } 14 | text, err := windows.UTF16PtrFromString("Calling MessageBox from Go!") 15 | if err != nil { 16 | panic(err) 17 | } 18 | 19 | messageBox(0, text, caption, 1000) // Invalid message box style. 20 | fmt.Printf("lastError: %v\n", getLastError()) 21 | messageBox(0, text, caption, 0) 22 | fmt.Printf("lastError: %v\n", getLastError()) 23 | } 24 | -------------------------------------------------------------------------------- /example/gui/notifyicon/README.md: -------------------------------------------------------------------------------- 1 | ## NotifyIcon Example 2 | 3 | This is an example application that uses Notify Icon. 4 | 5 | Here's a screenshot: 6 | 7 | ![screenshot](screenshot.png) 8 | 9 | ### Build 10 | 11 | You may use [rsrc] tool to embed icon as a resource in the executable: 12 | 13 | ``` 14 | $ go get github.com/akavel/rsrc 15 | $ rsrc -ico icon.ico -o rsrc.syso 16 | $ go build 17 | ``` 18 | 19 | In this way, you can distribute the executable without an icon file(recommended). 20 | 21 | Alternatively you can just build it, and place an icon file with the executable in same directory: 22 | 23 | ``` 24 | $ go build 25 | ``` 26 | 27 | [rsrc]: https://github.com/akavel/rsrc 28 | -------------------------------------------------------------------------------- /example/syscall/lasterror/syscall.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | var ( 10 | libuser32 = windows.NewLazySystemDLL("user32.dll") 11 | 12 | procMessageBoxW = libuser32.NewProc("MessageBoxW") 13 | 14 | lastError error 15 | ) 16 | 17 | func messageBox(hWnd uintptr, lpText *uint16, lpCaption *uint16, uType uint32) int32 { 18 | var r1 uintptr 19 | r1, _, lastError = procMessageBoxW.Call(hWnd, uintptr(unsafe.Pointer(lpText)), uintptr(unsafe.Pointer(lpCaption)), uintptr(uType)) 20 | return int32(r1) 21 | } 22 | 23 | func getLastError() error { 24 | if lastError.(windows.Errno) != 0 { 25 | return lastError 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /example/syscall/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | func main() { 10 | // Prepare libraries and procedures 11 | libuser32 := windows.NewLazySystemDLL("user32.dll") 12 | procMessageBoxW := libuser32.NewProc("MessageBoxW") 13 | 14 | // Encode Go string with UTF-16 15 | caption, err := windows.UTF16PtrFromString("Go Windows Programming") 16 | if err != nil { 17 | panic(err) 18 | } 19 | text, err := windows.UTF16PtrFromString("Calling MessageBox from Go!") 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | // Call the procedure 25 | procMessageBoxW.Call( 26 | 0, // HWND hWnd 27 | uintptr(unsafe.Pointer(text)), // LPCWSTR lpText 28 | uintptr(unsafe.Pointer(caption)), // LPCWSTR lpCaption 29 | 0) // UINT uType 30 | } 31 | -------------------------------------------------------------------------------- /example/processlist/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/hallazzang/go-windows-programming/pkg/win" 8 | ) 9 | 10 | type processInfo struct { 11 | id uint32 12 | name string 13 | sessionID uint32 14 | } 15 | 16 | func processList() ([]processInfo, error) { 17 | var pProcessInfo *win.WTS_PROCESS_INFO 18 | var count uint32 19 | var ps []processInfo 20 | if win.WTSEnumerateProcesses(win.WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &count) == 0 { 21 | return nil, win.GetLastError() 22 | } 23 | defer win.WTSFreeMemory(unsafe.Pointer(pProcessInfo)) 24 | size := unsafe.Sizeof(win.WTS_PROCESS_INFO{}) 25 | for i := uint32(0); i < count; i++ { 26 | p := *(*win.WTS_PROCESS_INFO)(unsafe.Pointer(uintptr(unsafe.Pointer(pProcessInfo)) + uintptr(size)*uintptr(i))) 27 | ps = append(ps, processInfo{ 28 | id: p.ProcessId, 29 | name: win.UTF16PtrToString(p.PProcessName), 30 | sessionID: p.SessionId, 31 | }) 32 | } 33 | return ps, nil 34 | } 35 | 36 | func main() { 37 | ps, err := processList() 38 | if err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("process list:\n%+v\n", ps) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/win/type.go: -------------------------------------------------------------------------------- 1 | package win 2 | 3 | type BOOL int32 4 | 5 | type GUID struct { 6 | Data1 uint32 7 | Data2 uint16 8 | Data3 uint16 9 | Data4 [8]byte 10 | } 11 | 12 | type MSG struct { 13 | Hwnd uintptr 14 | Message uint32 15 | WParam uintptr 16 | LParam uintptr 17 | Time uint32 18 | Pt POINT 19 | LPrivate uint32 20 | } 21 | 22 | type NOTIFYICONDATA struct { 23 | CbSize uint32 24 | HWnd uintptr 25 | UID uint32 26 | UFlags uint32 27 | UCallbackMessage uint32 28 | HIcon uintptr 29 | SzTip [128]uint16 30 | DwState uint32 31 | DwStateMask uint32 32 | SzInfo [256]uint16 33 | UVersion uint32 34 | SzInfoTitle [64]uint16 35 | DwInfoFlags uint32 36 | GuidItem GUID 37 | HBalloonIcon uintptr 38 | } 39 | 40 | type POINT struct { 41 | X int32 42 | Y int32 43 | } 44 | 45 | type SID struct { 46 | Revision byte 47 | SubAuthorityCount byte 48 | IdentifierAuthority SID_IDENTIFIER_AUTHORITY 49 | } 50 | 51 | type SID_IDENTIFIER_AUTHORITY struct { 52 | Value [6]byte 53 | } 54 | 55 | type WNDCLASSEX struct { 56 | CbSize uint32 57 | Style uint32 58 | LpfnWndProc uintptr 59 | CbClsExtra int32 60 | CbWndExtra int32 61 | HInstance uintptr 62 | HIcon uintptr 63 | HCursor uintptr 64 | HbrBackground uintptr 65 | LpszMenuName *uint16 66 | LpszClassName *uint16 67 | HIconSm uintptr 68 | } 69 | 70 | type WTS_PROCESS_INFO struct { 71 | SessionId uint32 72 | ProcessId uint32 73 | PProcessName *uint16 74 | PUserSid *SID 75 | } 76 | -------------------------------------------------------------------------------- /example/gui/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // https://docs.microsoft.com/en-us/cpp/windows/walkthrough-creating-windows-desktop-applications-cpp?view=vs-2017 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/hallazzang/go-windows-programming/pkg/win" 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | func wndProc(hWnd uintptr, msg uint32, wParam, lParam uintptr) uintptr { 13 | switch msg { 14 | case win.WM_DESTROY: 15 | win.PostQuitMessage(0) 16 | default: 17 | return win.DefWindowProc(hWnd, msg, wParam, lParam) 18 | } 19 | return 0 20 | } 21 | 22 | func main() { 23 | hInstance := win.GetModuleHandle(nil) 24 | 25 | windowClass, err := windows.UTF16PtrFromString("MyWindow") 26 | if err != nil { 27 | panic(err) 28 | } 29 | title, err := windows.UTF16PtrFromString("Go Windows Programming") 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | var wcex win.WNDCLASSEX 35 | wcex.CbSize = uint32(unsafe.Sizeof(wcex)) 36 | wcex.Style = win.CS_HREDRAW | win.CS_VREDRAW 37 | wcex.LpfnWndProc = windows.NewCallback(wndProc) 38 | wcex.CbClsExtra = 0 39 | wcex.CbWndExtra = 0 40 | wcex.HInstance = hInstance 41 | wcex.HIcon = win.LoadIcon(hInstance, win.MAKEINTRESOURCE(win.IDI_APPLICATION)) 42 | wcex.HCursor = win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW)) 43 | wcex.HbrBackground = win.COLOR_WINDOW + 1 44 | wcex.LpszMenuName = nil 45 | wcex.LpszClassName = windowClass 46 | wcex.HIconSm = win.LoadIcon(hInstance, win.MAKEINTRESOURCE(win.IDI_APPLICATION)) 47 | if win.RegisterClassEx(&wcex) == 0 { 48 | panic(win.GetLastError()) 49 | } 50 | 51 | hWnd := win.CreateWindowEx( 52 | 0, 53 | windowClass, 54 | title, 55 | win.WS_OVERLAPPEDWINDOW, 56 | win.CW_USEDEFAULT, 57 | win.CW_USEDEFAULT, 58 | 500, 59 | 100, 60 | 0, 61 | 0, 62 | hInstance, 63 | nil) 64 | if hWnd == 0 { 65 | panic(win.GetLastError()) 66 | } 67 | 68 | win.ShowWindow(hWnd, win.SW_SHOWNORMAL) 69 | win.UpdateWindow(hWnd) 70 | 71 | var msg win.MSG 72 | for win.GetMessage(&msg, 0, 0, 0) != 0 { 73 | win.TranslateMessage(&msg) 74 | win.DispatchMessage(&msg) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /example/gui/notifyicon/notifyicon.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "math/rand" 6 | "time" 7 | "unsafe" 8 | 9 | "github.com/hallazzang/go-windows-programming/pkg/win" 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | const notifyIconMsg = win.WM_APP + 1 14 | 15 | var errShellNotifyIcon = errors.New("Shell_NotifyIcon error") 16 | 17 | func init() { 18 | rand.Seed(time.Now().UnixNano()) 19 | } 20 | 21 | type notifyIcon struct { 22 | hwnd uintptr 23 | guid win.GUID 24 | } 25 | 26 | func newNotifyIcon(hwnd uintptr) (*notifyIcon, error) { 27 | ni := ¬ifyIcon{ 28 | hwnd: hwnd, 29 | guid: newGUID(), 30 | } 31 | data := ni.newData() 32 | data.UFlags |= win.NIF_MESSAGE 33 | data.UCallbackMessage = notifyIconMsg 34 | if win.Shell_NotifyIcon(win.NIM_ADD, data) == win.FALSE { 35 | return nil, errShellNotifyIcon 36 | } 37 | return ni, nil 38 | } 39 | 40 | func (ni *notifyIcon) Dispose() { 41 | win.Shell_NotifyIcon(win.NIM_DELETE, ni.newData()) 42 | } 43 | 44 | func (ni *notifyIcon) SetTooltip(tooltip string) error { 45 | data := ni.newData() 46 | data.UFlags |= win.NIF_TIP 47 | copy(data.SzTip[:], windows.StringToUTF16(tooltip)) 48 | if win.Shell_NotifyIcon(win.NIM_MODIFY, data) == win.FALSE { 49 | return errShellNotifyIcon 50 | } 51 | return nil 52 | } 53 | 54 | func (ni *notifyIcon) SetIcon(hIcon uintptr) error { 55 | data := ni.newData() 56 | data.UFlags |= win.NIF_ICON 57 | data.HIcon = hIcon 58 | if win.Shell_NotifyIcon(win.NIM_MODIFY, data) == win.FALSE { 59 | return errShellNotifyIcon 60 | } 61 | return nil 62 | } 63 | 64 | func (ni *notifyIcon) ShowNotification(title, text string) error { 65 | data := ni.newData() 66 | data.UFlags |= win.NIF_INFO 67 | copy(data.SzInfoTitle[:], windows.StringToUTF16(title)) 68 | copy(data.SzInfo[:], windows.StringToUTF16(text)) 69 | if win.Shell_NotifyIcon(win.NIM_MODIFY, data) == win.FALSE { 70 | return errShellNotifyIcon 71 | } 72 | return nil 73 | } 74 | 75 | func (ni *notifyIcon) ShowNotificationWithIcon(title, text string, hIcon uintptr) error { 76 | data := ni.newData() 77 | data.UFlags |= win.NIF_INFO 78 | copy(data.SzInfoTitle[:], windows.StringToUTF16(title)) 79 | copy(data.SzInfo[:], windows.StringToUTF16(text)) 80 | data.DwInfoFlags = win.NIIF_USER | win.NIIF_LARGE_ICON 81 | if win.Shell_NotifyIcon(win.NIM_MODIFY, data) == win.FALSE { 82 | return errShellNotifyIcon 83 | } 84 | return nil 85 | } 86 | 87 | func (ni *notifyIcon) newData() *win.NOTIFYICONDATA { 88 | var nid win.NOTIFYICONDATA 89 | nid.CbSize = uint32(unsafe.Sizeof(nid)) 90 | nid.UFlags = win.NIF_GUID 91 | nid.HWnd = ni.hwnd 92 | nid.GuidItem = ni.guid 93 | return &nid 94 | } 95 | 96 | func newGUID() win.GUID { 97 | var buf [16]byte 98 | rand.Read(buf[:]) 99 | return *(*win.GUID)(unsafe.Pointer(&buf[0])) 100 | } 101 | -------------------------------------------------------------------------------- /pkg/win/constant.go: -------------------------------------------------------------------------------- 1 | package win 2 | 3 | const ( 4 | NULL = 0 5 | 6 | TRUE = BOOL(1) 7 | FALSE = BOOL(0) 8 | ) 9 | 10 | // GetSysColor constants 11 | const ( 12 | COLOR_WINDOW = 5 13 | ) 14 | 15 | // Window class styles 16 | const ( 17 | CS_HREDRAW = 0x0002 18 | CS_VREDRAW = 0x0001 19 | ) 20 | 21 | const ( 22 | CW_USEDEFAULT = ^0x7fffffff 23 | ) 24 | 25 | // WM_SETICON constants 26 | const ( 27 | ICON_BIG = 1 28 | ICON_SMALL = 0 29 | ) 30 | 31 | // LoadCursor constants 32 | const ( 33 | IDC_ARROW = 32512 34 | ) 35 | 36 | // LoadIcon constants 37 | const ( 38 | IDI_APPLICATION = 32512 39 | ) 40 | 41 | // LoadImage constants 42 | const ( 43 | IMAGE_BITMAP = 0 44 | IMAGE_CURSOR = 2 45 | IMAGE_ICON = 1 46 | 47 | LR_CREATEDIBSECTION = 0x00002000 48 | LR_DEFAULTCOLOR = 0x00000000 49 | LR_DEFAULTSIZE = 0x00000040 50 | LR_LOADFROMFILE = 0x00000010 51 | LR_LOADMAP3DCOLORS = 0x00001000 52 | LR_LOADTRANSPARENT = 0x00000020 53 | LR_MONOCHROME = 0x00000001 54 | LR_SHARED = 0x00008000 55 | LR_VGACOLOR = 0x00000080 56 | ) 57 | 58 | // NotifyIcon constants 59 | const ( 60 | NIM_ADD = 0x00000000 61 | NIM_MODIFY = 0x00000001 62 | NIM_DELETE = 0x00000002 63 | NIM_SETFOCUS = 0x00000003 64 | NIM_SETVERSION = 0x00000004 65 | 66 | NIF_MESSAGE = 0x00000001 67 | NIF_ICON = 0x00000002 68 | NIF_TIP = 0x00000004 69 | NIF_STATE = 0x00000008 70 | NIF_INFO = 0x00000010 71 | NIF_GUID = 0x00000020 72 | NIF_REALTIME = 0x00000040 73 | NIF_SHOWTIP = 0x00000080 74 | 75 | NIS_HIDDEN = 0x00000001 76 | NIS_SHAREDICON = 0x00000002 77 | 78 | NIIF_NONE = 0x00000000 79 | NIIF_INFO = 0x00000001 80 | NIIF_WARNING = 0x00000002 81 | NIIF_ERROR = 0x00000003 82 | NIIF_USER = 0x00000004 83 | NIIF_NOSOUND = 0x00000010 84 | NIIF_LARGE_ICON = 0x00000020 85 | NIIF_RESPECT_QUIET_TIME = 0x00000080 86 | NIIF_ICON_MASK = 0x0000000F 87 | 88 | NIN_BALLOONSHOW = 0x0402 89 | NIN_BALLOONTIMEOUT = 0x0404 90 | NIN_BALLOONUSERCLICK = 0x0405 91 | ) 92 | 93 | // ShowWindow constants 94 | const ( 95 | SW_HIDE = 0 96 | SW_SHOW = 5 97 | SW_SHOWDEFAULT = 10 98 | SW_SHOWNORMAL = 1 99 | ) 100 | 101 | // Window messages 102 | const ( 103 | WM_DESTROY = 0x0002 104 | WM_SETICON = 0x0080 105 | WM_MOUSEMOVE = 0x0200 106 | WM_LBUTTONDOWN = 0x0201 107 | WM_LBUTTONUP = 0x0202 108 | WM_LBUTTONDBLCLK = 0x0203 109 | WM_RBUTTONDOWN = 0x0204 110 | WM_RBUTTONUP = 0x0205 111 | WM_RBUTTONDBLCLK = 0x0206 112 | WM_APP = 0x8000 113 | ) 114 | 115 | // Window styles 116 | const ( 117 | WS_CAPTION = 0x00c00000 118 | WS_MAXIMIZEBOX = 0x00010000 119 | WS_MINIMIZEBOX = 0x00020000 120 | WS_OVERLAPPED = 0x00000000 121 | WS_SYSMENU = 0x00080000 122 | WS_THICKFRAME = 0x00040000 123 | WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX 124 | ) 125 | 126 | // WTS API constants 127 | const ( 128 | WTS_CURRENT_SERVER_HANDLE = 0 129 | ) 130 | -------------------------------------------------------------------------------- /example/gui/notifyicon/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "unsafe" 6 | 7 | "github.com/hallazzang/go-windows-programming/pkg/win" 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | func wndProc(hWnd uintptr, msg uint32, wParam, lParam uintptr) uintptr { 12 | switch msg { 13 | case notifyIconMsg: 14 | switch nmsg := win.LOWORD(uint32(lParam)); nmsg { 15 | case win.NIN_BALLOONUSERCLICK: 16 | log.Print("User has clicked the balloon message") 17 | case win.WM_LBUTTONDOWN: 18 | clickHandler() 19 | } 20 | case win.WM_DESTROY: 21 | win.PostQuitMessage(0) 22 | default: 23 | return win.DefWindowProc(hWnd, msg, wParam, lParam) 24 | } 25 | return 0 26 | } 27 | 28 | func createMainWindow() (uintptr, error) { 29 | hInstance := win.GetModuleHandle(nil) 30 | 31 | wndClass := windows.StringToUTF16Ptr("MyWindow") 32 | 33 | var wcex win.WNDCLASSEX 34 | wcex.CbSize = uint32(unsafe.Sizeof(wcex)) 35 | wcex.Style = win.CS_HREDRAW | win.CS_VREDRAW 36 | wcex.LpfnWndProc = windows.NewCallback(wndProc) 37 | wcex.HInstance = hInstance 38 | wcex.HCursor = win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW)) 39 | wcex.HbrBackground = win.COLOR_WINDOW + 1 40 | wcex.LpszClassName = wndClass 41 | if win.RegisterClassEx(&wcex) == 0 { 42 | return 0, win.GetLastError() 43 | } 44 | 45 | hwnd := win.CreateWindowEx(0, wndClass, windows.StringToUTF16Ptr("NotifyIcon Example"), win.WS_OVERLAPPEDWINDOW, win.CW_USEDEFAULT, win.CW_USEDEFAULT, 400, 300, 0, 0, hInstance, nil) 46 | if hwnd == win.NULL { 47 | return 0, win.GetLastError() 48 | } 49 | win.ShowWindow(hwnd, win.SW_SHOW) 50 | 51 | return hwnd, nil 52 | } 53 | 54 | func loadIconFromResource(id uintptr) (uintptr, error) { 55 | hIcon := win.LoadImage( 56 | win.GetModuleHandle(nil), 57 | win.MAKEINTRESOURCE(id), 58 | win.IMAGE_ICON, 59 | 0, 0, 60 | win.LR_DEFAULTSIZE) 61 | if hIcon == win.NULL { 62 | return 0, win.GetLastError() 63 | } 64 | 65 | return hIcon, nil 66 | } 67 | func loadIconFromFile(name string) (uintptr, error) { 68 | hIcon := win.LoadImage( 69 | win.NULL, 70 | windows.StringToUTF16Ptr(name), 71 | win.IMAGE_ICON, 72 | 0, 0, 73 | win.LR_DEFAULTSIZE|win.LR_LOADFROMFILE) 74 | if hIcon == win.NULL { 75 | return 0, win.GetLastError() 76 | } 77 | 78 | return hIcon, nil 79 | } 80 | 81 | func clickHandler() { 82 | log.Print("User has clicked the notify icon") 83 | } 84 | 85 | func main() { 86 | hIcon, err := loadIconFromResource(10) // rsrc uses 10 for icon resource id 87 | if err != nil { 88 | hIcon, err = loadIconFromFile("icon.ico") // fallback to use file 89 | if err != nil { 90 | panic(err) 91 | } 92 | } 93 | defer win.DestroyIcon(hIcon) 94 | 95 | hwnd, err := createMainWindow() 96 | if err != nil { 97 | panic(err) 98 | } 99 | 100 | ni, err := newNotifyIcon(hwnd) 101 | if err != nil { 102 | panic(err) 103 | } 104 | defer ni.Dispose() 105 | 106 | ni.SetIcon(hIcon) 107 | ni.SetTooltip("NotifyIcon Example") 108 | ni.ShowNotificationWithIcon("Hello", "NotifyIcon!", hIcon) 109 | 110 | var msg win.MSG 111 | for win.GetMessage(&msg, 0, 0, 0) != 0 { 112 | win.TranslateMessage(&msg) 113 | win.DispatchMessage(&msg) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /pkg/win/win.go: -------------------------------------------------------------------------------- 1 | package win 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | var ( 10 | libkernel32 = windows.NewLazySystemDLL("kernel32.dll") 11 | libshell32 = windows.NewLazySystemDLL("shell32.dll") 12 | libuser32 = windows.NewLazySystemDLL("user32.dll") 13 | libwtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll") 14 | 15 | procCreateWindowExW = libuser32.NewProc("CreateWindowExW") 16 | procDefWindowProcW = libuser32.NewProc("DefWindowProcW") 17 | procDestroyIcon = libuser32.NewProc("DestroyIcon") 18 | procDispatchMessageW = libuser32.NewProc("DispatchMessageW") 19 | procGetMessageW = libuser32.NewProc("GetMessageW") 20 | procGetModuleHandleW = libkernel32.NewProc("GetModuleHandleW") 21 | procLoadCursorW = libuser32.NewProc("LoadCursorW") 22 | procLoadIconW = libuser32.NewProc("LoadIconW") 23 | procLoadImageW = libuser32.NewProc("LoadImageW") 24 | procPostQuitMessage = libuser32.NewProc("PostQuitMessage") 25 | procRegisterClassExW = libuser32.NewProc("RegisterClassExW") 26 | procSendMessageW = libuser32.NewProc("SendMessageW") 27 | procShell_NotifyIconW = libshell32.NewProc("Shell_NotifyIconW") 28 | procShowWindow = libuser32.NewProc("ShowWindow") 29 | procTranslateMessage = libuser32.NewProc("TranslateMessage") 30 | procUpdateWindow = libuser32.NewProc("UpdateWindow") 31 | procWTSEnumerateProcessesW = libwtsapi32.NewProc("WTSEnumerateProcessesW") 32 | procWTSFreeMemory = libwtsapi32.NewProc("WTSFreeMemory") 33 | 34 | lastError error 35 | ) 36 | 37 | func GetLastError() error { 38 | if lastError.(windows.Errno) != 0 { 39 | return lastError 40 | } 41 | return nil 42 | } 43 | 44 | func CreateWindowEx( 45 | dwExStyle uint32, 46 | lpClassName, lpWindowName *uint16, 47 | dwStyle uint32, 48 | X, Y, nWidth, nHeight int32, 49 | hWndParent, hMenu, hInstance uintptr, 50 | lpParam unsafe.Pointer) uintptr { 51 | var r1 uintptr 52 | r1, _, lastError = procCreateWindowExW.Call( 53 | uintptr(dwExStyle), 54 | uintptr(unsafe.Pointer(lpClassName)), 55 | uintptr(unsafe.Pointer(lpWindowName)), 56 | uintptr(dwStyle), 57 | uintptr(X), 58 | uintptr(Y), 59 | uintptr(nWidth), 60 | uintptr(nHeight), 61 | hWndParent, 62 | hMenu, 63 | hInstance, 64 | uintptr(lpParam)) 65 | return r1 66 | } 67 | 68 | func DefWindowProc(hWnd uintptr, Msg uint32, wParam, lParam uintptr) uintptr { 69 | var r1 uintptr 70 | r1, _, _ = procDefWindowProcW.Call(hWnd, uintptr(Msg), wParam, lParam) 71 | return r1 72 | } 73 | 74 | func DestroyIcon(hIcon uintptr) BOOL { 75 | var r1 uintptr 76 | r1, _, lastError = procDestroyIcon.Call(hIcon) 77 | return BOOL(r1) 78 | } 79 | 80 | func DispatchMessage(lpMsg *MSG) uintptr { 81 | var r1 uintptr 82 | r1, _, _ = procDispatchMessageW.Call(uintptr(unsafe.Pointer(lpMsg))) 83 | return r1 84 | } 85 | 86 | func GetMessage(lpMsg *MSG, hWnd uintptr, uMsgFilterMin, uMsgFilterMax uint32) BOOL { 87 | var r1 uintptr 88 | r1, _, lastError = procGetMessageW.Call( 89 | uintptr(unsafe.Pointer(lpMsg)), 90 | hWnd, 91 | uintptr(uMsgFilterMin), 92 | uintptr(uMsgFilterMax)) 93 | return BOOL(r1) 94 | } 95 | 96 | func GetModuleHandle(lpModuleName *uint16) uintptr { 97 | var r1 uintptr 98 | r1, _, lastError = procGetModuleHandleW.Call(uintptr(unsafe.Pointer(lpModuleName))) 99 | return r1 100 | } 101 | 102 | func LoadCursor(hInstance uintptr, lpCursorName *uint16) uintptr { 103 | var r1 uintptr 104 | r1, _, lastError = procLoadCursorW.Call(hInstance, uintptr(unsafe.Pointer(lpCursorName))) 105 | return r1 106 | } 107 | 108 | func LoadIcon(hInstance uintptr, lpIconName *uint16) uintptr { 109 | var r1 uintptr 110 | r1, _, lastError = procLoadIconW.Call(hInstance, uintptr(unsafe.Pointer(lpIconName))) 111 | return r1 112 | } 113 | 114 | func LoadImage(hInst uintptr, name *uint16, type_ uint32, cx, cy int32, fuLoad uint32) uintptr { 115 | var r1 uintptr 116 | r1, _, lastError = procLoadImageW.Call(hInst, uintptr(unsafe.Pointer(name)), uintptr(type_), uintptr(cx), uintptr(cy), uintptr(fuLoad)) 117 | return r1 118 | } 119 | 120 | func PostQuitMessage(nExitCode int32) { 121 | procPostQuitMessage.Call(uintptr(nExitCode)) 122 | } 123 | 124 | func RegisterClassEx(Arg1 *WNDCLASSEX) uint16 { 125 | var r1 uintptr 126 | r1, _, lastError = procRegisterClassExW.Call(uintptr(unsafe.Pointer(Arg1))) 127 | return uint16(r1) 128 | } 129 | 130 | func SendMessage(hWnd uintptr, Msg uint32, wParam, lParam uintptr) uintptr { 131 | var r1 uintptr 132 | r1, _, lastError = procSendMessageW.Call(hWnd, uintptr(Msg), wParam, lParam) 133 | return r1 134 | } 135 | 136 | func Shell_NotifyIcon(dwMessage uint32, lpData *NOTIFYICONDATA) BOOL { 137 | var r1 uintptr 138 | r1, _, _ = procShell_NotifyIconW.Call(uintptr(dwMessage), uintptr(unsafe.Pointer(lpData))) 139 | return BOOL(r1) 140 | } 141 | 142 | func ShowWindow(hWnd uintptr, nCmdShow int32) BOOL { 143 | var r1 uintptr 144 | r1, _, _ = procShowWindow.Call(hWnd, uintptr(nCmdShow)) 145 | return BOOL(r1) 146 | } 147 | 148 | func TranslateMessage(lpMsg *MSG) BOOL { 149 | var r1 uintptr 150 | r1, _, _ = procTranslateMessage.Call(uintptr(unsafe.Pointer(lpMsg))) 151 | return BOOL(r1) 152 | } 153 | 154 | func UpdateWindow(hWnd uintptr) BOOL { 155 | var r1 uintptr 156 | r1, _, lastError = procUpdateWindow.Call(hWnd) 157 | return BOOL(r1) 158 | } 159 | 160 | func WTSEnumerateProcesses( 161 | hServer uintptr, 162 | Reserved, Version uint32, 163 | ppProcessInfo **WTS_PROCESS_INFO, 164 | pCount *uint32) BOOL { 165 | var r1 uintptr 166 | r1, _, lastError = procWTSEnumerateProcessesW.Call( 167 | hServer, 168 | uintptr(Reserved), 169 | uintptr(Version), 170 | uintptr(unsafe.Pointer(ppProcessInfo)), 171 | uintptr(unsafe.Pointer(pCount))) 172 | return BOOL(r1) 173 | } 174 | 175 | func WTSFreeMemory(pMemory unsafe.Pointer) { 176 | procWTSFreeMemory.Call(uintptr(pMemory)) 177 | } 178 | --------------------------------------------------------------------------------