Pourquoi le message TVM_GETITEM échoue-t-il sur les vues d’arborescence comctl32.ocx ou mscomctl.ocx?

J’ai écrit une fonction qui peut donner le texte d’un élément de vue arborescente, même si l’arborescence est dans un processus distant. La fonction alloue deux blocs 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 bloc de mémoire distant dans un tampon local. Ceci est le code:

std::ssortingng 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 d’arborescence simple que vous obtenez lorsque vous passez le nom de classe WC_TREEVIEW à CreateWindow . Cependant, j’ai remarqué qu’il ne fonctionne pas avec les nouvelles arborescences fournies par MS Common Controls v5 (comctl32.ocx) ou MS Common Controls v6 (mscomctl.ocx). Dans ces cas, le texte renvoyé est toujours vide (le tampon est composé de zéros). J’ai également remarqué que l’appel SendMessage renvoie zéro (d’où la gestion des erreurs désignée par // handle error commentaires d’ // handle error ci-dessus). Il n’est pas clair pour moi si cela indique vraiment une erreur, en tout cas le tampon est rempli de tous les zéros.

Tous les autres messages de vue arborescente (comme TVM_GETITEMRECT) semblent parfaitement fonctionner.

Est-ce que quelqu’un sait pourquoi? J’ai essayé de jouer avec le drapeau UNICODE (j’ai remarqué que TVM_GETITEM est défini sur TVM_GETITEMA ou TVM_GETITEMW ) mais cela n’a pas semblé aider.

Ok, passons à autre chose.

Les TreeViews plus récentes attendent TVITEMEX au lieu de TVITEM , et comme il n’existe pas de champ cbSize habituel, le contrôle n’est pas en mesure de dire quelle version il reçoit et assume TVITEMEX . Il y a peut-être un problème avec l’arborescence ne pouvant pas accéder aux derniers membres de TVITEMEX car aucune mémoire n’est allouée. Essayez d’utiliser TVITEMEX ou d’allouer un peu plus de mémoire pour TVITEM que ce qui est réellement nécessaire.

Considérez aussi que

Le texte renvoyé ne sera pas nécessairement stocké dans le tampon d’origine transmis par l’application. Il est possible que pszText pointe sur du texte dans un nouveau tampon plutôt que de le placer dans l’ancien tampon.

Vous devrez donc peut-être lire un morceau de mémoire de processus différent.

Et le tampon est mis à zéro car VirtualAllocEx réinitialise la mémoire.

Et en dernier recours et probablement inutile, essayez d’utiliser MEM_RESERVE|MEM_COMMIT au lieu de MEM_COMMIT .

Le code ne fonctionne pas comme prévu s’il est compilé avec UNICODE défini, mais le processus distant ne l’est pas (ou inversement). Vous devez d’ abord appeler IsWindowUnicode sur le handle treeView pour vérifier si le côté distant attend des messages Unicode.

Cela est nécessaire car le système de double chemin standard utilisé par 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 est 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 courants, mais pas à l’ensemble de contrôles de base (qui utilise des messages de fenêtre <1024).

Je pense également que le code sera cassé s’il est compilé dans un binary 64 bits, mais le processus distant est 32 bits (ou inversement). Ceci est dû au fait que le code copie le TVITEM local (disons: 64 bits) dans le processus distant, puis attend que le processus distant l’anime comme prévu lorsqu’il traite du message TVM_GETITEM (A | W). Cependant, la taille de la structure peut être différente (en raison des différentes tailles de pointeur).

Utilisez Spy ++ pour voir si l’arborescence 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.