Sign in to follow this  
Followers 0
Fur

Reading ListView32 and SysTreeView32 controls

61 posts in this topic

So turns out if you want to peer into the contents of one of these controls inside another application, its a real pain in the butt. Never fear, for I have conquered this beast. If you want to include this in any auto it libraries or what not, feel free!

These functions work by putting the contents of the control into the clipboard, which you can then get via ClipGet() in your AutoIt scripts. This is some butt-ugly code, but it works. I know I've had several projects on hold until this was working, so I thought I'd post this as is...

HTREEITEM BasicExtractTreeNode(HANDLE hProcess, HWND hwndTV, HTREEITEM hItem, TV_ITEM *plvi, int indent, LPTSTR pClipData)
{
    while (hItem)
    {
        TVITEMEX item;
        item.hItem = hItem;
        item.mask = TVIF_TEXT | TVIF_CHILDREN;
        item.pszText = (LPTSTR) (plvi + 1);
        item.cchTextMax = 100;


          // Write the local LV_ITEM structure to the remote memory block
          WriteProcessMemory(hProcess, plvi, &item, sizeof(item), NULL);
        
        if (!TreeView_GetItem(hwndTV, plvi))
            fprintf(stderr, "Error calling SendMessage()\n");

          if (*pClipData) lstrcat(pClipData, __TEXT("\n"));
            char tabs[16] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
            if (indent < sizeof(tabs) - 1) tabs[indent] = '\0';
            if (indent) lstrcat(pClipData, tabs);
          // Read the remote text string into the end of our clipboard buffer
          ReadProcessMemory(hProcess, plvi + 1, &pClipData[lstrlen(pClipData)], 1024, NULL);

        // Check whether we have child items.
        if (1) //plvi->cChildren > 0)
        {
            // Recursively traverse child items.

            HTREEITEM hItemChild = TreeView_GetNextItem(hwndTV, hItem, TVGN_CHILD);
            //fprintf(stderr, "Found a parent node, first child is %p\n", (long) hItemChild);
            BasicExtractTreeNode(hProcess, hwndTV, hItemChild, plvi, ++indent, pClipData);

        }

        // Go to next sibling item.
        hItem = TreeView_GetNextItem(hwndTV, hItem, TVGN_NEXT);
        //fprintf(stderr, "Moving onto next sibling which is %p\n", (long) hItem);
    }

    return 0;
}



int EXPORT CopyTreeViewToClipboard(HWND hwnd, HWND hwndTV)
{
    //fprintf(stderr, "CopyTreeViewToClipboard(%p, %pd) called\n", hwnd, hwndTV);
   if (hwndTV == NULL) return 0;


   // Open a handle to the remote process's kernel object
   DWORD dwProcessId;
   GetWindowThreadProcessId(hwndTV, &dwProcessId);
   HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcessId);

   if (hProcess == NULL) {
      MessageBox(hwnd, __TEXT("Could not communicate with process"), "CopyTreeViewToClipboard", MB_OK | MB_ICONWARNING);
      return 0;
   }

   // Prepare a buffer to hold the TreeView's data.
   // Note: Hardcoded maximum of 10240 chars for clipboard data.
   // Note: Clipboard only accepts data that is in a block allocated with 
   //      GlobalAlloc using the GMEM_MOVEABLE and GMEM_DDESHARE flags.
   HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(TCHAR) * 10240);
   LPTSTR pClipData = (LPTSTR) GlobalLock(hClipData);
   pClipData[0] = 0;

   // Allocate memory in the remote process's address space
   TV_ITEM* ptvi = (TV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    HTREEITEM hItem = (HTREEITEM) SendMessage(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
    //fprintf(stderr, "For hwndTV=%p I just found root at %p\n", (long) hwndTV, (long) hItem);

    BasicExtractTreeNode(hProcess, hwndTV, hItem, ptvi, 0, pClipData);

   // Free the memory in the remote process's address space
   VirtualFreeEx(hProcess, ptvi, 0, MEM_RELEASE);

   // Cleanup and put our results on the clipboard
   CloseHandle(hProcess);
   OpenClipboard(hwnd);
   EmptyClipboard();
#ifdef UNICODE
   BOOL fOk = (SetClipboardData(CF_UNICODETEXT, hClipData) == hClipData);
#else
   BOOL fOk = (SetClipboardData(CF_TEXT, hClipData) == hClipData);
#endif
   CloseClipboard();
   if (!fOk) {
      GlobalFree(hClipData);
      MessageBox(hwnd, __TEXT("Error putting text on the clipboard"), "CopyTreeViewToClipboard", MB_OK | MB_ICONINFORMATION);
   }
   return (fOk) ? 1 : 0;
}



int EXPORT CopyListViewToClipboard(HWND hwnd, HWND hwndLV)
{
    //fprintf(stderr, "CopyListViewToClipboard(%p, %pd) called\n", hwnd, hwndLV);
   if (hwndLV == NULL) return 0;


   // Get the count of items in the ListView control
   int nCount = ListView_GetItemCount(hwndLV);

   // Open a handle to the remote process's kernel object
   DWORD dwProcessId;
   GetWindowThreadProcessId(hwndLV, &dwProcessId);
   HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcessId);

   if (hProcess == NULL) {
      MessageBox(hwnd, __TEXT("Could not communicate with process"), "CopyListViewToClipboard", MB_OK | MB_ICONWARNING);
      return 0;
   }

   // Prepare a buffer to hold the ListView's data.
   // Note: Hardcoded maximum of 10240 chars for clipboard data.
   // Note: Clipboard only accepts data that is in a block allocated with 
   //      GlobalAlloc using the GMEM_MOVEABLE and GMEM_DDESHARE flags.
   HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(TCHAR) * 10240);
   LPTSTR pClipData = (LPTSTR) GlobalLock(hClipData);
   pClipData[0] = 0;

   // Allocate memory in the remote process's address space
   LV_ITEM* plvi = (LV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

   // Get each ListView item's text data
   for (int nIndex = 0; nIndex < nCount; nIndex++) {

      // Initialize a local LV_ITEM structure
      LV_ITEM lvi;
      lvi.mask = LVIF_TEXT;
      lvi.iItem = nIndex;
      lvi.iSubItem = 0; 
      // NOTE: The text data immediately follows the LV_ITEM structure
      //       in the memory block allocated in the remote process.
      lvi.pszText = (LPTSTR) (plvi + 1); 
      lvi.cchTextMax = 100; 

      // Write the local LV_ITEM structure to the remote memory block
      WriteProcessMemory(hProcess, plvi, &lvi, sizeof(lvi), NULL);

      // Tell the ListView control to fill the remote LV_ITEM structure
      ListView_GetItem(hwndLV, plvi);

      // If this is not the first item, add a carriage-return/linefeed
      if (nIndex > 0) lstrcat(pClipData, __TEXT("\r\n"));

      // Read the remote text string into the end of our clipboard buffer
      ReadProcessMemory(hProcess, plvi + 1, &pClipData[lstrlen(pClipData)], 1024, NULL);
   }

   // Free the memory in the remote process's address space
   VirtualFreeEx(hProcess, plvi, 0, MEM_RELEASE);

   // Cleanup and put our results on the clipboard
   CloseHandle(hProcess);
   OpenClipboard(hwnd);
   EmptyClipboard();
#ifdef UNICODE
   BOOL fOk = (SetClipboardData(CF_UNICODETEXT, hClipData) == hClipData);
#else
   BOOL fOk = (SetClipboardData(CF_TEXT, hClipData) == hClipData);
#endif
   CloseClipboard();
   if (!fOk) {
      GlobalFree(hClipData);
      MessageBox(hwnd, __TEXT("Error putting text on the clipboard"), "CopyListViewToClipboard", MB_OK | MB_ICONINFORMATION);
   }
   return (fOk) ? 1 : 0;
}

Share this post


Link to post
Share on other sites



An embarrassingly basic question...

I'm very new to C++, but I managed to compile these functions into the publicly available AutoIt 3.1.0 source code (into script_win.cpp), and added corresponding entries into script.h. It compiled fine (I was shocked) and I have no reason to think it won't work.

My question is, now what do I do with it? On the AutoIt side, how do I automate a treeview control with these functions? What parameters do they need? Could you show me an example of an au3 script that uses these?

Share this post


Link to post
Share on other sites

Just want to point out that can read ListView32 from any window with "ControlListView" in AutoIt v3.1.

Share this post


Link to post
Share on other sites

Just want to point out that can read ListView32 from any window with "ControlListView" in AutoIt v3.1.

<{POST_SNAPBACK}>

I realize that. My main concern at the moment is interrogating treeviews.

Share this post


Link to post
Share on other sites

Me too. I'm just working on my own ControlTreeView() functionality in my spare time and will be posting it as I go along. Hopefully this can get included as an actual command within AutoIt as some point. Since ListViews are easier than TreeViews, I started work there first. However, the ControlListView() code is not in the AutoIt src folder for some reason.. ggrrrr!

Share this post


Link to post
Share on other sites

Me too.  I'm just working on my own ControlTreeView() functionality in my spare time and will be posting it as I go along.  Hopefully this can get included as an actual command within AutoIt as some point.  Since ListViews are easier than TreeViews, I started work there first.  However, the ControlListView() code is not in the AutoIt src folder for some reason.. ggrrrr!

<{POST_SNAPBACK}>

I realize it's still in the rough, but if you could give me some insight into how to use the code you have already posted...? just being able to tell what is in the treeview - the text of the nodes (even if just the visible nodes!) and their sequence - I can extrapolate from that all the rest of the functionality I need.

Specifically, I'm thinking I could au3-->exe a script that would use the special recompilation of autoit3.exe that I made from the available source code and your posted function. I could then pass messages between that and my main code using the clipboard functions, checking the clipboard contents periodically using AdLib. It wouldn't be pretty, but it should let me do what I need to do.

So, from an au3 file, how would I call CopyTreeViewToClipboard? From there I could do everything I need with Send and String functions. But without knowing what's in the tree, anything I do in a SysTreeView32 is completely blind!

Share this post


Link to post
Share on other sites

Integrating with AutoIt code is the one piece I don't have. I would suggest making a separate DLL to hold this code and just using DllCall() to run it.

Share this post


Link to post
Share on other sites

Integrating with AutoIt code is the one piece I don't have.  I would suggest making a separate DLL to hold this code and just using DllCall() to run it.

<{POST_SNAPBACK}>

do you know of anyone who would be willing to walk me through making it into a DLL? (this feature is something that I really need pretty badly, ideally by the end of this week) I somehow don't think I could just paste it into a cpp file and tell it to compile as a dll.

I realize this isn't a general help forum, so if whoever you know of would rather work with me elsewhere I can go along with that.

Also, I tried calling the functions (using handles for the HWIN variables) in the version i spliced together. AutoIt denied having any such functions.

Actually... if it is as straightforward to you as you make it sound, would you be willing to post a DLL version? I have no idea what I might be able to do to return the favor, but I am getting kinda desperate. I'm nowhere close to the calibar C++ programmer you are (I'm more of a VB guy) and will take whatever help you can spare.

Share this post


Link to post
Share on other sites

Well I am in crunch mode at my real job, so I'm not going to have a lot of time to finish my work on this for several days.

Use Visual C++, tell it to make a new DLL project.

Just copy and paste that code into the empty .cpp it provides.

Go into the .def file and add a line to export each function.

EXPORTS

; Explicit exports can go here

CopyListViewToClipboard

CopyTreeViewToClipboard

Press F7 to build it.

I like to create wrapper functions in AutoIt for each function like this:

Global $g_dll = DllOpen(@WorkingDir & "\path\to\your.dll");

Func CopyListViewToClipboard($parent_hwnd, $hwnd)
    Local $foo = DllCall($g_dll, "int", "CopyListViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyListViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

Func CopyTreeViewToClipboard($parent_hwnd, $hwnd)
    Local $foo = DllCall($g_dll, "int", "CopyTreeViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyTreeViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

Share this post


Link to post
Share on other sites

Well I am in crunch mode at my real job, so I'm not going to have a lot of time to finish my work on this for several days.

Use Visual C++, tell it to make a new DLL project.

Just copy and paste that code into the empty .cpp it provides.

Go into the .def file and add a line to export each function.

EXPORTS

    ; Explicit exports can go here

CopyListViewToClipboard

CopyTreeViewToClipboard

Press F7 to build it.

I like to create wrapper functions in AutoIt for each function like this:

Global $g_dll = DllOpen(@WorkingDir & "\path\to\your.dll");

Func CopyListViewToClipboard($parent_hwnd, $hwnd)
       Local $foo = DllCall($g_dll, "int", "CopyListViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyListViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

Func CopyTreeViewToClipboard($parent_hwnd, $hwnd)
       Local $foo = DllCall($g_dll, "int", "CopyTreeViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyTreeViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

<{POST_SNAPBACK}>

I am interested by testing your dll Can you send me your dll by pm

Share this post


Link to post
Share on other sites

I am interested by testing your dll  Can you send me your dll by pm

<{POST_SNAPBACK}>

Sorry I didn't see your post days ago Loulou - been very busy.

Here I'll just post it. Remember all the credit goes to Fur.

It works great on Windows 2000 and XP. Trying to figure out why it won't return item text on 98 or Millenium. I haven't been able to try it on NT.

Share this post


Link to post
Share on other sites

It works great on Windows 2000 and XP. Trying to figure out why it won't return item text on 98 or Millenium. I haven't been able to try it on NT.

<{POST_SNAPBACK}>

Come to think of it, let's get some actual experts in on this... Does anyone have any ideas how to make this work for WinMe or Win98? I think it would be an excellent addition to the AutoIt package as a whole if the compatability issues weren't there.

Share this post


Link to post
Share on other sites

It's the memory movement.... it is different in NT to 98...

<{POST_SNAPBACK}>

It figures that would be different between the cores. As to what to do about it, I'm still way out of my league. What would you suggest in terms of code?

(For my specific purposes, there would be no problem with having seperate DLLs for 98/ME and NT systems. That said, I'm still intrigued with the larger picture of cross-compatibility. Nothing is so satisfying as a good puzzle :) .)

Share this post


Link to post
Share on other sites

Yes, You can probably just take out the stuff that's copying memory from one process to another. Win98 doesn't care if you read/write to another process' memory.

Share this post


Link to post
Share on other sites

Latest version of my work: BTW, I use a case-insenstive string searching for 'matching' items in the control - not an exact text match. Your milage may very with this.

#include "stdafx.h"
#include "stristr.h"
#include <assert.h>
#include <map>
#include <string>

#pragma warning(disable : 4035)

// stristr /////////////////////////////////////////////////////////
//
// performs a case-insensitive lookup of a string within another
// (see C run-time strstr)
//
// str1 : buffer
// str2 : string to search for in the buffer
//
// example char* s = stristr("Make my day","DAY");
//
// S.Rodriguez, Jan 11, 2004
//
const char* stristr(const char* str1, const char* str2)
{
  __asm
  {
    mov ah, 'A'
    mov dh, 'Z'

    mov esi, str1
    mov ecx, str2
    mov dl, [ecx]
    test dl,dl; NULL?
    jz short str2empty_label

outerloop_label:
    mov ebx, esi; save esi
    inc ebx
innerloop_label:
    mov al, [esi]
    inc esi
    test al,al
    je short str2notfound_label; not found!

    cmp dl,ah         ; 'A'
    jb short skip1
    cmp dl,dh         ; 'Z'
    ja short skip1
    add dl,'a' - 'A'   ; make lowercase the current character in str2
skip1:      

    cmp al,ah         ; 'A'
    jb short skip2
    cmp al,dh         ; 'Z'
    ja short skip2
    add al,'a' - 'A'   ; make lowercase the current character in str1
skip2:      

    cmp al,dl
    je short onecharfound_label
    mov esi, ebx; restore esi value, +1
    mov ecx, str2; restore ecx value as well
    mov dl, [ecx]
    jmp short outerloop_label; search from start of str2 again
onecharfound_label:
    inc ecx
    mov dl,[ecx]
    test dl,dl
    jnz short innerloop_label
    jmp short str2found_label; found!
str2empty_label:
    mov eax, esi // empty str2 ==> return str1
    jmp short ret_label
str2found_label:
    dec ebx
    mov eax, ebx // str2 found ==> return occurence within str1
    jmp short ret_label
str2notfound_label:
    xor eax, eax // str2 nt found ==> return NULL
    jmp short ret_label
ret_label:
  }

}


#pragma warning(default : 4035)



using namespace std;


// given the hwnd for a listbox, put the contents into a pipe delimited string. however, if there are multiple versions of the same string, append a $$X occurance to the end of each one
const char EXPORT *ExtractListContents(HWND hwnd)
{
    static char results[4096];

    *results = '\0';

    long count = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
    map<string, int> SeenList;

    for (long i = 0; i < count; ++i)
    {
        char item[256] = "";
        SendMessage(hwnd, LB_GETTEXT, i, (long) item);

        int cnt = 0;
        map<string, int>::iterator itr = SeenList.find(string(item));
        if (itr == SeenList.end())
        {
            SeenList.insert(make_pair(string(item), 1));
        } else {
            cnt = ++(*itr).second;
        }
        if (i != 0)
            strcat(results, "|");
        strcat(results, item);
        if (cnt >= 2)
        {
            char tmp[32];
            sprintf(tmp, "$$%d", cnt);
            strcat(results, tmp);
        }
    }
    return results;
}


HTREEITEM BasicExtractTreeNode(HANDLE hProcess, HWND hwndTV, HTREEITEM hItem, TV_ITEM *plvi, int indent, LPTSTR pClipData)
{
    while (hItem)
    {
        TVITEMEX item;
        item.hItem = hItem;
        item.mask = TVIF_TEXT | TVIF_CHILDREN;
        item.pszText = (LPTSTR) (plvi + 1);
        item.cchTextMax = 100;


          // Write the local LV_ITEM structure to the remote memory block
          WriteProcessMemory(hProcess, plvi, &item, sizeof(item), NULL);
        
        if (!TreeView_GetItem(hwndTV, plvi))
            fprintf(stderr, "Error calling SendMessage()\n");

          if (*pClipData) lstrcat(pClipData, __TEXT("\n"));
            char tabs[16] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
            if (indent < sizeof(tabs) - 1) tabs[indent] = '\0';
            if (indent) lstrcat(pClipData, tabs);
          // Read the remote text string into the end of our clipboard buffer
          ReadProcessMemory(hProcess, plvi + 1, &pClipData[lstrlen(pClipData)], 1024, NULL);

        // Check whether we have child items.
        if (1) //plvi->cChildren > 0)
        {
            // Recursively traverse child items.

            HTREEITEM hItemChild = TreeView_GetNextItem(hwndTV, hItem, TVGN_CHILD);
            //fprintf(stderr, "Found a parent node, first child is %p\n", (long) hItemChild);
            if (hItemChild) BasicExtractTreeNode(hProcess, hwndTV, hItemChild, plvi, indent + 1, pClipData);

        }

        // Go to next sibling item.
        hItem = TreeView_GetNextItem(hwndTV, hItem, TVGN_NEXT);
        //fprintf(stderr, "Moving onto next sibling which is %p\n", (long) hItem);
    }

    return 0;
}



int EXPORT CopyTreeViewToClipboard(HWND hwnd, HWND hwndTV)
{
    //fprintf(stderr, "CopyTreeViewToClipboard(%p, %pd) called\n", hwnd, hwndTV);
   if (hwndTV == NULL) return 0;


   // Open a handle to the remote process's kernel object
   DWORD dwProcessId;
   GetWindowThreadProcessId(hwndTV, &dwProcessId);
   HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcessId);

   if (hProcess == NULL) {
      MessageBox(hwnd, __TEXT("Could not communicate with process"), "CopyTreeViewToClipboard", MB_OK | MB_ICONWARNING);
      return 0;
   }

   // Prepare a buffer to hold the TreeView's data.
   // Note: Hardcoded maximum of 10240 chars for clipboard data.
   // Note: Clipboard only accepts data that is in a block allocated with 
   //      GlobalAlloc using the GMEM_MOVEABLE and GMEM_DDESHARE flags.
   HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(TCHAR) * 10240);
   LPTSTR pClipData = (LPTSTR) GlobalLock(hClipData);
   pClipData[0] = 0;

   // Allocate memory in the remote process's address space
   TV_ITEM* ptvi = (TV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    HTREEITEM hItem = (HTREEITEM) SendMessage(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
    //fprintf(stderr, "For hwndTV=%p I just found root at %p\n", (long) hwndTV, (long) hItem);

    BasicExtractTreeNode(hProcess, hwndTV, hItem, ptvi, 0, pClipData);

   // Free the memory in the remote process's address space
   VirtualFreeEx(hProcess, ptvi, 0, MEM_RELEASE);

   // Cleanup and put our results on the clipboard
   CloseHandle(hProcess);
   OpenClipboard(hwnd);
   EmptyClipboard();
#ifdef UNICODE
   BOOL fOk = (SetClipboardData(CF_UNICODETEXT, hClipData) == hClipData);
#else
   BOOL fOk = (SetClipboardData(CF_TEXT, hClipData) == hClipData);
#endif
   CloseClipboard();
   if (!fOk) {
      GlobalFree(hClipData);
      MessageBox(hwnd, __TEXT("Error putting text on the clipboard"), "CopyTreeViewToClipboard", MB_OK | MB_ICONINFORMATION);
   }
   return (fOk) ? 1 : 0;
}



int EXPORT CopyListViewToClipboard(HWND hwnd, HWND hwndLV)
{
    //fprintf(stderr, "CopyListViewToClipboard(%p, %pd) called\n", hwnd, hwndLV);
   if (hwndLV == NULL) return 0;


   // Get the count of items in the ListView control
   int nCount = ListView_GetItemCount(hwndLV);

   // Open a handle to the remote process's kernel object
   DWORD dwProcessId;
   GetWindowThreadProcessId(hwndLV, &dwProcessId);
   HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcessId);

   if (hProcess == NULL) {
      MessageBox(hwnd, __TEXT("Could not communicate with process"), "CopyListViewToClipboard", MB_OK | MB_ICONWARNING);
      return 0;
   }

   // Prepare a buffer to hold the ListView's data.
   // Note: Hardcoded maximum of 10240 chars for clipboard data.
   // Note: Clipboard only accepts data that is in a block allocated with 
   //      GlobalAlloc using the GMEM_MOVEABLE and GMEM_DDESHARE flags.
   HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(TCHAR) * 10240);
   LPTSTR pClipData = (LPTSTR) GlobalLock(hClipData);
   pClipData[0] = 0;

   // Allocate memory in the remote process's address space
   LV_ITEM* plvi = (LV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

   // Get each ListView item's text data
   for (int nIndex = 0; nIndex < nCount; nIndex++) {

      // Initialize a local LV_ITEM structure
      LV_ITEM lvi;
      lvi.mask = LVIF_TEXT;
      lvi.iItem = nIndex;
      lvi.iSubItem = 0; 
      // NOTE: The text data immediately follows the LV_ITEM structure
      //       in the memory block allocated in the remote process.
      lvi.pszText = (LPTSTR) (plvi + 1); 
      lvi.cchTextMax = 100; 

      // Write the local LV_ITEM structure to the remote memory block
      WriteProcessMemory(hProcess, plvi, &lvi, sizeof(lvi), NULL);

      // Tell the ListView control to fill the remote LV_ITEM structure
      ListView_GetItem(hwndLV, plvi);

      // If this is not the first item, add a carriage-return/linefeed
      if (nIndex > 0) lstrcat(pClipData, __TEXT("\r\n"));

      // Read the remote text string into the end of our clipboard buffer
      ReadProcessMemory(hProcess, plvi + 1, &pClipData[lstrlen(pClipData)], 1024, NULL);
   }

   // Free the memory in the remote process's address space
   VirtualFreeEx(hProcess, plvi, 0, MEM_RELEASE);

   // Cleanup and put our results on the clipboard
   CloseHandle(hProcess);
   OpenClipboard(hwnd);
   EmptyClipboard();
#ifdef UNICODE
   BOOL fOk = (SetClipboardData(CF_UNICODETEXT, hClipData) == hClipData);
#else
   BOOL fOk = (SetClipboardData(CF_TEXT, hClipData) == hClipData);
#endif
   CloseClipboard();
   if (!fOk) {
      GlobalFree(hClipData);
      MessageBox(hwnd, __TEXT("Error putting text on the clipboard"), "CopyListViewToClipboard", MB_OK | MB_ICONINFORMATION);
   }
   return (fOk) ? 1 : 0;
}

int SneakyGetTreeItemName(HWND hWnd, HTREEITEM hItem, HANDLE hProcess, char *buffer, int buffer_size)
{
   // Allocate memory in the remote process's address space
   TVITEMEX* plvi = (TVITEMEX*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    long children = 0;

        TVITEMEX item;
        item.hItem = hItem;
        item.mask = TVIF_TEXT | TVIF_CHILDREN;
        item.pszText = (LPTSTR) (plvi + 1);
        item.cchTextMax = buffer_size;

          // Write the local LV_ITEM structure to the remote memory block
          WriteProcessMemory(hProcess, plvi, &item, sizeof(item), NULL);

        if (!TreeView_GetItem(hWnd, plvi))
        {
            fprintf(stderr, "Error calling SendMessage(TVIF_TEXT)\n");
        }

      // Read the remote text string into the end of our clipboard buffer
      ReadProcessMemory(hProcess, plvi + 1, buffer, buffer_size, NULL);
      ReadProcessMemory(hProcess, &plvi->cChildren, &children, sizeof(plvi->cChildren), NULL);  

   // Free the memory in the remote process's address space
   VirtualFreeEx(hProcess, plvi, 0, MEM_RELEASE);
   return children;
}


HTREEITEM EXPORT GetTreeViewItemByName(HWND hWnd, HTREEITEM hItem, const char *szItemName)
{
    //DWORD foo = GetCurrentProcessId();
   // Open a handle to the remote process's kernel object
   DWORD dwProcessId;
   GetWindowThreadProcessId(hWnd, &dwProcessId);
   HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcessId);

    // If hItem is NULL, start search from root item.
    if (hItem == 0) 
    {
        hItem = (HTREEITEM) SendMessage(hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
        //fprintf(stderr, "Incoming hItem for hWnd=%p was null, I just found root at %p\n", (long) hWnd, (long) hItem);
    }
    while (hItem)
    {

        char pClipData[100] = "";

        int children = SneakyGetTreeItemName(hWnd, hItem, hProcess, pClipData, 100);
            //fprintf(stderr, "The current item text is %s\n", pClipData);

        // Did we find it?
        if (stristr(pClipData, szItemName))
        {
            //fprintf(stderr, "I found the requested string, its hItem was %p\n", hItem);
             return hItem;
        }


        // Check whether we have child items.
        if (children > 0)
        {
            // Recursively traverse child items.

            HTREEITEM hItemChild = TreeView_GetNextItem(hWnd, hItem, TVGN_CHILD);
            //fprintf(stderr, "Found a parent node, first child is %p\n", (long) hItemChild);
            HTREEITEM hItemFound = GetTreeViewItemByName(hWnd, hItemChild, szItemName);

            // Did we find it?
            if (hItemFound)
            {
                return  hItemFound;
            }
        }

        // Go to next sibling item.
        hItem = TreeView_GetNextItem(hWnd, hItem, TVGN_NEXT);
        //if (hItem) fprintf(stderr, "Moving onto next sibling which is %p\n", (long) hItem);
    }

    // Not found.
    return 0;
}

int EXPORT SelectTreeViewItem(HWND hWnd, HTREEITEM hItem)
{
    return TreeView_SelectItem(hWnd, hItem);
}

int EXPORT EnsureVisibleTreeViewItem(HWND hWnd, HTREEITEM hItem)
{
    return TreeView_EnsureVisible(hWnd, hItem);
}

int EXPORT ExpandTreeViewItem(HWND hWnd, HTREEITEM hItem)
{
    return TreeView_Expand(hWnd, hItem, TVE_EXPAND);
}

int EXPORT CollapseTreeViewItem(HWND hWnd, HTREEITEM hItem)
{
    return TreeView_Expand(hWnd, hItem, TVE_COLLAPSE);
}

HTREEITEM EXPORT GetSelectedTreeViewItem(HWND hWnd)
{
    return TreeView_GetSelection(hWnd);
}

Share this post


Link to post
Share on other sites

Associated AutoIt wrapper funcs:

; return the hwnd for the active control in the given window
Func GetActiveControl($title)
   WinActivate($title)
   return ControlGetHandle($title, "", ControlGetFocus($title))
Endfunc

Func ExtractListContents($hwnd)
    Local $foo = DllCall($g_socdll, "str", "ExtractListContents", "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(ExtractListContents)")
    Endif
    Return $foo[0]
Endfunc

Func CopyListViewToClipboard($parent_hwnd, $hwnd)
    Local $foo = DllCall($g_socdll, "int", "CopyListViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyListViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

Func CopyTreeViewToClipboard($parent_hwnd, $hwnd)
    Local $foo = DllCall($g_socdll, "int", "CopyTreeViewToClipboard", "hwnd", $parent_hwnd, "hwnd", $hwnd)
    If @error Then
        MsgBox(0, "Debug", "Error with DllCall(CopyTreeViewToClipboard)")
    Endif
    Return $foo[0]
Endfunc

Func GetTreeViewItemByName($hwnd, $str, $hitem=0)
   Local $foo = DllCall($g_socdll, "long", "GetTreeViewItemByName", "hwnd", $hwnd, "long", $hitem, "str", $str)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(GetTreeViewItemByName)")
   Endif
   Return $foo[0]
Endfunc

Func SelectTreeViewItem($hwnd, $hitem)
   Local $foo = DllCall($g_socdll, "long", "SelectTreeViewItem", "hwnd", $hwnd, "long", $hitem)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(SelectTreeViewItem)")
   Endif
   Return $foo[0]
Endfunc

Func EnsureVisibleTreeViewItem($hwnd, $hitem)
   Local $foo = DllCall($g_socdll, "long", "EnsureVisibleTreeViewItem", "hwnd", $hwnd, "long", $hitem)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(EnsureVisibleTreeViewItem)")
   Endif
   Return $foo[0]
Endfunc

Func ExpandTreeViewItem($hwnd, $hitem)
   Local $foo = DllCall($g_socdll, "long", "ExpandTreeViewItem", "hwnd", $hwnd, "long", $hitem)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(ExpandTreeViewItem)")
   Endif
   Return $foo[0]
Endfunc

Func CollapseTreeViewItem($hwnd, $hitem)
   Local $foo = DllCall($g_socdll, "long", "CollapseTreeViewItem", "hwnd", $hwnd, "long", $hitem)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(CollapseTreeViewItem)")
   Endif
   Return $foo[0]
Endfunc

Func GetSelectedTreeViewItem($hwnd)
   Local $foo = DllCall($g_socdll, "long", "GetSelectedTreeViewItem", "hwnd", $hwnd, "long", $hitem)
   If @error Then
      MsgBox(0, "Debug", "Error with DllCall(GetSelectedTreeViewItem)")
   Endif
   Return $foo[0]
Endfunc

Share this post


Link to post
Share on other sites

This looks great, Fur! Your core functions have hardly changed (they will still have the memory compatability issues with 98 and Millenium), but this goes a long way towards full functionality in the Treeview!

Share this post


Link to post
Share on other sites

The memory management 9x/NT/XP differences is not a problem. I solved that by creating a class that abstracts all the differences between the OSes into nice functions like Read and Write. The class is used by the current ControlListView functions - that class is not in the public sources however... I'd actually done some treeview stuff but i've never finished it due to no time at the moment (it's just commented out in our sources atm) and not for any real technical problems - more problems like how best to use them and questions of AutoIt syntax :) Treeview controls were going to be my top priority when I got some time again.

Share this post


Link to post
Share on other sites

The memory management 9x/NT/XP differences is not a problem.  I solved that by creating a class that abstracts all the differences between the OSes into nice functions like Read and Write.  The class is used by the current ControlListView functions - that class is not in the public sources however...  I'd actually done some treeview stuff but i've never finished it due to no time at the moment (it's just commented out in our sources atm) and not for any real technical problems - more problems like how best to use them and questions of AutoIt syntax :)  Treeview controls were going to be my top priority when I got some time again.

<{POST_SNAPBACK}>

It might not be a problem in AutoIt proper, but I was still thinking in terms of making calls to this as an external DLL.

Share this post


Link to post
Share on other sites

The memory management 9x/NT/XP differences is not a problem.  I solved that by creating a class that abstracts all the differences between the OSes into nice functions like Read and Write.  The class is used by the current ControlListView functions - that class is not in the public sources however...  I'd actually done some treeview stuff but i've never finished it due to no time at the moment (it's just commented out in our sources atm) and not for any real technical problems - more problems like how best to use them and questions of AutoIt syntax :)  Treeview controls were going to be my top priority when I got some time again.

<{POST_SNAPBACK}>

please hurry!!!1 :D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0