Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions systray_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

var (
g32 = windows.NewLazySystemDLL("Gdi32.dll")
pCreateCompatibleBitmap = g32.NewProc("CreateCompatibleBitmap")
pCreateDIBSection = g32.NewProc("CreateDIBSection")
pCreateCompatibleDC = g32.NewProc("CreateCompatibleDC")
pDeleteDC = g32.NewProc("DeleteDC")
pSelectObject = g32.NewProc("SelectObject")
Expand Down Expand Up @@ -751,7 +751,36 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
defer pDeleteDC.Call(hMemDC)
cx, _, _ := pGetSystemMetrics.Call(SM_CXSMICON)
cy, _, _ := pGetSystemMetrics.Call(SM_CYSMICON)
hMemBmp, _, err := pCreateCompatibleBitmap.Call(hDC, cx, cy)

var bi struct {
Size uint32
Width int32
Height int32
Planes uint16
BitCount uint16
Compression uint32
SizeImage uint32
XPelsPerMeter int32
YPelsPerMeter int32
ClrUsed uint32
ClrImportant uint32
}
bi.Size = uint32(unsafe.Sizeof(bi))
bi.Width = int32(cx)
bi.Height = int32(-cy) // top-down
bi.Planes = 1
bi.BitCount = 32
bi.Compression = 0 // BI_RGB

var bits unsafe.Pointer
hMemBmp, _, err := pCreateDIBSection.Call(
hDC,
uintptr(unsafe.Pointer(&bi)),
0, // DIB_RGB_COLORS
uintptr(unsafe.Pointer(&bits)),
0,
0,
)
if hMemBmp == 0 {
return 0, err
}
Expand All @@ -761,6 +790,18 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
if res == 0 {
return 0, err
}
Comment on lines 784 to 792
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If DrawIconEx fails, the function returns without cleaning up hMemBmp, which leaks a GDI bitmap handle on the error path. Consider adding a DeleteObject proc and deferring cleanup after CreateDIBSection succeeds, then canceling the defer when returning the bitmap successfully.

Copilot uses AI. Check for mistakes.

if bits != nil {
pixels := (*[1 << 30]byte)(bits)[:cx*cy*4 : cx*cy*4]
// Pre-multiply alpha channel before creating HBITMAP
Comment on lines +794 to +796
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cx/cy are uintptr (returned from Proc.Call), but they’re used directly in the slice expression [:cx*cy*4]. Slice bounds must be int, so this won’t compile. Convert cx/cy to int (after sanity/overflow checks) and use those int values when computing the pixel buffer length and when passing sizes around in Go expressions.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says “Pre-multiply alpha channel before creating HBITMAP”, but the HBITMAP (hMemBmp) has already been created at this point. Rewording to indicate this is done before returning/using the bitmap would avoid confusion for future maintenance.

Suggested change
// Pre-multiply alpha channel before creating HBITMAP
// Pre-multiply alpha channel in-place before returning/using the HBITMAP

Copilot uses AI. Check for mistakes.
for i := 0; i < len(pixels); i += 4 {
a := uint32(pixels[i+3])
pixels[i] = byte(uint32(pixels[i]) * a / 255) // R
pixels[i+1] = byte(uint32(pixels[i+1]) * a / 255) // G
pixels[i+2] = byte(uint32(pixels[i+2]) * a / 255) // B
}
}

return windows.Handle(hMemBmp), nil
}

Expand Down
Loading