跨进程获取树控件节点信息
来源:互联网 发布:哈喽聊天软件 编辑:程序博客网 时间:2024/05/17 06:41
几天前在 myf1 上遇到人问如何获取其它程序中的树控件节点内容,当时就将要用到的几个 api 告诉了它,然后自己试了一试,成功!今天空闲时无意中又运行了它,结果却发现被探测的程序挂掉了!仔细回忆一下,原来测试时是在同一个 vfp 内运行两个表单,一个做测试对象,里面有一个 TreeView,另一个就是这个探测器;由于同在一个 vfp 进程内,所以没有发生错误。这次不同是,被探测的是 msdn 的窗口,与这个探测器是不同的两个进程,所以可能发生了存储器访问例外,导致被探测程序崩溃。原因找到了,解决方案也就出来了,关键就是当使用 SendMessage 发送获取节点消息时,指向 TVITEM 结构的指针必须指向树控件所在进程可以存取的地址空间内,这个可以先使用 GetWindowThreadProcessId 获取到树控件所在的进程 id,然后用 OpenProcess 打开该进程,之后使用 VirtualAllocEx 函数在它的进程地址空间内申请一块存储块用来存放 TVITEM 结构,最后用 Read/WriteProcessMemory 函数来进行进程间的数据交换 ;只是用 vfp 来实现还有一些麻烦的地方,首先是用 vfp 构造的结构块无法得到其地址,这样便无法直接使用 Read/WriteProcessMemory 这对函数,但是我们可以先使用 GlobalAlloc API 函数申请一块当前进程可访问的存储块并得到其地址指针,将它作为一个中转站,另外 vfp9 提供了 sys(2600) 函数让我们极大的简化了存储器的访问,不必再像以前那样使用 Heap* 序列加上 RtlMoveMemory 来交换数据;之后我们就可以通过 sys(2600) 函数加上这个中转站来与其它进程地址中的数据进行交换了。
下面是实现这个的示例代码,示例中只实现了获取最前面两个节点文本信息的方法,要获取其它节点可以继续使用 TVGN_NEXT 和 TVGN_CHILD 作为 TVM_GETNEXTITEM 消息的参数来遍历所有的节点并得到它们的 hItem,进而获取它们的节点信息。这里也只示例了获取节点文本的方法,其它信息相对来说就更简单了,因为都是同样从 TVITEM 结构中取成员的操作,而且都不涉及到字符串指针的操作。
运行后需要先用最下面的按钮获取到一个已存在的树控件 hWnd(你可以先打开一个带树控件的实例,例如 vfp 的帮助,其目录标签就有一个树控件),然后才能点“查看节点信息”按钮来提取节点文本。
发现一个问题,就是如果根节点没有展开的话,获取子结点文本时会失败,但这已不是代码的问题了,是树控件响应 SendMessage 消息时的错误。我想唯一的方法就是再发送 TVM_EXPAND 消息让它自动展开。
PUBLIC oForm oForm = NEWOBJECT( "MyForm" ) oForm.Show() READ EVENTS DEFINE CLASS MyForm AS Form Height = 142 Width = 312 ShowWindow = 2 DoCreate = .T. AutoCenter = .T. Caption = "Form1" MaxButton = .F. MinButton = .F. AllowOutput = .F. Name = "Form1" ADD OBJECT lblClsName AS label WITH ; AutoSize = .T., ; BackStyle = 0, ; Caption = "当前控件类名", ; Height = 17, ; Left = 20, ; Top = 15, ; Width = 74, ; Name = "lblClsName" ADD OBJECT txtClsName AS textbox WITH ; Height = 23, ; Left = 100, ; ReadOnly = .T., ; Top = 12, ; Width = 200, ; DisabledBackColor = RGB(255,255,255), ; Name = "txtClsName" ADD OBJECT lblTvHwnd AS label WITH ; AutoSize = .T., ; BackStyle = 0, ; Caption = "树控件 hWnd", ; Height = 17, ; Left = 20, ; Top = 45, ; Width = 73, ; Name = "lblTvHwnd" ADD OBJECT txtTvHwnd AS textbox WITH ; Alignment = 3, ; Height = 23, ; Left = 100, ; Top = 41, ; Width = 108, ; Name = "txtTvHwnd" ADD OBJECT cmdView AS commandbutton WITH ; Top = 39, ; Left = 212, ; Height = 27, ; Width = 90, ; Caption = "查看节点信息", ; Name = "cmdView" ADD OBJECT chkDetect AS checkbox WITH ; Top = 78, ; Left = 12, ; Height = 48, ; Width = 288, ; WordWrap = .T., ; Alignment = 0, ; Caption = (" 按下后将鼠标指针移到要获取 hWnd 的树控件上" ; + CHR(13) + " 获取成功后 hWnd 会显示在上面的文本框中"), ; Value = .F., ; Style = 1, ; Name = "chkDetect" ADD OBJECT timer1 AS timer WITH ; Top = 104, ; Left = 0, ; Height = 23, ; Width = 23, ; Name = "Timer1" PROCEDURE Init DECLARE Long GetLastError IN WIN32API DECLARE Long GetCursorPos IN WIN32API String @ cPoint DECLARE Long WindowFromPoint IN WIN32API Long x, Long y DECLARE Long GetClassName IN WIN32API ; Long hWnd, String @ lpClassName, Long nMaxCount DECLARE Long GetWindowThreadProcessId IN WIN32API ; Long hWnd, Long @ lpdwProcessId DECLARE Long OpenProcess IN WIN32API ; Long dwDesiredAccess, Long bInheritHandle, Long dwProcessId DECLARE Long CloseHandle IN WIN32API Long hObject DECLARE Long VirtualAllocEx IN WIN32API ; Long hProcess, Long @ lpAddress, Long dwSize, ; Long flAllocationType, Long flProtect DECLARE Long VirtualFreeEx IN WIN32API ; Long hProcess, Long lpAddress, Long dwSize, Long dwFreeType DECLARE Long WriteProcessMemory IN WIN32API ; Long hProcess, Long lpBaseAddress, Long lpBuffer, ; Long nSize, Long @ lpNumberOfBytesWritten DECLARE Long ReadProcessMemory IN WIN32API ; Long hProcess, Long lpBaseAddress, Long lpBuffer, ; Long nSize, Long @ lpNumberOfBytesWritten DECLARE Long GlobalAlloc IN WIN32API Long uFlags, Long dwBytes DECLARE Long GlobalFree IN WIN32API Long hMem DECLARE Long SendMessage IN WIN32API AS sendmsg_nn ; Long, Long, Long, Long DECLARE Long SendMessage IN WIN32API AS sendmsg_nc ; Long, Long, Long, String @ ENDPROC PROCEDURE Unload CLEAR EVENTS ENDPROC PROCEDURE cmdView.Click Thisform.GetNodeInfo() ENDPROC PROCEDURE Timer1.Timer LOCAL hWnd, cPoint, x, y, iLen, cBuff m.cPoint = REPLICATE( CHR(0), 8 ) GetCursorPos( @ m.cPoint ) m.x = CTOBIN( SUBSTR( m.cPoint, 1, 4 ), "rs" ) m.y = CTOBIN( SUBSTR( m.cPoint, 5, 4 ), "rs" ) m.hWnd = WindowFromPoint( m.x, m.y ) m.iLen = 260 m.cBuff = REPLICATE( CHR(0), m.iLen ) m.iLen = GetClassName( m.hWnd, @ m.cBuff, m.iLen ) m.cBuff = LEFT( m.cBuff, m.iLen ) Thisform.txtClsName.Value = m.cBuff IF ( "TreeView" $ m.cBuff ) STORE TRANSFORM( m.hWnd ) TO _ClipText, Thisform.txtTvHwnd.Value ENDIF ENDPROC PROCEDURE chkDetect.ProgrammaticChange Thisform.Timer1.Interval = IIF( This.Value, 100, 0 ) ENDPROC PROCEDURE chkDetect.InteractiveChange This.ProgrammaticChange() ENDPROC PROCEDURE GetNodeInfo #define TVGN_ROOT 0x0000 #define TVGN_NEXT 0x0001 #define TVGN_PREVIOUS 0x0002 #define TVGN_PARENT 0x0003 #define TVGN_CHILD 0x0004 #define TV_FIRST 0x1100 #define TVM_GETNEXTITEM TV_FIRST + 10 #define PROCESS_VM_OPERATION 0x0008 #define PROCESS_VM_READ 0x0010 #define PROCESS_VM_WRITE 0x0020 #define PROCESS_QUERY_INFORMATION 0x0400 LOCAL hWnd, iPid, hProc, cText, cMsg, hItem This.chkDetect.Value = .F. && 关闭取树控件句柄的计时器 m.hWnd = INT( VAL( Thisform.txtTvHwnd.Value )) && 树控件句柄 m.iPid = 0 GetWindowThreadProcessId( m.hWnd, @ m.iPid ) && 取树控件所在进程 id IF ( 0 == m.iPid ) MESSAGEBOX( "获取进程 id 失败" ) RETURN .F. ENDIF m.hProc = OpenProcess( ; && 打开该进程 BITOR( PROCESS_VM_OPERATION, PROCESS_VM_READ, ; PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION ), ; 0, m.iPid ) IF ( 0 == m.hProc ) MESSAGEBOX( "打开进程失败" ) RETURN .F. ENDIF *!* 先找到根节点的 hItem m.hItem = sendmsg_nn( m.hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0 ) *!* 取它的文字 m.cText = This.GetNodeText( m.hWnd, m.hProc, m.hItem ) IF !ISNULL( m.cText ) m.cMsg = "根节点文字: " + m.cText *!* 再找根节点的第一个子结点 m.hItem = sendmsg_nn( m.hWnd, TVM_GETNEXTITEM, TVGN_CHILD, m.hItem ) *!* 取它的文字 m.cText = This.GetNodeText( m.hWnd, m.hProc, m.hItem ) IF !ISNULL( m.cText ) m.cMsg = m.cMsg + 0h0d0a + "第一个子结点文字: " + m.cText ENDIF MESSAGEBOX( m.cMsg ) ENDIF CloseHandle( m.hProc ) && 关闭打开的进程 ENDPROC PROCEDURE GetNodeText LPARAMETERS thWnd, thProc, thItem #define TV_FIRST 0x1100 #define TVM_GETITEM TV_FIRST + 12 #define TVIF_TEXT 0x0001 #define TVIF_IMAGE 0x0002 #define TVIF_PARAM 0x0004 #define TVIF_STATE 0x0008 #define TVIF_HANDLE 0x0010 #define TVIF_SELECTEDIMAGE 0x0020 #define TVIF_CHILDREN 0x0040 #define GMEM_FIXED 0x0000 #define PAGE_READWRITE 0x04 #define MEM_COMMIT 0x1000 #define MEM_RELEASE 0x8000 LOCAL cStru, pText, pStru, pTransit, iSize, cText *!* typedef struct _TVITEM { // 节点信息结构 *!* UINT mask; *!* HTREEITEM hItem; *!* UINT state; *!* UINT stateMask; *!* LPTSTR pszText; *!* int cchTextMax; *!* int iImage; *!* int iSelectedImage; *!* int cChildren; *!* LPARAM lParam; *!* int iIntegral; *!* #if (_WIN32_IE >= 0x0600) *!* UINT uStateEx; *!* HWND hwnd; *!* int iExpandedImage; *!* #endif *!* } TVITEM *!* ------------------------------------------------------------------------------- *!* 由于进程只能存取属于自己的地址空间, 我们需要将结构复制到树控件所在的进程空间内, *!* 又因为我们无法得到 cStru 的地址, 所以要先申请一块用于中转的存储块并复制过去 *!* 我们不得不绕一个大弯来完成这项工作!!! *!* ------------------------------------------------------------------------------- *!* 下面注释中, A 代表 vfp 程序所在进程, B 代表树控件所在进程 *!* ------------------------------------------------------------------------------- *!* 先在 B 中分配一块存储器用于存放结构和节点文字信息(属于 B) m.pStru = VirtualAllocEx( m.thProc, 0, 0x300, MEM_COMMIT, PAGE_READWRITE ) IF ( 0 == m.pStru ) MESSAGEBOX( "分配存储器失败 !" ) RETURN "" ENDIF m.pText = m.pStru + 0x100 *!* 我们只获取节点的文字信息 m.cStru = BINTOC( TVIF_TEXT, "rs" ) ; && 构造节点信息结构 + BINTOC( m.thItem, "rs" ) + REPLICATE( CHR(0), 2*4 ) ; + BINTOC( m.pText, "rs" ) + BINTOC( 0x200, "rs" ) ; + REPLICATE( CHR(0), 8*4 ) m.iSize = LEN( m.cStru ) *!* 申请一块用于中转的存储块(属于 A), 先申请大一点, 以后还要用来存储节点文字 m.pTransit = GlobalAlloc( GMEM_FIXED, 0x200 ) SYS( 2600, m.pTransit, m.iSize, m.cStru ) && 将 cStru 复制过去 m.cText = "" *!* 将中转站中的结构复制到 B 中刚分配的存储块内 IF ( 0 == WriteProcessMemory( m.thProc, m.pStru, m.pTransit, m.iSize, 0 )) MESSAGEBOX( "复制结构到树控件所在进程失败 !" ) ELSE *!* 发消息给 B 中树控件获取节点信息 IF ( 0 == sendmsg_nn( m.thWnd, TVM_GETITEM, 0, m.pStru )) MESSAGEBOX( "取节点信息失败 !" ) ELSE *!* 将获取的信息从 B 中复制回我们的中转存储器 ReadProcessMemory( m.thProc, m.pText, m.pTransit, 0x200, 0 ) m.cText = SYS( 2600, m.pTransit, 0x200 ) m.cText = LEFT( m.cText, AT( CHR(0), m.cText )-1 ) ENDIF ENDIF *!* 释放分配的存储块 GlobalFree( m.pTransit ) VirtualFreeEx( m.thProc, m.pStru, 0, MEM_RELEASE ) RETURN m.cText ENDPROC ENDDEFINE |
- 跨进程获取树控件节点信息
- 研究关于跨进程信息获取
- 跨进程点击treeview节点
- ZTree 获取节点信息
- 跨进程 获取 syslistview32 内容
- XE6 跨进程获取SysListView32
- DOM节点信息的获取
- GetProcAddressEx跨进程获取导出函数地址
- GetWindowLong跨进程获取WndProc、DlgProc。。。
- GetProcAddressEx跨进程获取导出函数地址
- SharedPreferences跨应用跨进程获取数据
- SharePreferences应用跨进程获取数据
- 安卓 SharePreferences 跨进程获取数据
- 跨进程实现在Tree中快速定位节点
- 跨进程实现在Tree中快速定位节点
- 获取CTreeCtrl树控件某个节点的路径
- jquery zTree树开发实例之点击树节点获取节点信息显示到表格中
- VS2010-MFC获取某个树控件某个树节点下所有子节点的文本
- 汉语编程专利检索,实际上只是单片机的专利而已
- RepositionBars的用法和参数的意义(引用别人的)
- Code Smith第一步
- yaffs2申请Cache
- C#调用API[转]
- 跨进程获取树控件节点信息
- IDS 入侵检测系统
- C#: 监测 USB plugin and plugout
- 同时启动两个QTP的办法
- 提高网速的3种有效方法--XP系统用户请进
- 好书推荐《Founders at work: Stories of Startups’ Early Days》
- 关于jade!
- mmp中重复应用cpp文件的问题
- 6月Facebook独立访问用户增34% 高于MySpace