SysTreeView32 item captions

Posted on 2004-10-31
Last Modified: 2009-07-29
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.

Question by:Muerty
    LVL 17

    Accepted Solution

    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_OPERATION = &H8
    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

    LVL 27

    Expert Comment

    LVL 17

    Expert Comment

    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.
    LVL 27

    Expert Comment

    I'm too far from home now (about 2500 km) so I can not react so quickly :)
    LVL 3

    Author Comment

    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....
    LVL 17

    Expert Comment

    >>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)
    LVL 3

    Author Comment

    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?
    LVL 17

    Expert Comment

    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.

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    Free Trending Threat Insights Every Day

    Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

    Introduction This article makes the case for using two modules in your VBA/VB6 applications to provide both case-sensitive and case-insensitive text comparison operations.  Recently, I solved an EE question using the LIKE function.  In order for th…
    When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
    Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
    Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

    933 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    16 Experts available now in Live!

    Get 1:1 Help Now