8000 GitHub - rodrigocfd/windigo: Windows API and GUI in idiomatic Go.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

rodrigocfd/windigo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Reference GitHub go.mod Go version Lines of code MIT License

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 folder.

Screen capture

package main

import (
	"fmt"
	"runtime"

	"github.com/rodrigocfd/windigo/ui"
	"github.com/rodrigocfd/windigo/win/co"
)

func main() {
	runtime.LockOSThread() // important: Windows GUI is single-threaded

	myWindow := NewMyWindow() // instantiate
	myWindow.wnd.RunAsMain()  // ...and run
}

// This struct represents our main window.
type MyWindow struct {
	wnd     *ui.Main
	lblName *ui.Static
	txtName *ui.Edit
	btnShow *ui.Button
}

// Creates a new instance of our main window.
func NewMyWindow() *MyWindow {
	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 me
}

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

package main

import (
	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
)

func main() {
	// Open a registry key

	hKey, _ := 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

	allValues, _ := hKey.RegEnumValue()
	for _, value := range allValues {
		regVal, _ := hKey.RegQueryValueEx(value)

		if strVal, ok := regVal.Sz(); ok { // does it contain a string?
			println("Value str", value, strVal)
		} else if intVal, ok := regVal.Dword(); ok { // does it contain an uint32?
			println("Value int", value, intVal)
		} else {
			println("Value other", value, regVal.Type())
		}
	}
}
Enumerating running processes

Enumerating running processes

The example below takes a process snapshot to list the running processes:

package main

import (
	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
)

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 and saves it to a BMP file.

package main

import (
	"unsafe"

	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
)

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(uint(cxScreen), uint(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{
			BiWidth:       cxScreen,
			BiHeight:      cyScreen,
			BiPlanes:      1,
			BiBitCount:    32,
			BiCompression: co.BI_RGB,
		},
	}
	bi.BmiHeader.SetBiSize()

	bmpObj, _ := hBmp.GetObject()
	bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BiBitCount)

	rawMem, _ := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
	defer rawMem.GlobalFree()

	bmpSlice, _ := rawMem.GlobalLockSlice()
	defer rawMem.GlobalUnlock()

	hdcScreen.GetDIBits(hBmp, 0, uint(cyScreen), bmpSlice, &bi, co.DIB_RGB_COLORS)

	var bfh win.BITMAPFILEHEADER
	bfh.SetBfType()
	bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
	bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))

	fo, _ := win.FileOpen("C:\\Temp\\screenshot.bmp", co.FOPEN_RW_OPEN_OR_CREATE)
	defer fo.Close()

	fo.Write(bfh.Serialize())
	fo.Write(bi.BmiHeader.Serialize())
	fo.Write(bmpSlice)
}
Component Object Model (COM)

Component Object Model (COM)

Windigo has full support for C++ COM objects. The cleanup is performed by an ole.Releaser object, which calls Release on multiple COM objects at once, much like an arena allocator. Every function which produces a COM object requires an ole.Releaser to take care of its lifetime.

The example below uses COM objects to display the system native Open File window:

package main

import (
	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
	"github.com/rodrigocfd/windigo/win/ole"
	"github.com/rodrigocfd/windigo/win/ole/shell"
)

func main() {
	runtime.LockOSThread() // important: Windows GUI is single-threaded

	ole.CoInitializeEx(co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE)
	defer ole.CoUninitialize()

	releaser := ole.NewReleaser() // will release all COM objects created here
	defer releaser.Release()

	var fod *shell.IFileOpenDialog
	ole.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([]shell.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 COM interface and VARIAN 8000 T parameters, allowing you to invoke Automation methods.

The example below manipulates an Excel spreadsheet, saving a copy of it:

package main

import (
	"github.com/rodrigocfd/windigo/win/co"
	"github.com/rodrigocfd/windigo/win/ole"
	"github.com/rodrigocfd/windigo/win/ole/oleaut"
)

func main() {
	ole.CoInitializeEx(co.COINIT_APARTMENTTHREADED | co.COINIT_DISABLE_OLE1DDE)
	defer ole.CoUninitialize()

	rel := ole.NewReleaser()
	defer rel.Release()

	clsId, _ := ole.CLSIDFromProgID("Excel.Application")

	var dispatchExcel *oleaut.IDispatch
	ole.CoCreateInstance(rel, clsId, nil, co.CLSCTX_LOCAL_SERVER, &dispatchExcel)

	variantBooks, _ := dispatchExcel.InvokeGet(rel, "Workbooks")
	dispatchBooks, _ := variantBooks.IDispatch(rel)
	variantFile, _ := dispatchBooks.InvokeMethod(rel, "Open", "C:\\Temp\\foo.xlsx")

	dispatchFile, _ := variantFile.IDispatch(rel)
	dispatchFile.InvokeMethod(rel, "SaveAs", "C:\\Temp\\foo copy.xlsx")
	dispatchFile.InvokeMethod(rel, "Close")
}

Architecture

The library is divided in two main packages:

  • ui – high-level windows and controls;
  • win – low-level native Win32 bindings.

More specifically:

Package Description
ui High-level UI windows and controls.
win Native Win32 structs, handles and functions.
win/co Native Win32 constants, all typed.
win/ole COM bindings.
win/ole/oleaut COM automation bindings.
win/ole/shell Shell COM bindings.
win/wstr String and UTF-16 wide string management.

Internal package dependency:

flowchart BT
    internal/utl([internal/utl]) --> win/co
    ui --> win
    win --> internal/dll([internal/dll])
    win --> internal/utl
    win --> win/wstr
    win/ole --> win
    win/ole/oleaut --> win/ole
    win/ole/shell --> win/ole
Loading

License

Licensed under MIT license, see LICENSE.md for details.

Contributors 5

Languages

0