2010-02-11 1 views
2

J'ai écrit une fonction qui peut générer 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 finalement le contenu du second bloc de mémoire distant dans un tampon local. Voici le code:Pourquoi le message TVM_GETITEM échoue-t-il sur les vues arborescentes comctl32.ocx ou mscomctl.ocx?

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 d'arbres simples que vous obtenez en passant le nom de classe WC_TREEVIEW-CreateWindow. Cependant, j'ai remarqué que cela ne fonctionne pas avec les nouveaux arbres fournis 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 entièrement zéros). J'ai également remarqué que l'appel SendMessage renvoie zéro (d'où le traitement des erreurs indiqué par les commentaires // handle error ci-dessus). Il n'est pas clair pour moi si cela indique vraiment une erreur, en tout cas le tampon est rempli avec tous les zéros.

Tous les autres messages d'arborescence (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 soit défini à TVM_GETITEMA ou TVM_GETITEMW) mais cela n'a pas semblé aider.

+0

Après SendMessage retournant 0, essayez GetLastError. Ce qu'il restaure MSDN: Microsoft Windows Vista et versions ultérieures Lorsqu'un message est bloqué par UIPI, la dernière erreur, récupérée avec GetLastError, est définie sur 5 (accès refusé). – Igor

+0

@Igor: Bonne idée, mais pas de chance: appeler GetLastError() après SendMessage() renvoie le code d'erreur 0. Peut-être que le code d'erreur est défini dans le processus distant car une erreur est survenue lors du transfert de TVM_GETITEM? –

Répondre

3

Ok, essayons de nouveau.

récent TreeViews attendent TVITEMEX au lieu de TVITEM, et puisqu'il n'y a pas de champ cbSize d'habitude, le contrôle ne sont pas en mesure de dire quelle version il reçoit et prend TVITEMEX. Peut-être que l'arborescence ne peut 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 nécessaire.

considèrent également que

Le texte retourné ne sera pas nécessairement être 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 placer dans l'ancien tampon.

Il est donc possible que vous deviez lire dans une autre mémoire de processus.

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

Et comme un dernier recours et probablement inutile, essayez d'utiliser MEM_RESERVE|MEM_COMMIT au lieu de seulement MEM_COMMIT.

+0

Merci pour vos suggestions! J'ai essayé TVITEMEX, mais ça n'a eu aucun effet. À la fin, il s'est avéré que je devais vérifier explicitement si le côté distant est une fenêtre Unicode ou non, car la traduction bidirectionnelle SendMessage par défaut entre les processus ANSI et Unicode ne s'applique pas. Voir ma réponse pour plus de détails. –

1

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, la malchance. Les données réelles sont stockées en interne en quelque sorte et vous avez peu de chance de le retirer.

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

+0

Bon indice! Cependant, il semble que le Spy ++ fourni avec Visual Studio 2008 n'affiche aucun message de fenêtre pour le bloc-notes. Cela fonctionne bien avec, par exemple, Firefox, mais pour le bloc-notes, je ne vois rien. J'ai revérifié que la journalisation est bien activée et que tous les types de messages sont activés. Pity: -/ –

+0

Ensuite, vérifiez également les paramètres UAC. Spy ++ devrait être autorisé à courir élevé. Juste vérifié, tous les messages du Bloc-notes sont là. – GSerg

+0

Malheureusement, il ne semble pas y avoir de WM_NOTIFY impliqué. –

5

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).Vous devez d'abord appeler IsWindowUnicode sur la poignée treeView pour vérifier si le côté distant attend les messages Unicode.

Ceci est nécessaire car le routage bidirectionnel standard effectué 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 les messages de fenêtre < 1024). Je crois aussi que le code va casser s'il est compilé dans un binaire 64 bits, mais le processus distant est 32 bits (ou l'inverse). C'est parce que le code copie son TVITEM local (disons: 64bit) dans le processus distant et s'attend ensuite à ce que le processus distant le tienne comme prévu en traitant le message TVM_GETITEM (A | W). Cependant, la taille de la structure peut être différente (en raison de la taille différente des pointeurs).

+0

Etrange, ça marche très bien ici quand j'envoie TVM_GETITEMA à Windows 7 treeviews qui sont unicode (mais l'inverse n'est pas bon, c'est vrai). La question du pointeur est un très bon point cependant. – GSerg

Questions connexes