使用 FindNextWindow 找到目标窗体里的 LIST_VIEW 控件,然后发送 LB_SETSEL 消息。
LB_SETSEL是用于设置选择状态吧?
我主要是想单击Listview下的子项目,也是发送这个消息吗?
我还没试
@MarcoFly: WM_LBUTTONDOWN
@Launcher:
WM_LBUTTONDOWN只能触发listview控件的吧?
我是想单击listview控件中的“每一项文本内容”
@MarcoFly: 消息流程处理是这样的,鼠标点击后,listview 会收到 WM_LBUTTONDOWN 消息,然后通过鼠标坐标计算出点击在了哪个ITEM上。
我没做过测试,你可以自己测试下,在发送WM_LBUTTONDOWN 消息的时候伪造一个鼠标坐标的值来传入。
@Launcher: 我没有表达清楚。我只想通过一个循环处理,就能将每一listview的子项都让其响应“单击事件”,因为有滚动条的缘故(部分子项需要拉动滚动条才能显示处理),根据坐标去处理似乎不可行
@Launcher: 多谢你的回答,如果问题解决了,定会再追加分数的,谢谢了
@MarcoFly: 你到底是想通过代码模拟鼠标点击,还是想编写一个处理函数来处理item被点击时的事件?
@Launcher: 就是想写一个处理函数,该函数的功能是 让指定的listview控件下(如:任务管理器)的每一行都绑定一个鼠标单击的事件,让该消息进入消息队列中,响应事件在其他程序中会去响应。
总之,我的需求是:将listview控件下的每一行都“点击”(不是模拟鼠标去点击)一次
@MarcoFly: 不一定非要点击事件吧,事实上,你所谓的“点击事件”,在listview中是通过处理WM_NOTIFY消息来实现的,也就是NM_CLICK事件,不是WM_LBUTTONDOWN(这里只有DOWN,没有UP,只有在UP后鼠标仍然在ITEM上时,才会派发NM_CLICK事件)。
你应该写一个HOOK程序,拦截任务管理器的列表控件的WM_NOTIFY消息,然后处理NM_CLICK事件(类似Spy++的功能)
然后回到你的需求,如何让listview控件下的每一行都“点击”(不是模拟鼠标去点击),我感觉这无法实现。你这个应该是你提出的解决方案,而非你的需求,你应该把你为什么要这么做的原因写出来,或许实现方法并不是你想的这么做的。
。
@Launcher: 嗯,多谢你的回答。
原本的需求是这样子的:一个listview控件和一个combox控件,需要将listview控件中的所有内容(子项)全部add到combox中去。
原本手工操作时是需要 “单击一下子项” ,就会自动将该子项add到combox中去。现在想让其自动化操作,一次性将listview下的所有子项都添加到combox中去,提高效率。
@MarcoFly: 这多简单啊,咋被你搞的那么复杂呢。
GetRowCount 获取 listView 有多少行
然后for 循环,用 GetCell 取值不就行了。
@Launcher:
问题就出在这儿,如果将我们自己获取的内容add到combox中,会无法使用(不知道他内部程序是怎么去实现的)
所以目前只能采用单击子项,让他们的程序自动把我们所单击的子项添加到combox中
-------------------
补充:
1.我提升积分了
2.这是前天实现的跨进程获取listview控件下的所有子项信息(http://www.cnblogs.com/hongfei/archive/2012/12/24/2829799.html)
现在想在获取的同时(for循环中),同时给当前的子项发送一个“鼠标单击”消息?
大虾,请问该怎么去实现呢?
API接触不多,搞了好久,如果能帮忙搞定,再提升积分,谢谢了
@MarcoFly: 啥叫无法使用?
@Launcher:
就是A软件的界面中有个按钮(将数据入库),每点击一次,就会将当前选中的下拉列表的值写入数据库(估计是:隐藏id+产品名称),但是我如果自己处理将listview中的子项加入到下拉列表中的时候,当选中某个产品,再点击入库,这时候是无法入库的,这就是问题所在
@Launcher:
主要是我不清楚 他的 下拉列表中 是以什么字段来存储信息的
@MarcoFly: 是不是你已经获取到了它的界面显示的出来的数据,并把这些数据显示到了你的进程的控件上,但是你再用你的控件上的数据去插入数据库时,就失败了?
我是不是可以推断出你用你的控件上的数据去插入的时候,实际上是少了某些字段的值所以才无法插入的?
@Launcher:
嗯 对,正是这样的
现在 你能看下该怎么在我之前获取数据的代码中 再稍加修改,是每次获取一条数据的同时,也给让该数据所在的行被单击呢?
@Launcher:
这是获取数据的关键代码
@MarcoFly: 应该是把结构体指针付给了 lParam ,每次它会从 lParam 中取数据来插入。你能不能用Spy++监控一下它的消息处理,看看它是在NM_CLICK,还是在 LVN_ITEMCHANGED 中处理的。
@Launcher:
单击的时候 没有触发WM_NOTIFY,而你收的LVN_ITEMCHANGED,好像没看到有这个选项
只有在将鼠标移动到新的一行的时候,才会出发WM_NOTIFY
@MarcoFly:我看了你写的代码,估计你对Windows控件应该比较熟悉。所以我就讲下我的分析,一般会用NM_CLICK(此通知报告的项有可能不准确,我们会用HITTEST来得到正确的值)和LVN_ITEMCHANGED来处理项选中后的操作。当鼠标单击时肯定会触发NM_CLICK(无论当前项是否已经被选中),但是只有当选中项改变时才会触发LVN_ITEMCHANGED。那么我的想法是,通过你已经得到的项的信息来模拟NM_CLICK或LVN_ITEMCHANGED通知,让它的控件在这两个或其中一个通知消息上挂接的处理方法执行。
//NM_CLICK
NMLISTVIEW nmlv={0};
SendMessage(hWnd,WM_NOTIFY,(WPARAM)nmlv.hdr.idFrom,(LPARAM)(&nmlv));
//LVN_ITEMCHANGED
NMITEMACTIVATE nmav={0};
SendMessage(hWnd,WM_NOTIFY,(WPARAM)nmav.hdr.idFrom,(LPARAM)(&nmav));
通常我们不会在LIST_VIEW控件内部来处理 WM_NOTIFY 消息,而是在其父窗体中处理,所以上面代码中的 hWnd 并不一定就是LIST_VIEW控件,也可能是其父窗体。换句话说,插入数据库的代码最可有可能出现在父窗体中。
你可以通过SPY++,或者通过代码来测试出它正确的行为。
@MarcoFly:补充一点:NM_CLICK和LVN_ITEMCHANGED通知代码是通过 hdr.code 来获取的。也就是说收到 WM_NOTIFY 消息后,需要把 lParam 参数转化为 NMHDR(如:NMHDR *pNMHDR = reinterpret_cast<NMHDR*>(lParam)),然后通过 pNMHDR->code 等于 NM_CLICK或LVN_ITEMCHANGED来再分别转换为 NMLISTVIEW 或 NMITEMACTIVATE 结构。
@Launcher:
你那有类似的代码吗?参考下,刚刚搞得有点头绪了。
你上面的指示,我想实现出来,感觉有点懵。自己写了段程序一直在调试,就是调试不出来。
private string[,] GetListViewItmeValue(int rows, int cols) { string[,] tempStr = new string[rows, cols];//二维数组:保存LV控件的文本信息 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { byte[] vBuffer = new byte[256];//定义一个临时缓冲区 int vNumberOfBytesRead = 0; NMLISTVIEW[] data1 = new NMLISTVIEW[1]; NMITEMACTIVATE[] data = new NMITEMACTIVATE[1]; WriteProcessMemory(process, pointer, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), Marshal.SizeOf(typeof(NMITEMACTIVATE)), ref vNumberOfBytesRead); const int NM_FIRST = 0; const int NM_CLICK = NM_FIRST-2; const int LVN_FIRST = -100; const int LVN_ITEMCHANGED=(LVN_FIRST-1); const int BM_CLICK = 0x00F5; const int WM_NOTIFY = 78; const int WM_CAPTURECHANGED=533; const int WM_SETFOCUS = 7; const int WM_LBUTTONDOWN=513; const int WM_LBUTTONUP=514; hwnd = 789788; int buttonHandle = 1051934; data[0].hdr.code = NM_CLICK; data[0].hdr.hwndFrom = hwnd; data[0].hdr.idFrom = 1000; data[0].iItem = i; //SendMessage(buttonHandle,BM_CLICK,0,0);//可以触发BM_CLICK事件 //SendMessage(hwnd,WM_CAPTURECHANGED, 0, 0);//可以响应 //SendMessage(hwnd, WM_SETFOCUS, 0, 0);//可以相应 SendMessage(593170,WM_NOTIFY, data[0].hdr.idFrom, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0).ToInt32()); //从pointer指向的内存地址开始读取数据,写入缓冲区vBuffer中 //ReadProcessMemory(process, ((int)pointer + Marshal.SizeOf(typeof(LVITEM))), Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), vBuffer.Length, ref vNumberOfBytesRead); ReadProcessMemory(process, ((int)pointer + Marshal.SizeOf(typeof(NMITEMACTIVATE))), Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), vBuffer.Length, ref vNumberOfBytesRead); string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead); ; tempStr[i, j] = vText; } } VirtualFreeEx(process, pointer, 0, MEM_RELEASE);//在其它进程中释放申请的虚拟内存空间,MEM_RELEASE方式很彻底,完全回收 CloseHandle(process);//关闭打开的进程对象 return tempStr; }
@Launcher:
补充下,按照你说的,notify消息是在其父窗口中去响应的,我用spy测试过了,可以的。
但是晚上写的那段程序,出问题了,连父窗口都无法去响应,我知道肯定是哪里写错了,但就是检查不出来,被各种API函数搞得头大了
@MarcoFly: 没有现成的代码,你等我写两个程序测试下。
@Launcher:
好的,麻烦你了。现在似乎快成功了,我先去把那几个参数搞清楚下,
@MarcoFly: 我简单测试了下,LVN_ITEMCHANGED 在实际编程时是无法用来确定单击了哪行的,所以应该是用 NM_CLICK,我在进程内通过下面的代码正确触发了父窗体的处理过程:
NMITEMACTIVATE nmia = {0}; nmia.hdr.hwndFrom = GetDlgItem(IDC_LIST1)->m_hWnd; // SysListView32 控件的句柄 nmia.hdr.idFrom = IDC_LIST1; // SysListView32 控件的 ID nmia.hdr.code = NM_CLICK; // 通知代码 nmia.iItem = 3; // 单击的行的序号 nmia.iSubItem = 0; // 单击的项的序号,设置为 0 即可。 // 下面都设置为 0 即可。 nmia.uNewState = 0; nmia.uOldState = 0; nmia.uChanged = 0; nmia.ptAction.x = 0; nmia.ptAction.y = 0; nmia.lParam = 0; nmia.uKeyFlags = 0; // m_hWnd 为 SysListView32 控件的父窗口句柄。 ::SendMessage(m_hWnd,WM_NOTIFY,(WPARAM)nmia.hdr.idFrom,(LPARAM)(&nmia));
@Launcher:
SendMessage(m_hWnd,WM_NOTIFY,(WPARAM)nmia.hdr.idFrom,(LPARAM)(&nmia))
后面的两个参数必须得是WPARM和LPARM吗?
我C#写的,两个参数都写成int的,之前在别的地方都是可以用的
@MarcoFly: x86的话是 unsigned int ,x64的话是 unsigned __int64。此处的 x86和x64是指待编译时设置的选项,非实际运行时的OS。
@Launcher:
我参照你的写法,不知道哪里错了,能帮忙看下吗?
完成后,我一定再加分的,真的很感谢!
代码:
[DllImport("user32.DLL")] private static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam); private struct NMHDR{ public int hwndFrom; public int idFrom; public int code; } private struct NMITEMACTIVATE{ public NMHDR hdr; public int iItem; public int iSubItem; public int uNewState; public int uOldState; public int uChanged; public Point ptAction; public string lParam; public int uKeyFlags; }
NMITEMACTIVATE[] data = new NMITEMACTIVATE[1];
hwnd = 394998; //主窗口(Form1)句柄 data[0].hdr.code = NM_CLICK;//通知代码 data[0].hdr.hwndFrom = 198392;//listview控件的句柄 data[0].hdr.idFrom = 198392;//listview控件的id data[0].iItem = 3;//单击的行号 data[0].iSubItem = 0;//单击的项的序号 data[0].uNewState = 0; data[0].uOldState = 0; data[0].uChanged = 0; data[0].ptAction.X = 0; data[0].ptAction.Y = 0; data[0].lParam = null; data[0].uKeyFlags = 0; SendMessage( hwnd, //主窗口(即Form1)的句柄 WM_NOTIFY, data[0].hdr.idFrom, //listview控件的句柄 Convert.ToUInt32(Marshal.UnsafeAddrOfPinnedArrayElement(data, 0))//data变量的地址 );
@MarcoFly: 你的目标程序是WinForm的吗?是不是可以直接反编译它的源码?
我测试用的目标窗体是MFC的,我是不是要把目标窗体换成 WinForm的测试下?
@Launcher:
嗯,我的是winform的,对MFC不熟悉,
如果你可以用winform的也测试下的话,那真的太感谢你了
@Launcher:
补充:这个常量不懂有没有错,在c++中是这样定义的
#define NM_FIRST (0U- 0U) // generic to all controls
#define NM_CLICK (NM_FIRST-2) // uses NMCLICK struct
我是这样转换的:
const int NM_FIRST = 0;
const int NM_CLICK = NM_FIRST-2;
@Launcher:
现在是这样的,代码也能照常执行,但是没有触发WM_NOTIFY消息
NMITEMACTIVATE[] data = new NMITEMACTIVATE[1]; hwnd = 2296236; //主窗口(Form1)句柄 data[0].hdr.code = NM_CLICK;//通知代码 data[0].hdr.hwndFrom = hwnd;//listview控件的句柄 data[0].hdr.idFrom = 1572976;//listview控件的id data[0].iItem = 3;//单击的行号 data[0].iSubItem = 0;//单击的项的序号 data[0].uNewState = 0; data[0].uOldState = 0; data[0].uChanged = 0; data[0].ptAction.X = 0; data[0].ptAction.Y = 0; data[0].lParam = null; data[0].uKeyFlags = 0; SendMessage( hwnd, //主窗口(即Form1)的句柄 WM_NOTIFY, data[0].hdr.idFrom, //listview控件的句柄 (uint)Marshal.UnsafeAddrOfPinnedArrayElement(data, 0)//data变量的地址 //Convert.ToUInt32(Marshal.UnsafeAddrOfPinnedArrayElement(data, 0)) );
没有捕捉到wm_notify消息
@MarcoFly: 非常遗憾,WM_NOTIFY 不能跨进程发送,陷入死胡同了。
@Launcher:
但是我如果采用注入呢?
注入应该是可以的吧?
@MarcoFly: 注入应该可以,你可以测试下。
@Launcher:
你好,我给你100分,你能否帮我把这个棘手的问题解决下吗?我刚学不久,很多东西还不熟悉
我之前http://www.cnblogs.com/hongfei/archive/2012/12/24/2829799.html 应该也是采用注入的,但是就是不知道怎么去抛出WM_NOTIFY消息。
@MarcoFly:
http://www.yesadmin.com/358/54009/index.html
比较麻烦,没那么多时间,我说下我的想法吧。
要用HOOK,然后通过SetWindowLong将hwnd的窗口过程替换掉,你的窗口过程中应该是这样:
LRESULT CALLBACK BlockComboControl::ParentWindowProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if(nMsg == WM_USER+0x1000)
return ::SendMessage(hWnd,WM_NOTIFY,wParam,lParam);
else
return CallWindowProc( m_parentWndProc, hWnd, nMsg, wParam, lParam );
}
调用变为:
SendMessage(
hwnd, //主窗口(即Form1)的句柄
WM_USER+0x1000, // 自定义消息代码
data[0].hdr.idFrom, //listview控件的句柄
(uint)Marshal.UnsafeAddrOfPinnedArrayElement(data, 0)//data变量的地址
@Launcher:
好的,谢谢
@Launcher:
能再请教你一个问题吗?
为什么我在本进程中给listview发送NM_CLICK的时候,SPY有捕捉到,但是,没有触发listview控件下的“单击事件”呢?
代码:
/// <summary> /// 响应Listview控件的单击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void listView1_MouseClick(object sender, MouseEventArgs e) { listBox1.Items.Add("第"+listView1.SelectedItems[0].SubItems[0].Text +"项 "+ listView1.SelectedItems[0].SubItems[1].Text + "被单击"); } /// <summary> /// 触发WM_NOTIFY /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { const int NM_FIRST = 0; const int NM_CLICK = NM_FIRST - 3; const int WM_NOTIFY = 78; NMITEMACTIVATE[] data = new NMITEMACTIVATE[1]; data[0].hdr.code = NM_CLICK;//通知代码 data[0].hdr.hwndFrom = this.Handle.ToInt32(); //listview控件的句柄 data[0].hdr.idFrom = this.listView1.Handle.ToInt32(); //listview控件的id data[0].iItem = 3;//单击的行号 data[0].iSubItem = 1;//单击的项的序号 data[0].uNewState = 0; data[0].uOldState = 0; data[0].uChanged = 0; data[0].ptAction.X = 0; data[0].ptAction.Y = 0; data[0].lParam = null; data[0].uKeyFlags = 0; SendMessage( this.Handle.ToInt32(), WM_NOTIFY, data[0].hdr.idFrom, //listview控件的句柄 (uint)Marshal.UnsafeAddrOfPinnedArrayElement(data, 0)//data变量的地址 ); }
@MarcoFly:NM_CLICK是个通知代码,是 MouseClick 后发出的, listView1 的 MouseClick 事件和WM_NOTIFY不是一个东西,是.net封装后转换出来的事件,同Win32的消息(Message)是不同的概念。
你可以查阅下ListView的源码中的WmReflectNotify,WmNotify,WndProc这几个方法,可以发现它并没有在收到NM_CLICK通知代码后调用 OnMouseClick 的代码。所以直接发送NM_CLICK并不能触发 listView1_MouseClick 方法。
@MarcoFly: 再补充一下 WM_NOTIFY 是用于通知的消息,是被动的,是用于调用方在某个事件后插入处理逻辑用的。如果我能正确模拟参数,那么我顺序发送两个消息WM_LBUTTONDOWN,WM_LBUTTONUP,那么我就可以收到一个WM_NOTIFY(NM_CLICK)通知消息;反过来,我发送一个 WM_NOTIFY(NM_CLICK),是无法收到WM_LBUTTONDOWN或WM_LBUTTONUP消息的。
通常来说,控件在顺序收到WM_LBUTTONDOWN,WM_LBUTTONUP后,会在 WM_LBUTTONUP中通过HITTEST来判断鼠标落在哪个项上了,如果成功,就修改项的状态并发送WM_NOTIFY(NM_CLICK)通知消息。
@Launcher:
嗯 ,后面再去补充下相关知识。
---------------------------
除了WM_LBUTTONDOWN和WM_LBUTTONUP,我想让本进程(这次没有跨进程了,后面再去学下DLL注入)的listview控件的子项都触发“鼠标左键单击事件”(不要通过坐标去定位),具体要sendmessage哪些呢?
简单说下思路就可以了哈~ 再次麻烦你了
@MarcoFly: http://mdir.blog.163.com/blog/static/305590472010101451228821/
WM_LBUTTONDOWN的消息说明。WM_LBUTTONUP和此类似。但是我不能保证它肯定会正常工作,
因为,它可能会在WM_LBUTTONUP 中使用 HitTest 来重新读取鼠标位置,也可能在WM_NCHITTEST中去判断鼠标的BUTTON DOWN或UP。因为我自己编写的CommandBar就是这么实现的。
@Launcher: 点错了,,,,,,
头大,我这有个listview右键出菜单的,我想通过API呼出他的菜单....也不知道怎么发右键消息
遇到相同问题,不知道楼主后面怎么处理的?
相同问题 你是怎么处理的呢