Skip to content

[v3] w32.GetStockObject calls GetDeviceCaps and always returns 0 #5874

Description

@roachadam

Wails version family

v3

Exact Wails version

v3.0.0-beta.2

Operating System

Windows

Description

w32.GetStockObject invokes the wrong Win32 export and returns 0 for every input. It has never worked.

v3/pkg/w32/gdi32.go:

func GetStockObject(fnObject int) HGDIOBJ {
	ret, _, _ := procGetDeviceCaps.Call(
		uintptr(fnObject))

	return HGDIOBJ(ret)
}

It calls procGetDeviceCaps, not procGetStockObject — even though procGetStockObject is declared a few lines above, at gdi32.go:40, and is otherwise unused.

GetDeviceCaps takes (HDC, index). Passing a stock-object constant such as DEFAULT_GUI_FONT (17) as the HDC is an invalid handle, so the call returns 0. Every GetStockObject call therefore yields a null GDI handle.

There are no callers in v3 today, which is why this has gone unnoticed. It only surfaces for a new caller, and then indirectly: the null handle is passed to some other GDI function, which fails for reasons that point away from the actual cause. I lost time to exactly this while prototyping owner-drawn menus — SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)) terminated the process, and the null handle was several steps removed from the visible symptom.

This is the same class of defect as the DestroyMenu argument bug already covered by v3/pkg/w32/menu_windows_test.go, where a syscall wrapper passed the wrong arguments and silently returned failure for every call.

To Reproduce

No special hardware or Windows version required.

//go:build windows

package w32_test

import (
	"testing"

	"github.com/wailsapp/wails/v3/pkg/w32"
)

func TestRepro(t *testing.T) {
	for name, obj := range map[string]int{
		"DEFAULT_GUI_FONT": w32.DEFAULT_GUI_FONT,
		"SYSTEM_FONT":      w32.SYSTEM_FONT,
		"BLACK_BRUSH":      w32.BLACK_BRUSH,
		"WHITE_BRUSH":      w32.WHITE_BRUSH,
	} {
		if got := w32.GetStockObject(obj); got == 0 {
			t.Errorf("GetStockObject(%s) = 0, want a valid handle", name)
		}
	}
}

go test ./pkg/w32/ on Windows: every case fails with 0.

Expected behaviour

GetStockObject returns a valid, usable GDI handle for standard stock objects. Stock objects are always available, so a 0 return indicates the wrapper is calling the wrong export.

Screenshots

No response

Attempted Fixes

No response

System Details

Wails: v3.0.0-beta.2
OS:    Any Windows build — not version-specific
Arch:  amd64

Found on Windows Server 2019 (build 17763) but the defect is in the wrapper
itself and is independent of Windows version.

Additional context

Found while investigating menu theming on Windows; unrelated to that work, so filing separately.

While confirming this, I also noticed that SelectObject panics on failure rather than returning 0. That turns a null handle from this bug into a process termination, since SelectObject is called from inside Windows message handlers. I am not proposing a change to it here: panicking on failure is the established convention across pkg/w32 (39 call sites in 10 files, including SetTextColor and SetBkMode in the same file), so changing one function in isolation would be arbitrary. Happy to raise that separately as a design question if it is worth discussing.

AI Usage Disclosure: I used AI in investigating and writing this issue to confirm my own initial findings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingP2Medium priorityWindowsv3

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions