Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

SysTreeView32 item captions

Posted on 2004-10-31
9
Medium Priority
?
1,379 Views
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.

TIA
0
Comment
Question by:Muerty
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
  • 2
9 Comments
 
LVL 17

Accepted Solution

by:
zzzzzooc earned 1000 total points
ID: 12461214
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:
http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_20096375.html


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


0
 
LVL 28

Expert Comment

by:Ark
ID: 12461248
0
 
LVL 17

Expert Comment

by:zzzzzooc
ID: 12461395
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.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 28

Expert Comment

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

Author Comment

by:Muerty
ID: 12488897
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....
0
 
LVL 17

Expert Comment

by:zzzzzooc
ID: 12489084
>>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)
0
 
LVL 3

Author Comment

by:Muerty
ID: 12489275
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?
0
 
LVL 17

Expert Comment

by:zzzzzooc
ID: 12489671
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.
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Enums (shorthand for ‘enumerations’) are not often used by programmers but they can be quite valuable when they are.  What are they? An Enum is just a type of variable like a string or an Integer, but in this case one that you create that contains…
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
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…
Suggested Courses

618 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