2016-01-10 1 views
0

Я провел пару часов, просачиваясь через Dev Center от Microsoft; Однако, я не могу показаться, чтобы выяснить, как сделать следующие две вещи:Как правильно выполнить цикл/получить текст/выбрать элемент окна SysTreeView32

  1. Цикл через и просмотреть названия каждой программы в разделе «Советники» подпункта окна «Навигатор» (для пример «MACD Sample» на снимке экрана ниже)

  2. выберите и дважды щелкните по программе (например, «MACD Sample»).

Winspector(Left) | Application(Right)

Моя главная проблема, кажется, что я не знаю, как правильно использовать HTREEITEM для доступа к информации. Я заметил, что есть функция ListView_GetItemText, но мне не удалось найти TreeView_GetItemText или эквивалентную функцию.

Любая помощь была бы принята с благодарностью.

Ниже основная функция моей программы:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    wcout << TEXT("Enumerating Windows...") << endl; 
    HWND handle = NULL; 

    //--- Success: gets application handle 
    bool success1 = getHandle(L"MetaTrader", L"20", handle); 

    cout << "Success1: " << success1 << endl; 
    cout << "Result1: " << handle << endl; 

    //--- Success: gets navigator window 
    bool success2 = getChildHandle(handle, L"", L"Navigator", handle); 

    cout << "Success2: " << success2 << endl; 
    cout << "Result2: " << handle << endl; 

    //--- Success: gets "SysTreeView32" handle 
    handle = FindWindowEx(handle, 0, L"SysTreeView32", L""); 

    cout << "Result3: " << handle << endl; 

    //--- Success: get "SysTreeView32" root nod 
    HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT); 
    cout << "root: " << root << endl; 

    return 0; 
} 

Результат выполнения кода, кажется, работает правильно

Весь код для полноты:

// MT4Terminal-test.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

#pragma once 

#include "targetver.h" 


#include <iostream> 
#include <map> 
#include <string> 

namespace std { 
#if defined _UNICODE || defined UNICODE 
    typedef wstring tstring; 
#else 
    typedef string tstring; 
#endif 
} 

#include <stdio.h> 
#include <tchar.h> 
#include <Windows.h> 
#include <psapi.h> 

#include <Windows.h> 
#include <Commctrl.h> 
#include <windows.system.h> 

using namespace std; 

HWND glb_handle; 
tstring glb_searchWindowTitle; 
tstring glb_seachClassName; 

BOOL CALLBACK enumWindowsChildProc(
    __in HWND hWnd, 
    __in LPARAM lParam 
    ) { 


    return TRUE; 
} 

BOOL CALLBACK enumWindowsProc(
    __in HWND hWnd, 
    __in LPARAM lParam 
    ) { 

    int length = ::GetWindowTextLength(hWnd); 
    if (0 == length) return TRUE; 

    TCHAR* bufferA; 
    bufferA = new TCHAR[length + 1]; 
    memset(bufferA, 0, (length + 1) * sizeof(TCHAR)); 

    TCHAR* bufferB; 
    bufferB = new TCHAR[100]; 
    memset(bufferB, 0, 100 * sizeof(TCHAR)); 

    GetWindowText(hWnd, bufferA, length + 1); 
    GetClassName(hWnd, bufferB, 100); 
    tstring windowTitle = tstring(bufferA); 
    tstring className = tstring(bufferB); 
    delete bufferA; 
    delete bufferB; 

    if (windowTitle.find(glb_searchWindowTitle) < string::npos && 
     className.find(glb_seachClassName) < string::npos) 
      glb_handle = hWnd; 

    wcout.clear(); 

    return TRUE; 
} 

bool getHandle(wstring searchClassName, wstring searchWindowTitle, HWND &handle) 
{ 
    handle = NULL; 
    glb_handle = NULL; 
    glb_searchWindowTitle = searchWindowTitle; 
    glb_seachClassName = searchClassName; 
    BOOL enumeratingWindowsSucceeded = EnumWindows(enumWindowsProc, NULL); 

    if (enumeratingWindowsSucceeded) 
    { 
     if (glb_handle != NULL) 
     { 
      handle = glb_handle; 
      return true; 
     } 
    } 

    glb_handle = NULL; 
    glb_searchWindowTitle = L""; 
    glb_seachClassName = L""; 
    return false; 
} 

bool getChildHandle(HWND parent_handle, wstring searchClassName, wstring searchWindowTitle, HWND &handle) 
{ 
    handle = NULL; 
    glb_handle = NULL; 
    glb_searchWindowTitle = searchWindowTitle; 
    glb_seachClassName = searchClassName; 
    BOOL enumeratingWindowsSucceeded = EnumChildWindows(parent_handle, enumWindowsProc, NULL); 

    if (enumeratingWindowsSucceeded) 
    { 
     if (glb_handle != NULL) 
     { 
      handle = glb_handle; 
      return true; 
     } 
    } 

    glb_handle = NULL; 
    glb_searchWindowTitle = L""; 
    glb_seachClassName = L""; 
    return false; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    wcout << TEXT("Enumerating Windows...") << endl; 
    HWND handle = NULL; 

    //--- Success: gets application handle 
    bool success1 = getHandle(L"MetaTrader", L"20", handle); 

    cout << "Success1: " << success1 << endl; 
    cout << "Result1: " << handle << endl; 

    //--- Success: gets navigator window 
    bool success2 = getChildHandle(handle, L"", L"Navigator", handle); 

    cout << "Success2: " << success2 << endl; 
    cout << "Result2: " << handle << endl; 

    //--- Success: gets "SysTreeView32" handle 
    handle = FindWindowEx(handle, 0, L"SysTreeView32", L""); 

    cout << "Result3: " << handle << endl; 

    //--- Success: get "SysTreeView32" root nod 
    HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT); 
    cout << "root: " << root << endl; 

    return 0; 
} 

Выбор SysTreeView32 позиция

(Для пояснения, когда я говорю о выборе элемента SysTreeView32, я имею в виду имитацию операции двойного щелчка на узле дерева - подобно тому, как можно дважды щелкнуть значок на своем рабочем столе, чтобы открыть программу)

После просмотра документации, я убежден:

  1. Там не существует явное сообщение, которое будет имитировать дважды щелкнув узел на дереве, используя дескриптор элемента древовидной

  2. Возможной работой было бы отправить сообщение TVM_GETITEMRECT, чтобы получить координаты дерева no de, а затем использовать SendInput() для отправки щелчка

Правильно ли указаны эти два утверждения?

После реализации кода Barmak Shemirani я попытался реализовать № 2 выше, используя ту же методологию, что и в исправлении Barmak Shemirani. В частности, я попытался выделить структуру Rect в другой памяти прикладной программы с помощью VirtualAllocEx(), вызвать макрос TreeView_GetItemRect в моей программе с указателем на прямоугольник и прочитать результаты с помощью ReadProcessMemory().

Однако моя программа вылетает, когда я вызываю TreeView_GetItemRect(), передавая указатель на Rect в другой памяти приложений. Скорее всего, потому что TreeView_GetItemRect() пытается записать координаты Rect в неверный адрес памяти.Это заставило меня понять, что я не очень понимаю, что делает макрос:

  1. Следовательно, проверяя источник, я нашел:

    #define HELLO 
    #define TV_FIRST    0x1100  // TreeView messages 
    
    #define TVM_GETITEMRECT   (TV_FIRST + 4) 
    #define TreeView_GetItemRect(hwnd, hitem, prc, code) \ 
    (*(HTREEITEM *)(prc) = (hitem), (BOOL)SNDMSG((hwnd), TVM_GETITEMRECT, (WPARAM)(code), (LPARAM)(RECT *)(prc))) 
    

основном я понимаю все, кроме для части до функции SNDMSG:

(*(HTREEITEM *)(prc) = (hitem), 

Что именно означает это утверждение? Является ли это отбрасыванием указателя прямоугольника, который я передаю указателю HTREEITEM, что каким-то образом вызывает сбой программы?

Screenshot of console freezing

Новый код

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    wcout << TEXT("Enumerating Windows...") << endl; 
    HWND handle = NULL; 

    //--- Success: gets application handle 
    bool success1 = getHandle(L"MetaTrader", L"20", handle); 

    //--- Success: gets navigator window 
    bool success2 = getChildHandle(handle, L"", L"Navigator", handle); 

    //--- Success: gets "SysTreeView32" handle 
    handle = FindWindowEx(handle, 0, L"SysTreeView32", L""); 

    //--- Success: get "SysTreeView32" root nod 
    HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT); 

    unsigned long pid; 

    GetWindowThreadProcessId(handle, &pid); 

    HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | 
    PROCESS_QUERY_INFORMATION, FALSE, pid); 

    TVITEM item, *_item; 

    wchar_t buf[CHAR_BUF_LEN]; 
    wchar_t *_buf; 
    memset(buf, 0, sizeof(buf)/sizeof(buf[0])); 

    _item = (TVITEM*)VirtualAllocEx(process, NULL, sizeof(TVITEM), MEM_COMMIT, PAGE_READWRITE); 
    _buf = (wchar_t*)VirtualAllocEx(process, NULL, CHAR_BUF_LEN, MEM_COMMIT, PAGE_READWRITE); 

    item.cchTextMax = CHAR_BUF_LEN; 
    item.pszText = _buf; 
    item.mask = TVIF_TEXT; 

    //--- find Experts Advisors branch in tree 
    HTREEITEM node = TreeView_GetNextItem(handle, root, TVGN_CHILD); 
    node = TreeView_GetNextItem(handle, node, TVGN_NEXT); 
    node = TreeView_GetNextItem(handle, node, TVGN_NEXT); 

    RECT rect, *_rect; 

    _rect = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE); 

    rect = { 0 }; 

    WriteProcessMemory(process, _rect, &rect, sizeof(RECT), NULL); 

    //--- step into Expert Advisors 
    node = TreeView_GetNextItem(handle, node, TVGN_CHILD); 

    //--- target program to open 
    wchar_t ea_name[] = L"MACD Sample"; 

    while (node != NULL) 
    { 
     ZeroMemory(buf, CHAR_BUF_LEN); 

     item.hItem = node; 

     //Binds item and _item 
     WriteProcessMemory(process, _item, &item, sizeof(TVITEM), NULL); 

     TreeView_GetItem(handle, _item); 

     //Read buffer back to this program's process memory 
     ReadProcessMemory(process, _buf, buf, CHAR_BUF_LEN, NULL); 

     //Print program name 
     wcout << buf << endl; 

     if (wcscmp(ea_name, buf) == 0) 
     { 
      cout << "Found target program: " << ea_name << endl; 
      cout << "get rectangle coordinates: " << TreeView_GetItemRect(handle, node, _rect, TRUE) << endl; 
     } 

     node = TreeView_GetNextItem(handle, node, TVGN_NEXT); 
    } 

    VirtualFreeEx(process, _item, 0, MEM_RELEASE); 
    VirtualFreeEx(process, _buf, 0, MEM_RELEASE); 
    VirtualFreeEx(process, _rect, 0, MEM_RELEASE); 

    return 0; 
} 

ответ

2

Это метод, который вы обычно используете для чтения текста в виде TreeView элемента:

wchar_t buf[100]; 
memset(buf, 0, sizeof(buf)); 
TVITEM item = { 0 }; 
item.hItem = hitem; 
item.cchTextMax = 100; 
item.pszText = buf; 
item.mask = TVIF_TEXT; 
TreeView_GetItem(hwnd, &item); 

Это не будет работать в вашей программе. TreeView_GetItem - макрос на основе SendMessage, он копирует данные через параметр LPARAM. Но этот обмен не допускается между различными процессами.

Вы могли провести часы, возможно, дней, пытаясь взломать его (See this example)

Или вы можете исследовать и посмотреть, если целевая программа поддерживает UI Automation


Редактировать, вот пример, чтобы получить HTREEITEM текст. Это не будет работать, если:

  • вызывающего абонента и целевая программа не являются 32-разрядные, или оба 64-разрядные
  • вызывающего абонента и целевая программа оба являются Юникод

Если целевая программа является ANSI, то измените эту функцию на ANSI.

HTREEITEM hitem = TreeView_GetSelection(hwndTree); 
if (!hitem) 
    debug << "!hitem\n"; 

const int buflen = 512; 

DWORD pid; 
GetWindowThreadProcessId(hwndTree, &pid); 
HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE 
    | PROCESS_QUERY_INFORMATION, FALSE, pid); 
TVITEMEX* ptv = (TVITEMEX*)VirtualAllocEx(process, NULL, sizeof(TVITEMEX), 
    MEM_COMMIT, PAGE_READWRITE); 
wchar_t* pbuf = (wchar_t*)VirtualAllocEx(process, NULL, buflen, 
    MEM_COMMIT, PAGE_READWRITE); 

TVITEMEX tv = { 0 }; 
tv.hItem = hitem; 
tv.cchTextMax = buflen/2; 
tv.pszText = pbuf; 
tv.mask = TVIF_TEXT | TVIF_HANDLE; 

WriteProcessMemory(process, ptv, &tv, sizeof(TVITEMEX), NULL); 

if (SendMessageW(hwndTree, TVM_GETITEM, 0, (LPARAM)(TVITEMEX*)(ptv))) 
{ 
    wchar_t buf[buflen/2]; 
    ReadProcessMemory(process, pbuf, buf, buflen, 0); 
    debug << "Result:" << buf << "\n"; 
} 
else 
    debug << "!SendMessageW\n"; 

VirtualFreeEx(process, ptv, 0, MEM_RELEASE); 
VirtualFreeEx(process, pbuf, 0, MEM_RELEASE); 
CloseHandle(process); //*** I forgot this line before 
+0

Это * может быть сделано между различными процессами, это просто не тривиально. Я помню [письмо] (http://stackoverflow.com/questions/4857602/get-listview-items-from-other-windows/4857746#4857746) о том, как это сделать для ListView давным-давно. ListViews и TreeViews достаточно похожи, чтобы кто-то со знанием Win32 мог адаптировать код, чтобы делать то, что он хочет. Разумеется, классы-помощники .NET CLR намного проще. –

+0

Большое спасибо за ваши сообщения; Я бы никогда не думал, что SendMessage() не может использоваться в разных процессах. Я заглянул в UI Automation, но это довольно старое приложение и, похоже, не поддерживает его. Я внедрил предложенное предложение без особых проблем; однако я столкнулся с трудностями при попытке сделать небольшое расширение. – user2079438

+0

Я перепечатывал свое оригинальное сообщение, чтобы включить: Выбор элемента SysTreeView32 – user2079438

Смежные вопросы