2 rocmemory rocmemory 于 2014.12.08 22:21 提问

跨进程控制SysTreeView32树状图控件的难题
最近公司在做一个智能化股票交易的项目,需要控制股票交易软件树状图进行翻页,刚开始我使用普通的WM_LBUTTONDOWN和WM_LBUTTONUP消息进行发送,发现只是实现了选择树状图节点,而没有达到实际效果,也就是控制页面跳转,遂怀疑是WM_NCHITTEST的问题,可是加入了WM_NCHITTEST消息,并把截获的消息全部依次发送后,仍无法成功。
后来发现在WM_LBUTTONUP消息后,有一个关键的TVM_HITTEST我没有进行处理,此时得知这个SysTreeView32的触发事件并不是ItemSelected,而是在鼠标点击时通过HitTest获取点击的节点,再触发下一步动作。
TVM_HITTEST消息向树状图发送了一个地址,这个地址指向一个TV_HITTESTINFO结构数据:
public struct TV_HITTESTINFO
{
    public POINTAPI pt;
    public int flags;
    public UInt32 hItem;
}
实际情况下发送的数据是这样的:
![](http://113.11.198.136:82/FileStore/1.jpg)
如果仅仅使用普通的鼠标模拟消息发送过去,pt的坐标值均为负数,显然达不到效果。可是我用WriteProcessMemory将有效的坐标值和节点句柄写入这个地址再发送过去发现,TVM_HITTEST消息中的pt坐标值会瞬间变为负值,仍然失败。如果新申请一个内存进行写入,结果相同。
![](http://113.11.198.136:82/FileStore/2.jpg)

以下是用spy++截获的关键消息:
![](http://113.11.198.136:82/FileStore/3.jpg)

以下是模拟点击的代码,其中一些十六进制数值已根据实际数值进行替换
class Test
{

    private int Handle, MainHandle, PID;

    public Test(int handle, int mainHandle, int pid)
    {
        this.Handle = handle; this.MainHandle = mainHandle; this.PID = pid;
        pHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, PID);
    }

    public void SelectTreeItem()
    {
        int rtn;
        //windows鼠标消息
        rtn = SendMessage(Handle, WM_NCHITTEST, 0x0, 0xAB0068);  //0xAB0068是屏幕鼠标的坐标
        rtn = SendMessage(Handle, WM_MOUSEACTIVATE, MainHandle, 0x2010001);
        rtn = SendMessage(Handle, WM_SETCURSOR, Handle, 0x2010001);
        //左键按下
        rtn = PostMessage(Handle, WM_LBUTTONDOWN, 0x1, 0x9001D);  //0x9001D是树状图内鼠标点击的相对坐标
        //刷新树状图
        rtn = SendMessage(Handle, WM_GETDLGCODE, 0x0, 0x0);
        rtn = PostMessage(Handle, WM_PAINT, 0x0, 0x0);
        //左键弹起
        rtn = PostMessage(Handle, WM_LBUTTONUP, 0x0, 0x9001D);  //0x9001D是树状图内鼠标点击的相对坐标
        //改变节点
        rtn = SendMessage(Handle, WM_CAPTURECHANGED, 0x0, 0x0);
        //HitTest欺骗
        Hittest();
        rtn = SendMessage(Handle, TVM_HITTEST, 0x0, 0x18F2AC);  //0x18F2AC储存TV_HITTESTINFO结构数据
        //选择节点
        rtn = SendMessage(Handle, TVM_SELECTITEM, 0x9, 0x661770); //0x661770是目标节点的句柄(根据实际情况更改)
        rtn = SendMessage(Handle, WM_NCHITTEST, 0x0, 0xAB0068);
    }

    private void Hittest()
    {
        TV_HITTESTINFO NewItem = default(TV_HITTESTINFO);
        NewItem.pt = new POINTAPI(29, 9);
        NewItem.flags = TVHT_ONITEM | TVHT_ONITEMRIGHT | TVHT_BELOW | TVHT_TORIGHT | TVHT_TOLEFT | 0x77615080;
        NewItem.hItem = 0xF4F25FD0;
        WriteProcessMemory(pHandle, 0x18F2AC, NewItem, Marshal.SizeOf(typeof(TV_HITTESTINFO)), 0);
    }

}

地址0x18F2AC也尝试过自行申请内存,结果同样。 无论怎么控制TV_HITTESTINFO数据,它的pt坐标总会变成负数。
被这个问题困扰了很久,尝试过各种办法均告失败,请各位老师帮忙看看,还有没有成功的可能性。

1个回答

u011512808
u011512808   2015.10.01 10:02
已采纳

打开进程
申请内存
填充结构
发送消息 sendmessage TVM_HITTEST,0,内存地址
读取内存
这样就可以跨进程操作了 ,我目前只做到遍历节点信息和坐标,获取节点状态还不会,如果你研究出来了告诉我下

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!