https://github.com/rodrigocfd/windigo
Windows API and GUI in idiomatic Go.
https://github.com/rodrigocfd/windigo
bindings ffi go golang gui native win32 windows
Last synced: 2 days ago
JSON representation
Windows API and GUI in idiomatic Go.
- Host: GitHub
- URL: https://github.com/rodrigocfd/windigo
- Owner: rodrigocfd
- License: mit
- Created: 2021-02-18T10:46:06.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2025-10-01T19:56:52.000Z (7 days ago)
- Last Synced: 2025-10-01T21:23:48.076Z (7 days ago)
- Topics: bindings, ffi, go, golang, gui, native, win32, windows
- Language: Go
- Homepage: https://pkg.go.dev/github.com/rodrigocfd/windigo
- Size: 3.28 MB
- Stars: 539
- Watchers: 12
- Forks: 28
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
[](https://pkg.go.dev/github.com/rodrigocfd/windigo)
[](https://github.com/rodrigocfd/windigo)
[](https://github.com/rodrigocfd/windigo/blob/master/LICENSE.md)
[](https://github.com/rodrigocfd/windigo)# Windigo
Win32 API and GUI in idiomatic Go.
Windigo is designed to be familiar to C/C++ Win32 programmers, using the same concepts, and an API as close as possible to the original Win32 API. This allows most C/C++ Win32 tutorials and examples to be translated to Go.
Notably, Windigo is written 100% in pure Go – CGo is **not** used, just native syscalls.
## Install
```
go get -u github.com/rodrigocfd/windigo
```## Examples
*In the examples below, error checking is ommited for brevity.*
GUI window
### GUI window
The example below creates a window programmatically, and handles the button click. Also, it uses the `minimal.syso` provided in the [_resources](_resources/) folder.

```go
package mainimport (
"fmt"
"runtime""github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/ui"
)func main() {
runtime.LockOSThread() // important: Windows GUI is single-threadedShowMainWindow()
}// This struct represents our main window.
type MyWindow struct {
wnd *ui.Main
lblName *ui.Static
txtName *ui.Edit
btnShow *ui.Button
}// Displays the main window, blocking until it is closed.
func ShowMainWindow() int {
wnd := ui.NewMain( // create the main window
ui.OptsMain().
Title("Hello you").
Size(ui.Dpi(340, 80)).
ClassIconId(101), // ID of icon resource, see _resources folder
)lblName := ui.NewStatic( // create the child controls
wnd,
ui.OptsStatic().
Text("Your name").
Position(ui.Dpi(10, 22)),
)
txtName := ui.NewEdit(
wnd,
ui.OptsEdit().
Position(ui.Dpi(80, 20)).
Width(ui.DpiX(150)),
)
btnShow := ui.NewButton(
wnd,
ui.OptsButton().
Text("&Show").
Position(ui.Dpi(240, 19)),
)me := &MyWindow{wnd, lblName, txtName, btnShow}
me.events()
return wnd.RunAsMain()
}func (me *MyWindow) events() {
me.btnShow.On().BnClicked(func() {
msg := fmt.Sprintf("Hello, %s!", me.txtName.Text())
me.wnd.Hwnd().MessageBox(msg, "Saying hello", co.MB_ICONINFORMATION)
})
}
```To compile the final `.exe` file, run the command:
```
go build -ldflags "-s -w -H=windowsgui"
```Registry access
### Registry access
```go
package mainimport (
"github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/win"
)func main() {
// Open a registry keyhKey, _ := win.HKEY_CURRENT_USER.RegOpenKeyEx(
"Control Panel\\Mouse",
co.REG_OPTION_NONE,
co.KEY_READ) // open key as read-only
defer hKey.RegCloseKey()// Read a single value from this key
regVal, _ := hKey.RegQueryValueEx("Beep") // data can be string, uint32, etc.
if strVal, ok := regVal.Sz(); ok { // try to extract a string value
println("Beep is", strVal)
}// Enumerate all values under this key
namesVals, _ := hKey.RegEnumValue()
for _, nameVal := range namesVals {
if str, ok := nameVal.Val.Sz(); ok { // does it contain a string?
println("Value str", nameVal.Name, str)
} else if num, ok := nameVal.Val.Dword(); ok { // does it contain an uint32?
println("Value int", nameVal.Name, num)
} else {
println("Value other", nameVal.Name)
}
}
}
```Enumerating running processes
### Enumerating running processes
The example below takes a [process snapshot](https://learn.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes) to list the running processes:
```go
package mainimport (
"github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/win"
)func main() {
hSnap, _ := win.CreateToolhelp32Snapshot(co.TH32CS_SNAPPROCESS, 0)
defer hSnap.CloseHandle()processes, _ := hSnap.EnumProcesses()
for _, nfo := range processes {
println("PID:", nfo.Th32ProcessID, "name:", nfo.SzExeFile())
}println(len(processes), "found")
}
```Taking a screenshot
### Taking a screenshot
This complex example takes a screenshot using [GDI](https://learn.microsoft.com/en-us/windows/win32/gdi/windows-gdi) and saves it to a BMP file.
```go
package mainimport (
"unsafe""github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/win"
)func main() {
cxScreen := win.GetSystemMetrics(co.SM_CXSCREEN)
cyScreen := win.GetSystemMetrics(co.SM_CYSCREEN)hdcScreen, _ := win.HWND(0).GetDC()
defer win.HWND(0).ReleaseDC(hdcScreen)hBmp, _ := hdcScreen.CreateCompatibleBitmap(int(cxScreen), int(cyScreen))
defer hBmp.DeleteObject()hdcMem, _ := hdcScreen.CreateCompatibleDC()
defer hdcMem.DeleteDC()hBmpOld, _ := hdcMem.SelectObjectBmp(hBmp)
defer hdcMem.SelectObjectBmp(hBmpOld)_ = hdcMem.BitBlt(
win.POINT{X: 0, Y: 0},
win.SIZE{Cx: cxScreen, Cy: cyScreen},
hdcScreen,
win.POINT{X: 0, Y: 0},
co.ROP_SRCCOPY,
)bi := win.BITMAPINFO{
BmiHeader: win.BITMAPINFOHEADER{
Width: cxScreen,
Height: cyScreen,
Planes: 1,
BitCount: 32,
Compression: co.BI_RGB,
},
}
bi.BmiHeader.SetSize()bmpObj, _ := hBmp.GetObject()
bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BitCount)rawMem, _ := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
defer rawMem.GlobalFree()bmpSlice, _ := rawMem.GlobalLockSlice()
defer rawMem.GlobalUnlock()_, _ = hdcScreen.GetDIBits(
hBmp,
0,
int(cyScreen),
bmpSlice,
&bi,
co.DIB_COLORS_RGB,
)var bfh win.BITMAPFILEHEADER
bfh.SetBfType()
bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))fout, _ := win.FileOpen(
"C:\\Temp\\screenshot.bmp",
co.FOPEN_RW_OPEN_OR_CREATE,
)
defer fout.Close()_, _ = fout.Write(bfh.Serialize())
_, _ = fout.Write(bi.BmiHeader.Serialize())
_, _ = fout.Write(bmpSlice)
}
```Component Object Model (COM)
### Component Object Model (COM)
Windigo has full support for C++ [COM](https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal) objects. The cleanup is performed by a `win.OleReleaser` object, which calls [`Release`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release) on multiple COM objects at once, much like an arena allocator. Every function which produces a COM object requires a `win.OleReleaser` to take care of its lifetime.
The example below uses COM objects to display the system native [Open File](https://learn.microsoft.com/en-us/windows/win32/learnwin32/example--the-open-dialog-box) window:
```go
package mainimport (
"github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/win"
)func main() {
runtime.LockOSThread() // important: Windows GUI is single-threaded_, _ := win.CoInitializeEx(
co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE)
defer win.CoUninitialize()releaser := win.NewOleReleaser() // will release all COM objects created here
defer releaser.Release()var fod *win.IFileOpenDialog
_ = win.CoCreateInstance(
releaser,
co.CLSID_FileOpenDialog,
nil,
co.CLSCTX_INPROC_SERVER,
&fod,
)defOpts, _ := fod.GetOptions()
_ = fod.SetOptions(defOpts |
co.FOS_FORCEFILESYSTEM |
co.FOS_FILEMUSTEXIST,
)_ = fod.SetFileTypes([]win.COMDLG_FILTERSPEC{
{Name: "Text files", Spec: "*.txt"},
{Name: "All files", Spec: "*.*"},
})
_ = fod.SetFileTypeIndex(1)if ok, _ := fod.Show(win.HWND(0)); ok { // in real applications, pass the parent HWND
item, _ := fod.GetResult(releaser)
fileName, _ := item.GetDisplayName(co.SIGDN_FILESYSPATH)
println(fileName)
}
}
```COM Automation
### COM Automation
Windigo has bindings to [`IDispatch`](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-idispatch) COM interface and [`VARIANT`](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant) parameters, allowing you to [invoke](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-invoke) Automation methods.
The example below manipulates an Excel spreadsheet, saving a copy of it:
```go
package mainimport (
"github.com/rodrigocfd/windigo/co"
"github.com/rodrigocfd/windigo/win"
)func main() {
_, _ = win.CoInitializeEx(
co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE)
defer win.CoUninitialize()rel := win.NewOleReleaser()
defer rel.Release()clsId, _ := win.CLSIDFromProgID("Excel.Application")
var excel *win.IDispatch
_ = win.CoCreateInstance(
rel,
clsId,
nil,
co.CLSCTX_LOCAL_SERVER,
&excel,
)books, _ := excel.InvokeGetIDispatch(rel, "Workbooks")
file, _ := books.InvokeMethodIDispatch(rel, "Open", "C:\\Temp\\foo.xlsx")
_, _ = file.InvokeMethod(rel, "SaveAs", "C:\\Temp\\foo copy.xlsx")
_, _ = file.InvokeMethod(rel, "Close")
}
```## Architecture
The library is divided in four packages:
| Package | Description |
| - | - |
| [`co`](https://pkg.go.dev/github.com/rodrigocfd/windigo/co) | Native Win32 constants, all typed. |
| [`ui`](https://pkg.go.dev/github.com/rodrigocfd/windigo/ui) | High-level UI windows and controls. |
| [`win`](https://pkg.go.dev/github.com/rodrigocfd/windigo/win) | Native Win32 structs, handles and functions. |
| [`wstr`](https://pkg.go.dev/github.com/rodrigocfd/windigo/wstr) | String and UTF-16 wide string management. |Package dependency:
```mermaid
flowchart BT
internal/utl([internal/utl]) --> co
ui --> win
win --> internal/dll([internal/dll])
win --> internal/utl
win --> wstr
```## License
Licensed under [MIT license](https://opensource.org/licenses/MIT), see [LICENSE.md](LICENSE.md) for details.