From 6928e7489517a2909c6d255a81a584072f3d6919 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 21 Jun 2022 14:27:19 +0100 Subject: [PATCH] Refactor (windows). --- file_windows.go | 4 +-- internal/win/ole32.go | 5 ++-- internal/win/shell32.go | 6 ++--- internal/win/user32.go | 8 +++++- internal/win/win32.go | 2 +- internal/win/zsyscall_windows.go | 46 +++++++++++++++++++++++++++----- util_unix.go | 5 +++- util_windows.go | 28 ++++++++----------- zenity.go | 2 +- 9 files changed, 70 insertions(+), 36 deletions(-) diff --git a/file_windows.go b/file_windows.go index 5e43903..51fdf76 100644 --- a/file_windows.go +++ b/file_windows.go @@ -274,7 +274,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) } defer item.Call(item.Release) - var ptr unsafe.Pointer + var ptr win.Pointer hr, _, _ = item.Call(item.GetDisplayName, _SIGDN_FILESYSPATH, uintptr(unsafe.Pointer(&ptr))) if int32(hr) < 0 { @@ -337,7 +337,7 @@ func browseForFolder(opts options) (string, []string, error) { if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() } - if ptr == nil { + if ptr == nullptr { return "", nil, ErrCanceled } defer win.CoTaskMemFree(ptr) diff --git a/internal/win/ole32.go b/internal/win/ole32.go index e925e20..03982f6 100644 --- a/internal/win/ole32.go +++ b/internal/win/ole32.go @@ -31,8 +31,6 @@ 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 IUnknownVtbl struct { @@ -57,4 +55,5 @@ func (o *COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr er } } -//sys CoCreateInstance(clsid uintptr, unkOuter unsafe.Pointer, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance +//sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance +//sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree diff --git a/internal/win/shell32.go b/internal/win/shell32.go index a3ae27b..ca976f2 100644 --- a/internal/win/shell32.go +++ b/internal/win/shell32.go @@ -57,7 +57,7 @@ type _IShellItemVtbl struct { Compare uintptr } -//sys SHBrowseForFolder(bi *BROWSEINFO) (ret unsafe.Pointer) = shell32.SHBrowseForFolder -//sys SHCreateItemFromParsingName(path *uint16, bc unsafe.Pointer, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName +//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 -//sys SHGetPathFromIDListEx(ptr unsafe.Pointer, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx +//sys SHGetPathFromIDListEx(ptr Pointer, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx diff --git a/internal/win/user32.go b/internal/win/user32.go index 90715df..e15e5f5 100644 --- a/internal/win/user32.go +++ b/internal/win/user32.go @@ -214,6 +214,9 @@ const ( SC_MONITORPOWER = 0xf170 SC_CONTEXTHELP = 0xf180 + // SetWindowsHookEx types + WH_CALLWNDPROCRET = 12 + USER_DEFAULT_SCREEN_DPI = 96 DPI_AWARENESS_CONTEXT_UNAWARE = ^uintptr(1) + 1 @@ -350,12 +353,13 @@ type NONCLIENTMETRICS struct { MessageFont LOGFONT } +//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 +//sys DefWindowProc(wnd HWND, msg uint32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.DefWindowProcW //sys DestroyIcon(icon Handle) (err error) = user32.DestroyIcon //sys DestroyWindow(wnd HWND) (err error) = user32.DestroyWindow //sys DispatchMessage(msg *MSG) (ret uintptr) = user32.DispatchMessageW -//sys DefWindowProc(wnd HWND, msg uint32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.DefWindowProcW //sys EnableWindow(wnd HWND, enable bool) (ok bool) = user32.EnableWindow //sys EnumChildWindows(parent HWND, enumFunc uintptr, lparam unsafe.Pointer) = user32.EnumChildWindows //sys EnumWindows(enumFunc uintptr, lparam unsafe.Pointer) (err error) = user32.EnumChildWindows @@ -379,8 +383,10 @@ type NONCLIENTMETRICS struct { //sys setThreadDpiAwarenessContext(dpiContext uintptr) (ret uintptr) = user32.SetThreadDpiAwarenessContext //sys SetWindowLong(wnd HWND, index int, newLong int) (ret int, err error) = user32.SetWindowLongW //sys SetWindowPos(wnd HWND, wndInsertAfter HWND, x int, y int, cx int, cy int, flags int) (err error) = user32.SetWindowPos +//sys SetWindowsHookEx(idHook int, fn uintptr, mod Handle, threadID uint32) (ret Handle, err error) = user32.SetWindowsHookExW //sys SetWindowText(wnd HWND, text *uint16) (err error) = user32.SetWindowTextW //sys ShowWindow(wnd HWND, cmdShow int) (ok bool) = user32.ShowWindow //sys SystemParametersInfo(action int, uiParam uintptr, pvParam unsafe.Pointer, winIni int) (err error) = user32.SystemParametersInfoW //sys TranslateMessage(msg *MSG) (ok bool) = user32.TranslateMessage +//sys UnhookWindowsHookEx(hk Handle) (err error) = user32.UnhookWindowsHookEx //sys UnregisterClass(className *uint16, instance Handle) (err error) = user32.UnregisterClassW diff --git a/internal/win/win32.go b/internal/win/win32.go index 9064c96..2ca0d9a 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 = windows.Pointer +type Pointer uintptr func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) { return windows.RtlGetNtVersionNumbers() diff --git a/internal/win/zsyscall_windows.go b/internal/win/zsyscall_windows.go index d390e21..c2c5c70 100644 --- a/internal/win/zsyscall_windows.go +++ b/internal/win/zsyscall_windows.go @@ -62,10 +62,12 @@ 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") procShell_NotifyIconW = modshell32.NewProc("Shell_NotifyIconW") + procCallNextHookEx = moduser32.NewProc("CallNextHookEx") procCreateIconFromResource = moduser32.NewProc("CreateIconFromResource") procCreateWindowExW = moduser32.NewProc("CreateWindowExW") procDefWindowProcW = moduser32.NewProc("DefWindowProcW") @@ -95,9 +97,11 @@ var ( procSetWindowLongW = moduser32.NewProc("SetWindowLongW") procSetWindowPos = moduser32.NewProc("SetWindowPos") procSetWindowTextW = moduser32.NewProc("SetWindowTextW") + procSetWindowsHookExW = moduser32.NewProc("SetWindowsHookExW") procShowWindow = moduser32.NewProc("ShowWindow") procSystemParametersInfoW = moduser32.NewProc("SystemParametersInfoW") procTranslateMessage = moduser32.NewProc("TranslateMessage") + procUnhookWindowsHookEx = moduser32.NewProc("UnhookWindowsHookEx") procUnregisterClassW = moduser32.NewProc("UnregisterClassW") procWTSSendMessageW = modwtsapi32.NewProc("WTSSendMessageW") ) @@ -195,29 +199,34 @@ func ReleaseActCtx(actCtx Handle) { return } -func CoCreateInstance(clsid uintptr, unkOuter unsafe.Pointer, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) { - r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(clsid), uintptr(unkOuter), uintptr(clsContext), uintptr(iid), uintptr(address), 0) +func CoCreateInstance(clsid uintptr, unkOuter *COMObject, 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) } return } -func SHBrowseForFolder(bi *BROWSEINFO) (ret unsafe.Pointer) { +func CoTaskMemFree(address Pointer) { + 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) - ret = unsafe.Pointer(r0) + ret = Pointer(r0) return } -func SHCreateItemFromParsingName(path *uint16, bc unsafe.Pointer, iid uintptr, item **IShellItem) (res error) { - r0, _, _ := syscall.Syscall6(procSHCreateItemFromParsingName.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(bc), uintptr(iid), uintptr(unsafe.Pointer(item)), 0, 0) +func SHCreateItemFromParsingName(path *uint16, bc *COMObject, 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) } return } -func SHGetPathFromIDListEx(ptr unsafe.Pointer, path *uint16, pathLen int, opts int) (ok bool) { +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) ok = r0 != 0 return @@ -232,6 +241,12 @@ func ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) return } +func CallNextHookEx(hk Handle, code int32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) { + r0, _, _ := syscall.Syscall6(procCallNextHookEx.Addr(), 4, uintptr(hk), uintptr(code), uintptr(wparam), uintptr(lparam), 0, 0) + ret = uintptr(r0) + return +} + func CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, err error) { var _p0 *byte if len(resBits) > 0 { @@ -460,6 +475,15 @@ func SetWindowText(wnd HWND, text *uint16) (err error) { return } +func SetWindowsHookEx(idHook int, fn uintptr, mod Handle, threadID uint32) (ret Handle, err error) { + r0, _, e1 := syscall.Syscall6(procSetWindowsHookExW.Addr(), 4, uintptr(idHook), uintptr(fn), uintptr(mod), uintptr(threadID), 0, 0) + ret = Handle(r0) + if ret == 0 { + err = errnoErr(e1) + } + return +} + func ShowWindow(wnd HWND, cmdShow int) (ok bool) { r0, _, _ := syscall.Syscall(procShowWindow.Addr(), 2, uintptr(wnd), uintptr(cmdShow), 0) ok = r0 != 0 @@ -480,6 +504,14 @@ func TranslateMessage(msg *MSG) (ok bool) { return } +func UnhookWindowsHookEx(hk Handle) (err error) { + r1, _, e1 := syscall.Syscall(procUnhookWindowsHookEx.Addr(), 1, uintptr(hk), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func UnregisterClass(className *uint16, instance Handle) (err error) { r1, _, e1 := syscall.Syscall(procUnregisterClassW.Addr(), 2, uintptr(unsafe.Pointer(className)), uintptr(instance), 0) if r1 == 0 { diff --git a/util_unix.go b/util_unix.go index 26c5c39..f427127 100644 --- a/util_unix.go +++ b/util_unix.go @@ -5,6 +5,7 @@ package zenity import ( "bytes" "os/exec" + "reflect" "strconv" "strings" @@ -98,7 +99,9 @@ func pwdResult(sep string, opts options, out []byte, err error) (string, string, return "", str, err } -func hwnd(i uint64) uintptr { return uintptr(i) } +func hwnd(v reflect.Value) uintptr { + return uintptr(v.Uint()) +} // Replace with strings.Cut after 1.18. func cut(s, sep string) (before, after string, found bool) { diff --git a/util_windows.go b/util_windows.go index 9502e04..0deadfd 100644 --- a/util_windows.go +++ b/util_windows.go @@ -13,15 +13,6 @@ import ( "unsafe" "github.com/ncruces/zenity/internal/win" - "golang.org/x/sys/windows" -) - -var ( - user32 = windows.NewLazySystemDLL("user32.dll") - - callNextHookEx = user32.NewProc("CallNextHookEx") - setWindowsHookEx = user32.NewProc("SetWindowsHookExW") - unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx") ) const ( @@ -32,9 +23,14 @@ 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) } -func hwnd(i uint64) win.HWND { return win.HWND(uintptr(i)) } + +func hwnd(v reflect.Value) win.HWND { + return win.HWND(uintptr(v.Uint())) +} func setup() context.CancelFunc { var wnd win.HWND @@ -99,7 +95,7 @@ type dialogHook struct { ctx context.Context tid uint32 wnd uintptr - hook uintptr + hook win.Handle done chan struct{} icon any title *string @@ -108,8 +104,8 @@ type dialogHook struct { func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd win.HWND)) (*dialogHook, error) { tid := win.GetCurrentThreadId() - hk, _, err := setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET - syscall.NewCallback(dialogHookProc), 0, uintptr(tid)) + hk, err := win.SetWindowsHookEx(win.WH_CALLWNDPROCRET, + syscall.NewCallback(dialogHookProc), 0, tid) if hk == 0 { return nil, err } @@ -154,9 +150,7 @@ func dialogHookProc(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr { } } } - next, _, _ := callNextHookEx.Call( - 0, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam))) - return next + return win.CallNextHookEx(0, code, wparam, unsafe.Pointer(lparam)) } func (h *dialogHook) unhook() { @@ -164,7 +158,7 @@ func (h *dialogHook) unhook() { if h.done != nil { close(h.done) } - unhookWindowsHookEx.Call(h.hook) + win.UnhookWindowsHookEx(h.hook) } func (h *dialogHook) wait() { diff --git a/zenity.go b/zenity.go index d09b997..8056c08 100644 --- a/zenity.go +++ b/zenity.go @@ -201,7 +201,7 @@ func Attach(id any) Option { switch runtime.GOOS { case "windows": if v := reflect.ValueOf(id); v.Kind() == reflect.Uintptr { - id = hwnd(v.Uint()) + id = hwnd(v) } else { panic("interface conversion: expected uintptr") }