diff --git a/color_windows.go b/color_windows.go index 2641afd..53d4eac 100644 --- a/color_windows.go +++ b/color_windows.go @@ -72,6 +72,7 @@ func selectColor(opts options) (color.Color, error) { return color.RGBA{R: r, G: g, B: b, A: 255}, nil } +// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-choosecolorw-r1 type _CHOOSECOLORW struct { StructSize uint32 Owner uintptr diff --git a/entry_windows.go b/entry_windows.go index f1b4a9d..30b4f50 100644 --- a/entry_windows.go +++ b/entry_windows.go @@ -32,13 +32,11 @@ var ( createWindowExW = user32.NewProc("CreateWindowExW") defWindowProcW = user32.NewProc("DefWindowProcW") destroyWindowW = user32.NewProc("DestroyWindow") - dispatchMessageW = user32.NewProc("DispatchMessageW") - getMessageW = user32.NewProc("GetMessageW") - sendMessageW = user32.NewProc("SendMessageW") + dispatchMessage = user32.NewProc("DispatchMessageW") postQuitMessageW = user32.NewProc("PostQuitMessage") registerClassExW = user32.NewProc("RegisterClassExW") unregisterClassW = user32.NewProc("UnregisterClassW") - translateMessageW = user32.NewProc("TranslateMessage") + translateMessage = user32.NewProc("TranslateMessage") getWindowTextLengthW = user32.NewProc("GetWindowTextLengthW") getWindowTextW = user32.NewProc("GetWindowTextW") getWindowLongW = user32.NewProc("GetWindowLongW") @@ -47,7 +45,7 @@ var ( setWindowPosW = user32.NewProc("SetWindowPos") showWindowW = user32.NewProc("ShowWindow") updateWindowW = user32.NewProc("UpdateWindow") - isDialogMessageW = user32.NewProc("IsDialogMessageW") + isDialogMessage = user32.NewProc("IsDialogMessageW") getSystemMetricsW = user32.NewProc("GetSystemMetrics") systemParametersInfoW = user32.NewProc("SystemParametersInfoW") @@ -121,14 +119,14 @@ type wndClassExW struct { iconSm syscall.Handle } -// msgW https://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx -type msgW struct { - hwnd syscall.Handle - message uint32 - wParam uintptr - lParam uintptr - time uint32 - pt pointW +// https://docs.microsoft.com/en-ie/windows/win32/api/winuser/ns-winuser-msg +type _MSG struct { + Owner syscall.Handle + Message uint32 + WParam uintptr + LParam uintptr + Time uint32 + Pt _POINT } // nonClientMetricsW https://msdn.microsoft.com/en-us/library/windows/desktop/ff729175.aspx @@ -168,11 +166,13 @@ type logfontW struct { lfFaceName [32]uint16 } -type pointW struct { +// https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point +type _POINT struct { x, y int32 } -type rectW struct { +// https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect +type _RECT struct { left int32 top int32 right int32 @@ -231,33 +231,10 @@ func unregisterClass(className string, instance syscall.Handle) bool { return ret != 0 } -func getMessage(msg *msgW, hwnd syscall.Handle, msgFilterMin, msgFilterMax uint32) (bool, error) { - ret, _, err := getMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(msgFilterMin), uintptr(msgFilterMax)) - - if int32(ret) == -1 { - return false, err - } - - return int32(ret) != 0, nil -} - -func _sendMessage(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr { - ret, _, _ := sendMessageW.Call(uintptr(hwnd), uintptr(msg), wparam, lparam, 0, 0) - return ret -} - -func dispatchMessage(msg *msgW) { - dispatchMessageW.Call(uintptr(unsafe.Pointer(msg))) -} - func postQuitMessage(exitCode int32) { postQuitMessageW.Call(uintptr(exitCode)) } -func translateMessage(msg *msgW) { - translateMessageW.Call(uintptr(unsafe.Pointer(msg))) -} - func getWindowTextLength(hwnd syscall.Handle) int { ret, _, _ := getWindowTextLengthW.Call(uintptr(hwnd)) return int(ret) @@ -292,7 +269,7 @@ func setWindowLong(hwnd syscall.Handle, index, value int32) int32 { return int32(ret) } -func getWindowRect(hwnd syscall.Handle, rect *rectW) bool { +func getWindowRect(hwnd syscall.Handle, rect *_RECT) bool { ret, _, _ := getWindowRectW.Call(uintptr(hwnd), uintptr(unsafe.Pointer(rect)), 0) return ret != 0 } @@ -313,18 +290,13 @@ func updateWindow(hwnd syscall.Handle) bool { return ret != 0 } -func isDialogMessage(hwnd syscall.Handle, msg *msgW) bool { - ret, _, _ := isDialogMessageW.Call(uintptr(hwnd), uintptr(unsafe.Pointer(msg)), 0) - return ret != 0 -} - func getSystemMetrics(nindex int32) int32 { ret, _, _ := getSystemMetricsW.Call(uintptr(nindex), 0, 0) return int32(ret) } func centerWindow(hwnd syscall.Handle) { - var rc rectW + var rc _RECT getWindowRect(hwnd, &rc) xPos := (getSystemMetrics(smCxScreen) - (rc.right - rc.left)) / 2 yPos := (getSystemMetrics(smCyScreen) - (rc.bottom - rc.top)) / 2 @@ -350,25 +322,24 @@ func registerClass(className string, instance syscall.Handle, fn interface{}) er return err } -func messageLoop(hwnd syscall.Handle) error { +// https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues +func messageLoop(hwnd uintptr) error { for { - msg := msgW{} - gotMessage, err := getMessage(&msg, 0, 0, 0) - if err != nil { + var msg _MSG + ret, _, err := getMessage.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0) + if int32(ret) == -1 { return err } + if ret == 0 { + return nil + } - if gotMessage { - if !isDialogMessage(hwnd, &msg) { - translateMessage(&msg) - dispatchMessage(&msg) - } - } else { - break + ret, _, _ = isDialogMessage.Call(hwnd, uintptr(unsafe.Pointer(&msg)), 0) + if ret == 0 { + translateMessage.Call(uintptr(unsafe.Pointer(&msg))) + dispatchMessage.Call(uintptr(unsafe.Pointer(&msg))) } } - - return nil } // editBox displays textedit/inputbox dialog. @@ -433,17 +404,17 @@ func editBox(title, text, defaultText, className string, password bool) (string, setWindowLong(hwnd, gwlStyle, getWindowLong(hwnd, gwlStyle)^wsMaximizeBox) font := getMessageFont() - _sendMessage(hwndText, wmSetFont, font, 0) - _sendMessage(hwndEdit, wmSetFont, font, 0) - _sendMessage(hwndOK, wmSetFont, font, 0) - _sendMessage(hwndCancel, wmSetFont, font, 0) + sendMessage.Call(uintptr(hwndText), wmSetFont, font, 0) + sendMessage.Call(uintptr(hwndEdit), wmSetFont, font, 0) + sendMessage.Call(uintptr(hwndOK), wmSetFont, font, 0) + sendMessage.Call(uintptr(hwndCancel), wmSetFont, font, 0) centerWindow(hwnd) showWindow(hwnd, swShowNormal) updateWindow(hwnd) - err = messageLoop(hwnd) + err = messageLoop(uintptr(hwnd)) if err != nil { return out, false, err } diff --git a/file_windows.go b/file_windows.go index 1b56b00..57c8b8c 100644 --- a/file_windows.go +++ b/file_windows.go @@ -208,10 +208,10 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if int32(hr) < 0 { return browseForFolder(opts) } - defer dialog.Call(dialog.vtbl.Release) + defer dialog.Call(dialog.Release) var flgs int - hr, _, _ = dialog.Call(dialog.vtbl.GetOptions, uintptr(unsafe.Pointer(&flgs))) + hr, _, _ = dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs))) if int32(hr) < 0 { return "", nil, syscall.Errno(hr) } @@ -221,14 +221,14 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if opts.showHidden { flgs |= 0x10000000 // FOS_FORCESHOWHIDDEN } - hr, _, _ = dialog.Call(dialog.vtbl.SetOptions, uintptr(flgs|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM + hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM if int32(hr) < 0 { return "", nil, syscall.Errno(hr) } if opts.title != nil { ptr := syscall.StringToUTF16Ptr(*opts.title) - dialog.Call(dialog.vtbl.SetTitle, uintptr(unsafe.Pointer(ptr))) + dialog.Call(dialog.SetTitle, uintptr(unsafe.Pointer(ptr))) } if opts.filename != "" { @@ -240,8 +240,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) uintptr(unsafe.Pointer(&item))) if int32(hr) >= 0 && item != nil { - dialog.Call(dialog.vtbl.SetFolder, uintptr(unsafe.Pointer(item))) - item.Call(item.vtbl.Release) + dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item))) + item.Call(item.Release) } } @@ -254,7 +254,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) } activate() - hr, _, _ = dialog.Call(dialog.vtbl.Show, 0) + hr, _, _ = dialog.Call(dialog.Show, 0) if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() } @@ -271,10 +271,10 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if int32(hr) < 0 { return syscall.Errno(hr) } - defer item.Call(item.vtbl.Release) + defer item.Call(item.Release) var ptr uintptr - hr, _, _ = item.Call(item.vtbl.GetDisplayName, + hr, _, _ = item.Call(item.GetDisplayName, 0x80058000, // SIGDN_FILESYSPATH uintptr(unsafe.Pointer(&ptr))) if int32(hr) < 0 { @@ -292,22 +292,22 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) if multi { var items *_IShellItemArray - hr, _, _ = dialog.Call(dialog.vtbl.GetResults, uintptr(unsafe.Pointer(&items))) + hr, _, _ = dialog.Call(dialog.GetResults, uintptr(unsafe.Pointer(&items))) if int32(hr) < 0 { return "", nil, syscall.Errno(hr) } - defer items.Call(items.vtbl.Release) + defer items.Call(items.Release) var count uint32 - hr, _, _ = items.Call(items.vtbl.GetCount, uintptr(unsafe.Pointer(&count))) + hr, _, _ = items.Call(items.GetCount, uintptr(unsafe.Pointer(&count))) if int32(hr) < 0 { return "", nil, syscall.Errno(hr) } for i := uintptr(0); i < uintptr(count) && err == nil; i++ { - err = shellItemPath(&items._COMObject, items.vtbl.GetItemAt, i) + err = shellItemPath(&items._COMObject, items.GetItemAt, i) } } else { - err = shellItemPath(&dialog._COMObject, dialog.vtbl.GetResult) + err = shellItemPath(&dialog._COMObject, dialog.GetResult) } return } @@ -387,6 +387,7 @@ func initFilters(filters []FileFilter) []uint16 { return res } +// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew type _OPENFILENAME struct { StructSize uint32 Owner uintptr @@ -413,6 +414,7 @@ type _OPENFILENAME struct { FlagsEx uint32 } +// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow type _BROWSEINFO struct { Owner uintptr Root uintptr @@ -424,9 +426,7 @@ type _BROWSEINFO struct { Image int32 } -func uuid(s string) uintptr { - return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data -} +// 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") @@ -436,17 +436,17 @@ var ( type _IFileOpenDialog struct { _COMObject - vtbl *_IFileOpenDialogVtbl + *_IFileOpenDialogVtbl } type _IShellItem struct { _COMObject - vtbl *_IShellItemVtbl + *_IShellItemVtbl } type _IShellItemArray struct { _COMObject - vtbl *_IShellItemArrayVtbl + *_IShellItemArrayVtbl } type _IFileOpenDialogVtbl struct { diff --git a/notify_windows.go b/notify_windows.go index d3f4afc..0c545bd 100644 --- a/notify_windows.go +++ b/notify_windows.go @@ -97,20 +97,21 @@ func wtsMessage(text string, opts options) error { return nil } +// https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw type _NOTIFYICONDATA struct { StructSize uint32 - Owner uintptr + Wnd uintptr ID uint32 Flags uint32 CallbackMessage uint32 Icon uintptr - Tip [128]uint16 + Tip [128]uint16 // NOTIFYICONDATAA_V1_SIZE State uint32 StateMask uint32 Info [256]uint16 Version uint32 InfoTitle [64]uint16 InfoFlags uint32 - // GuidItem [16]byte - // BalloonIcon uintptr + // GuidItem [16]byte // NOTIFYICONDATAA_V2_SIZE + // BalloonIcon syscall.Handle // NOTIFYICONDATAA_V3_SIZE } diff --git a/util_windows.go b/util_windows.go index ec42e80..76a62e0 100644 --- a/util_windows.go +++ b/util_windows.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "reflect" "sync" "syscall" "unsafe" @@ -27,6 +28,7 @@ var ( coCreateInstance = ole32.NewProc("CoCreateInstance") coTaskMemFree = ole32.NewProc("CoTaskMemFree") + getMessage = user32.NewProc("GetMessageW") sendMessage = user32.NewProc("SendMessageW") getClassName = user32.NewProc("GetClassNameW") setWindowsHookEx = user32.NewProc("SetWindowsHookExW") @@ -68,6 +70,7 @@ func commDlgError() error { } } +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct type _CWPRETSTRUCT struct { Result uintptr LParam uintptr @@ -149,6 +152,18 @@ func hookDialogTitle(ctx context.Context, title *string) (unhook context.CancelF return hookDialog(ctx, init) } +// 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 +} + type _COMObject struct{} func (o *_COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) { @@ -165,9 +180,3 @@ func (o *_COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr e panic("COM call with too many arguments.") } } - -type _IUnknownVtbl struct { - QueryInterface uintptr - AddRef uintptr - Release uintptr -}