Question

J'ai écrit une fonction qui peut donner le texte d'un élément d'arborescence, même si la vue de l'arborescence est dans un processus distant. La fonction alloue deux morceaux de mémoire dans le processus distant, remplit une structure TVITEM (qui est copiée dans le processus distant), envoie un message TVM_GETITEM et lit enfin le contenu du deuxième morceau de mémoire distant dans un tampon local. Ceci est le code:

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

Ce code fonctionne très bien avec les vues de l'arborescence simples que vous obtenez lors du passage du WC_TREEVIEW Nom de la classe à CreateWindow. Cependant, j'ai remarqué qu'il ne fonctionne pas avec les nouveaux arbres tels que fournis par MS Common Controls V5 (COMCTL32.OCX) ou MS Common Controls V6 (MSCOMCTL.OCX). Dans ces cas, le texte retourné est toujours vide (le tampon est tous des zéros). J'ai également remarqué que l'appel SendMessage renvoie zéro (d'où le traitement des erreurs indiqué par le // handle error Les commentaires ci-dessus entrent en jeu). On ne sait pas si cela indique vraiment une erreur, en tout cas, le tampon est rempli de tous les zéros.

Tous les autres messages d'arborescence (comme tvm_getItemrect) semblent bien fonctionner.

Quelqu'un sait-il pourquoi c'est? J'ai essayé de jouer avec le drapeau Unicode (j'ai remarqué que TVM_GETITEM est défini à TVM_GETITEMA ou TVM_GETITEMW) Mais cela ne semblait pas aider.

Était-ce utile?

La solution

Ok, donnons-lui un autre coup.

Les nouveaux arbres s'attendent à TVITEMEX à la place de TVITEM, et puisqu'il n'y a pas d'habitude cbSize champ, le contrôle n'est pas en mesure de dire quelle version il reçoit et suppose TVITEMEX. Il y a peut-être un problème avec le TreeView ne pouvant pas accéder aux derniers membres de TVITEMEX car aucune mémoire n'est allouée. Essayer d'utiliser TVITEMEX ou allouer un peu plus de mémoire pour TVITEM que réellement requis.

Considérez aussi que

Le texte retourné ne sera pas nécessairement stocké dans le tampon d'origine passé par l'application. Il est possible que PSZText pointe vers le texte dans un nouveau tampon plutôt que de le placer dans l'ancien tampon.

Vous pourriez donc avoir besoin de lire à partir d'un autre morceau de mémoire de processus.

Et, le tampon est à zéro car VirtualAllocex réinitialise la mémoire.

Et comme une dernière station balnéaire et probablement inutile, essayez d'utiliser MEM_RESERVE|MEM_COMMIT au lieu de juste MEM_COMMIT.

Autres conseils

Le code ne fonctionne pas comme prévu s'il est compilé avec Unicode défini, mais le processus distant n'est pas (ou l'inverse). Tu devrais appeler Iswindowunicode Premièrement sur le treeView gérer pour vérifier si le côté distant attend des messages Unicode.

Cela est nécessaire car le marshalling à double sens standard que fait SendMessage n'est pas suffisant dans ce cas: vous devez envoyer deux messages de fenêtre entièrement différents selon que le côté distant soit une fenêtre Unicode ou non. Si c'est Unicode, utilisez SendMessageW avec tvm_getItemw. Si c'est ANSI, utilisez SendMessagea avec TVM_GetItema.

Cela s'applique à tous les contrôles communs, mais pas à l'ensemble de base des contrôles (qui utilise des messages de fenêtre <1024).

Je crois également que le code se cassera s'il est compilé en binaire 64 bits, mais le processus distant est de 32 bits (ou de l'autre sens). En effet, le code copie qu'il est local (disons: 64bit) TVITEM dans le processus distant, puis s'attend à ce que le processus distant le soit comme prévu tout en traitant avec le message TVM_GETITEM (A | W). Cependant, la taille de la structure peut être différente (en raison de différentes tailles de pointeur).

Utilisez SPY ++ pour voir si TreeView gère les messages WM_notify avec l'indicateur de notification NM_CustomDraw. Si c'est le cas, alors, malchance. Les données réelles sont stockées en interne d'une manière ou d'une autre et vous avez peu de chance de les retirer.

Cela s'applique également aux versions précédentes de Windows BTW.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top