Refactor (windows).

This commit is contained in:
Nuno Cruces 2022-06-22 15:14:52 +01:00
parent e2dc86414f
commit 762d54a00d
13 changed files with 225 additions and 152 deletions

View file

@ -72,7 +72,6 @@ func TestCalendar_script(t *testing.T) {
if skip, err := skip(err); skip { if skip, err := skip(err); skip {
t.Skip("skipping:", err) t.Skip("skipping:", err)
} }
got.Date()
if !DateEquals(got, tt.want) || err != tt.err { if !DateEquals(got, tt.want) || err != tt.err {
t.Errorf("Calendar() = %v, %v; want %v, %v", got, err, tt.want, tt.err) t.Errorf("Calendar() = %v, %v; want %v, %v", got, err, tt.want, tt.err)
} }

View file

@ -139,7 +139,7 @@ func TestSelectFile_script(t *testing.T) {
t.Skip("skipping:", err) t.Skip("skipping:", err)
} }
if str == "" || err != nil { if str == "" || err != nil {
t.Errorf("SelectFile() = %q, %v; want [path], nil", str, err) t.Fatalf("SelectFile() = %q, %v; want [path], nil", str, err)
} }
if _, serr := os.Stat(str); serr != nil { if _, serr := os.Stat(str); serr != nil {
t.Errorf("SelectFile() = %q, %v; %v", str, err, serr) t.Errorf("SelectFile() = %q, %v; %v", str, err, serr)
@ -152,7 +152,7 @@ func TestSelectFile_script(t *testing.T) {
t.Skip("skipping:", err) t.Skip("skipping:", err)
} }
if str == "" || err != nil { if str == "" || err != nil {
t.Errorf("SelectFile() = %q, %v; want [path], nil", str, err) t.Fatalf("SelectFile() = %q, %v; want [path], nil", str, err)
} }
if s, serr := os.Stat(str); serr != nil { if s, serr := os.Stat(str); serr != nil {
t.Errorf("SelectFile() = %q, %v; %v", str, err, serr) t.Errorf("SelectFile() = %q, %v; %v", str, err, serr)
@ -183,7 +183,7 @@ func TestSelectFileMultiple_script(t *testing.T) {
t.Skip("skipping:", err) t.Skip("skipping:", err)
} }
if lst == nil || err != nil { if lst == nil || err != nil {
t.Errorf("SelectFileMultiple() = %v, %v; want [path, path], nil", lst, err) t.Fatalf("SelectFileMultiple() = %v, %v; want [path, path], nil", lst, err)
} }
for _, str := range lst { for _, str := range lst {
if _, serr := os.Stat(str); serr != nil { if _, serr := os.Stat(str); serr != nil {
@ -201,7 +201,7 @@ func TestSelectFileMultiple_script(t *testing.T) {
t.Skip("was not unsupported:", err) t.Skip("was not unsupported:", err)
} }
if lst == nil || err != nil { if lst == nil || err != nil {
t.Errorf("SelectFileMultiple() = %v, %v; want [path, path], nil", lst, err) t.Fatalf("SelectFileMultiple() = %v, %v; want [path, path], nil", lst, err)
} }
for _, str := range lst { for _, str := range lst {
if s, serr := os.Stat(str); serr != nil { if s, serr := os.Stat(str); serr != nil {

View file

@ -10,16 +10,6 @@ import (
"github.com/ncruces/zenity/internal/win" "github.com/ncruces/zenity/internal/win"
) )
const (
_FOS_NOCHANGEDIR = 0x00000008
_FOS_PICKFOLDERS = 0x00000020
_FOS_FORCEFILESYSTEM = 0x00000040
_FOS_ALLOWMULTISELECT = 0x00000200
_FOS_FORCESHOWHIDDEN = 0x10000000
_SIGDN_FILESYSPATH = 0x80058000
)
func selectFile(opts options) (string, error) { func selectFile(opts options) (string, error) {
if opts.directory { if opts.directory {
res, _, err := pickFolders(opts, false) res, _, err := pickFolders(opts, false)
@ -189,10 +179,10 @@ func selectFileSave(opts options) (string, error) {
return syscall.UTF16ToString(res[:]), nil return syscall.UTF16ToString(res[:]), nil
} }
func pickFolders(opts options, multi bool) (str string, lst []string, err error) { func pickFolders(opts options, multi bool) (string, []string, error) {
defer setup()() defer setup()()
err = win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE) err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
if err != win.RPC_E_CHANGED_MODE { if err != win.RPC_E_CHANGED_MODE {
if err != nil { if err != nil {
return "", nil, err return "", nil, err
@ -216,12 +206,12 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM flgs |= win.FOS_NOCHANGEDIR | win.FOS_PICKFOLDERS | win.FOS_FORCEFILESYSTEM
if multi { if multi {
flgs |= _FOS_ALLOWMULTISELECT flgs |= win.FOS_ALLOWMULTISELECT
} }
if opts.showHidden { if opts.showHidden {
flgs |= _FOS_FORCESHOWHIDDEN flgs |= win.FOS_FORCESHOWHIDDEN
} }
err = dialog.SetOptions(flgs) err = dialog.SetOptions(flgs)
if err != nil { if err != nil {
@ -236,8 +226,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
var item *win.IShellItem var item *win.IShellItem
win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item) win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item)
if item != nil { if item != nil {
defer item.Release()
dialog.SetFolder(item) dialog.SetFolder(item)
item.Release()
} }
} }
@ -261,15 +251,6 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return "", nil, err return "", nil, err
} }
shellItemPath := func(item *win.IShellItem) error {
defer item.Release()
str, err := item.GetDisplayName(_SIGDN_FILESYSPATH)
if err == nil {
lst = append(lst, str)
}
return err
}
if multi { if multi {
items, err := dialog.GetResults() items, err := dialog.GetResults()
if err != nil { if err != nil {
@ -281,25 +262,31 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
for i := uint32(0); i < count; i++ {
item, err := items.GetItemAt(i) var lst []string
if err == nil { for i := uint32(0); i < count && err == nil; i++ {
err = shellItemPath(item) str, err := shellItemPath(items.GetItemAt(i))
}
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
lst = append(lst, str)
} }
return "", lst, nil
} else { } else {
item, err := dialog.GetResult() str, err := shellItemPath(dialog.GetResult())
if err == nil {
err = shellItemPath(item)
}
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
return str, nil, nil
} }
return }
func shellItemPath(item *win.IShellItem, err error) (string, error) {
if err != nil {
return "", err
}
defer item.Release()
return item.GetDisplayName(win.SIGDN_FILESYSPATH)
} }
func browseForFolder(opts options) (string, []string, error) { func browseForFolder(opts options) (string, []string, error) {
@ -327,10 +314,10 @@ func browseForFolder(opts options) (string, []string, error) {
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", nil, opts.ctx.Err() return "", nil, opts.ctx.Err()
} }
if ptr == nullptr { if ptr == nil {
return "", nil, ErrCanceled return "", nil, ErrCanceled
} }
defer win.CoTaskMemFree(ptr) defer win.CoTaskMemFree(unsafe.Pointer(ptr))
var res [32768]uint16 var res [32768]uint16
win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0) win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)

View file

@ -2,6 +2,26 @@
package win package win
const (
ICC_LISTVIEW_CLASSES = 0x00000001
ICC_TREEVIEW_CLASSES = 0x00000002
ICC_BAR_CLASSES = 0x00000004
ICC_TAB_CLASSES = 0x00000008
ICC_UPDOWN_CLASS = 0x00000010
ICC_PROGRESS_CLASS = 0x00000020
ICC_HOTKEY_CLASS = 0x00000040
ICC_ANIMATE_CLASS = 0x00000080
ICC_WIN95_CLASSES = 0x000000FF
ICC_DATE_CLASSES = 0x00000100
ICC_USEREX_CLASSES = 0x00000200
ICC_COOL_CLASSES = 0x00000400
ICC_INTERNET_CLASSES = 0x00000800
ICC_PAGESCROLLER_CLASS = 0x00001000
ICC_NATIVEFNTCTL_CLASS = 0x00002000
ICC_STANDARD_CLASSES = 0x00004000
ICC_LINK_CLASS = 0x00008000
)
// https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-initcommoncontrolsex // https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-initcommoncontrolsex
type INITCOMMONCONTROLSEX struct { type INITCOMMONCONTROLSEX struct {
Size uint32 Size uint32

View file

@ -32,38 +32,41 @@ func CoInitializeEx(reserved uintptr, coInit uint32) error {
func CoUninitialize() { windows.CoUninitialize() } func CoUninitialize() { windows.CoUninitialize() }
func CoTaskMemFree(address unsafe.Pointer) { windows.CoTaskMemFree(address) }
// https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl // https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl
type _IUnknownBase struct{ COMObject } type IUnknown struct{}
type _IUnknownVtbl struct { type iUnknownVtbl struct {
QueryInterface uintptr QueryInterface uintptr
AddRef uintptr AddRef uintptr
Release uintptr Release uintptr
} }
func (c *_IUnknownBase) Release() { func (u *IUnknown) Release() {
vtbl := (*(**_IUnknownVtbl)(unsafe.Pointer(c))) vtbl := *(**iUnknownVtbl)(unsafe.Pointer(u))
c.Call(vtbl.Release) u.call(vtbl.Release)
} }
type COMObject struct{}
//go:uintptrescapes //go:uintptrescapes
func (o *COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) { func (u *IUnknown) call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) {
switch nargs := uintptr(len(a)); nargs { switch nargs := uintptr(len(a)); nargs {
case 0: case 0:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), 0, 0) return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(u)), 0, 0)
case 1: case 1:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], 0) return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(u)), a[0], 0)
case 2: case 2:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], a[1]) return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(u)), a[0], a[1])
default: default:
panic("COM call with too many arguments.") panic("COM call with too many arguments.")
} }
} }
//sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance // https://github.com/wine-mirror/wine/blob/master/include/objidl.idl
//sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree
type IBindCtx struct{ IUnknown }
//sys CoCreateInstance(clsid uintptr, unkOuter *IUnknown, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance
func uuid(s string) uintptr { func uuid(s string) uintptr {
return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data

View file

@ -14,8 +14,73 @@ const (
BFFM_INITIALIZED = 1 BFFM_INITIALIZED = 1
BFFM_SETSELECTION = WM_USER + 103 BFFM_SETSELECTION = WM_USER + 103
NIM_ADD = 0 // ShellNotifyIcon messages
NIM_DELETE = 2 NIM_ADD = 0
NIM_MODIFY = 1
NIM_DELETE = 2
NIM_SETFOCUS = 3
NIM_SETVERSION = 4
// NOTIFYICONDATA flags
NIF_MESSAGE = 0x01
NIF_ICON = 0x02
NIF_TIP = 0x04
NIF_STATE = 0x08
NIF_INFO = 0x10
NIF_GUID = 0x20
NIF_REALTIME = 0x40
NIF_SHOWTIP = 0x80
// NOTIFYICONDATA info flags
NIIF_NONE = 0x00
NIIF_INFO = 0x01
NIIF_WARNING = 0x02
NIIF_ERROR = 0x03
NIIF_USER = 0x04
NIIF_NOSOUND = 0x10
NIIF_LARGE_ICON = 0x20
NIIF_RESPECT_QUIET_TIME = 0x80
NIIF_ICON_MASK = 0x0F
// NOTIFYICONDATA state
NIS_HIDDEN = 0x1
NIS_SHAREDICON = 0x2
// IFileOpenDialog options
FOS_OVERWRITEPROMPT = 0x2
FOS_STRICTFILETYPES = 0x4
FOS_NOCHANGEDIR = 0x8
FOS_PICKFOLDERS = 0x20
FOS_FORCEFILESYSTEM = 0x40
FOS_ALLNONSTORAGEITEMS = 0x80
FOS_NOVALIDATE = 0x100
FOS_ALLOWMULTISELECT = 0x200
FOS_PATHMUSTEXIST = 0x800
FOS_FILEMUSTEXIST = 0x1000
FOS_CREATEPROMPT = 0x2000
FOS_SHAREAWARE = 0x4000
FOS_NOREADONLYRETURN = 0x8000
FOS_NOTESTFILECREATE = 0x10000
FOS_HIDEMRUPLACES = 0x20000
FOS_HIDEPINNEDPLACES = 0x40000
FOS_NODEREFERENCELINKS = 0x100000
FOS_OKBUTTONNEEDSINTERACTION = 0x200000
FOS_DONTADDTORECENT = 0x2000000
FOS_FORCESHOWHIDDEN = 0x10000000
FOS_DEFAULTNOMINIMODE = 0x20000000
FOS_FORCEPREVIEWPANEON = 0x40000000
FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
SIGDN_NORMALDISPLAY = 0x00000000
SIGDN_PARENTRELATIVEPARSING = ^(^0x18001 + 0x80000000)
SIGDN_DESKTOPABSOLUTEPARSING = ^(^0x28000 + 0x80000000)
SIGDN_PARENTRELATIVEEDITING = ^(^0x31001 + 0x80000000)
SIGDN_DESKTOPABSOLUTEEDITING = ^(^0x4c000 + 0x80000000)
SIGDN_FILESYSPATH = ^(^0x58000 + 0x80000000)
SIGDN_URL = ^(^0x68000 + 0x80000000)
SIGDN_PARENTRELATIVEFORADDRESSBAR = ^(^0x7c001 + 0x80000000)
SIGDN_PARENTRELATIVE = ^(^0x80001 + 0x80000000)
SIGDN_PARENTRELATIVEFORUI = ^(^0x94001 + 0x80000000)
) )
// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow // https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow
@ -49,6 +114,8 @@ type NOTIFYICONDATA struct {
// BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE // BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE
} }
type IDLIST struct{}
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl // https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
var ( var (
@ -57,30 +124,25 @@ var (
CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7") CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
) )
type IFileOpenDialog struct { type IFileOpenDialog struct{ IFileDialog }
_IFileOpenDialogBase type iFileOpenDialogVtbl struct {
_ *_IFileOpenDialogVtbl iFileDialogVtbl
}
type _IFileOpenDialogBase struct{ _IFileDialogBase }
type _IFileOpenDialogVtbl struct {
_IFileDialogVtbl
GetResults uintptr GetResults uintptr
GetSelectedItems uintptr GetSelectedItems uintptr
} }
func (c *_IFileOpenDialogBase) GetResults() (res *IShellItemArray, err error) { func (u *IFileOpenDialog) GetResults() (res *IShellItemArray, err error) {
vtbl := (*(**_IFileOpenDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileOpenDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetResults, uintptr(unsafe.Pointer(&res))) hr, _, _ := u.call(vtbl.GetResults, uintptr(unsafe.Pointer(&res)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
type _IFileDialogBase struct{ _IModalWindowBase } type IFileDialog struct{ IModalWindow }
type _IFileDialogVtbl struct { type iFileDialogVtbl struct {
_IModalWindowVtbl iModalWindowVtbl
SetFileTypes uintptr SetFileTypes uintptr
SetFileTypeIndex uintptr SetFileTypeIndex uintptr
GetFileTypeIndex uintptr GetFileTypeIndex uintptr
@ -106,74 +168,69 @@ type _IFileDialogVtbl struct {
SetFilter uintptr SetFilter uintptr
} }
func (c *_IFileDialogBase) SetOptions(fos int) (err error) { func (u *IFileDialog) SetOptions(fos int) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.SetOptions, uintptr(fos)) hr, _, _ := u.call(vtbl.SetOptions, uintptr(fos))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
func (c *_IFileDialogBase) GetOptions() (fos int, err error) { func (u *IFileDialog) GetOptions() (fos int, err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos))) hr, _, _ := u.call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
func (c *_IFileDialogBase) SetFolder(item *IShellItem) (err error) { func (u *IFileDialog) SetFolder(item *IShellItem) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.SetFolder, uintptr(unsafe.Pointer(item))) hr, _, _ := u.call(vtbl.SetFolder, uintptr(unsafe.Pointer(item)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
func (c *_IFileDialogBase) SetTitle(title *uint16) (err error) { func (u *IFileDialog) SetTitle(title *uint16) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.SetTitle, uintptr(unsafe.Pointer(title))) hr, _, _ := u.call(vtbl.SetTitle, uintptr(unsafe.Pointer(title)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
func (c *_IFileDialogBase) GetResult() (item *IShellItem, err error) { func (u *IFileDialog) GetResult() (item *IShellItem, err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetResult, uintptr(unsafe.Pointer(&item))) hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
type _IModalWindowBase struct{ _IUnknownBase } type IModalWindow struct{ IUnknown }
type _IModalWindowVtbl struct { type iModalWindowVtbl struct {
_IUnknownVtbl iUnknownVtbl
Show uintptr Show uintptr
} }
func (c *_IModalWindowBase) Show(wnd HWND) (err error) { func (u *IModalWindow) Show(wnd HWND) (err error) {
vtbl := (*(**_IModalWindowVtbl)(unsafe.Pointer(c))) vtbl := *(**iModalWindowVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.Show, uintptr(wnd)) hr, _, _ := u.call(vtbl.Show, uintptr(wnd))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
type IShellItem struct { type IShellItem struct{ IUnknown }
_IShellItemBase type iShellItemVtbl struct {
_ *_IShellItemVtbl iUnknownVtbl
}
type _IShellItemBase struct{ _IUnknownBase }
type _IShellItemVtbl struct {
_IUnknownVtbl
BindToHandler uintptr BindToHandler uintptr
GetParent uintptr GetParent uintptr
GetDisplayName uintptr GetDisplayName uintptr
@ -181,10 +238,10 @@ type _IShellItemVtbl struct {
Compare uintptr Compare uintptr
} }
func (c *_IShellItemBase) GetDisplayName(name int) (res string, err error) { func (u *IShellItem) GetDisplayName(name int) (res string, err error) {
var ptr uintptr var ptr uintptr
vtbl := (*(**_IShellItemVtbl)(unsafe.Pointer(c))) vtbl := *(**iShellItemVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr))) hr, _, _ := u.call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} else { } else {
@ -196,14 +253,9 @@ func (c *_IShellItemBase) GetDisplayName(name int) (res string, err error) {
return return
} }
type IShellItemArray struct { type IShellItemArray struct{ IUnknown }
_IShellItemArrayBase type iShellItemArrayVtbl struct {
_ *_IShellItemArrayVtbl iUnknownVtbl
}
type _IShellItemArrayBase struct{ _IUnknownBase }
type _IShellItemArrayVtbl struct {
_IUnknownVtbl
BindToHandler uintptr BindToHandler uintptr
GetPropertyStore uintptr GetPropertyStore uintptr
GetPropertyDescriptionList uintptr GetPropertyDescriptionList uintptr
@ -213,25 +265,25 @@ type _IShellItemArrayVtbl struct {
EnumItems uintptr EnumItems uintptr
} }
func (c *_IShellItemArrayBase) GetCount() (numItems uint32, err error) { func (u *IShellItemArray) GetCount() (numItems uint32, err error) {
vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c))) vtbl := *(**iShellItemArrayVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetCount, uintptr(unsafe.Pointer(&numItems))) hr, _, _ := u.call(vtbl.GetCount, uintptr(unsafe.Pointer(&numItems)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
func (c *_IShellItemArrayBase) GetItemAt(index uint32) (item *IShellItem, err error) { func (u *IShellItemArray) GetItemAt(index uint32) (item *IShellItem, err error) {
vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c))) vtbl := *(**iShellItemArrayVtbl)(unsafe.Pointer(u))
hr, _, _ := c.Call(vtbl.GetItemAt, uintptr(index), uintptr(unsafe.Pointer(&item))) hr, _, _ := u.call(vtbl.GetItemAt, uintptr(index), uintptr(unsafe.Pointer(&item)))
if hr != 0 { if hr != 0 {
err = syscall.Errno(hr) err = syscall.Errno(hr)
} }
return return
} }
//sys SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) = shell32.SHBrowseForFolder //sys SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) = shell32.SHBrowseForFolder
//sys SHCreateItemFromParsingName(path *uint16, bc *COMObject, 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) (ret int, err error) = shell32.Shell_NotifyIconW //sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) = shell32.Shell_NotifyIconW
//sys SHGetPathFromIDListEx(ptr Pointer, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx //sys SHGetPathFromIDListEx(ptr *IDLIST, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx

View file

@ -19,6 +19,9 @@ const (
IDYES = 6 IDYES = 6
IDNO = 7 IDNO = 7
// Control IDs
IDC_STATIC_OK = 20
// MessageBox types // MessageBox types
MB_OK = windows.MB_OK MB_OK = windows.MB_OK
MB_OKCANCEL = windows.MB_OKCANCEL MB_OKCANCEL = windows.MB_OKCANCEL
@ -60,7 +63,7 @@ const (
STM_SETICON = 0x0170 STM_SETICON = 0x0170
// CreateWindow // CreateWindow
CW_USEDEFAULT = 0x80000000 CW_USEDEFAULT = -0x80000000
// Window classes // Window classes
PROGRESS_CLASS = "msctls_progress32" PROGRESS_CLASS = "msctls_progress32"
@ -217,18 +220,34 @@ const (
// SetWindowsHookEx types // SetWindowsHookEx types
WH_CALLWNDPROCRET = 12 WH_CALLWNDPROCRET = 12
USER_DEFAULT_SCREEN_DPI = 96 // System colors
COLOR_WINDOW = 5
// DPI awareness
USER_DEFAULT_SCREEN_DPI = 96
DPI_AWARENESS_CONTEXT_UNAWARE = ^uintptr(1) + 1 DPI_AWARENESS_CONTEXT_UNAWARE = ^uintptr(1) + 1
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = ^uintptr(2) + 1 DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = ^uintptr(2) + 1
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ^uintptr(3) + 1 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ^uintptr(3) + 1
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ^uintptr(4) + 1 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ^uintptr(4) + 1
DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = ^uintptr(5) + 1 DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = ^uintptr(5) + 1
// LoadImage type
IMAGE_BITMAP = 0 IMAGE_BITMAP = 0
IMAGE_ICON = 1 IMAGE_ICON = 1
IMAGE_CURSOR = 2 IMAGE_CURSOR = 2
// LoadIcon resources
IDI_APPLICATION = 32512
IDI_ERROR = 32513
IDI_HAND = 32513
IDI_QUESTION = 32514
IDI_WARNING = 32515
IDI_EXCLAMATION = 32515
IDI_ASTERISK = 32516
IDI_INFORMATION = 32516
IDI_WINLOGO = 32517
IDI_SHIELD = 32518
// LoadResource (image/icon) flags // LoadResource (image/icon) flags
LR_DEFAULTCOLOR = 0x00000000 LR_DEFAULTCOLOR = 0x00000000
LR_MONOCHROME = 0x00000001 LR_MONOCHROME = 0x00000001

View file

@ -7,7 +7,7 @@ import "golang.org/x/sys/windows"
type Handle = windows.Handle type Handle = windows.Handle
type HWND = windows.HWND type HWND = windows.HWND
type Pointer uintptr type Pointer = windows.Pointer
// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
type SYSTEMTIME struct { type SYSTEMTIME struct {

View file

@ -62,7 +62,6 @@ var (
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW") procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx") procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx")
procCoCreateInstance = modole32.NewProc("CoCreateInstance") procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder") procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder")
procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName") procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName")
procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx") procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx")
@ -199,7 +198,7 @@ func ReleaseActCtx(actCtx Handle) {
return return
} }
func CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) { func CoCreateInstance(clsid uintptr, unkOuter *IUnknown, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) {
r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(clsid), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsContext), uintptr(iid), uintptr(address), 0) r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(clsid), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsContext), uintptr(iid), uintptr(address), 0)
if r0 != 0 { if r0 != 0 {
res = syscall.Errno(r0) res = syscall.Errno(r0)
@ -207,18 +206,13 @@ func CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid
return return
} }
func CoTaskMemFree(address Pointer) { func SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) {
syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0)
return
}
func SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) {
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 = Pointer(r0) ret = (*IDLIST)(unsafe.Pointer(r0))
return return
} }
func SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item **IShellItem) (res error) { func SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid uintptr, item **IShellItem) (res error) {
r0, _, _ := syscall.Syscall6(procSHCreateItemFromParsingName.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(bc)), uintptr(iid), uintptr(unsafe.Pointer(item)), 0, 0) r0, _, _ := syscall.Syscall6(procSHCreateItemFromParsingName.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(bc)), uintptr(iid), uintptr(unsafe.Pointer(item)), 0, 0)
if r0 != 0 { if r0 != 0 {
res = syscall.Errno(r0) res = syscall.Errno(r0)
@ -226,8 +220,8 @@ func SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item
return return
} }
func SHGetPathFromIDListEx(ptr Pointer, path *uint16, pathLen int, opts int) (ok bool) { func SHGetPathFromIDListEx(ptr *IDLIST, path *uint16, pathLen int, opts int) (ok bool) {
r0, _, _ := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(ptr), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0) r0, _, _ := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(unsafe.Pointer(ptr)), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0)
ok = r0 != 0 ok = r0 != 0
return return
} }

View file

@ -187,7 +187,7 @@ func listProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Pointer)
dlg.out = make([]string, len) dlg.out = make([]string, len)
if len > 0 { if len > 0 {
indices := make([]int32, len) indices := make([]int32, len)
win.SendMessage(dlg.listCtl, win.LB_GETSELITEMS, len, uintptr(unsafe.Pointer(&indices[0]))) win.SendMessagePointer(dlg.listCtl, win.LB_GETSELITEMS, len, unsafe.Pointer(&indices[0]))
for i, idx := range indices { for i, idx := range indices {
dlg.out[i] = dlg.items[idx] dlg.out[i] = dlg.items[idx]
} }

View file

@ -88,6 +88,7 @@ func message(kind messageKind, text string, opts options) error {
} }
func hookMessageDialog(opts options) (unhook context.CancelFunc, err error) { func hookMessageDialog(opts options) (unhook context.CancelFunc, err error) {
// TODO: use GetDlgItem, SetDlgItemText instead of EnumChildWindows.
return hookDialog(opts.ctx, opts.windowIcon, nil, func(wnd win.HWND) { return hookDialog(opts.ctx, opts.windowIcon, nil, func(wnd win.HWND) {
win.EnumChildWindows(wnd, syscall.NewCallback(hookMessageDialogCallback), win.EnumChildWindows(wnd, syscall.NewCallback(hookMessageDialogCallback),
unsafe.Pointer(&opts)) unsafe.Pointer(&opts))
@ -110,7 +111,7 @@ func hookMessageDialogCallback(wnd win.HWND, lparam *options) uintptr {
win.SetWindowText(wnd, strptr(*text)) win.SetWindowText(wnd, strptr(*text))
} }
if ctl == 20 /*IDC_STATIC_OK*/ { if ctl == win.IDC_STATIC_OK {
icon := getIcon(lparam.icon) icon := getIcon(lparam.icon)
if icon.handle != 0 { if icon.handle != 0 {
defer icon.delete() defer icon.delete()

View file

@ -19,8 +19,8 @@ func notify(text string, opts options) error {
var args win.NOTIFYICONDATA var args win.NOTIFYICONDATA
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.ID = rand.Uint32() args.ID = rand.Uint32()
args.Flags = 0x00000010 // NIF_INFO args.Flags = win.NIF_INFO
args.State = 0x00000001 // NIS_HIDDEN args.State = win.NIS_HIDDEN
info := syscall.StringToUTF16(text) info := syscall.StringToUTF16(text)
copy(args.Info[:len(args.Info)-1], info) copy(args.Info[:len(args.Info)-1], info)
@ -32,18 +32,18 @@ func notify(text string, opts options) error {
switch opts.icon { switch opts.icon {
case InfoIcon, QuestionIcon: case InfoIcon, QuestionIcon:
args.InfoFlags |= 0x1 // NIIF_INFO args.InfoFlags |= win.NIIF_INFO
case WarningIcon: case WarningIcon:
args.InfoFlags |= 0x2 // NIIF_WARNING args.InfoFlags |= win.NIIF_WARNING
case ErrorIcon: case ErrorIcon:
args.InfoFlags |= 0x3 // 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 = win.Handle(icon.handle) args.Icon = win.Handle(icon.handle)
args.Flags |= 0x00000002 // NIF_ICON args.Flags |= win.NIF_ICON
args.InfoFlags |= 0x4 // NIIF_USER args.InfoFlags |= win.NIIF_USER
} }
} }

View file

@ -23,8 +23,6 @@ const (
_WS_ZEN_BUTTON = _WS_ZEN_CONTROL _WS_ZEN_BUTTON = _WS_ZEN_CONTROL
) )
const nullptr win.Pointer = 0
func intptr(i int64) uintptr { return uintptr(i) } func intptr(i int64) uintptr { return uintptr(i) }
func strptr(s string) *uint16 { return syscall.StringToUTF16Ptr(s) } func strptr(s string) *uint16 { return syscall.StringToUTF16Ptr(s) }
@ -56,7 +54,7 @@ func setup() context.CancelFunc {
var icc win.INITCOMMONCONTROLSEX var icc win.INITCOMMONCONTROLSEX
icc.Size = uint32(unsafe.Sizeof(icc)) icc.Size = uint32(unsafe.Sizeof(icc))
icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS icc.ICC = win.ICC_STANDARD_CLASSES | win.ICC_DATE_CLASSES | win.ICC_PROGRESS_CLASS
win.InitCommonControlsEx(&icc) win.InitCommonControlsEx(&icc)
return func() { return func() {
@ -264,13 +262,13 @@ func getIcon(i any) icon {
var resource uintptr var resource uintptr
switch i { switch i {
case ErrorIcon: case ErrorIcon:
resource = 32513 // IDI_ERROR resource = win.IDI_ERROR
case QuestionIcon: case QuestionIcon:
resource = 32514 // IDI_QUESTION resource = win.IDI_QUESTION
case WarningIcon: case WarningIcon:
resource = 32515 // IDI_WARNING resource = win.IDI_WARNING
case InfoIcon: case InfoIcon:
resource = 32516 // IDI_INFORMATION resource = win.IDI_INFORMATION
} }
if resource != 0 { if resource != 0 {
res.handle, _ = win.LoadIcon(0, resource) res.handle, _ = win.LoadIcon(0, resource)
@ -331,7 +329,7 @@ func registerClass(instance, icon win.Handle, proc uintptr) (*uint16, error) {
wcx.WndProc = proc wcx.WndProc = proc
wcx.Icon = icon wcx.Icon = icon
wcx.Instance = instance wcx.Instance = instance
wcx.Background = 5 // COLOR_WINDOW wcx.Background = win.COLOR_WINDOW
wcx.ClassName = strptr(name) wcx.ClassName = strptr(name)
if err := win.RegisterClassEx(&wcx); err != nil { if err := win.RegisterClassEx(&wcx); err != nil {