Почему сообщение TVM_GetItem не удается на Comctl32.ocx или MSComctl.ocx Views?

StackOverflow https://stackoverflow.com/questions/2244037

Вопрос

Я написал функцию, которая может дать текст элемента представления дерева, даже если представление дерева находится в удаленном процессе. Функция выделяет две куски памяти в удаленном процессе, заполняет структуру TVITEM (которая копируется в удаленный процесс), отправляет сообщение TVM_GetItem и, наконец, считывает содержимое второй кусочки удаленной памяти обратно в локальный буфер. Это код:

std::string getTreeViewItemText( HWND treeView, HTREEITEM item )
{
    DWORD pid;
    ::GetWindowThreadProcessId( treeView, &pid );

    HANDLE proc = ::OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pid );
    if ( !proc )
        // handle error

    TVITEM tvi;
    ZeroMemory( &tvi, sizeof(tvi) );

    LPVOID tvi_ = ::VirtualAllocEx( proc, NULL, sizeof(tvi), MEM_COMMIT, PAGE_READWRITE);
    if ( !tvi_ )
        // handle error

    TCHAR buffer[100] = { 'X' };

    LPVOID txt_ = ::VirtualAllocEx( proc, NULL, sizeof(buffer), MEM_COMMIT, PAGE_READWRITE );
    if ( !txt_ )
        // handle error

    tvi.mask = TVIF_TEXT | TVIF_HANDLE;
    tvi.pszText =  (LPTSTR)txt_;
    tvi.cchTextMax = sizeof(buffer) / sizeof(buffer[0] );
    tvi.hItem = item;

    if ( !::WriteProcessMemory( proc, tvi_, &tvi, sizeof(tvi), NULL ) )
        // handle error

    if ( !::SendMessage( treeView, TVM_GETITEM, 0, (LPARAM)tvi_ ) )
        // handle error

    if ( !::ReadProcessMemory( proc, (LPCVOID)txt_, buffer, sizeof( buffer ), NULL ) )
        // handle error

    ::VirtualFreeEx( proc, tvi_, 0, MEM_RELEASE );

    ::VirtualFreeEx( proc, txt_, 0, MEM_RELEASE );

    ::CloseHandle( proc );

    return buffer;
}

Этот код очень хорошо работает с простыми видами дерева, которые вы получаете при передаче WC_TREEVIEW Название класса на CreateWindow. Анкет Тем не менее, я заметил, что он не работает с более новыми деревьями, которые предоставляются MS Common Controls V5 (comctl32.ocx) или MS Common Controls V6 (mscomctl.ocx). В этих случаях возвращенный текст всегда пуст (буфер - все нули). Я также заметил, что вызов SendMessage возвращает ноль (следовательно, обработка ошибок обозначена // handle error Комментарии выше начинаются). Мне неясно, действительно ли это указывает на ошибку, в любом случае буфер заполнен всеми нулями.

Все остальные сообщения о просмотре деревьев (такие как TVM_GetItemRect), кажется, работают отлично хорошо.

Кто -нибудь знает, почему это? Я пытался поиграть с флагом Unicode (я заметил, что TVM_GETITEM либо определено, чтобы TVM_GETITEMA или же TVM_GETITEMW), но это, похоже, не помогло.

Это было полезно?

Решение

Хорошо, давайте сделаем еще один выстрел.

Новые деревья ожидают TVITEMEX вместо TVITEM, и, поскольку нет обычного cbSize Поле, элемент управления не может сказать, какую версию он получает и предполагает TVITEMEX. Анкет Может быть, есть проблема с тем, что Treeview не может получить доступ к последним членам TVITEMEX потому что память не распределяется. Попробуйте использовать TVITEMEX или распределить немного больше памяти для TVITEM чем на самом деле требовалось.

Также рассмотрим это

Возвращенный текст не обязательно будет храниться в исходном буфере, переданном приложением. Вполне возможно, что PSZText будет указывать на текст в новом буфере, а не поместить его в старый буфер.

Поэтому вам может потребоваться прочитать из другой части процесса памяти.

И буфер нулевой, потому что Virtualallocex сбрасывает память.

И как последний и, вероятно, бесполезный курорт, попробуйте использовать MEM_RESERVE|MEM_COMMIT вместо просто MEM_COMMIT.

Другие советы

Код не работает, как и ожидалось, если он составлен с определением Unicode, но удаленный процесс не (или наоборот). Вы должны позвонить ISWINDOWUNICODE Сначала на treeView Обработка, чтобы проверить, ожидает ли удаленная сторона сообщения Unicode.

Это необходимо, поскольку стандартное двустороннее маршалтинг, который делает Sendmessage, недостаточно в этом случае: вы должны отправлять два совершенно разных окна сообщений в зависимости от того, является ли удаленная сторона окном Unicode или нет. Если это Unicode, используйте SendMessagew с TVM_GetItemw. Если это ANSI, используйте SendMessagea с TVM_GetItema.

Это относится ко всем общим элементам управления, но не к основному набору элементов управления (который использует сообщения оконных сообщений <1024).

Я также считаю, что код сломается, если он будет составлен в 64 -битный двоичный файл, но удаленный процесс составляет 32 -битный (или наоборот). Это связано с тем, что код копирует его локальный (скажем: 64 -битный) TVItem в удаленный процесс, а затем ожидает удаленного процесса, как и ожидалось, при рассмотрении сообщения TVM_GetItem (A | W). Однако размер структуры может быть другим (из -за разных размеров указателей).

Используйте Spy ++, чтобы увидеть, обрабатывает ли Treeview WM_NOTIFY Messages с помощью флага уведомлений NM_CUSTOMDRAW. Если это так, то неудача. Фактические данные каким -то образом хранятся внутри, и у вас мало шансов вытащить их.

Это в равной степени относится к предыдущим версиям Windows, кстати.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top