SysTreeView32 item captions

Let me see if I can fiind a simple way of explaining what I need to do ....

I have the need to read the captions from a SysTreeView32 control in another application so I can differentiate a specific item to select based upon it's name since the position of the item may move from time to time.

Let me be more specific. I have several folders in Outlook Express that has messages coming in from various email addresses. What I need to do is be able to identify a specific email folder, set it current, and then enumerate the messages to extract data from the messages.

I am able to identify the SysTreeView32 control in the Folders window, and I am able to operate on the control using the SendMessage API, but I am currently not able to extract the caption from the items in the control.

I have tried sending a GetWindowText call to the individual items in the TreeView, but it does not return the caption to the hItem returned by the GetChild API.

Who is Participating?
zzzzzoocConnect With a Mentor Commented:
SysTreeView32 (ListView) stores the information of items within structures (UDTs). You can send LVM_GETITEM or LVM_GETITEMTEXT messages to the control's window but you need to pass a pointer to a structure (LVITEM) for it to be "filled" with the item's information. Now, the process the control is within cannot access memory from your process so you're in a bit of trouble.

Ark has posted examples to overcome this, though:

Based on the above, here's an example of getting the text for the first 5 items of the desktop's SysListView32 (I just hard-coded the handle to make it easier for me). This'll work for Win2k/XP but the PAQ above mentions how to do the same for Win9x.

Option Explicit

Private Type LVITEM
    mask As Long
    iItem As Long
    iSubitem As Long
    state As Long
    stateMask As Long
    pszText As Long
    cchTextMax As Long
    iImage As Long
    lParam As Long
    iIndent As Long
End Type

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long

Private Const LVIF_IMAGE = &H2
Private Const LVIF_TEXT = &H1

Private Const LVM_FIRST As Long = &H1000
Private Const LVM_GETITEM As Long = (LVM_FIRST + 5)

Private Const PAGE_READWRITE = &H4&

Private Const MEM_RESERVE = &H2000
Private Const MEM_COMMIT = &H1000
Private Const MEM_RELEASE = &H8000

Private Const PROCESS_VM_READ = &H10
Private Const PROCESS_VM_WRITE = &H20
Private Sub Form_Load()
    Dim intLoop As Integer
    Me.AutoRedraw = True
    For intLoop = 0 To 4
        '&H2004E is the handle to my desktop's SysListView32..
        'use Spy++ to replace it with yours for testing
        Me.Print TreeViewGetText(&H2004E, 0, intLoop)
    Next intLoop
End Sub
Private Function TreeViewGetText(ByVal hWnd As Long, ByVal iSubitem As Integer, ByVal iItem As Integer) As String
    Dim lngProcID As Long, lngProcHandle As Long
    Dim typLvItem As LVITEM, strLvItem As String
    Dim lngVarPtr1 As Long, lngVarPtr2 As Long
    Dim lngMemVar1 As Long, lngMemVar2 As Long
    Dim lngMemLen1 As Long, lngMemLen2 As Long
    Call GetWindowThreadProcessId(hWnd, lngProcID)
    If lngProcID <> 0 Then
        lngProcHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, lngProcID)
        If lngProcHandle <> 0 Then
            strLvItem = String(255, vbNullChar)
            lngVarPtr1 = StrPtr(strLvItem)
            lngVarPtr2 = VarPtr(typLvItem)
            lngMemLen1 = LenB(strLvItem)
            lngMemLen2 = LenB(typLvItem)
            lngMemVar1 = VirtualAllocEx(lngProcHandle, 0, lngMemLen1, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
            lngMemVar2 = VirtualAllocEx(lngProcHandle, 0, lngMemLen2, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
            With typLvItem
                .cchTextMax = 255
                .iItem = iItem
                .iSubitem = iSubitem
                .mask = LVIF_TEXT
                .pszText = lngMemVar1
            End With
            Call WriteProcessMemory(lngProcHandle, ByVal lngMemVar1, ByVal lngVarPtr1, lngMemLen1, 0)
            Call WriteProcessMemory(lngProcHandle, ByVal lngMemVar2, ByVal lngVarPtr2, lngMemLen2, 0)
            Call SendMessage(hWnd, LVM_GETITEM, ByVal 0, ByVal lngMemVar2)
            Call ReadProcessMemory(lngProcHandle, ByVal lngMemVar1, ByVal lngVarPtr1, lngMemLen1, 0)
            strLvItem = StrConv(strLvItem, vbUnicode)
            strLvItem = Left(strLvItem, InStr(1, strLvItem, vbNullChar) - 1)
            TreeViewGetText = strLvItem
            Call VirtualFreeEx(lngProcHandle, ByVal lngMemVar1, lngMemLen1, MEM_RELEASE)
            Call VirtualFreeEx(lngProcHandle, ByVal lngMemVar2, lngMemLen2, MEM_RELEASE)
            Call CloseHandle(lngProcHandle)
        End If
    End If
End Function

Would've saved me time if you posted sooner! :P The other PAQ of yours helped me with something else so thanks... the empty strings on Win2k/XP were because of the buffer string having a static-length.
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

I'm too far from home now (about 2500 km) so I can not react so quickly :)
MuertyAuthor Commented:
Ok forgive me for being a bit ignorant on this matter, but where in the above code do you ever set the value of strLvItem ......
I simply placed  a msgbox to display the value returned from TreeViewGetText and it returns a zero length string.... I also decided to embed the msgbox into the functio, it also returned a zero length string.

I feel as though I am missing something....
>>but where in the above code do you ever set the value of strLvItem
I referred to strLvItem by it's pointer (string pointer, since the variable's pointer would just point to another pointer in memory (BSTR)). lngVarPtr1 is the pointer to strLvItem and lngMemVar1 is the pointer for the allocated memory which the SendMessage() line fills (when passing it the LVITEM UDT). After lngMemVar1 was "filled", the ReadProcessMemory() line copied the string to lngMemVar1 (which is basically "setting the value"). You should refer to Ark's links (and his links from there and so forth) since he's detailed a lot of it.

>>and it returns a zero length string
I mentioned hard-coding the window-handle for the ListView control so if you didn't change it, that's probably the cause.

Change to something such as:
Me.Print TreeViewGetText(ListView1.hWnd, 0, intLoop)
MuertyAuthor Commented:
I hard coded the handle of the SysTreeView32 directly from Spy++, but I actually use in my programming the EnumChildWindows API ...

I am still unable to get the info from there... I will see if there is another route that will solve the issue ...

I read the issues with XP/2000 and I am using XP... is there another issue that I am missing?
It worked for me on Win2k and same method for a friend on XP.

Set up break-points to see where the procedure leads. Replace "Call" with "Debug.Print". The WriteProcessMemory(), ReadProcessMemory(), and SendMessage() functions should return non-zero. If not, they failed and you can try getting the last DLL error.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.