-1

I want to send message about mouse double click to selected item in treeview in other application with winapi.

  • How get the handle of selected item in treeview in other application?

p.s. In spy++ i get handle only for treeview.

Thank you!

  • 3
    Stop faking input. Start getting used to [UI Automation](https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview). Once you're there, you'll see that you get access to individual items in a treeview control (assuming the target control implements the UI Automation interfaces). – IInspectable Apr 06 '18 at 14:53
  • What are you talking about hysterical??? If you don't know, how to get handle selected item in treeview, then pass by. Thank you, for your time. – Iichi Kohuro Apr 06 '18 at 15:03
  • 4
    Treeview items do not have window handles associated with them. – IInspectable Apr 06 '18 at 15:24
  • Thank you for answer. Honestly, the first time I've heard of UI Automation. – Iichi Kohuro Apr 07 '18 at 07:04
  • @IInspectable a standard Win32 TreeView implements support for UI Automation – Remy Lebeau Apr 07 '18 at 15:54
  • @RemyLebeau: All standard common controls implement the accessibility interfaces. Since the OP didn't identify their target application, I pointed out, that this is a requirement. Not everything that looks like a treeview control is in fact a standard treeview control. – IInspectable Apr 07 '18 at 16:18

2 Answers2

0

This is best handled using UI Automation, which exposes access to UI elements, like TreeView controls and TreeView items, using COM interfaces:

  • create an instance of the CUIAutomation COM object via CoCreateInstance() and get its IUIAutomation interface.

  • call its ElementFromHandle() method to retrieve an IUIAutomationElement interface for the TreeView window.

  • call its FindFirst() method to find the first selected item and get its IUIAutomationElement interface.

  • call its GetCurrentPattern() method to get an IUnknown interface for the Invoke control pattern.

  • query it for the IUIAutomationInvokePattern interface, and then call its Invoke() method

But, if you want to stick with just window messages, you can do the following:

  • get the selected item using the TVM_GETNEXTITEM message, specifying TVGN_CARET for the wParam.

  • get the rectangle of the item's text within the TreeView's client area using the TVM_GETITEMRECT message, specifying TRUE for its wParam.

    However, this requires manually marshaling a RECT across process boundaries, as the RECT must exist in the same process that owns the TreeView.

    • use GetWindowThreadProcessId() to get the ID of the process that owns the TreeView

    • use OpenProcess() to get a HANDLE to the process ID, requesting PROCESS_VM_OPERATION and PROCESS_VM_READ access

    • use VirtualAllocEx() to allocate a RECT in the process

    • pass that allocated pointer in the lParam of TVM_GETITEMRECT

    • use ReadProcessMemory() to read the RECT data

    • free the memory with VirtualFreeEx()

    • close the process handle with CloseHandle().

  • send a WM_LBUTTONDBLCLK message to the TreeView window, specifying X,Y coordinates that are within the item text's rectangle.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Read about UI Automation, and I did not understand how to work with it. Maybe you have a sample or article about it. It would be great. Thank you. – Iichi Kohuro Apr 07 '18 at 07:08
  • UI Аutomation is not suitable, because the window should be visible and the mouse cursor will jump (if you press a button), which is not very pleasant when the cursor disappears and it has to be searched. And I need to prevent this from being visible to the end user. But the second option is more suitable. I'll try. Thanks for the advice! – Iichi Kohuro Apr 07 '18 at 12:55
  • p.s. work with TestStackWhite, the link: http://teststackwhite.readthedocs.io/en/latest/ – Iichi Kohuro Apr 07 '18 at 12:57
  • 1
    @IichiKohuro: UI Automation does *not* require, that the target application be visible, nor does it actually move the mouse. – IInspectable Apr 07 '18 at 17:27
  • @IInspectable I've tested the teststack.white, I thought this based on UI Automation. OK then, does anyone have a C# sample. In the internet I did not find understandable samples about it. – Iichi Kohuro Apr 07 '18 at 17:51
0

I Know this is old, but I tried many ways to make it work in VB.net. I need to create 2 different structures for RECT because it was returning wrong values for the RECT position with Long parameters. Here is the example, you can easily convert to C#

 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim SelNodeRectangle As RECT = GetTreeViewSelectedNodeRECT(TreeViewPtr)
End Sub

Private Const TV_FIRST As Integer = &H1100
Private Const TVGN_CARET As Integer = &H9
Private Const TVM_GETNEXTITEM As Integer = (TV_FIRST + 10)
Private Const TVM_GETITEMRECT As Integer = (TV_FIRST + 4)

Private Const PROCESS_VM_OPERATION As Integer = &H8
Private Const PROCESS_VM_READ As Integer = &H10
Private Const PROCESS_VM_WRITE = (&H20)

Private Const PAGE_READWRITE As Long = &H4
Private Const MEM_COMMIT As Long = &H1000&
Private Const MEM_RELEASE As Long = &H8000&

Private Structure RECTPointer
    Public Left As IntPtr
End Structure

Private Structure RECT
    Public Left As Integer
    Public Top As Integer
    Public Right As Integer
    Public Bottom As Integer
End Structure

<Flags>
Public Enum AllocationType
    Commit = &H1000
    Reserve = &H2000
    Decommit = &H4000
    Release = &H8000
    Reset = &H80000
    Physical = &H400000
    TopDown = &H100000
    WriteWatch = &H200000
    LargePages = &H20000000
End Enum


Private Declare Function SendMessageA Lib "user32" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Long) As IntPtr

Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByRef buffer As RECTPointer, ByVal dwSize As Integer, ByVal lpNumberOfBytesWritten As IntPtr) As Boolean

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As IntPtr, ByRef lpdwProcessID As Integer) As Integer

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr

Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal flAllocationType As UInteger, ByVal flProtect As UInteger) As IntPtr

Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByVal lpBaseAddress As Long, ByVal lpBuffer As Long, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer


Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As IntPtr,
                  ByVal lpAddress As IntPtr,
                  ByVal dwSize As Integer,
                  ByVal dwFreeType As AllocationType) As Boolean


Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Bool)> Boolean

Private Function GetTreeViewSelectedNodeRECT(TreeViewPTR As IntPtr) As RECT


    Const dwBufferSize As Integer = 1024
    Dim dwProcessID As Integer
    Dim processHandle As IntPtr = IntPtr.Zero
    Dim remotePointerBuffer As IntPtr = IntPtr.Zero
    Dim localPointerBuffer As IntPtr = IntPtr.Zero
    Dim threadId As IntPtr = IntPtr.Zero


    localPointerBuffer = Runtime.InteropServices.Marshal.AllocHGlobal(dwBufferSize)

    threadId = GetWindowThreadProcessId(TreeViewPTR, dwProcessID)

    processHandle = OpenProcess(PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, dwProcessID)


    remotePointerBuffer = VirtualAllocEx(processHandle, IntPtr.Zero, dwBufferSize, MEM_COMMIT, PAGE_READWRITE)

    Dim bSuccess As Boolean
    Dim tvItem As New RECTPointer()

    Dim SelectedNodePTR As IntPtr = SendMessageA(TreeViewPTR, TVM_GETNEXTITEM, TVGN_CARET, 0)
    tvItem.Left = SelectedNodePTR

    bSuccess = WriteProcessMemory(processHandle, remotePointerBuffer, tvItem, Runtime.InteropServices.Marshal.SizeOf(GetType(RECTPointer)), IntPtr.Zero)
    If Not bSuccess Then
        Throw New SystemException("Fail writing to process memory!")
    End If

    SendMessageA(TreeViewPTR, TVM_GETITEMRECT, 0, remotePointerBuffer)

    bSuccess = ReadProcessMemory(processHandle, remotePointerBuffer, localPointerBuffer, dwBufferSize, IntPtr.Zero)

    If Not bSuccess Then
        Throw New SystemException("Fail reading from process memory!")
    End If



    Dim returnRECT As RECT = Runtime.InteropServices.Marshal.PtrToStructure(localPointerBuffer, GetType(RECT))

    If localPointerBuffer <> IntPtr.Zero Then
        Runtime.InteropServices.Marshal.FreeHGlobal(localPointerBuffer)
    End If
    If remotePointerBuffer <> IntPtr.Zero Then
        VirtualFreeEx(processHandle, remotePointerBuffer, 0, MEM_RELEASE)
    End If
    If processHandle <> IntPtr.Zero Then
        CloseHandle(processHandle)
    End If

    Return returnRECT
End Function