问题: 得到了syslistview32 类句柄如何获其内容?并定位到某一行? ( 积分: 200 )
分类: 数据库-文件型
 
来自: fbi9999, 时间: 2005-09-30 14:04:00, ID: 3224504

得到了syslistview32 类句柄如何获其内容?并定位到某一行?

来自: suninrain, 时间: 2005-09-30 14:30:36, ID: 3224559

看看下面这个能不能帮你的忙
---------------------------
1.根据StringGrid组件的句柄,想直接通过消息如WM_GETTEXT等来获取StringGrid的内容,显然不可行,普通的Windows消息不能直接获取到某个Cell的内容,TStringGrid并不是Windows的组件,而是Delphi自己的组件,它的Cell内容是存放在TStrings列表中的。
2.根据鼠标所在的位置,通过屏幕取词的技术,是可以Hook到鼠标下的某个Cell的内容,但在实际应用中也不可行,因为不是所有想Hook的Cell都是在显示区可见的,而ApiHook的屏幕取词技术要求Cell的文本重画,才能取得其内容。

既然上面两种方法都不可行,那么我们就要寻找新的方法。
1.思考
由于StringGrid是Delphi自写的组件,而不是Windows的控件,因此要获取StringGrid的某个Cell的内容,只能是获取到StringGrid的对象实例,才能以StringGrid.Cell[X, Y]这样的方式取得任意Cell的内容。
2.启发
在Delphi中,在自己的程序中,用FindControl,是可以根据组件对象的句柄获得对象实例的。那么我们在Hook其他程序的时候能不能采用这种方法来获取StringGrid对象的实例呢?这有个问题,即使我们能够获取到StringGrid的对象实例,但是返回的实例地址是个在其他程序进程地址空间的私有地址(<2GB),这在我们的程序中是无法进行访问的。这个问题不难解决,屏幕取词技术不也是截获到了其他程序的屏幕输出文本吗!?
3.实现
有了上面的思考和启发,那我们就能整理出一个思路来啦。
a.写一个DLL,将Hook StringGrid的代码都放在DLL中,在DLL中,还要包括GetMessage钩子的代码,目的是为了能通过全局钩子将DLL注入到目标程序,使Hook StringGrid的代码运行在目标进程中,这样就可以正确使用FindControl返回的StringGrid实例(也只有在目标进程内调用FindControl,才能够返回StringGrid实例地址)。
b.注入到目标进程的Hook功能,我们怎么进行控制呢?我们怎么让Hook代码知道我们要Hook哪个Cell的内容?怎么询问得知目标StringGrid的行数、列数,以进行正确的遍历获取Cell?... 通过内存映射进行Hook DLL和主程序的数据共享,就可以啦。不过这样实用时不是很方便,因为需要在目标进程中执行Hook代码,显然不能直接调用Hook DLL中的函数,在GetMessage钩子回调函数中来做,也不是很妥当,控制不方便。最好的方法就是在目标进程中创建一个隐形窗口,在该隐形窗口的消息处理中作文章。我们可以通过发送消息的方式通知隐形窗口,执行Hook代码。OK!
c.按照上面的思路,我编写了HookSG.dll和测试程序,不过并没有Hook得到想要的结果。是哪里出了问题呢?分析上面的流程,最大的可能就是FindControl并返回实际的StringGrid对象实例,否则,后面的StringGrid.Cells[X, Y]这样的获取Cell内容的代码是断不会有问题的,我也能够确认Hook的代码是在目标进程中执行的。查看FindControl的源代码:
function FindControl(Handle: HWnd): TWinControl;
var
 OwningProcess: DWORD;
begin
 Result := nil;
 if (Handle <> 0) and (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and
    (OwningProcess = GetCurrentProcessId) then // 都进行了判断调用进程ID是否为Handle所在进程
 begin
   if GlobalFindAtom(PChar(ControlAtomString)) = ControlAtom then
     Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)))
   else
     Result := ObjectFromHWnd(Handle);
 end;
end;
function ObjectFromHWnd(Handle: HWnd): TWinControl;
var
 OwningProcess: DWORD;
begin
 if (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and
    (OwningProcess = GetCurrentProcessID) then
   Result := Pointer(SendMessage(Handle, RM_GetObjectInstance, 0, 0))
 else
   Result := nil;
end;
再看InitControls中:
procedure InitControls;
var
 UserHandle: HMODULE;
begin
 WindowAtomString := Format('Delphi%.8X',[GetCurrentProcessID]);
 WindowAtom := GlobalAddAtom(PChar(WindowAtomString));
 ControlAtomString := Format('ControlOfs%.8X%.8X', [HInstance, GetCurrentThreadID]);
 ControlAtom := GlobalAddAtom(PChar(ControlAtomString));
 RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));
 ...
end;
问题就在这里啦。ControlAtomString是根据模块句柄(模块加载基地址)和线程ID动态生成的,目标进程的模块基地址就是EXE基地址,一般是0x00400000,但DLL的模块加载基地址就不是这个了,默认是0x10000000,而实际上可能因为这个地址已经被占用(有其他DLL被加载到这个地址)而进行重定位,所以初始化时添加的Atom和目标进程的ControlAtom就不一样,FindControl当然就不能找到StringGrid对象实例啦。
清楚了这一点,解决起来就简单了,我们自己写个FindControl函数,以目标进程基地址来动态生成ControlAtomString,添加ControlAtom不就可以啦。在DLL中取EXE的基地址,用GetModuleHandle(nil)即可。
OK。解决了这些问题,Hook其他程序的StringGrid的内容就水到渠成,没什么障碍啦。

补充:
上面提到的方法,不仅仅可以Hook其他程序的StringGrid的内容,实际上还可以获取更多的从TWinControl继承的窗口组件的内容、属性、...,拥有了对象的实例,你基本上就拥有了对对象的完全控制。

源代码:



2004-12-31 14:14:26    
发表评语&raquo;&raquo;&raquo;    

2004-12-31 14:27:25    不知道怎么贴上附件。给个下载地址吧:http://www.jpdocomo.com/lichengbin/HookSG.zip


2005-1-3 16:43:20    说明:
HookSG.EXE/SG.EXE都是Test.dpr编译得到,SG.EXE是被Hook的程序(编译时Hook按钮的Enabled设为False,避免操作乱套),HookSG.EXE是Hook主程序。
测试:
启动HookSG.EXE、SG.EXE,将SG.EXE中Handle编辑框的内容复制到HookSG.EXE的Handle编辑框中,点击Hook按钮,当HookSG.EXE程序的Row/Col、Cell[X,Y]按钮激活之后说明HookSG.dll已经成功注入SG.EXE的进程,隐形窗口也已经被创建,HookSG.EXE获取SG.EXE中的StringGrid页的StringGrid的任意Cell内容的功能已经具备。Row/Col按钮为查询StringGrid的行列数目,Cell[X,Y]为获取Row/Column对应Cell的内容。


2005-1-4 11:51:49    重新更新了一下,测试时比以前方便直观一些啦。
http://www.jpdocomo.com/lichengbin/HookSG(New).zip  


2005-1-11 14:09:24    补充:
运用这种技术,实际上能够做到的远不止此!甚至可以Hook到其他程序中Form上的任意继承自TWinControl的窗口组件(有句柄)或其他非窗口组件(无句柄、可视或不可视)的内容,如以前DFW好象有个贴子就是问然后Hook其他程序的Label的内容的。要实现上面所说的,只要知道被Hook的Form的定义即可,而通常Delphi创建的程序,Form都会在EXE的资源部分,只需要通过一些资源查看工具提取出资源中Form定义内容即可。有了窗体的定义,我们在Hook DLL中同样声明一个这样的定义。如
type
 TFormHack = class(TForm)
   Label1: TLabel;
   Button1: TButton;
   Label2: TLabel;
   Memo1: TMemo;
 end;
这样我们通过FindControl找到Form的对象实例Form1,然后只需象TFormHack(Form1).Label1这样,就可以获取到Label1的实例啦。
另外,实际上我们也可以通过Form1.Components记录的Form1上的组件列表来进行Form1上的组件遍历,这种方式同样也可以得到Form1上的任意组件的实例。


2005-2-2 18:00:01    原存放域名2月份将到期,原源代码下载地址不再有效,请到新的下载地址进行下载:
<A HREF="http://lichengbin.iii-grp.com/HookSG.zip">HookSG初版</A>,  <A HREF="http://lichengbin.iii-grp.com/HookSG(New).zip">HookSG改进版</A>

来自: fbi9999, 时间: 2005-09-30 15:10:01, ID: 3224655

to suninrain:
多谢你,好像太深奥了.可能我是菜鸟吧.我要如何改你的程序才能为我所用呀?
在HookSG(New).zip源代码里
我改了按钮"hook"里面的代码.加了destwnd:=2558916;
点击按钮"Cell[X,Y]"后却说我"索引超界了"为何? 我的syslistview32类里明明有三行六列内容呀.句柄是没错的.

注:其实我取的东西是中游游戏房间的syslistview32类.里面是游戏玩家的信息

来自: lichengbin, 时间: 2005-09-30 15:20:40, ID: 3224677

上面那个只能获取Delphi开发的程序的

刘麻子大虾的,试试

uses CommCtrl;

function ListView_GetItemText_Ex(hwndLV: HWND; i, iSubItem: Integer;
 pszText: PChar; cchTextMax: Integer): Integer;
var
 LVItem: TLVItem;
 ProcessID, ProcessHD, Temp: DWORD;
 MemPoint: Pointer;
begin
 GetWindowThreadProcessId(hwndLV, ProcessID);

 ProcessHD := OpenProcess(
   PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE,
   FALSE, ProcessID);

 MemPoint := VirtualAllocEx(ProcessHD, nil, SizeOf(TLVItem) + cchTextMax,
   MEM_COMMIT, PAGE_READWRITE);

 LVItem.iSubItem := iSubItem;
 LVItem.cchTextMax := cchTextMax;
 LVItem.pszText := PChar(Integer(MemPoint) + SizeOf(TLVItem));
   
 WriteProcessMemory(ProcessHD, MemPoint, @LVItem, SizeOf(TLVItem), Temp);
 Result := SendMessage(hwndLV, LVM_GETITEMTEXT, i, Integer(MemPoint));

 ReadProcessMemory(ProcessHD, Pointer(Integer(MemPoint) + SizeOf(TLVItem)),
   pszText, cchTextMax, Temp);

 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem) + cchTextMax, MEM_DECOMMIT);  
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 TextBuffer: array[0..100] of Char;
begin
 ListView_GetItemText_Ex($01590346, 0, 0, TextBuffer, 100);
 ShowMessage(TextBuffer);
end;
至于定位,用LVM_SETITEMSTATE消息同理应用即可

来自: fbi9999, 时间: 2005-09-30 15:35:48, ID: 3224698

to lichengbin:
多谢.成功获得信息了.我的SysListView32类的句柄是通过第三方工具获得的.我自己编的以下两行代码为什么不能获取SysListView32类句柄?请能再指点我一下吗?

hparent:=findwindow('clientframe_cmainframe',nil);
hparent:= FindWindowEx(hparent,0,'SysListView32','list1');

//SysListView32类是clientframe_cmainframe类里的组件.clientframe_cmainframe类就是中游的游戏房间.

来自: lichengbin, 时间: 2005-09-30 17:43:42, ID: 3224849

那可能这个SysListView32控件不是clientframe_cmainframe的直接子窗口呗

来自: fbi9999, 时间: 2005-10-01 11:47:35, ID: 3224859

to lichengbin and to 大家:
  如何定位行?我用LVM_SETITEMSTATE替换原来的LVM_GETITEMTEXT却不行. 分我会照给诸位的.

来自: cpjin, 时间: 2005-10-01 15:06:00, ID: 3225090

取句柄
GetCursorPos(Poss);
Handle := WindowFromPoint(Poss);
HandleEdit.Text := IntToStr(Handle);
GetClassName(Handle, Buf, 1024);
ClassEdit.Text := Buf;
SendMessage(Handle, WM_GETTEXT, 1024, Integer(@Buf));
TextEdit.Text := Buf;
s:=TextEdit.Text;

来自: fbi9999, 时间: 2005-10-01 16:15:58, ID: 3225110

to 大家:
定位到某一行问题,我已解决了.代码如下,但如何能发鼠标右键消息过去那一行?或获得那一行坐标值?

uses CommCtrl;
{$R *.dfm}

function ListView_GetItemText_Ex(hwndLV: HWND; i, iSubItem: Integer;
 pszText: PChar; cchTextMax: Integer): Integer;
var
 LVItem: TLVItem;
 ProcessID, ProcessHD, Temp: DWORD;
 MemPoint: Pointer;
begin
 GetWindowThreadProcessId(hwndLV, ProcessID);


 ProcessHD := OpenProcess(
   PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE,
   FALSE, ProcessID);

 MemPoint := VirtualAllocEx(ProcessHD, nil, SizeOf(TLVItem) + cchTextMax,
   MEM_COMMIT, PAGE_READWRITE);

lvitem.state:=LVIS_SELECTED;
lvitem.stateMask:=LVIS_SELECTED;
WriteProcessMemory(ProcessHD, MemPoint,@LVItem,SizeOf(TLVItem), Temp);
SendMessage(hwndLV, LVM_SETITEMSTATE,i, Integer(MemPoint));


 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem) + cchTextMax, MEM_DECOMMIT);  
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);

end;

procedure TForm1.Button1Click(Sender: TObject);
var
 TextBuffer: array[0..100] of Char;
begin
  ListView_GetItemText_Ex(984608,10, 0, TextBuffer, 100);

end;

来自: 刘麻子, 时间: 2005-10-01 17:07:39, ID: 3225140

先贴一些代码片断,仅供参考:

 // 扩展的ListView项目选取函数
function ListView_SetItemState_Ex(hwndLV: HWND; i: Integer; data, mask: UINT): Bool;
var          
 LVItem: TLVItem;
 ProcessID, ProcessHD, Temp: DWORD;
 MemPoint: Pointer;
begin
 GetWindowThreadProcessId(hwndLV, 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.stateMask := mask;
 LVItem.state := data;
 WriteProcessMemory(ProcessHD, MemPoint, @LVItem, SizeOf(TLVItem), Temp);

 Result := (SendMessage(hwndLV, LVM_SETITEMSTATE, i, Integer(MemPoint)) <> 0);

 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem), MEM_DECOMMIT);
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
end;

 // 扩展的ListView项目读取函数
function ListView_GetItemText_Ex(hwndLV: HWND; i, iSubItem: Integer;
 pszText: PChar; cchTextMax: Integer): Integer;
var
 LVItem: TLVItem;
 ProcessID, ProcessHD, Temp: DWORD;
 MemPoint: Pointer;
begin
 GetWindowThreadProcessId(hwndLV, ProcessID);

 ProcessHD := OpenProcess(
   PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE,
   FALSE, ProcessID);

 MemPoint := VirtualAllocEx(ProcessHD, nil, SizeOf(TLVItem) + cchTextMax,
   MEM_COMMIT, PAGE_READWRITE);

 LVItem.iSubItem := iSubItem;
 LVItem.cchTextMax := cchTextMax;
 LVItem.pszText := PChar(Integer(MemPoint) + SizeOf(TLVItem));

 WriteProcessMemory(ProcessHD, MemPoint, @LVItem, SizeOf(TLVItem), Temp);
 Result := SendMessage(hwndLV, LVM_GETITEMTEXT, i, Integer(MemPoint));

 ReadProcessMemory(ProcessHD, Pointer(Integer(MemPoint) + SizeOf(TLVItem)),
   pszText, cchTextMax, Temp);

 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVItem) + cchTextMax, MEM_DECOMMIT);
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
end;

 // 扩展的TreeView_GetItem (取得TreeView指定子项目)
function TreeView_GetItem_Ex(hwndTV: HWND; var TVItem: TTVItem): Bool;
var
 ProcessID, ProcessHD, Temp: DWORD;
 pszText, MemPoint: Pointer;
begin
 Result := FALSE;

 GetWindowThreadProcessId(hwndTV, ProcessID);

 ProcessHD := OpenProcess(
   PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, FALSE, ProcessID);
 if (ProcessHD = 0) then Exit;

 MemPoint := VirtualAllocEx(ProcessHD, nil,
   SizeOf(TTVItem) + TVItem.cchTextMax + 1,
   MEM_COMMIT, PAGE_READWRITE);
 if (MemPoint = nil) then Exit;

 pszText := TVItem.pszText; // 保存本进程地址
 PChar(pszText)^ := #0;
 TVItem.pszText := PChar(Integer(MemPoint) + SizeOf(TTVItem));
 if (WriteProcessMemory(ProcessHD, MemPoint, @TVItem, SizeOf(TVItem), Temp) = FALSE) then Exit;
 if (WriteProcessMemory(ProcessHD, TVItem.pszText, pszText, 1, Temp) = FALSE) then Exit;

 Result := (SendMessage(hwndTV, TVM_GETITEM, 0, LongInt(MemPoint)) <> 0);

 if (ReadProcessMemory(ProcessHD, TVItem.pszText, pszText, TVItem.cchTextMax + 1, Temp) = FALSE) then
 begin
   Result := FALSE;
   Exit;
 end;
 TVItem.pszText := pszText; // 恢复本进程地址

 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TTVItem) + TVItem.cchTextMax + 1, MEM_DECOMMIT);
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
end;

 // 取指定TreeView子项目文字
function TreeView_GetItem_Text(hwndTV: HWND; hitem: HTreeItem): string;
var
 Buffer: array[0..50] of Char;
 TVItem: TTVItem;
begin
 TVItem.hItem := hitem;
 TVItem.mask := TVIF_TEXT;
 TVItem.pszText := @Buffer[0];
 TVItem.cchTextMax := 50;

 if TreeView_GetItem_Ex(hwndTV, TVItem) then
   Result := Buffer
 else
   Result := '';
end;

 // 扩展的ListView_FindItem
function ListView_FindItem_Ex(hwndLV: HWND; iStart: Integer; var LVFindInfo: TLVFindInfo): Integer;
var
 ProcessID, ProcessHD, Temp: DWORD;
 psz, MemPoint: Pointer;
begin
 Result := -1; // 默认查找失败

 GetWindowThreadProcessId(hwndLV, ProcessID);

 ProcessHD := OpenProcess(
   PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, FALSE, ProcessID);
 if (ProcessHD = 0) then Exit;

 MemPoint := VirtualAllocEx(
   ProcessHD, nil, SizeOf(TLVFindInfo) + StrLen(LVFindInfo.psz) + 1, MEM_COMMIT, PAGE_READWRITE);
 if (MemPoint = nil) then Exit;

 psz := LVFindInfo.psz; // 保存本进程地址
 LVFindInfo.psz := PChar(Integer(MemPoint) + SizeOf(TLVFindInfo));
 if (WriteProcessMemory(ProcessHD, MemPoint, @LVFindInfo, SizeOf(TLVFindInfo), Temp) = FALSE) then Exit;
 if (WriteProcessMemory(ProcessHD, LVFindInfo.psz, psz, StrLen(psz) + 1, Temp) = FALSE) then Exit;

 Result := SendMessage(hWndLv, LVM_FINDITEM, iStart, LongInt(MemPoint));

 LVFindInfo.psz := psz; // 恢复本进程地址

 VirtualFreeEx(ProcessHD, MemPoint, SizeOf(TLVFindInfo) + StrLen(psz) + 1, MEM_DECOMMIT);
 VirtualFreeEx(ProcessHD, MemPoint, 0, MEM_RELEASE);
end;

来自: leosoft, 时间: 2005-10-01 17:13:23, ID: 3225145

都是高手啊,长见识!

来自: 刘麻子, 时间: 2005-10-01 17:17:08, ID: 3225149

右键消息,应该可以直接发给ListView控件,求坐标值则可以试试LVM_GETITEMPOSITION,注意地址也需要是目标进程的..

问题讨论没有结束 ...

Last modification:August 16th, 2009 at 12:30 pm
如果觉得我的文章对你有用,请随意赞赏