package zenity import ( "runtime" "syscall" "unsafe" ) var ( messageBox = user32.NewProc("MessageBoxW") ) func message(kind messageKind, text string, options []Option) (bool, error) { opts := applyOptions(options) var flags uintptr switch { case kind == questionKind && opts.extraButton != "": flags |= 0x3 // MB_YESNOCANCEL case kind == questionKind || opts.extraButton != "": flags |= 0x1 // MB_OKCANCEL } switch opts.icon { case ErrorIcon: flags |= 0x10 // MB_ICONERROR case QuestionIcon: flags |= 0x20 // MB_ICONQUESTION case WarningIcon: flags |= 0x30 // MB_ICONWARNING case InfoIcon: flags |= 0x40 // MB_ICONINFORMATION } if kind == questionKind && opts.defaultCancel { if opts.extraButton == "" { flags |= 0x100 // MB_DEFBUTTON2 } else { flags |= 0x200 // MB_DEFBUTTON3 } } if opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" { runtime.LockOSThread() defer runtime.UnlockOSThread() hook, err := hookMessageLabels(kind, opts) if hook == 0 { return false, err } defer unhookWindowsHookEx.Call(hook) } s, _, err := messageBox.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title))), flags) if s == 0 { return false, err } if s == 7 || s == 2 && kind != questionKind { // IDNO return false, ErrExtraButton } if s == 1 || s == 6 { // IDOK, IDYES return true, nil } return false, nil } func hookMessageLabels(kind messageKind, opts options) (hook uintptr, err error) { tid, _, _ := getCurrentThreadId.Call() hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET syscall.NewCallback(func(code int, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr { if lparam.Message == 0x0110 { // WM_INITDIALOG name := [7]uint16{} getClassName.Call(lparam.Wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name))) if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box enumChildWindows.Call(lparam.Wnd, syscall.NewCallback(func(wnd, lparam uintptr) uintptr { name := [7]uint16{} getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name))) if syscall.UTF16ToString(name[:]) == "Button" { ctl, _, _ := getDlgCtrlID.Call(wnd) var text string switch ctl { case 1, 6: // IDOK, IDYES text = opts.okLabel case 2: // IDCANCEL if kind == questionKind { text = opts.cancelLabel } else if opts.extraButton != "" { text = opts.extraButton } else { text = opts.okLabel } case 7: // IDNO text = opts.extraButton } if text != "" { ptr := syscall.StringToUTF16Ptr(text) setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr))) } } return 1 }), 0) } } next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam))) return next }), 0, tid) return }