From 566fbb4e60665748ea609278f3d470ad8c2b3deb Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 26 Jul 2022 14:49:49 +0100 Subject: [PATCH] Icon fixes (windows), see #35. --- file_windows.go | 20 ++++++++------------ internal/win/user32.go | 2 +- internal/win/zsyscall_windows.go | 6 +++--- msg_windows.go | 15 +++++++++++---- notify_windows.go | 2 +- util_windows.go | 25 ++++++++++++------------- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/file_windows.go b/file_windows.go index a059a57..9123564 100644 --- a/file_windows.go +++ b/file_windows.go @@ -223,13 +223,11 @@ func pickFolders(opts options, multi bool) (string, []string, error) { } } - if opts.ctx != nil || opts.windowIcon != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) - if err != nil { - return "", nil, err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) + if err != nil { + return "", nil, err } + defer unhook() err = dialog.Show(owner) if opts.ctx != nil && opts.ctx.Err() != nil { @@ -293,13 +291,11 @@ func browseForFolder(opts options) (string, []string, error) { args.CallbackFunc = syscall.NewCallback(browseForFolderCallback) } - if opts.ctx != nil || opts.windowIcon != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) - if err != nil { - return "", nil, err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) + if err != nil { + return "", nil, err } + defer unhook() ptr := win.SHBrowseForFolder(&args) if opts.ctx != nil && opts.ctx.Err() != nil { diff --git a/internal/win/user32.go b/internal/win/user32.go index a09a7e2..1b31505 100644 --- a/internal/win/user32.go +++ b/internal/win/user32.go @@ -380,7 +380,7 @@ type CWPRETSTRUCT struct { } //sys CallNextHookEx(hk Handle, code int32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.CallNextHookEx -//sys CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, err error) = user32.CreateIconFromResource +//sys CreateIconFromResourceEx(resBits []byte, icon bool, ver uint32, cx int, cy int, flags int) (ret Handle, err error) = user32.CreateIconFromResourceEx //sys CreateWindowEx(exStyle uint32, className *uint16, windowName *uint16, style uint32, x int, y int, width int, height int, parent HWND, menu Handle, instance Handle, param unsafe.Pointer) (ret HWND, err error) = user32.CreateWindowExW //sys DefWindowProc(wnd HWND, msg uint32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.DefWindowProcW //sys DestroyIcon(icon Handle) (err error) = user32.DestroyIcon diff --git a/internal/win/zsyscall_windows.go b/internal/win/zsyscall_windows.go index c360f94..5e84641 100644 --- a/internal/win/zsyscall_windows.go +++ b/internal/win/zsyscall_windows.go @@ -70,7 +70,7 @@ var ( procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx") procShell_NotifyIconW = modshell32.NewProc("Shell_NotifyIconW") procCallNextHookEx = moduser32.NewProc("CallNextHookEx") - procCreateIconFromResource = moduser32.NewProc("CreateIconFromResource") + procCreateIconFromResourceEx = moduser32.NewProc("CreateIconFromResourceEx") procCreateWindowExW = moduser32.NewProc("CreateWindowExW") procDefWindowProcW = moduser32.NewProc("DefWindowProcW") procDestroyIcon = moduser32.NewProc("DestroyIcon") @@ -267,7 +267,7 @@ func CallNextHookEx(hk Handle, code int32, wparam uintptr, lparam unsafe.Pointer return } -func CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, err error) { +func CreateIconFromResourceEx(resBits []byte, icon bool, ver uint32, cx int, cy int, flags int) (ret Handle, err error) { var _p0 *byte if len(resBits) > 0 { _p0 = &resBits[0] @@ -276,7 +276,7 @@ func CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, if icon { _p1 = 1 } - r0, _, e1 := syscall.Syscall6(procCreateIconFromResource.Addr(), 4, uintptr(unsafe.Pointer(_p0)), uintptr(len(resBits)), uintptr(_p1), uintptr(ver), 0, 0) + r0, _, e1 := syscall.Syscall9(procCreateIconFromResourceEx.Addr(), 7, uintptr(unsafe.Pointer(_p0)), uintptr(len(resBits)), uintptr(_p1), uintptr(ver), uintptr(cx), uintptr(cy), uintptr(flags), 0, 0) ret = Handle(r0) if ret == 0 { err = errnoErr(e1) diff --git a/msg_windows.go b/msg_windows.go index b78bee7..3ec6733 100644 --- a/msg_windows.go +++ b/msg_windows.go @@ -82,8 +82,9 @@ func message(kind messageKind, text string, opts options) error { } } -func hookMessageDialog(opts options) (unhook context.CancelFunc, err error) { +func hookMessageDialog(opts options) (context.CancelFunc, error) { var init func(wnd win.HWND) + var icon icon if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || opts.icon != nil { init = func(wnd win.HWND) { if opts.okLabel != nil { @@ -97,13 +98,19 @@ func hookMessageDialog(opts options) (unhook context.CancelFunc, err error) { win.SetDlgItemText(wnd, win.IDNO, strptr(*opts.extraButton)) } - icon := getIcon(opts.icon) if icon.handle != 0 { - defer icon.delete() ctl, _ := win.GetDlgItem(wnd, win.IDC_STATIC_OK) win.SendMessage(ctl, win.STM_SETICON, uintptr(icon.handle), 0) } } } - return hookDialog(opts.ctx, opts.windowIcon, nil, init) + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, init) + if err != nil || opts.icon == nil { + return unhook, err + } + icon = getIcon(opts.icon) + return func() { + icon.delete() + unhook() + }, nil } diff --git a/notify_windows.go b/notify_windows.go index 4b8a3e7..a8c0aa2 100644 --- a/notify_windows.go +++ b/notify_windows.go @@ -41,7 +41,7 @@ func notify(text string, opts options) error { icon := getIcon(opts.icon) if icon.handle != 0 { defer icon.delete() - args.Icon = win.Handle(icon.handle) + args.Icon = icon.handle args.Flags |= win.NIF_ICON args.InfoFlags |= win.NIIF_USER } diff --git a/util_windows.go b/util_windows.go index 581afcc..b9f2235 100644 --- a/util_windows.go +++ b/util_windows.go @@ -75,7 +75,7 @@ func setupEnumCallback(wnd win.HWND, lparam *win.HWND) uintptr { } func hookDialog(ctx context.Context, icon any, title *string, init func(wnd win.HWND)) (unhook context.CancelFunc, err error) { - if ctx == nil && icon == nil && init == nil { + if ctx == nil && icon == nil && title == nil && init == nil { return func() {}, nil } if ctx != nil && ctx.Err() != nil { @@ -94,7 +94,7 @@ type dialogHook struct { wnd uintptr hook win.Handle done chan struct{} - icon any + icon icon title *string init func(wnd win.HWND) } @@ -111,7 +111,7 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd w ctx: ctx, tid: tid, hook: hk, - icon: icon, + icon: getIcon(icon), title: title, init: init, } @@ -132,12 +132,8 @@ func dialogHookProc(code int32, wparam uintptr, lparam *win.CWPRETSTRUCT) uintpt if hook.ctx != nil && hook.ctx.Err() != nil { win.SendMessage(lparam.Wnd, win.WM_SYSCOMMAND, win.SC_CLOSE, 0) } else { - if hook.icon != nil { - icon := getIcon(hook.icon) - if icon.handle != 0 { - defer icon.delete() - win.SendMessage(lparam.Wnd, win.WM_SETICON, 0, uintptr(icon.handle)) - } + if hook.icon.handle != 0 { + win.SendMessage(lparam.Wnd, win.WM_SETICON, 0, uintptr(hook.icon.handle)) } if hook.title != nil { win.SetWindowText(lparam.Wnd, strptr(*hook.title)) @@ -155,6 +151,7 @@ func (h *dialogHook) unhook() { if h.done != nil { close(h.done) } + h.icon.delete() win.UnhookWindowsHookEx(h.hook) } @@ -289,17 +286,19 @@ func getIcon(i any) icon { res.handle, _ = win.LoadImage(0, strptr(path), win.IMAGE_ICON, 0, 0, - win.LR_LOADFROMFILE|win.LR_DEFAULTSIZE|win.LR_SHARED) + win.LR_LOADFROMFILE|win.LR_DEFAULTSIZE) + res.destroy = true case bytes.HasPrefix(data, []byte("\x89PNG\r\n\x1a\n")): - res.handle, err = win.CreateIconFromResource( - data, true, 0x00030000) + res.handle, _ = win.CreateIconFromResourceEx( + data, true, 0x00030000, 0, 0, + win.LR_DEFAULTSIZE) res.destroy = true } return res } func (i *icon) delete() { - if i.handle != 0 { + if i.destroy && i.handle != 0 { win.DestroyIcon(i.handle) i.handle = 0 }