Domanda

ho scritto una funzione che può cedere il testo di un elemento ad albero, anche se la vista albero è in un processo remoto. La funzione alloca due blocchi di memoria del processo remoto, popola una struttura TVITEM (che rappresenta l'copiato nella processo remoto), invia un messaggio TVM_GETITEM e infine legge il contenuto del secondo blocco di memoria remota di nuovo in un buffer locale. Questo è il codice:

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

Questo codice funziona molto bene con i punti di vista degli alberi di pianura che si ottiene quando si passa il nome della classe WC_TREEVIEW a CreateWindow. Tuttavia, ho notato che non funziona con gli alberi più recenti come previsto dalla MS Common Controls v5 (comctl32.ocx) o MS Common Controls v6 (mscomctl.ocx). In tali casi, il testo restituito è sempre vuota (il buffer è tutti zeri). Ho anche notato che la chiamata SendMessage restituisce zero (da qui la gestione degli errori indicata con le osservazioni sopra // handle error calci). Non è chiaro per me se questo significa davvero un errore, in ogni caso, il buffer è pieno di tutti zeri.

Tutti gli altri messaggi vista albero (come TVM_GETITEMRECT) sembrano funzionare perfettamente.

Qualcuno sa perché? Ho provato a giocare con la bandiera Unicode (ho notato che TVM_GETITEM o è definito in modo da TVM_GETITEMA o TVM_GETITEMW), ma che non sembra aiutare.

È stato utile?

Soluzione

Ok, diamo un altro colpo.

TreeViews recenti aspettano TVITEMEX invece di TVITEM, e dal momento che non c'è al solito campo cbSize, il controllo non è in grado di dire quale versione riceve e assume TVITEMEX. Forse c'è un problema con il controllo TreeView non essere in grado di accedere ultimi membri della TVITEMEX perché nessuna memoria è allocata. Provate ad usare TVITEMEX o assegnazione di un po 'di più memoria per TVITEM che effettivamente richiesto.

Si consideri inoltre che

  

Il testo restituito non sarà necessariamente   essere memorizzati nel buffer originale   superato dall'applicazione. È   possibile che pszText punterà   testo in un nuovo buffer, piuttosto che luogo   nel vecchio buffer.

È quindi potrebbe essere necessario per leggere da un altro pezzo di memoria di processo.

E, il buffer è azzerato perché VirtualAllocEx azzera la memoria.

E come ultima e probabilmente inutile resort, provare a utilizzare MEM_RESERVE|MEM_COMMIT invece di MEM_COMMIT.

Altri suggerimenti

Il codice non funziona come previsto se è compilato con UNICODE definito, ma il processo remoto non è (o viceversa). Si dovrebbe chiamare IsWindowUnicode prima sul manico treeView a verificare se il lato remoto si aspetta messaggi Unicode.

Questo è necessario in quanto il marshalling standard a due vie che SendMessage fa non è sufficiente in questo caso: è necessario inviare due completamente diversi messaggi di finestra a seconda che il lato remoto è una finestra Unicode o meno. Se si tratta di Unicode, utilizzare SendMessageW con TVM_GETITEMW. Se si tratta di ANSI, utilizzare SendMessageA con TVM_GETITEMA.

Questo vale per tutti i controlli comuni, ma non al set di base di controlli (che utilizza i messaggi di finestra <1024).

Credo anche che il codice si romperà se è compilato in un binario 64 bit, ma il processo remoto è 32 bit (o viceversa). Questo perché il codice copia è locale (diciamo: 64bit) TVITEM nel processo remoto e quindi si aspetta che il processo remoto Toread come previsto nei rapporti con la TVM_GETITEM (A | W) messaggio. Tuttavia, la dimensione della struttura potrebbe essere diversa (a causa di differenti dimensioni puntatore).

Usa Spy ++ per vedere se la vista ad albero gestisce i messaggi WM_NOTIFY con NM_CUSTOMDRAW bandiera notifica. Se lo fa, poi, la sfortuna. I dati effettivi vengono memorizzati internamente in qualche modo e hai poche possibilità di tirarlo fuori.

Questo vale anche per le versioni precedenti di Windows BTW.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top