diff --git a/comboboxes.go b/comboboxes.go new file mode 100644 index 0000000..77b7046 --- /dev/null +++ b/comboboxes.go @@ -0,0 +1,83 @@ +// 10 february 2014 +package main + +import ( +// "syscall" +// "unsafe" +) + +// Combobox styles. +const ( + // from winuser.h + CBS_SIMPLE = 0x0001 + CBS_DROPDOWN = 0x0002 + CBS_DROPDOWNLIST = 0x0003 + CBS_OWNERDRAWFIXED = 0x0010 + CBS_OWNERDRAWVARIABLE = 0x0020 + CBS_AUTOHSCROLL = 0x0040 + CBS_OEMCONVERT = 0x0080 + CBS_SORT = 0x0100 + CBS_HASSTRINGS = 0x0200 + CBS_NOINTEGRALHEIGHT = 0x0400 + CBS_DISABLENOSCROLL = 0x0800 + CBS_UPPERCASE = 0x2000 + CBS_LOWERCASE = 0x4000 +) + +// Combobox messages. +// TODO filter out messages not provided in windows 2000 +const ( + // from winuser.h + CB_GETEDITSEL = 0x0140 + CB_LIMITTEXT = 0x0141 + CB_SETEDITSEL = 0x0142 + CB_ADDSTRING = 0x0143 + CB_DELETESTRING = 0x0144 + CB_DIR = 0x0145 + CB_GETCOUNT = 0x0146 + CB_GETCURSEL = 0x0147 + CB_GETLBTEXT = 0x0148 + CB_GETLBTEXTLEN = 0x0149 + CB_INSERTSTRING = 0x014A + CB_RESETCONTENT = 0x014B + CB_FINDSTRING = 0x014C + CB_SELECTSTRING = 0x014D + CB_SETCURSEL = 0x014E + CB_SHOWDROPDOWN = 0x014F + CB_GETITEMDATA = 0x0150 + CB_SETITEMDATA = 0x0151 + CB_GETDROPPEDCONTROLRECT = 0x0152 + CB_SETITEMHEIGHT = 0x0153 + CB_GETITEMHEIGHT = 0x0154 + CB_SETEXTENDEDUI = 0x0155 + CB_GETEXTENDEDUI = 0x0156 + CB_GETDROPPEDSTATE = 0x0157 + CB_FINDSTRINGEXACT = 0x0158 + CB_SETLOCALE = 0x0159 + CB_GETLOCALE = 0x015A + CB_GETTOPINDEX = 0x015B + CB_SETTOPINDEX = 0x015C + CB_GETHORIZONTALEXTENT = 0x015D + CB_SETHORIZONTALEXTENT = 0x015E + CB_GETDROPPEDWIDTH = 0x015F + CB_SETDROPPEDWIDTH = 0x0160 + CB_INITSTORAGE = 0x0161 + CB_MULTIPLEADDSTRING = 0x0163 + CB_GETCOMBOBOXINFO = 0x0164 +) + +// Combobox WM_COMMAND notificaitons. +const ( + // from winuser.h + CBN_ERRSPACE = (-1) // TODO this will blow up the Go compiler if it's used + CBN_SELCHANGE = 1 + CBN_DBLCLK = 2 + CBN_SETFOCUS = 3 + CBN_KILLFOCUS = 4 + CBN_EDITCHANGE = 5 + CBN_EDITUPDATE = 6 + CBN_DROPDOWN = 7 + CBN_CLOSEUP = 8 + CBN_SELENDOK = 9 + CBN_SELENDCANCEL = 10 +) diff --git a/common.go b/common.go index 4505397..a51583d 100644 --- a/common.go +++ b/common.go @@ -3,8 +3,11 @@ package main import ( "syscall" + "unsafe" ) +// TODO filter out commctrl.h stuff because the combobox stuff has values that differ between windows versions and bleh + var ( user32 = syscall.NewLazyDLL("user32.dll") kernel32 = syscall.NewLazyDLL("kernel32.dll") @@ -37,7 +40,30 @@ func (w WPARAM) HIWORD() uint16 { return uint16((w >> 16) & 0xFFFF) } +func LPARAMFromString(str string) LPARAM { + return LPARAM(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) +} + // microsoft's header files do this func MAKEINTRESOURCE(what uint16) uintptr { return uintptr(what) } + +// TODO adorn error messages with which step failed? +func getText(hwnd HWND) (text string, err error) { + var tc []uint16 + + length, err := SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0) + if err != nil { + return "", err + } + tc = make([]uint16, length + 1) + _, err = SendMessage(hwnd, + WM_GETTEXT, + WPARAM(length + 1), + LPARAM(unsafe.Pointer(&tc[0]))) + if err != nil { + return "", err + } + return syscall.UTF16ToString(tc), nil +} diff --git a/main.go b/main.go index dd82c13..b551b40 100644 --- a/main.go +++ b/main.go @@ -21,17 +21,47 @@ func fatalf(format string, args ...interface{}) { const ( IDC_BUTTON = 100 + IDC_VARCOMBO = 101 + IDC_FIXCOMBO = 102 ) +var varCombo, fixCombo HWND + func wndProc(hwnd HWND, msg uint32, wParam WPARAM, lParam LPARAM) LRESULT { switch msg { case WM_COMMAND: if wParam.LOWORD() == IDC_BUTTON { + buttonclick := "neither clicked nor double clicked (somehow)" if wParam.HIWORD() == BN_CLICKED { - MessageBox(hwnd, "clicked", "", MB_OK) + buttonclick = "clicked" } else if wParam.HIWORD() == BN_DOUBLECLICKED { - MessageBox(hwnd, "double clicked", "", MB_OK) + buttonclick = "double clicked" } + + varText, err := getText(varCombo) + if err != nil { + fatalf("error getting variable combo box text: %v", err) + } + + fixTextWM, err := getText(fixCombo) + if err != nil { + fatalf("error getting fixed combo box text with WM_GETTEXT: %v", err) + } + + fixTextIndex, err := SendMessage(fixCombo, CB_GETCURSEL, 0, 0) + if err != nil { + fatalf("error getting fixed combo box current selection: %v", err) + } + // TODO get text from index + + MessageBox(hwnd, + fmt.Sprintf("button state: %s\n" + + "variable combo box text: %s\n" + + "fixed combo box text with WM_GETTEXT: %s\n" + + "fixed combo box current index: %d\n", + buttonclick, varText, fixTextWM, fixTextIndex), + "note", + MB_OK) } return 0 case WM_CLOSE: @@ -103,12 +133,48 @@ func main() { 0, "BUTTON", "Click Me", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, - 20, 20, 100, 100, + 20, 20, 100, 20, hwnd, HMENU(IDC_BUTTON), hInstance, NULL) if err != nil { fatalf("error creating button: %v", err) } + varCombo, err = CreateWindowEx( + 0, + "COMBOBOX", "", + CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_TABSTOP, + 140, 20, 100, 20, + hwnd, HMENU(IDC_VARCOMBO), hInstance, NULL) + if err != nil { + fatalf("error creating variable combo box: %v", err) + } + vcItems := []string{"a", "b", "c", "d"} + for _, v := range vcItems { + _, err := SendMessage(varCombo, CB_ADDSTRING, 0, + LPARAMFromString(v)) + if err != nil { + fatalf("error adding %q to variable combo box: %v", v, err) + } + } + + fixCombo, err = CreateWindowEx( + 0, + "COMBOBOX", "", + CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_TABSTOP, + 140, 50, 100, 20, + hwnd, HMENU(IDC_FIXCOMBO), hInstance, NULL) + if err != nil { + fatalf("error creating fixed combo box: %v", err) + } + fcItems := []string{"e", "f", "g", "h"} + for _, v := range fcItems { + _, err := SendMessage(fixCombo, CB_ADDSTRING, 0, + LPARAMFromString(v)) + if err != nil { + fatalf("error adding %q to fixed combo box: %v", v, err) + } + } + _, err = ShowWindow(hwnd, nCmdShow) if err != nil { fatalf("error showing window: %v", err) diff --git a/messages.go b/messages.go index 4459035..e74b9fa 100644 --- a/messages.go +++ b/messages.go @@ -6,6 +6,11 @@ import ( "unsafe" ) +// SendMessage constants. +const ( + HWND_BROADCAST = HWND(0xFFFF) +) + type MSG struct { Hwnd HWND Message uint32 @@ -19,6 +24,7 @@ var ( dispatchMessage = user32.NewProc("DispatchMessageW") getMessage = user32.NewProc("GetMessageW") postQuitMessage = user32.NewProc("PostQuitMessage") + sendMessage = user32.NewProc("SendMessageW") translateMessage = user32.NewProc("TranslateMessage") ) @@ -49,6 +55,16 @@ func PostQuitMessage(nExitCode int) (err error) { return nil } +// TODO handle errors +func SendMessage(hWnd HWND, Msg uint32, wParam WPARAM, lParam LPARAM) (result LRESULT, err error) { + r1, _, _ := sendMessage.Call( + uintptr(hWnd), + uintptr(Msg), + uintptr(wParam), + uintptr(lParam)) + return LRESULT(r1), nil +} + // TODO handle errors func TranslateMessage(lpMsg *MSG) (translated bool, err error) { r1, _, _ := translateMessage.Call(uintptr(unsafe.Pointer(lpMsg)))