Por que a mensagem TVM_GETITEM falha nas visualizações de árvore comctl32.ocx ou mscomctl.ocx?

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

Pergunta

Eu escrevi uma função que pode produzir o texto de um item de exibição de árvore, mesmo que a visualização da árvore esteja em um processo remoto. A função aloca dois pedaços de memória no processo remoto, preenche uma estrutura de TVItem (que é copiada no processo remoto), envia uma mensagem TVM_GETITEM e finalmente lê o conteúdo do segundo pedaço de memória remota de volta para um buffer local. Este é o 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 muito bem com as vistas simples da árvore que você recebe ao passar o WC_TREEVIEW Nome da classe para CreateWindow. No entanto, notei que ele não funciona com as árvores mais recentes, conforme fornecido pelo MS Common Controls V5 (COMCTL32.OCX) ou MS Common Controls V6 (msComctl.ocx). Nesses casos, o texto retornado está sempre vazio (o buffer é todo zeros). Também notei que a chamada de sendmessage retorna zero (daí o manuseio de erros denotado pelo // handle error Comentários acima entram em ação). Não está claro para mim se isso realmente indica um erro, em qualquer caso o buffer está preenchido com todos os zeros.

Todas as outras mensagens de exibição de árvores (como TVM_GetItemrect) parecem funcionar perfeitamente bem.

Alguém sabe por que isso é? Eu tentei brincar com a bandeira Unicode (notei que TVM_GETITEM ou é definido para TVM_GETITEMA ou TVM_GETITEMW) Mas isso não parecia ajudar.

Foi útil?

Solução

Ok, vamos dar outra chance.

As visualizações de árvores mais recentes esperam TVITEMEX ao invés de TVITEM, e já que não há usual cbSize Campo, o controle não é capaz de dizer qual versão recebe e assume TVITEMEX. Talvez haja um problema com o Treeview não poder acessar os últimos membros de TVITEMEX Porque nenhuma memória é alocada. Tente usar TVITEMEX ou alocar um pouco mais de memória para TVITEM do que realmente necessário.

Também considere isso

O texto retornado não será necessariamente armazenado no buffer original aprovado pelo aplicativo. É possível que o PSZText aponte para o texto em um novo buffer, em vez de colocá -lo no buffer antigo.

Portanto, você pode precisar ler a partir de uma memória de processo diferente.

E, o buffer é zerado porque o VirtualAlocex redefine a memória.

E como último e provavelmente inútil resort, tente usar MEM_RESERVE|MEM_COMMIT em vez de apenas MEM_COMMIT.

Outras dicas

O código não funciona como esperado se for compilado com o Unicode definido, mas o processo remoto não é (ou o contrário). Você deve ligar Iswindowunicode primeiro no treeView Puxa para verificar se o lado remoto espera mensagens unicode.

Isso é necessário, pois o marechaling bidirecional padrão que o SendMessage faz não é suficiente neste caso: você deve enviar duas mensagens de janela totalmente diferentes, dependendo se o lado remoto é uma janela Unicode ou não. Se for unicode, use sendMessageW com tvm_getItemw. Se for Ansi, use o sendMessagea com tvm_getItema.

Isso se aplica a todos os controles comuns, mas não ao conjunto básico de controles (que usa mensagens de janela <1024).

Eu também acredito que o código quebrará se for compilado em um binário de 64 bits, mas o processo remoto é de 32 bits (ou o contrário). Isso ocorre porque o código copia, é local (digamos: 64bits) TVItem no processo remoto e, em seguida, espera que o processo remoto o torne o esperado ao lidar com a mensagem TVM_GETITEM (a | W). No entanto, o tamanho da estrutura pode ser diferente (devido a diferentes tamanhos de ponteiro).

Use SPY ++ para ver se o TreeView lida com as mensagens WM_Notify com o sinalizador de notificação NM_CUSTOMDRAW. Se isso acontecer, então, má sorte. Os dados reais são armazenados internamente de alguma forma e você tem poucas chances de retirá -los.

Isso se aplica igualmente às versões anteriores do Windows BTW.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top