Fur Posted March 30, 2005 Share Posted March 30, 2005 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... expandcollapse popupHTREEITEM 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; } Link to comment Share on other sites More sharing options...
sohfeyr Posted April 3, 2005 Share Posted April 3, 2005 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? Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
bshoenhair Posted April 3, 2005 Share Posted April 3, 2005 Just want to point out that can read ListView32 from any window with "ControlListView" in AutoIt v3.1. Link to comment Share on other sites More sharing options...
sohfeyr Posted April 4, 2005 Share Posted April 4, 2005 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. Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Fur Posted April 4, 2005 Author Share Posted April 4, 2005 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! Link to comment Share on other sites More sharing options...
sohfeyr Posted April 4, 2005 Share Posted April 4, 2005 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! Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Fur Posted April 4, 2005 Author Share Posted April 4, 2005 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. Link to comment Share on other sites More sharing options...
sohfeyr Posted April 4, 2005 Share Posted April 4, 2005 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. Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Fur Posted April 5, 2005 Author Share Posted April 5, 2005 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 Link to comment Share on other sites More sharing options...
LOULOU Posted April 8, 2005 Share Posted April 8, 2005 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 CopyTreeViewToClipboardPress 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 Link to comment Share on other sites More sharing options...
sohfeyr Posted April 8, 2005 Share Posted April 8, 2005 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. Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
sohfeyr Posted April 10, 2005 Share Posted April 10, 2005 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. Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
sohfeyr Posted April 11, 2005 Share Posted April 11, 2005 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 .) Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Fur Posted April 11, 2005 Author Share Posted April 11, 2005 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. Link to comment Share on other sites More sharing options...
Fur Posted April 11, 2005 Author Share Posted April 11, 2005 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. expandcollapse popup#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); } Link to comment Share on other sites More sharing options...
Fur Posted April 11, 2005 Author Share Posted April 11, 2005 Associated AutoIt wrapper funcs: expandcollapse popup; 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 Link to comment Share on other sites More sharing options...
sohfeyr Posted April 12, 2005 Share Posted April 12, 2005 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! Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Administrators Jon Posted April 13, 2005 Administrators Share Posted April 13, 2005 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. Deployment Blog:Â https://www.autoitconsulting.com/site/blog/ SCCM SDK Programming:Â https://www.autoitconsulting.com/site/sccm-sdk/ Link to comment Share on other sites More sharing options...
sohfeyr Posted April 13, 2005 Share Posted April 13, 2005 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. Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt Link to comment Share on other sites More sharing options...
Fur Posted April 13, 2005 Author Share Posted April 13, 2005 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 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now