发送消息,修改其他程序的下拉框的选择。
先说结论
public void SelectItem(int index)
{
COMBOBOXINFO cbi = new COMBOBOXINFO();
cbi.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(cbi);
if (User32.GetComboBoxInfo(Wnd, ref cbi))
{
User32.SendMessage(Wnd, ComboBoxMsg.CB_SETCURSEL, index, 0);
int cbId = User32.GetDlgCtrlID(cbi.hwndList);
int wParam = WordHelper.GetWord(ComboBoxMsg.CBN_SELCHANGE, cbId);
User32.SendMessage(Wnd, Constant_WM.WM_COMMAND, wParam, (int)cbi.hwndList);
}
}
User32是一个封装了User32.dll里面的API函数的静态类,嘛,学习这方面的人都会有吧。
摸索的过程费了点时间,希望“后来人”搜索的时候可以找到这里,记录一下过程。
我以前就简单的学过一点MFC编程,现在用的是c#。估计对于原本知道的人来说这些(像是GetComboBoxInfo)都是理所当然的,没必要特别写出来的吧。
1. 先是百度一下,很快找到信息
Windows消息之ComboBox
发假消息让下拉列表菜单选中, 但是只有第一句的时候不能激发select change,很多业务逻辑耦合在这个上面
所以还要人为的做出select change的消息
PostMessage(comboBoxHWND, CB_SETCURSEL, selectItem, 0);
PostMessage(targetHWND,WM_COMMAND, MAKEWPARAM(GetWindowLong(comboBoxHWND, GWL_ID), CBN_SELCHANGE), (LPARAM)comboBoxHWND);
和
c#通过SendMessage发送消息,改变其他程序的下拉框控件(ComboBox)的值
IntPtr mwh = (IntPtr)Convert.ToInt32(handle); //ComboBox的句柄
int result = SendMessage(mwh, 0x014D, -1, selectStr); //改变ComboBox的值,selectStr为预期的下拉框选项
int mwh_p = GetWindowLong(mwh, -8); //获取ComboBox所属窗口的句柄
IntPtr mwh2 = (IntPtr)Convert.ToInt32(mwh_p); //转换ComboBox所属窗口的句柄
string cbn_selchange;
int cb_id = GetWindowLong(mwh, -12); //获取ComboBox的控件ID
cbn_selchange = "0001" + string.Format("{0:X4}", cb_id);
IntPtr s1 = (IntPtr)Convert.ToInt32(Tools.ToD(cbn_selchange, 16));
SendMessage(mwh2, 0x0111, s1, mwh); //给ComboBox所属窗口发送WM_COMMAND命令,第3个参数wParam是(CBN_SELCHANGE(高位) + 控件ID(低位))
SendMessage(mwh2, 0x0111, s1, mwh);可以参考SPY++捕捉的消息日志
GetWindowLong的参数可以参考:
http://blog.csdn.net/hnhyhongmingjiang/archive/2008/03/06/2154410.aspx
SendMessage的参数可以参考:
http://topic.csdn.net/t/20050713/18/4142641.html
我按照自己的程序进行了一下修改
ntPtr mwh = Wnd;
User32.SendMessage(mwh, ComboBoxMsg.CB_SETCURSEL, index, 0);
int mwh_p = User32.GetWindowLong(mwh, GetWindowLongFlag.GWL_HWNDPARENT);
IntPtr mwh2 = (IntPtr)Convert.ToInt32(mwh_p);
string cbn_selchange;
nt cb_id = User32.GetWindowLong(mwh, GetWindowLongFlag.GWL_ID);
int s1 = WordHelper.GetWord(ComboBoxMsg.CBN_SELCHANGE, cb_id);
User32.SendMessage(mwh2, Constant_WM.WM_COMMAND, s1, (int)mwh);
结果失败,最后一个消息没有效果。
第一个CB_SETCURSEL消息只是修改了下拉框的显示,并没有触发SelectChange事件,程序没有实质上的变化。
第二个消息就是要触发事件。
2. 用Spy++查看消息,发现
<00377> 000C0BFA S WM_COMMAND wNotifyCode:0001 wID:1000 hwndCtl:001B08E4
发现修改成
User32.SendMessage(mwh, Constant_WM.WM_COMMAND, 1000, 110734);
一种110734是001B08E4的十进制数
有效果!
说明前面说的SendMessage第一个参数错了,应该是ComboBox本身的句柄而不是父窗体,在之前查找ToolBar发送消息点击按钮时也碰到过。
3. 然后在Spy++中001B08E4是一个ComboLBox的类,其父是桌面。
寻找ComboLBox的信息,得到:
ComboLBox :The class for the list box contained in a combo box.
In Windows(, before a new type of window is created, it must register a special class name to the system. Every window has its own class name, which could be used to tell the window‘s type. In the case of combo box, its edit box‘s class name is "Edit" and its list box‘s class name is "ComboLBox". Please note that this class name has nothing to do with MFC classes. It is used by the operating system to identify the window types rather than a programming implementation.
如何从ComboBox的到ComboLBox的句柄就卡住了,查询ComboLBox得不到有用的信息,不过有个地方提到它的句柄和ListBox的句柄一样。
用中文“获取Combo里面的Listbox的句柄”不行,用“获取ComboBox的Listbox的句柄”也不行。
4. 用“get listbox in combobox”找到了!(google连不上,翻墙不知道现在方便不,就用必应搜索了)
Win32: How do I get the listbox for a combobox
I‘m writing some code and I need to get the window handle of the listbox associated with a combo box. When looking in spy++, it looks like the parent of the listbox is the desktop, not the combo box. How can I programmatically find the listbox window handle?
Answer
Send CB_GETCOMBOBOXINFO to the combo box, and it fills in a COMBOBOXINFO structure, which contains the window handles to the edit control and the list box control.
接下来搜索CB_GETCOMBOBOXINFO 和COMBOBOXINFO,找到了发送消息和调用User32函数GetComboBoxInfo两种方法,发送消息没具体研究,我使用的是GetComboBoxInfo。
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO {
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
public enum ComboBoxButtonState {
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
COMBOBOXINFO cbi = new COMBOBOXINFO();
cbi.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(cbi);
if(GetComboBoxInfo(comboBox1.Handle, ref cbi)) {
if(cbi.hwndEdit == IntPtr.Zero) {
throw new Exception("ComboBox must have the DropDown style!");
}
}
上面是从pinvoke上找的,pinvoke真是方便,好想有个脱机版本!(工作的电脑无法上网!)
最终我测试成功的代码就是开头的代码。