请教SysListView32中如何将某行Checkbox打上勾和SysTreeView32如何模拟鼠标点击(300)

D

dadabox

Unregistered / Unconfirmed
GUEST, unregistred user!
目前在写程序中,遇到难题1、在SysListView32中,我已做到能选中某行,但不能将checkbox打上勾。有一折衷办法,发送消息模拟鼠标去点,但不想用此办法。var: udata, umask: UINT; h:hwnd;begin udata := LVIS_SELECTED + LVIS_FOCUSED + LVIS_ACTIVATING; umask := DWord(-1); // LVIS_SELECTED + LVIS_FOCUSED + LVIS_ACTIVATING; ListView_SetItemState_Ex(h,0,udata,umask);end;但从下面这个函数,实现不了将checkbox框选中。 ListView_SetCheckState(h,0,true);且用ListView_GetCheckState(h,0),读出的值也为0,无论我是否选中该checkbox。2、在SysTreeView32中,能将所有节点内容读出,且能选中节点。但需要模拟点击该节点。用此能读出节点文本内容SendMessage(vHandle, TVM_GETITEM, 0, lparam(vPointer));用此能选中该节点SendMessage(vHandle, TVM_SELECTITEM, TVGN_CARET, Integer(vTreeItem)); 但没法实现模拟鼠标点击某节点。请教各位大侠!
 
1、选中,打勾,为什么不直接用TTreeView相关方法呢,一定要用API就这样:var udata, umask: UINT; H: HWND;begin umask := LVIS_STATEIMAGEMASK + LVIS_SELECTED + LVIS_FOCUSED + LVIS_ACTIVATING; udata := LVIS_SELECTED + LVIS_FOCUSED + ((Integer(True) and 1) + 1) shl 12; ListView_SetItemState(H, 1, udata, umask);2、鼠标点击你要模拟鼠标左键按下及松开
 
点击TreeView的第四个节点(坐标x,y为50,60):var H: HWND;begin SendMessage(H, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(50, 60)); SendMessage(H, WM_LBUTTONUP, MK_LBUTTON, MakeLong(50, 60));
 
xianjun,您好! 对于第一个问题,即SysListView32中选中打勾的问题,我按你的方式,也仅能选中该行,但没法打上勾。我之前的方法也是只能选中,没法打上勾。第二个问题,如果按此办法,是可以模拟点击,但我需要根据不同的节点的内容来决定是否点击,那么就存在一个定位的问题。请问我能如何的定位到该节点的坐标以让我能模拟点击的,且该SysTreeView32的节点较多,还存在滑动框上下移动的问题,谢谢解答!
 
先让SYSLISTVIEW32获得焦点,然后再选中
 
不好意思,我刚发现第二个问题中的模拟点击,只是将焦点移到了那个地方,但并没有产生真正的点击。否则,就应该会产生鼠标点击该地方后出现别的界面。
 
不如你把能重现问题的代码贴出来得了,我上面的代码是试过没问题才贴上来的。如果有滚动条,那你要把TreeView的节点MakeVisible,然后再点击。
 
procedure TForm1.Button9Click(Sender: TObject);var vHandle: THandle; vItemCount: Integer; vBuffer: array[0..255] of Char; vProcessId: DWORD; vProcess: THandle; vPointer: Pointer; vNumberOfBytesRead: Cardinal; I: Integer; vItem: TTVItem; vTreeItem: HTreeItem; nm: NMHDR; nTreeID:Word;begin vHandle := StrToInt(Edit7.Text); GetWindowThreadProcessId(vHandle, @vProcessId); // 获得窗体进程ID vProcess := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, False, vProcessId); vPointer := VirtualAllocEx(vProcess, nil, 4096, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE); Memo2.Lines.Clear; try vItemCount := TreeView_GetCount(vHandle); vTreeItem := TreeView_GetRoot(vHandle); for I := 0 to vItemCount - 1 do begin with vItem do begin mask := TVIF_TEXT; state := TVIS_SELECTED + TVIS_FOCUSED; cchTextMax := SizeOf(vBuffer); pszText := Pointer(Cardinal(vPointer) + SizeOf(vItem)); hItem := vTreeItem; end; WriteProcessMemory(vProcess, vPointer, @vItem, SizeOf(vItem), vNumberOfBytesRead); SendMessage(vHandle, TVM_GETITEM, 0, lparam(vPointer)); ReadProcessMemory(vProcess, Pointer(Cardinal(vPointer) + SizeOf(TLVItem)), @vBuffer[0], SizeOf(vBuffer), vNumberOfBytesRead); Memo2.Lines.Add(vBuffer+'vTree:'+InttoStr(Integer(vTreeItem))+'; hItem:'+inttostr(Integer(vItem.hItem))); if SameText(vBuffer, Edit8.Text) then // 通过节点文本确定 begin SendMessage(vHandle, TVM_SELECTITEM, TVGN_CARET, Integer(vTreeItem)); // 选中该节点// SendMessage(vHandle, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(50, 60));// SendMessage(vHandle, WM_LBUTTONUP, MK_LBUTTON, MakeLong(50, 60)); end; vTreeItem := TreeNodeGetNext(vHandle, vTreeItem); end; finally VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE); CloseHandle(vProcess); end;end;这段是模拟点击SysTreeView32的,因为原例子中测试的代码太多,比较乱,是找出来的代码,不是全部的,但有效的基本上都在这里了。麻烦xianjun看看,谢谢!至于有滑动条这个问题,不知道有没有办法通过当前的节点,得到在控件中的坐标。如果能的话,再通过鼠标模拟点击该坐标也许就可实现。
 
procedure TForm1.Button13Click(Sender: TObject);var LVItem: TLVItem; ProcessID, ProcessHD, Temp: DWORD; MemPoint: Pointer; h: Hwnd; udata, umask: UINT;begin h :=396288; //724636 GetWindowThreadProcessId(h, ProcessID); ProcessHD := OpenProcess( PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, FALSE, ProcessID); MemPoint := VirtualAllocEx(ProcessHD, nil, SizeOf(TLVItem), MEM_COMMIT, PAGE_READWRITE); LVItem.mask := 0; LVItem.iItem := 1; LVItem.iSubItem := 0; LVItem.state := LVIS_FOCUSED or LVIS_SELECTED; LVItem.stateMask := 3; LVItem.iImage := 1; LVItem.pszText := '052E8018'; LVItem.cchTextMax := 19153156; LVItem.lParam := 91; WriteProcessMemory(ProcessHD, MemPoint, @LVItem, SizeOf(TLVItem), Temp); SendMessage(h, LVM_SETITEMSTATE, 0, Integer(MemPoint)); SendMessage(h, WM_SETFOCUS, 68580, 0); ListView_SetCheckState(h,0,true); ListView_SetItemState(h,0,LVIS_SELECTED+LVIS_FOCUSED ,LVIS_SELECTED); VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem), MEM_DECOMMIT); VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);end;这段是想将SysListView32中的选择框打勾,但能实现选中该行,却不能实现打勾。
 
要操作别的进程,WM_LBUTTONDOWN等消息要用PostMessage,不然是没有效果的。可能用TVM_GETITEMRECT消息来获取节点的坐标。
 
打勾的直接把我上面的代码加进去就好了:var LVItem: TLVItem; ProcessID, ProcessHD, Temp: DWORD; MemPoint: Pointer; h: Hwnd; udata, umask: UINT;begin h :=StrToInt(Edit1.Text); //724636 GetWindowThreadProcessId(h, ProcessID); ProcessHD := OpenProcess( PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, FALSE, ProcessID); MemPoint := VirtualAllocEx(ProcessHD, nil, SizeOf(TLVItem), MEM_COMMIT, PAGE_READWRITE); //LVItem.mask := 0; //LVItem.iItem := 3; //LVItem.iSubItem := 0; //LVItem.state := LVIS_FOCUSED or LVIS_SELECTED; //LVItem.stateMask := 3; //LVItem.iImage := 1; //LVItem.pszText := '052E8018'; //LVItem.cchTextMax := 19153156; //LVItem.lParam := 91; umask := LVIS_STATEIMAGEMASK + LVIS_SELECTED + LVIS_FOCUSED; udata := LVIS_SELECTED + LVIS_FOCUSED + ((Integer(True) and 1) + 1) shl 12; LVItem.stateMask := umask; LVItem.state := udata; WriteProcessMemory(ProcessHD, MemPoint, @LVItem, SizeOf(TLVItem), Temp); SendMessage(h, LVM_SETITEMSTATE, 3, Integer(MemPoint)); //SendMessage(h, WM_SETFOCUS, 68580, 0); //ListView_SetCheckState(h,0,true); //ListView_SetItemState(h,0,LVIS_SELECTED+LVIS_FOCUSED ,LVIS_SELECTED); VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem), MEM_DECOMMIT); VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
 
xianjun,你好!用PostMessage还是不能触发点击事件,只是将焦点定位到了那个地方。就是点击这个事件没有触发。
 
如果用消息实在不行,就用mouse_event方法吧,先把目前程序激活,然后再执行。这是肯定可以的。
 
我想知道的是为什么消息不行。整个WINDOWS都是消息机制的呀。用mouse_event的办法,我知道行。但我不到万不得以,我是不想用这个办法的。
 
windows是基于消息机制,但它也有安全机制,程序自己也可以做安全机制,并不是说谁发一个相应的消息都能获得正常的结果。当然这可能并不是上面的代码不能达到预期的原因,但应该也有类似的原因。因为你可以试一下,同一个程序,用那个消息是可以实现点击效果的。或者你可以考虑更复杂一点的实现方式,使用远程线程,把自己的代码插入目标进程中执行,这样就是在同一进程中操作了,这样也是可以的。
 
我用SPY++监测消息,发现有个Nesting Level,我用鼠标点击的值是1,而如果自己发送的消息是35,36之类的。且在消息监控窗口中,在消息号后面的值是P,而我发送的消息是R。如下:<00001>000100A8 P ........WM_MOUSEDOWN...鼠标点击,对应Nesting Level为1<00001>000200A8 R ........WM_MOUSEDOWN...程序发送消息,对应Nesting Level为36不知是否跟这个Nesting Level有些关系。另外,我发送的消息有时候起作用,有时候不起作用。感觉像是消息没发送到位,但我用的sendmessage,应该是收到反馈结果后再发送的下一条呀。我希望每次发送都能成功触发,不知上面这个是什么原因,又如何能实现每次成功实现触发消息。谢谢!消息远程注入,这个似乎难度较高,还没研究过。
 
根据你的监测可以看到,自己程序的消息跟其他程序发的消息还是可以区别出来的至于为什么有时起作用有时不起作用就不是很清楚了。远程线程注入难度不高,你搜索一下,很多现成的代码可以参考的,相信用这个会稳定一些你可以搜索CreateRemoteThread,看相关的例子即可。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
549
import
I
S
回复
0
查看
823
SUNSTONE的Delphi笔记
S
顶部