diff --git a/date_windows.go b/date_windows.go index 48cd06c..c57da55 100644 --- a/date_windows.go +++ b/date_windows.go @@ -89,11 +89,11 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) { } if opts.time != nil { - var date _SYSTEMTIME + var date win.SYSTEMTIME year, month, day := opts.time.Date() - date.year = uint16(year) - date.month = uint16(month) - date.day = uint16(day) + date.Year = uint16(year) + date.Month = uint16(month) + date.Day = uint16(day) win.SendMessagePointer(dlg.dateCtl, win.MCM_SETCURSEL, 0, unsafe.Pointer(&date)) } @@ -168,9 +168,9 @@ func calendarProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Point default: return 1 case win.IDOK, win.IDYES: - var date _SYSTEMTIME + var date win.SYSTEMTIME win.SendMessagePointer(dlg.dateCtl, win.MCM_GETCURSEL, 0, unsafe.Pointer(&date)) - dlg.out = time.Date(int(date.year), time.Month(date.month), int(date.day), 0, 0, 0, 0, time.UTC) + dlg.out = time.Date(int(date.Year), time.Month(date.Month), int(date.Day), 0, 0, 0, 0, time.UTC) case win.IDCANCEL: dlg.err = ErrCanceled case win.IDNO: diff --git a/file_windows.go b/file_windows.go index 51fdf76..0e6587b 100644 --- a/file_windows.go +++ b/file_windows.go @@ -3,7 +3,6 @@ package zenity import ( "fmt" "path/filepath" - "reflect" "syscall" "unicode/utf16" "unsafe" @@ -201,22 +200,21 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) defer win.CoUninitialize() } - var dialog *_IFileOpenDialog + var dialog *win.IFileOpenDialog err = win.CoCreateInstance( - _CLSID_FileOpenDialog, nil, win.CLSCTX_ALL, - _IID_IFileOpenDialog, unsafe.Pointer(&dialog)) + win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL, + win.IID_IFileOpenDialog, unsafe.Pointer(&dialog)) if err != nil { if multi { return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported) } return browseForFolder(opts) } - defer dialog.Call(dialog.Release) + defer dialog.Release() - var flgs int - hr, _, _ := dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs))) - if int32(hr) < 0 { - return "", nil, syscall.Errno(hr) + flgs, err := dialog.GetOptions() + if err != nil { + return "", nil, err } flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM if multi { @@ -225,24 +223,21 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if opts.showHidden { flgs |= _FOS_FORCESHOWHIDDEN } - hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs)) - if int32(hr) < 0 { - return "", nil, syscall.Errno(hr) + err = dialog.SetOptions(flgs) + if err != nil { + return "", nil, err } if opts.title != nil { - ptr := strptr(*opts.title) - dialog.Call(dialog.SetTitle, uintptr(unsafe.Pointer(ptr))) + dialog.SetTitle(strptr(*opts.title)) } if opts.filename != "" { var item *win.IShellItem - ptr := strptr(opts.filename) - win.SHCreateItemFromParsingName(ptr, nil, _IID_IShellItem, &item) - - if int32(hr) >= 0 && item != nil { - dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item))) - item.Call(item.Release) + win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item) + if item != nil { + dialog.SetFolder(item) + item.Release() } } @@ -255,59 +250,54 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) } owner, _ := opts.attach.(win.HWND) - hr, _, _ = dialog.Call(dialog.Show, uintptr(owner)) + err = dialog.Show(owner) if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() } - if hr == uintptr(win.E_CANCELED) { + if err == win.E_CANCELED { return "", nil, ErrCanceled } - if int32(hr) < 0 { - return "", nil, syscall.Errno(hr) + if err != nil { + return "", nil, err } - shellItemPath := func(obj *win.COMObject, trap uintptr, a ...uintptr) error { - var item *win.IShellItem - hr, _, _ := obj.Call(trap, append(a, uintptr(unsafe.Pointer(&item)))...) - if int32(hr) < 0 { - return syscall.Errno(hr) + shellItemPath := func(item *win.IShellItem) error { + defer item.Release() + str, err := item.GetDisplayName(_SIGDN_FILESYSPATH) + if err == nil { + lst = append(lst, str) } - defer item.Call(item.Release) - - var ptr win.Pointer - hr, _, _ = item.Call(item.GetDisplayName, - _SIGDN_FILESYSPATH, uintptr(unsafe.Pointer(&ptr))) - if int32(hr) < 0 { - return syscall.Errno(hr) - } - defer win.CoTaskMemFree(ptr) - - var res []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res)) - hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768 - str = syscall.UTF16ToString(res) - lst = append(lst, str) - return nil + return err } if multi { - var items *_IShellItemArray - hr, _, _ = dialog.Call(dialog.GetResults, uintptr(unsafe.Pointer(&items))) - if int32(hr) < 0 { - return "", nil, syscall.Errno(hr) + items, err := dialog.GetResults() + if err != nil { + return "", nil, err } - defer items.Call(items.Release) + defer items.Release() - var count uint32 - hr, _, _ = items.Call(items.GetCount, uintptr(unsafe.Pointer(&count))) - if int32(hr) < 0 { - return "", nil, syscall.Errno(hr) + count, err := items.GetCount() + if err != nil { + return "", nil, err } - for i := uintptr(0); i < uintptr(count) && err == nil; i++ { - err = shellItemPath(&items.COMObject, items.GetItemAt, i) + for i := uint32(0); i < count; i++ { + item, err := items.GetItemAt(i) + if err == nil { + err = shellItemPath(item) + } + if err != nil { + return "", nil, err + } } } else { - err = shellItemPath(&dialog.COMObject, dialog.GetResult) + item, err := dialog.GetResult() + if err == nil { + err = shellItemPath(item) + } + if err != nil { + return "", nil, err + } } return } @@ -389,70 +379,3 @@ func initFilters(filters FileFilters) []uint16 { } return res } - -// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl - -var ( - _IID_IShellItem = uuid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe") - _IID_IFileOpenDialog = uuid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60") - _CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7") -) - -type _IFileOpenDialog struct { - win.COMObject - *_IFileOpenDialogVtbl -} - -type _IShellItemArray struct { - win.COMObject - *_IShellItemArrayVtbl -} - -type _IFileOpenDialogVtbl struct { - _IFileDialogVtbl - GetResults uintptr - GetSelectedItems uintptr -} - -type _IFileDialogVtbl struct { - _IModalWindowVtbl - SetFileTypes uintptr - SetFileTypeIndex uintptr - GetFileTypeIndex uintptr - Advise uintptr - Unadvise uintptr - SetOptions uintptr - GetOptions uintptr - SetDefaultFolder uintptr - SetFolder uintptr - GetFolder uintptr - GetCurrentSelection uintptr - SetFileName uintptr - GetFileName uintptr - SetTitle uintptr - SetOkButtonLabel uintptr - SetFileNameLabel uintptr - GetResult uintptr - AddPlace uintptr - SetDefaultExtension uintptr - Close uintptr - SetClientGuid uintptr - ClearClientData uintptr - SetFilter uintptr -} - -type _IModalWindowVtbl struct { - _IUnknownVtbl - Show uintptr -} - -type _IShellItemArrayVtbl struct { - _IUnknownVtbl - BindToHandler uintptr - GetPropertyStore uintptr - GetPropertyDescriptionList uintptr - GetAttributes uintptr - GetCount uintptr - GetItemAt uintptr - EnumItems uintptr -} diff --git a/internal/win/ole32.go b/internal/win/ole32.go index 03982f6..c734227 100644 --- a/internal/win/ole32.go +++ b/internal/win/ole32.go @@ -3,6 +3,7 @@ package win import ( + "reflect" "syscall" "unsafe" @@ -33,12 +34,18 @@ func CoUninitialize() { windows.CoUninitialize() } // https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl -type IUnknownVtbl struct { +type _IUnknownBase struct{ COMObject } +type _IUnknownVtbl struct { QueryInterface uintptr AddRef uintptr Release uintptr } +func (c *_IUnknownBase) Release() { + vtbl := (*(**_IUnknownVtbl)(unsafe.Pointer(c))) + c.Call(vtbl.Release) +} + type COMObject struct{} //go:uintptrescapes @@ -57,3 +64,7 @@ func (o *COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr er //sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance //sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree + +func uuid(s string) uintptr { + return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data +} diff --git a/internal/win/shell32.go b/internal/win/shell32.go index ca976f2..f57f25f 100644 --- a/internal/win/shell32.go +++ b/internal/win/shell32.go @@ -2,6 +2,12 @@ package win +import ( + "reflect" + "syscall" + "unsafe" +) + const ( BIF_RETURNONLYFSDIRS = 0x00000001 @@ -43,13 +49,131 @@ type NOTIFYICONDATA struct { // BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE } -type IShellItem struct { - COMObject - *_IShellItemVtbl +// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl + +var ( + IID_IShellItem = uuid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe") + IID_IFileOpenDialog = uuid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60") + CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7") +) + +type IFileOpenDialog struct { + _IFileOpenDialogBase + _ *_IFileOpenDialogVtbl } +type _IFileOpenDialogBase struct{ _IFileDialogBase } +type _IFileOpenDialogVtbl struct { + _IFileDialogVtbl + GetResults uintptr + GetSelectedItems uintptr +} + +func (c *_IFileOpenDialogBase) GetResults() (res *IShellItemArray, err error) { + vtbl := (*(**_IFileOpenDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetResults, uintptr(unsafe.Pointer(&res))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +type _IFileDialogBase struct{ _IModalWindowBase } +type _IFileDialogVtbl struct { + _IModalWindowVtbl + SetFileTypes uintptr + SetFileTypeIndex uintptr + GetFileTypeIndex uintptr + Advise uintptr + Unadvise uintptr + SetOptions uintptr + GetOptions uintptr + SetDefaultFolder uintptr + SetFolder uintptr + GetFolder uintptr + GetCurrentSelection uintptr + SetFileName uintptr + GetFileName uintptr + SetTitle uintptr + SetOkButtonLabel uintptr + SetFileNameLabel uintptr + GetResult uintptr + AddPlace uintptr + SetDefaultExtension uintptr + Close uintptr + SetClientGuid uintptr + ClearClientData uintptr + SetFilter uintptr +} + +func (c *_IFileDialogBase) SetOptions(fos int) (err error) { + vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.SetOptions, uintptr(fos)) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +func (c *_IFileDialogBase) GetOptions() (fos int, err error) { + vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +func (c *_IFileDialogBase) SetFolder(item *IShellItem) (err error) { + vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.SetFolder, uintptr(unsafe.Pointer(item))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +func (c *_IFileDialogBase) SetTitle(title *uint16) (err error) { + vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.SetTitle, uintptr(unsafe.Pointer(title))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +func (c *_IFileDialogBase) GetResult() (item *IShellItem, err error) { + vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetResult, uintptr(unsafe.Pointer(&item))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +type _IModalWindowBase struct{ _IUnknownBase } +type _IModalWindowVtbl struct { + _IUnknownVtbl + Show uintptr +} + +func (c *_IModalWindowBase) Show(wnd HWND) (err error) { + vtbl := (*(**_IModalWindowVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.Show, uintptr(wnd)) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +type IShellItem struct { + _IShellItemBase + _ *_IShellItemVtbl +} + +type _IShellItemBase struct{ _IUnknownBase } type _IShellItemVtbl struct { - IUnknownVtbl + _IUnknownVtbl BindToHandler uintptr GetParent uintptr GetDisplayName uintptr @@ -57,6 +181,56 @@ type _IShellItemVtbl struct { Compare uintptr } +func (c *_IShellItemBase) GetDisplayName(name int) (res string, err error) { + var ptr uintptr + vtbl := (*(**_IShellItemVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr))) + if hr != 0 { + err = syscall.Errno(hr) + } else { + var buf []uint16 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768 + res = syscall.UTF16ToString(buf) + } + return +} + +type IShellItemArray struct { + _IShellItemArrayBase + _ *_IShellItemArrayVtbl +} + +type _IShellItemArrayBase struct{ _IUnknownBase } +type _IShellItemArrayVtbl struct { + _IUnknownVtbl + BindToHandler uintptr + GetPropertyStore uintptr + GetPropertyDescriptionList uintptr + GetAttributes uintptr + GetCount uintptr + GetItemAt uintptr + EnumItems uintptr +} + +func (c *_IShellItemArrayBase) GetCount() (numItems uint32, err error) { + vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetCount, uintptr(unsafe.Pointer(&numItems))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + +func (c *_IShellItemArrayBase) GetItemAt(index uint32) (item *IShellItem, err error) { + vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c))) + hr, _, _ := c.Call(vtbl.GetItemAt, uintptr(index), uintptr(unsafe.Pointer(&item))) + if hr != 0 { + err = syscall.Errno(hr) + } + return +} + //sys SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) = shell32.SHBrowseForFolder //sys SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName //sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) = shell32.Shell_NotifyIconW diff --git a/internal/win/user32.go b/internal/win/user32.go index e15e5f5..8265fd8 100644 --- a/internal/win/user32.go +++ b/internal/win/user32.go @@ -353,6 +353,15 @@ type NONCLIENTMETRICS struct { MessageFont LOGFONT } +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct +type CWPRETSTRUCT struct { + Result uintptr + LParam uintptr + WParam uintptr + Message uint32 + Wnd HWND +} + //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 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 diff --git a/internal/win/win32.go b/internal/win/win32.go index 2ca0d9a..888baff 100644 --- a/internal/win/win32.go +++ b/internal/win/win32.go @@ -9,6 +9,18 @@ type Handle = windows.Handle type HWND = windows.HWND type Pointer uintptr +// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime +type SYSTEMTIME struct { + Year uint16 + Month uint16 + DayOfWeek uint16 + Day uint16 + Hour uint16 + Minute uint16 + Second uint16 + Milliseconds uint16 +} + func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) { return windows.RtlGetNtVersionNumbers() } diff --git a/util_windows.go b/util_windows.go index 0deadfd..f8d1301 100644 --- a/util_windows.go +++ b/util_windows.go @@ -127,7 +127,7 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd w return &hook, nil } -func dialogHookProc(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr { +func dialogHookProc(code int32, wparam uintptr, lparam *win.CWPRETSTRUCT) uintptr { if lparam.Message == win.WM_INITDIALOG { tid := win.GetCurrentThreadId() hook := (*dialogHook)(loadBackRef(uintptr(tid))) @@ -360,36 +360,3 @@ func enableVisualStyles() (cookie uintptr) { } return } - -// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct -type _CWPRETSTRUCT struct { - Result uintptr - LParam uintptr - WParam uintptr - Message uint32 - Wnd win.HWND -} - -// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime -type _SYSTEMTIME struct { - year uint16 - month uint16 - dayOfWeek uint16 - day uint16 - hour uint16 - minute uint16 - second uint16 - milliseconds uint16 -} - -// https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl - -type _IUnknownVtbl struct { - QueryInterface uintptr - AddRef uintptr - Release uintptr -} - -func uuid(s string) uintptr { - return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data -}