要操作的目标进程是别人用VC++开发的外部程序。
现在我要在自己的程序里模拟鼠标双击目标进程内的TreeView控件中的节点。
已经获得:
要操作的进程句柄、TreeView控件句柄、要双击的节点的句柄、目标进程句柄、目标进程ID。
可以正确展开TreeView节点、可以选择TreeView节点:
SendMessage(TreeViewHwnd, TVM_SELECTITEM, TVGN_CARET, ItemHwnd);
如何发送消息双击该节点?不要问我双击节点的用途是什么,我仅仅是模仿鼠标双击该节点的操作,而不是要选择该节点或展开该节点。

解决方案 »

  1.   

    如果要模拟双击,用消息是不行的,要用SendInput或mouse_event。
    如果要展开节点或者要让TreeView的父窗口响应双击事件,可以用消息。
      

  2.   

    发送WM_LBUTTONDBLCLK消息试一试wParam和lParam的定义在下面,其中鼠标位置需要取得.
    可以通过TVM_GETITEMRECT消息取得
    SendMessage(TreeViewHwnd, TVM_GETITEMRECT, fItemRect , prc );TVM_GETITEMRECT:
    fItemRect 
    Boolean value that specifies the portion of the item for which to retrieve the bounding rectangle. If set to TRUE, the bounding rectangle includes only the text of the item. If set to FALSE, it includes the entire line that the item occupies in the tree view control. 
    prc 
    Pointer to a RECT structure that, when sending the message, contains the handle of the item to retrieve the rectangle for. For more information on how to place the item handle in this parameter, see the Res section. After returning from the message, this parameter contains the bounding rectangle. The coordinates are relative to the upper-left corner of the tree view control. 
    WM_LBUTTONDBLCLK:
    wParam
    Indicates whether various virtual keys are down. This parameter can be one or more of the following values. 
    MK_CONTROL
    The CTRL key is down.
    MK_LBUTTON
    The left mouse button is down.
    MK_MBUTTON
    The middle mouse button is down.
    MK_RBUTTON
    The right mouse button is down.
    MK_SHIFT
    The SHIFT key is down.
    MK_XBUTTON1
    Windows 2000/XP: The first X button is down.
    MK_XBUTTON2
    Windows 2000/XP: The second X button is down.
    lParam
    The low-order word specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. The high-order word specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. 
      

  3.   

    我想过控制鼠标移动和双击来实现,但这样有个缺点,如果TreeView的节点很多,我要双击的节点不在屏幕可视区域就没办法了。 
      

  4.   

    使用 TVM_ENSUREVISIBLE消息:TVM_ENSUREVISIBLE Message
    Ensures that a tree-view item is visible, expanding the parent item or scrolling the tree-view control, if necessary. You can send this message explicitly or by using the TreeView_EnsureVisible macro. ParameterswParam
    Must be zero.
    hitem
    Handle to the item. 
    Return ValueResIf the TVM_ENSUREVISIBLE message expands the parent item, the parent window receives the TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification messages.参考:
    http://msdn.microsoft.com/en-us/library/bb773566(VS.85).aspx
      

  5.   

    先移动窗口确保TreeView全部可见,再向其发TVM_ENSUREVISIBLE消息,让节点可见,然后发TVM_GETITEMRECT消息获取节点位置(注意,要在目标进程中分配内存),再控制移动鼠标和模拟点击。
      

  6.   

    谢谢cnzdgs、findcaiyzh 这2位朋友的建议。我试试看。
      

  7.   

    cnzdgs 能否给一段代码?以下代码好象得不到正确的值,是否哪里错了。[DllImport("user32.dll ", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, RECT rect); [DllImport("kernel32 ", CharSet = CharSet.Unicode)]
    public static extern int CopyMemory(RECT Destination, IntPtr Source, int Length);[StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
           public int left;
           public int top;
           public int right;
           public int bottom;
    }public static RECT GetItemRECT(IntPtr TreeViewHwnd, IntPtr ItemHwnd)
    {
            IntPtr rect = Marshal.SizeOf(typeof(RECT));
            RECT itemRect = new RECT();
            SendMessage(TreeViewHwnd, TVM_GETITEMRECT, 0, rect);  
            CopyMemory(itemRect, rect, Marshal.SizeOf(typeof(RECT)));
            Marshal.FreeHGlobal(rect);
            return itemRect;
    }
      

  8.   

    我前面说了,要在目标进程中分配内存,看一下VirtualAllocEx和ReadProcessMemory这两个API。
      

  9.   

    我使用以下代码还是拿不到RECT,请指教!
    public static RECT GetTreeViewItemRECT(IntPtr TreeViewHandle, IntPtr TreeItemHandle)
    {
        uint vProcessId;
        GetWindowThreadProcessId(TreeViewHandle, out vProcessId);
        IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, vProcessId);
        IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        RECT[] rect = new RECT[1];
        try
        {
            uint vNumberOfBytesRead = 0;
            WriteProcessMemory(vProcess, vPointer, Marshal.UnsafeAddrOfPinnedArrayElement(rect, 0), Marshal.SizeOf(typeof(RECT)), ref vNumberOfBytesRead);
            SendMessage(TreeViewHandle, TVM_GETITEMRECT, false, (int)vPointer);
            ReadProcessMemory(vProcess, (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(RECT))), Marshal.UnsafeAddrOfPinnedArrayElement(rect, 0), Marshal.SizeOf(typeof(RECT)), ref vNumberOfBytesRead);
        }
        finally
        {
            VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
            CloseHandle(vProcess);
        }
        return rect[0];
    }
      

  10.   

    给你一段C++代码参考一下:
    bool GetTreeItemRect(HWND TreeView, HTREEITEM TreeItem, RECT& Rect)
    {
    bool result = false;
    DWORD processId = 0;
    GetWindowThreadProcessId(TreeView, &processId);
    HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, processId);
    if (process != NULL)
    {
    LPVOID buffer = VirtualAllocEx(process, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (buffer != NULL)
    {
    SIZE_T bytes;
    WriteProcessMemory(process, buffer, &TreeItem, sizeof(TreeItem), &bytes);
    result = (SendMessage(TreeView, TVM_GETITEMRECT, FALSE, (LPARAM)buffer) != 0);
    ReadProcessMemory(process, buffer, &Rect, sizeof(Rect), &bytes);
    VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
    }
    CloseHandle(process);
    }
    return result;
    }
      

  11.   

    result = (SendMessage(TreeView, TVM_GETITEMRECT, FALSE, (LPARAM)buffer) != 0);
    这一句每次都返回是false,每次调用这个API都不成功,不知道什么原因。。
    读取内存后RECT结构都是0,0,0,0
      

  12.   

    public static bool GetTreeViewItemRECT(IntPtr TreeViewHandle, IntPtr TreeItemHandle,ref RECT[] Rect)
    {
        bool result = false;
        uint processId;
        GetWindowThreadProcessId(TreeViewHandle, out processId);
        IntPtr process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, processId);
        IntPtr buffer = VirtualAllocEx(process, IntPtr.Zero, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        try
        {
            uint bytes = 0;
            WriteProcessMemory(process, buffer, TreeItemHandle, Marshal.SizeOf(TreeItemHandle), ref bytes);
            result = SendMessage(TreeViewHandle, TVM_GETITEMRECT, false, buffer);
            ReadProcessMemory(process, buffer, Marshal.UnsafeAddrOfPinnedArrayElement(Rect, 0), Marshal.SizeOf(typeof(RECT)), ref bytes);
        }
        finally
        {
            VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
            CloseHandle(process);
        }
        return result;
    }
    代码是这样的。
      

  13.   

    WriteProcessMemory的第3个参数需要的是TreeItemHandle的地址,估计是这里错了。
      

  14.   

    WriteProcessMemory方法里第三个参数我确实是用的TreeItemHandle的地址啊。晕。。
      

  15.   

    我查了MSDN,SendMessage第3个参数用True就只返回包含文字的矩形,False返回整个TreeItem的矩形。
    SendMessage(TreeViewHandle, TVM_GETITEMRECT, false, buffer);可是我这样发消息每次返回值都是false,说明不成功。。接下来从内存中复制出来的RECT肯定是0,0,0,0
    因为SendMessage都没调用成功。。
      

  16.   

    GetTreeViewItemRECT函数的参数TreeItemHandle是一个句柄值,在调用WriteProcessMemory时需要的是储存这个句柄值的地址。
      

  17.   

    使用TreeItemHandle句柄所在的内存地址果然可以得到正确的RECT。。感谢cnzdgs的鼎立帮助。结贴给分。
      

  18.   

    TreeItemHandle句柄所在的内存地址怎么得到?谢谢!