diff --git a/date_test.go b/date_test.go index c38f32d..20e5752 100644 --- a/date_test.go +++ b/date_test.go @@ -72,7 +72,6 @@ func TestCalendar_script(t *testing.T) { if skip, err := skip(err); skip { t.Skip("skipping:", err) } - got.Date() if !DateEquals(got, tt.want) || err != tt.err { t.Errorf("Calendar() = %v, %v; want %v, %v", got, err, tt.want, tt.err) } diff --git a/file_test.go b/file_test.go index 9aef10d..d1df66e 100644 --- a/file_test.go +++ b/file_test.go @@ -139,7 +139,7 @@ func TestSelectFile_script(t *testing.T) { t.Skip("skipping:", err) } 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 { t.Errorf("SelectFile() = %q, %v; %v", str, err, serr) @@ -152,7 +152,7 @@ func TestSelectFile_script(t *testing.T) { t.Skip("skipping:", err) } 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 { t.Errorf("SelectFile() = %q, %v; %v", str, err, serr) @@ -183,7 +183,7 @@ func TestSelectFileMultiple_script(t *testing.T) { t.Skip("skipping:", err) } 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 { if _, serr := os.Stat(str); serr != nil { @@ -201,7 +201,7 @@ func TestSelectFileMultiple_script(t *testing.T) { t.Skip("was not unsupported:", err) } 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 { if s, serr := os.Stat(str); serr != nil { diff --git a/file_windows.go b/file_windows.go index 0e6587b..ced30f7 100644 --- a/file_windows.go +++ b/file_windows.go @@ -10,16 +10,6 @@ import ( "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) { if opts.directory { res, _, err := pickFolders(opts, false) @@ -189,10 +179,10 @@ func selectFileSave(opts options) (string, error) { 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()() - 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 != nil { return "", nil, err @@ -216,12 +206,12 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if err != nil { return "", nil, err } - flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM + flgs |= win.FOS_NOCHANGEDIR | win.FOS_PICKFOLDERS | win.FOS_FORCEFILESYSTEM if multi { - flgs |= _FOS_ALLOWMULTISELECT + flgs |= win.FOS_ALLOWMULTISELECT } if opts.showHidden { - flgs |= _FOS_FORCESHOWHIDDEN + flgs |= win.FOS_FORCESHOWHIDDEN } err = dialog.SetOptions(flgs) if err != nil { @@ -236,8 +226,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) var item *win.IShellItem win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item) if item != nil { + defer item.Release() dialog.SetFolder(item) - item.Release() } } @@ -261,15 +251,6 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) 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 { items, err := dialog.GetResults() if err != nil { @@ -281,25 +262,31 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if err != nil { return "", nil, err } - for i := uint32(0); i < count; i++ { - item, err := items.GetItemAt(i) - if err == nil { - err = shellItemPath(item) - } + + var lst []string + for i := uint32(0); i < count && err == nil; i++ { + str, err := shellItemPath(items.GetItemAt(i)) if err != nil { return "", nil, err } + lst = append(lst, str) } + return "", lst, nil } else { - item, err := dialog.GetResult() - if err == nil { - err = shellItemPath(item) - } + str, err := shellItemPath(dialog.GetResult()) if err != nil { 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) { @@ -327,10 +314,10 @@ func browseForFolder(opts options) (string, []string, error) { if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() } - if ptr == nullptr { + if ptr == nil { return "", nil, ErrCanceled } - defer win.CoTaskMemFree(ptr) + defer win.CoTaskMemFree(unsafe.Pointer(ptr)) var res [32768]uint16 win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0) diff --git a/internal/win/comctl32.go b/internal/win/comctl32.go index 63b971c..ea58ca8 100644 --- a/internal/win/comctl32.go +++ b/internal/win/comctl32.go @@ -2,6 +2,26 @@ 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 type INITCOMMONCONTROLSEX struct { Size uint32 diff --git a/internal/win/ole32.go b/internal/win/ole32.go index c734227..fa8419f 100644 --- a/internal/win/ole32.go +++ b/internal/win/ole32.go @@ -32,38 +32,41 @@ func CoInitializeEx(reserved uintptr, coInit uint32) error { func CoUninitialize() { windows.CoUninitialize() } +func CoTaskMemFree(address unsafe.Pointer) { windows.CoTaskMemFree(address) } + // https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl -type _IUnknownBase struct{ COMObject } -type _IUnknownVtbl struct { +type IUnknown struct{} +type iUnknownVtbl struct { QueryInterface uintptr AddRef uintptr Release uintptr } -func (c *_IUnknownBase) Release() { - vtbl := (*(**_IUnknownVtbl)(unsafe.Pointer(c))) - c.Call(vtbl.Release) +func (u *IUnknown) Release() { + vtbl := *(**iUnknownVtbl)(unsafe.Pointer(u)) + u.call(vtbl.Release) } -type COMObject struct{} - //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 { 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: - 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: - 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: panic("COM call with too many arguments.") } } -//sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance -//sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree +// https://github.com/wine-mirror/wine/blob/master/include/objidl.idl + +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 { return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data diff --git a/internal/win/shell32.go b/internal/win/shell32.go index f57f25f..829a632 100644 --- a/internal/win/shell32.go +++ b/internal/win/shell32.go @@ -14,8 +14,73 @@ const ( BFFM_INITIALIZED = 1 BFFM_SETSELECTION = WM_USER + 103 - NIM_ADD = 0 - NIM_DELETE = 2 + // ShellNotifyIcon messages + 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 @@ -49,6 +114,8 @@ type NOTIFYICONDATA struct { // BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE } +type IDLIST struct{} + // https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl 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") ) -type IFileOpenDialog struct { - _IFileOpenDialogBase - _ *_IFileOpenDialogVtbl -} - -type _IFileOpenDialogBase struct{ _IFileDialogBase } -type _IFileOpenDialogVtbl struct { - _IFileDialogVtbl +type IFileOpenDialog struct{ IFileDialog } +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))) +func (u *IFileOpenDialog) GetResults() (res *IShellItemArray, err error) { + vtbl := *(**iFileOpenDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.call(vtbl.GetResults, uintptr(unsafe.Pointer(&res))) if hr != 0 { err = syscall.Errno(hr) } return } -type _IFileDialogBase struct{ _IModalWindowBase } -type _IFileDialogVtbl struct { - _IModalWindowVtbl +type IFileDialog struct{ IModalWindow } +type iFileDialogVtbl struct { + iModalWindowVtbl SetFileTypes uintptr SetFileTypeIndex uintptr GetFileTypeIndex uintptr @@ -106,74 +168,69 @@ type _IFileDialogVtbl struct { SetFilter uintptr } -func (c *_IFileDialogBase) SetOptions(fos int) (err error) { - vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c))) - hr, _, _ := c.Call(vtbl.SetOptions, uintptr(fos)) +func (u *IFileDialog) SetOptions(fos int) (err error) { + vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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))) +func (u *IFileDialog) GetOptions() (fos int, err error) { + vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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))) +func (u *IFileDialog) SetFolder(item *IShellItem) (err error) { + vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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))) +func (u *IFileDialog) SetTitle(title *uint16) (err error) { + vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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))) +func (u *IFileDialog) GetResult() (item *IShellItem, err error) { + vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item))) if hr != 0 { err = syscall.Errno(hr) } return } -type _IModalWindowBase struct{ _IUnknownBase } -type _IModalWindowVtbl struct { - _IUnknownVtbl +type IModalWindow struct{ IUnknown } +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)) +func (u *IModalWindow) Show(wnd HWND) (err error) { + vtbl := *(**iModalWindowVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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 +type IShellItem struct{ IUnknown } +type iShellItemVtbl struct { + iUnknownVtbl BindToHandler uintptr GetParent uintptr GetDisplayName uintptr @@ -181,10 +238,10 @@ type _IShellItemVtbl struct { 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 - vtbl := (*(**_IShellItemVtbl)(unsafe.Pointer(c))) - hr, _, _ := c.Call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr))) + vtbl := *(**iShellItemVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr))) if hr != 0 { err = syscall.Errno(hr) } else { @@ -196,14 +253,9 @@ func (c *_IShellItemBase) GetDisplayName(name int) (res string, err error) { return } -type IShellItemArray struct { - _IShellItemArrayBase - _ *_IShellItemArrayVtbl -} - -type _IShellItemArrayBase struct{ _IUnknownBase } -type _IShellItemArrayVtbl struct { - _IUnknownVtbl +type IShellItemArray struct{ IUnknown } +type iShellItemArrayVtbl struct { + iUnknownVtbl BindToHandler uintptr GetPropertyStore uintptr GetPropertyDescriptionList uintptr @@ -213,25 +265,25 @@ type _IShellItemArrayVtbl struct { EnumItems uintptr } -func (c *_IShellItemArrayBase) GetCount() (numItems uint32, err error) { - vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c))) - hr, _, _ := c.Call(vtbl.GetCount, uintptr(unsafe.Pointer(&numItems))) +func (u *IShellItemArray) GetCount() (numItems uint32, err error) { + vtbl := *(**iShellItemArrayVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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))) +func (u *IShellItemArray) GetItemAt(index uint32) (item *IShellItem, err error) { + vtbl := *(**iShellItemArrayVtbl)(unsafe.Pointer(u)) + hr, _, _ := u.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 SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) = shell32.SHBrowseForFolder +//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 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 diff --git a/internal/win/user32.go b/internal/win/user32.go index 8265fd8..ad48156 100644 --- a/internal/win/user32.go +++ b/internal/win/user32.go @@ -19,6 +19,9 @@ const ( IDYES = 6 IDNO = 7 + // Control IDs + IDC_STATIC_OK = 20 + // MessageBox types MB_OK = windows.MB_OK MB_OKCANCEL = windows.MB_OKCANCEL @@ -60,7 +63,7 @@ const ( STM_SETICON = 0x0170 // CreateWindow - CW_USEDEFAULT = 0x80000000 + CW_USEDEFAULT = -0x80000000 // Window classes PROGRESS_CLASS = "msctls_progress32" @@ -217,18 +220,34 @@ const ( // SetWindowsHookEx types 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_SYSTEM_AWARE = ^uintptr(2) + 1 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ^uintptr(3) + 1 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ^uintptr(4) + 1 DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = ^uintptr(5) + 1 + // LoadImage type IMAGE_BITMAP = 0 IMAGE_ICON = 1 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 LR_DEFAULTCOLOR = 0x00000000 LR_MONOCHROME = 0x00000001 diff --git a/internal/win/win32.go b/internal/win/win32.go index 888baff..b026bd4 100644 --- a/internal/win/win32.go +++ b/internal/win/win32.go @@ -7,7 +7,7 @@ import "golang.org/x/sys/windows" type Handle = windows.Handle type HWND = windows.HWND -type Pointer uintptr +type Pointer = windows.Pointer // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime type SYSTEMTIME struct { diff --git a/internal/win/zsyscall_windows.go b/internal/win/zsyscall_windows.go index c2c5c70..25cce39 100644 --- a/internal/win/zsyscall_windows.go +++ b/internal/win/zsyscall_windows.go @@ -62,7 +62,6 @@ var ( procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW") procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx") procCoCreateInstance = modole32.NewProc("CoCreateInstance") - procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder") procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName") procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx") @@ -199,7 +198,7 @@ func ReleaseActCtx(actCtx Handle) { 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) if r0 != 0 { res = syscall.Errno(r0) @@ -207,18 +206,13 @@ func CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid return } -func CoTaskMemFree(address Pointer) { - syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0) - return -} - -func SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) { +func SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) { r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0) - ret = Pointer(r0) + ret = (*IDLIST)(unsafe.Pointer(r0)) 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) if r0 != 0 { res = syscall.Errno(r0) @@ -226,8 +220,8 @@ func SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item return } -func SHGetPathFromIDListEx(ptr Pointer, 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) +func SHGetPathFromIDListEx(ptr *IDLIST, path *uint16, pathLen int, opts int) (ok bool) { + r0, _, _ := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(unsafe.Pointer(ptr)), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0) ok = r0 != 0 return } diff --git a/list_windows.go b/list_windows.go index ac8a2c8..3daa229 100644 --- a/list_windows.go +++ b/list_windows.go @@ -187,7 +187,7 @@ func listProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Pointer) dlg.out = make([]string, len) if len > 0 { 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 { dlg.out[i] = dlg.items[idx] } diff --git a/msg_windows.go b/msg_windows.go index e285146..46b483f 100644 --- a/msg_windows.go +++ b/msg_windows.go @@ -88,6 +88,7 @@ func message(kind messageKind, text string, opts options) 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) { win.EnumChildWindows(wnd, syscall.NewCallback(hookMessageDialogCallback), unsafe.Pointer(&opts)) @@ -110,7 +111,7 @@ func hookMessageDialogCallback(wnd win.HWND, lparam *options) uintptr { win.SetWindowText(wnd, strptr(*text)) } - if ctl == 20 /*IDC_STATIC_OK*/ { + if ctl == win.IDC_STATIC_OK { icon := getIcon(lparam.icon) if icon.handle != 0 { defer icon.delete() diff --git a/notify_windows.go b/notify_windows.go index f19a004..3251727 100644 --- a/notify_windows.go +++ b/notify_windows.go @@ -19,8 +19,8 @@ func notify(text string, opts options) error { var args win.NOTIFYICONDATA args.StructSize = uint32(unsafe.Sizeof(args)) args.ID = rand.Uint32() - args.Flags = 0x00000010 // NIF_INFO - args.State = 0x00000001 // NIS_HIDDEN + args.Flags = win.NIF_INFO + args.State = win.NIS_HIDDEN info := syscall.StringToUTF16(text) copy(args.Info[:len(args.Info)-1], info) @@ -32,18 +32,18 @@ func notify(text string, opts options) error { switch opts.icon { case InfoIcon, QuestionIcon: - args.InfoFlags |= 0x1 // NIIF_INFO + args.InfoFlags |= win.NIIF_INFO case WarningIcon: - args.InfoFlags |= 0x2 // NIIF_WARNING + args.InfoFlags |= win.NIIF_WARNING case ErrorIcon: - args.InfoFlags |= 0x3 // NIIF_ERROR + args.InfoFlags |= win.NIIF_ERROR default: icon := getIcon(opts.icon) if icon.handle != 0 { defer icon.delete() args.Icon = win.Handle(icon.handle) - args.Flags |= 0x00000002 // NIF_ICON - args.InfoFlags |= 0x4 // NIIF_USER + args.Flags |= win.NIF_ICON + args.InfoFlags |= win.NIIF_USER } } diff --git a/util_windows.go b/util_windows.go index f8d1301..e97b8cf 100644 --- a/util_windows.go +++ b/util_windows.go @@ -23,8 +23,6 @@ const ( _WS_ZEN_BUTTON = _WS_ZEN_CONTROL ) -const nullptr win.Pointer = 0 - func intptr(i int64) uintptr { return uintptr(i) } func strptr(s string) *uint16 { return syscall.StringToUTF16Ptr(s) } @@ -56,7 +54,7 @@ func setup() context.CancelFunc { var icc win.INITCOMMONCONTROLSEX 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) return func() { @@ -264,13 +262,13 @@ func getIcon(i any) icon { var resource uintptr switch i { case ErrorIcon: - resource = 32513 // IDI_ERROR + resource = win.IDI_ERROR case QuestionIcon: - resource = 32514 // IDI_QUESTION + resource = win.IDI_QUESTION case WarningIcon: - resource = 32515 // IDI_WARNING + resource = win.IDI_WARNING case InfoIcon: - resource = 32516 // IDI_INFORMATION + resource = win.IDI_INFORMATION } if resource != 0 { res.handle, _ = win.LoadIcon(0, resource) @@ -331,7 +329,7 @@ func registerClass(instance, icon win.Handle, proc uintptr) (*uint16, error) { wcx.WndProc = proc wcx.Icon = icon wcx.Instance = instance - wcx.Background = 5 // COLOR_WINDOW + wcx.Background = win.COLOR_WINDOW wcx.ClassName = strptr(name) if err := win.RegisterClassEx(&wcx); err != nil {