Pregunta

Escribí una función que puede producir el texto de un elemento de vista de árbol, incluso si la vista de árbol está en un proceso remoto. La función asigna dos fragmentos de memoria en el proceso remoto, pobla una estructura de TVItem (que se copia en el proceso remoto), envía un mensaje TVM_GetItem y finalmente lee el contenido del segundo fragmento de memoria remota en un búfer local. Este es el código:

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;
}

Este código funciona muy bien con las vistas de los árboles simples que obtienes al pasar el WC_TREEVIEW nombre de clase para CreateWindow. Sin embargo, noté que no funciona con los árboles más nuevos según lo dispuesto por MS Common Controls V5 (Comctl32.ocx) o MS Common Controls V6 (MSCOMCTL.OCX). En esos casos, el texto devuelto siempre está vacío (el búfer es todos ceros). También noté que la llamada SendMessage devuelve cero (de ahí el manejo de errores denotado por el // handle error Comentarios anteriores se inicia). No está claro para mí si esto realmente indica un error, en cualquier caso, el búfer está lleno de todos los ceros.

Todos los demás mensajes de vista de árbol (como TVM_GetItemrect) parecen funcionar perfectamente bien.

¿Alguien sabe por qué es eso? Intenté jugar con la bandera unicode (noté que TVM_GETITEM está definido a TVM_GETITEMA o TVM_GETITEMW) Pero eso no parecía ayudar.

¿Fue útil?

Solución

Ok, vamos a darle otra oportunidad.

Se espera TVITEMEX en vez de TVITEM, y como no hay habitual cbSize campo, el control no puede saber qué versión recibe y asume TVITEMEX. Tal vez hay un problema con TreeView no poder acceder a los últimos miembros de TVITEMEX Porque no se asigna memoria. Intenta usar TVITEMEX o asignar un poco más de memoria para TVITEM que realmente requerido.

También considere que

El texto devuelto no necesariamente se almacenará en el búfer original aprobado por la aplicación. Es posible que PSZText apunte al mensaje de texto en un nuevo búfer en lugar de colocarlo en el antiguo búfer.

Por lo tanto, es posible que necesite leer de una memoria de proceso diferente.

Y, el búfer está a cero porque VirtualLoCex restablece la memoria.

Y como un recurso último y probablemente inútil, intente usar MEM_RESERVE|MEM_COMMIT en lugar de solo MEM_COMMIT.

Otros consejos

El código no funciona como se esperaba si se compila con Unicode definido, pero el proceso remoto no es (o al revés). Deberías llamar Iswindowunicode primero en el treeView Manejar para verificar si el lado remoto espera mensajes unicode.

Esto es necesario ya que el mariscal de dos vías estándar que hace SendMessage no es suficiente en este caso: debe enviar dos mensajes de ventana completamente diferentes dependiendo de si el lado remoto es una ventana Unicode o no. Si es unicode, use SendMessagew con TVM_GetItemw. Si es ANSI, use SendMessagea con TVM_Getitema.

Esto se aplica a todos los controles comunes, pero no al conjunto básico de controles (que utiliza mensajes de ventana <1024).

También creo que el código se romperá si se compila en un binario de 64 bits, pero el proceso remoto es de 32 bits (o al revés). Esto se debe a que el código copia su TVItem local (digamos: 64 bits) en el proceso remoto y luego espera que el proceso remoto lo atiga como se esperaba al tratar con el mensaje TVM_GetItem (A | W). Sin embargo, el tamaño de la estructura puede ser diferente (debido a diferentes tamaños de puntero).

Use Spy ++ para ver si TreeView maneja los mensajes WM_NOTIFY con el indicador de notificación NM_CustomDraw. Si es así, entonces, mala suerte. Los datos reales se almacenan internamente de alguna manera y tienes pocas posibilidades de sacarlos.

Esto se aplica igualmente a versiones anteriores de Windows BTW.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top