Icon from EXE (windows), see #35.

This commit is contained in:
Nuno Cruces 2022-07-26 16:56:43 +01:00
parent 566fbb4e60
commit fc4cc53c87
10 changed files with 54 additions and 23 deletions

View file

@ -41,7 +41,7 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
defer setup(owner)() defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon, _ := getIcon(opts.windowIcon)
defer icon.delete() defer icon.delete()
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -40,7 +40,7 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) {
defer setup(owner)() defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon, _ := getIcon(opts.windowIcon)
defer icon.delete() defer icon.delete()
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -284,6 +284,7 @@ func (u *IShellItemArray) GetItemAt(index uint32) (item *IShellItem, err error)
return return
} }
//sys ExtractAssociatedIcon(instance Handle, path *uint16, icon *uint16) (ret Handle, err error) = shell32.ExtractAssociatedIconW
//sys SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) = shell32.SHBrowseForFolder //sys SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) = shell32.SHBrowseForFolder
//sys SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName //sys SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName
//sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ok bool) = shell32.Shell_NotifyIconW //sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ok bool) = shell32.Shell_NotifyIconW

View file

@ -65,6 +65,7 @@ var (
procGlobalFree = modkernel32.NewProc("GlobalFree") procGlobalFree = modkernel32.NewProc("GlobalFree")
procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx") procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx")
procCoCreateInstance = modole32.NewProc("CoCreateInstance") procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procExtractAssociatedIconW = modshell32.NewProc("ExtractAssociatedIconW")
procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder") procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder")
procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName") procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName")
procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx") procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx")
@ -235,6 +236,15 @@ func CoCreateInstance(clsid uintptr, unkOuter *IUnknown, clsContext int32, iid u
return return
} }
func ExtractAssociatedIcon(instance Handle, path *uint16, icon *uint16) (ret Handle, err error) {
r0, _, e1 := syscall.Syscall(procExtractAssociatedIconW.Addr(), 3, uintptr(instance), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(icon)))
ret = Handle(r0)
if ret == 0 {
err = errnoErr(e1)
}
return
}
func SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) { func SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) {
r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0) r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0)
ret = (*IDLIST)(unsafe.Pointer(r0)) ret = (*IDLIST)(unsafe.Pointer(r0))

View file

@ -57,7 +57,7 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) {
defer setup(owner)() defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon, _ := getIcon(opts.windowIcon)
defer icon.delete() defer icon.delete()
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -104,11 +104,15 @@ func hookMessageDialog(opts options) (context.CancelFunc, error) {
} }
} }
} }
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, init) icon, err := getIcon(opts.icon)
if err != nil || opts.icon == nil { if err != nil {
return unhook, err return nil, err
}
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, init)
if err != nil {
icon.delete()
return nil, err
} }
icon = getIcon(opts.icon)
return func() { return func() {
icon.delete() icon.delete()
unhook() unhook()

View file

@ -38,7 +38,7 @@ func notify(text string, opts options) error {
case ErrorIcon: case ErrorIcon:
args.InfoFlags |= win.NIIF_ERROR args.InfoFlags |= win.NIIF_ERROR
default: default:
icon := getIcon(opts.icon) icon, _ := getIcon(opts.icon)
if icon.handle != 0 { if icon.handle != 0 {
defer icon.delete() defer icon.delete()
args.Icon = icon.handle args.Icon = icon.handle

View file

@ -120,7 +120,7 @@ func (dlg *progressDialog) setup(opts options) error {
defer setup(owner)() defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon, _ := getIcon(opts.windowIcon)
defer icon.delete() defer icon.delete()
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -50,7 +50,7 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) {
defer setup(owner)() defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon, _ := getIcon(opts.windowIcon)
defer icon.delete() defer icon.delete()
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"os" "os"
"path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"sync" "sync"
@ -12,6 +13,7 @@ import (
"unsafe" "unsafe"
"github.com/ncruces/zenity/internal/win" "github.com/ncruces/zenity/internal/win"
"golang.org/x/sys/windows"
) )
const ( const (
@ -106,12 +108,13 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd w
if hk == 0 { if hk == 0 {
return nil, err return nil, err
} }
ico, _ := getIcon(icon)
hook := dialogHook{ hook := dialogHook{
ctx: ctx, ctx: ctx,
tid: tid, tid: tid,
hook: hk, hook: hk,
icon: getIcon(icon), icon: ico,
title: title, title: title,
init: init, init: init,
} }
@ -253,8 +256,7 @@ type icon struct {
destroy bool destroy bool
} }
func getIcon(i any) icon { func getIcon(i any) (icon icon, err error) {
var res icon
var resource uintptr var resource uintptr
switch i { switch i {
case ErrorIcon: case ErrorIcon:
@ -267,38 +269,52 @@ func getIcon(i any) icon {
resource = win.IDI_INFORMATION resource = win.IDI_INFORMATION
} }
if resource != 0 { if resource != 0 {
res.handle, _ = win.LoadIcon(0, resource) icon.handle, err = win.LoadIcon(0, resource)
return res return icon, err
} }
path, ok := i.(string) path, ok := i.(string)
if !ok { if !ok {
return res return icon, nil
} }
data, err := os.ReadFile(path) data, err := os.ReadFile(path)
if err != nil { if err != nil {
return res return icon, err
} }
switch { switch {
case bytes.HasPrefix(data, []byte("\x00\x00\x01\x00")): case bytes.HasPrefix(data, []byte("\x00\x00\x01\x00")):
res.handle, _ = win.LoadImage(0, icon.handle, err = win.LoadImage(0,
strptr(path), strptr(path),
win.IMAGE_ICON, 0, 0, win.IMAGE_ICON, 0, 0,
win.LR_LOADFROMFILE|win.LR_DEFAULTSIZE) win.LR_LOADFROMFILE|win.LR_DEFAULTSIZE)
res.destroy = true icon.destroy = true
case bytes.HasPrefix(data, []byte("\x89PNG\r\n\x1a\n")): case bytes.HasPrefix(data, []byte("\x89PNG\r\n\x1a\n")):
res.handle, _ = win.CreateIconFromResourceEx( icon.handle, err = win.CreateIconFromResourceEx(
data, true, 0x00030000, 0, 0, data, true, 0x00030000, 0, 0,
win.LR_DEFAULTSIZE) win.LR_DEFAULTSIZE)
res.destroy = true icon.destroy = true
case bytes.HasPrefix(data, []byte("MZ")):
var instance windows.Handle
instance, err = win.GetModuleHandle(nil)
if err != nil {
break
} }
return res path, err = filepath.Abs(path)
if err != nil {
break
}
var i uint16
icon.handle, err = win.ExtractAssociatedIcon(
instance, strptr(path), &i)
icon.destroy = true
}
return icon, err
} }
func (i *icon) delete() { func (i *icon) delete() {
if i.destroy && i.handle != 0 { if i.handle != 0 && i.destroy {
win.DestroyIcon(i.handle) win.DestroyIcon(i.handle)
i.handle = 0 i.handle = 0
} }