当前位置:主页>仓库管理软件> 列表

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

进销存管理软件版1楼: 得到了syslistview32 类句柄如何获其内容?并定位到某一行?

2楼: 看看下面这个能不能帮你的忙
---------------------------
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
发表评语»»»

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月份将到期,原源代码下载地址不再有效,请到新的下载地址进行下载:
HookSG初版HookSG改进版客户关系管理软件

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

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

4楼: 上面那个只能获取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消息同理应用即可

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

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

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

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

进销存管理软件版7楼: to lichengbin and to 大家:
如何定位行?我用LVM_SETITEMSTATE替换原来的LVM_GETITEMTEXT却不行. 分我会照给诸位的.

8楼: 取句柄
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;

9楼: 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;

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

// 扩展的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;

11楼: 都是高手啊,长见识!

12楼: 右键消息,应该可以直接发给ListView控件,求坐标值则可以试试LVM_GETITEMPOSITION,注意地址也需要是目标进程的.. 如药品免费进销存软件