Visual Basic > Visual Basic Forums > Visual Basic General > Sendmessage lvi_getitemtext not working on 64-BITS?
Ask a questionAsk a question
 

AnswerSendmessage lvi_getitemtext not working on 64-BITS?

  • Tuesday, April 24, 2007 2:26 PMTonio Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hello,

     

    I am trying to get the position and text of all DeskTop icons, by sending a SendMessage to the desktop's listview, using LVI_GETITEMTEXT. Works oke on 32 bits, but on 64-bits I always get an empty string back. I also tried to do this using LVI_GET_ITEM< but the same result. No errors, but also no text string. (I do get the x,y position of the icon). What is wrong here?

     

    Thanks in advance,

     

    Option Explicit

    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hWndChildAfter As Long, ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function IsWindow Lib "user32" (ByVal hWnd As Long) As Long
    Private Declare Function CloseHandle Lib "Kernel32" (ByVal hObject As Long) As Long

    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 Const LVM_FIRST = &H1000
    Private Const LVM_SETIMAGELIST = (LVM_FIRST + 3)
    Private Const LVM_GETITEMCOUNT = (LVM_FIRST + 4)
    Private Const LVM_GETITEM = (LVM_FIRST + 5)
    Private Const LVM_SETITEM = (LVM_FIRST + 6)
    Private Const LVM_INSERTITEM = (LVM_FIRST + 7)
    Private Const LVM_DELETEITEM = (LVM_FIRST + 8)
    Private Const LVM_GETNEXTITEM = (LVM_FIRST + 12)
    Private Const LVM_GETITEMRECT = (LVM_FIRST + 14)
    Private Const LVM_SETITEMPOSITION = (LVM_FIRST + 15)
    Private Const LVM_GETITEMPOSITION = (LVM_FIRST + 16)
    Private Const LVM_HITTEST = (LVM_FIRST + 18)
    Private Const LVM_ENSUREVISIBLE = (LVM_FIRST + 19)
    Private Const LVM_SCROLL = (LVM_FIRST + 20)
    Private Const LVM_ARRANGE = (LVM_FIRST + 22)
    Private Const LVM_SETCOLUMNWIDTH = (LVM_FIRST + 30)
    Private Const LVM_GETVIEWRECT = (LVM_FIRST + 34)
    Private Const LVM_GETORIGIN = (LVM_FIRST + 41)
    Private Const LVM_SETITEMSTATE = (LVM_FIRST + 43)
    Private Const LVM_GETITEMTEXT = (LVM_FIRST + 45)
    Private Const LVM_SETITEMPOSITION32 = (LVM_FIRST + 49)
    Private Const LVM_GETWORKAREAS = (LVM_FIRST + 70)

    Private Const CB_MAXLVITEMTEXT = 255    ' user-defined, exceptions will happen if not big enough...

    Private Type lvItem   ' was LV_ITEM
      mask As Long
      iItem As Long
      iSubItem As Long
      state As Long
      stateMask As Long
      pszText As Long  ' if String, must be pre-allocated
      cchTextMax As Long
      iImage As Long
      lParam As Long
    '#If (WIN32_IE >= &H300) Then
      iIndent As Long
    '#End If
      iGroupid As Long
      cColumns As Long
      puColumns As Long
    End Type

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

    Type POINTAPI
        X As Long
        Y As Long
    End Type

    Private Enum AccessProtectionFlags
      PAGE_NOACCESS = &H1
    '  PAGE_READONLY = &H2
      PAGE_READWRITE = &H4
    '  PAGE_WRITECOPY = &H8
      PAGE_EXECUTE = &H10
      PAGE_EXECUTE_READ = &H20
      PAGE_EXECUTE_READWRITE = &H40
    '  PAGE_EXECUTE_WRITECOPY = &H80
      PAGE_GUARD = &H100
      PAGE_NOCACHE = &H200
      PAGE_WRITECOMBINE = &H400
    End Enum

    Private Enum VirtualAllocExTypes   ' commented defs are for APIs other than VirtualAllocEx
      MEM_COMMIT = &H1000
      MEM_RESERVE = &H2000
      MEM_DECOMMIT = &H4000
      MEM_FREE = &H10000
      MEM_RELEASE = &H8000&
    '  MEM_public = &H20000
    '  MEM_MAPPED = &H40000
      MEM_RESET = &H80000   '  (Win2K oinly)
      MEM_TOP_DOWN = &H100000
    '  MEM_WRITE_WATCH = &H200000   ' Win98 only
    '  MEM_PHYSICAL = &H400000   ' Win2K only
    '  MEM_4MB_PAGES = &H80000000   ' ??
    '  SEC_IMAGE = &H1000000
    '  MEM_IMAGE = SEC_IMAGE
    '  WRITE_WATCH_FLAG_RESET = &H1   ' Win98 only
    End Enum

    Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
    Private Const CB_LVITEM As Long = 36   ' user-defined

    ' CodePage
    Private Const CP_ACP = 0        ' ANSI code page

    Private Enum ProcessAccessTypes
      PROCESS_TERMINATE = (&H1)
      PROCESS_CREATE_THREAD = (&H2)
      PROCESS_SET_SESSIONID = (&H4)
      PROCESS_VM_OPERATION = (&H8)
      PROCESS_VM_READ = (&H10)
      PROCESS_VM_WRITE = (&H20)
      PROCESS_DUP_HANDLE = (&H40)
      PROCESS_CREATE_PROCESS = (&H80)
      PROCESS_SET_QUOTA = (&H100)
      PROCESS_SET_INFORMATION = (&H200)
      PROCESS_QUERY_INFORMATION = (&H400)
    '  STANDARD_RIGHTS_REQUIRED = &HF0000
      SYNCHRONIZE = &H100000
      PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF)
    End Enum

    Private Declare Function GetWindowThreadProcessId Lib "user32" _
                                  (ByVal hWnd As Long, _
                                   lpdwProcessId As Long) As Long
                                  
    Private Declare Function OpenProcess Lib "Kernel32" _
                                  (ByVal dwDesiredAccess As ProcessAccessTypes, _
                                  ByVal bInheritHandle As Long, _
                                  ByVal dwProcessId As Long) As Long
                                
    Private Declare Function GetCurrentProcessId Lib "Kernel32" () As Long

    Private Declare Function VirtualAllocEx Lib "Kernel32" _
                                  (ByVal hProcess As Long, _
                                  ByVal lpAddress As Long, _
                                  ByVal dwSize As Long, _
                                  ByVal flAllocationType As VirtualAllocExTypes, _
                                  ByVal flProtect As AccessProtectionFlags) As Long
                                 
    Private Declare Function VirtualFreeEx Lib "Kernel32" _
                                  (ByVal hProcess As Long, _
                                  ByVal lpAddress As Long, _
                                  ByVal dwSize As Long, _
                                  ByVal dwFreeType As Long) As Long

                                 
    Private Declare Function ReadProcessMemory Lib "Kernel32" _
                                  (ByVal hProcess As Long, _
                                  ByVal lpBaseAddress As Long, _
                                  lpBuffer As Any, _
                                  ByVal nSize As Long, _
                                  lpNumberOfBytesRead As Long) As Long

    Private Declare Function WriteProcessMemory Lib "Kernel32" _
                                  (ByVal hProcess As Long, _
                                  ByVal lpBaseAddress As Long, _
                                  lpBuffer As Any, _
                                  ByVal nSize As Long, _
                                  lpNumberOfBytesWritten As Long) As Long
                                 
    Private Declare Function lstrlenA Lib "Kernel32" (ByRef lpstring As Long) As Long
                               
    Private Declare Function MultiByteToWideChar Lib "Kernel32" _
                               (ByVal CodePage As Long, _
                                ByVal dwFlags As Long, _
                                lpMultiByteStr As Any, _
                                ByVal cchMultiByte As Long, _
                                lpWideCharStr As Any, _
                                ByVal cchWideChar As Long) As Long
                               
    Sub modStart()
        Dim lngHandle As Long
        Dim mlngDeskTopItemsCount As Long
        Dim nItems As Long
        Dim i As Long
        Dim ptItem As POINTAPI
        Dim strItemName As String
        Dim itmX As ListItem
       
        lngHandle = FindWindow("Progman", vbNullString)
        lngHandle = FindWindowEx(lngHandle, 0, "SHELLDLL_defVIEW", vbNullString)
        lngHandle = FindWindowEx(lngHandle, 0, "SysListView32", "FolderView")

        If lngHandle = 0 Then
            MsgBox "No handle for desktop"
            Exit Sub
        End If
       
        Send.lvwRes.ListItems.Clear
       
        If IsWindow(lngHandle) Then
            mlngDeskTopItemsCount = 0
                   
            'Number of desktop items
            nItems = ListView_GetItemCount(lngHandle)
            If nItems = 0 Then
                MsgBox "0 Items found on desktop"
                Exit Sub
            End If
           
            For i = 0 To nItems - 1
               
                strItemName = GetLVItemTextEx(lngHandle, i)
                Set itmX = Send.lvwRes.ListItems.Add(, , strItemName, , 0)
               
                If SendMessageRemote(lngHandle, LVM_GETITEMPOSITION, i, VarPtr(ptItem), Len(ptItem)) Then
                    itmX.SubItems(1) = ptItem.X
                    itmX.SubItems(2) = ptItem.Y
                Else
                    MsgBox "No position found for item " & CStr(i)
                End If
                DoEvents
            Next
        End If

    End Sub

    Private Function ListView_GetItemCount(hWnd As Long) As Long
    On Local Error Resume Next

        ListView_GetItemCount = SendMessage(hWnd, LVM_GETITEMCOUNT, ByVal 0&, ByVal 0&)

    End Function

    Private Function SendMessageRemote(hWnd As Long, uMsg As Long, wParam As Long, lParam As Long, cbLParam As Long) As Long
    On Local Error Resume Next
       
        Dim dwProcessId As Long
        Dim hProcess As Long
        Dim lpMem As Long
        Dim lp As Long
        Dim hFile As Long
     
        If GetWindowThreadProcessId(hWnd, dwProcessId) Then
            hProcess = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, _
                                                     0, dwProcessId)
            If hProcess Then
             
              ' Allocate memory in the remote process's address space
              lpMem = VirtualAllocEx(hProcess, 0, cbLParam, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
              If lpMem Then
               
                ' Write the local lParam to the remote memory block
                If WriteProcessMemory(hProcess, lpMem, ByVal lParam, cbLParam, lp) = 0 Then
                    MsgBox "WriteProcessMemory failed: " & CStr(Err.LastDllError)
                End If
               
                SendMessageRemote = SendMessage(hWnd, uMsg, wParam, ByVal lpMem)
               
                ' Read the remote memory back into lParam
                If ReadProcessMemory(hProcess, lpMem, ByVal lParam, cbLParam, lp) = 0 Then
                    MsgBox "ReadProcessmemory failed: " & CStr(Err.LastDllError)
                End If
               
                ' Free the memory in the remote process's address space
                Call VirtualFreeEx(hProcess, lpMem, 0, MEM_RELEASE)
              Else
                MsgBox "lpMem invalid (NULL)"
              End If  ' lpMem
             
              Call CloseHandle(hProcess)
            End If   ' hProcess
           
        End If   ' GetWindowThreadProcessId
     
    End Function

    Private Function GetLVItemTextEx(hwndLV As Long, iItem As Long) As String
    On Local Error Resume Next
     
      Dim lvi As lvItem
      Dim dwProcessId As Long
      Dim abText(CB_MAXLVITEMTEXT) As Byte
      Dim hProcess As Long
      Dim cbMem As Long
      Dim lpMem As Long
      Dim lp As Long
      Dim hFile As Long
     
      ' initialize the local LVITEM struct
      'lvi.mask = LVIF_TEXT
      lvi.iItem = 0
      lvi.iSubItem = 0
      lvi.cchTextMax = CB_MAXLVITEMTEXT
      lvi.iImage = 0
      lvi.iIndent = 0
      lvi.lParam = 0
      lvi.state = 0
      lvi.stateMask = 0
     
      ' get the process ID of hwndLV
      If GetWindowThreadProcessId(hwndLV, dwProcessId) Then

            ' =============================================================================
            ' WinNT, Win2K: read and write memory we allocate in the remote process, see:
            ' http://msdn.microsoft.com/library/periodic/period97/win32qa997.htm
           
            ' open a handle to the remote process
            hProcess = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, _
                                                     0, dwProcessId)
            If hProcess Then
             
              ' Allocate memory in the remote process's address space
              lpMem = VirtualAllocEx(hProcess, 0, Len(lvi), MEM_COMMIT, PAGE_READWRITE)
              If lpMem Then
                ' Allocate space for the result string
                lvi.pszText = VirtualAllocEx(hProcess, 0, CB_MAXLVITEMTEXT, MEM_COMMIT, PAGE_READWRITE)
                If lvi.pszText = 0 Then
                    MsgBox "Failed to allocate space for textbuffer " & CStr(Err.LastDllError)
                End If
               
                ' Write the local LVITEM to the remote memory block
                If WriteProcessMemory(hProcess, lpMem, lvi, LenB(lvi), lp) = 0 Then
                    MsgBox "WriteProcessmemory failed for CB_LVITEM"
                End If
                Call WriteProcessMemory(hProcess, lvi.pszText, abText(0), CB_MAXLVITEMTEXT, 0)
                
                If SendMessage(hwndLV, LVM_GETITEMTEXT, iItem, ByVal lpMem) > -1 Then
                  ' Read the remote text string into our local array buffer and convert it to a VB string
                  If ReadProcessMemory(hProcess, lvi.pszText, ByVal VarPtr(abText(0)), CB_MAXLVITEMTEXT, lp) > 0 Then
                     GetLVItemTextEx = GetStrFromPtrA(VarPtr(abText(0)))
                  Else
                     MsgBox "ReadProcessmemory failed: " & CStr(Err.LastDllError)
                  End If
                Else
                  MsgBox "Sendmessage failed: " & CStr(Err.LastDllError)
                End If  ' SendMessage
             
                ' Free the memory in the remote process's address space
                Call VirtualFreeEx(hProcess, lpMem, 0, MEM_RELEASE)
                Call VirtualFreeEx(hProcess, lvi.pszText, 0, MEM_RELEASE)
              Else
                MsgBox "lpMem = NULL"
              End If   ' lpMem
             
              Call CloseHandle(hProcess)
            Else
                MsgBox "OpenProcess Failed (LvItemText)"
            End If   ' hProcess
       
      End If   ' GetWindowThreadProcessId

    End Function

    Private Function GetStrFromPtrA(lpszA As Long) As String
    On Local Error Resume Next
     
      Dim nChars As Long
     
      nChars = lstrlenA(ByVal lpszA)
      If nChars Then
        GetStrFromPtrA = Space$(nChars)
        Call MultiByteToWideChar(CP_ACP, 0, ByVal lpszA, nChars, ByVal StrPtr(GetStrFromPtrA), nChars)
      End If
     
    End Function

     

Answers

All Replies

  • Tuesday, April 24, 2007 3:53 PMkleinmaMVP, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    This forum is for VB.NET programming questions only.

     

    Please read this about VB6 questions

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=551512&SiteID=1

  • Saturday, May 17, 2008 11:02 AMNeoTrantor Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Same Problem here, is there already a solution to this?
  • Saturday, May 17, 2008 12:24 PMTon Dorland Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    In the meantime I have find out what the issue is. The problem is that a VB program operates in 32-bit mode, but the explorer in 64 bits mode. Therefore the adresses used by the LVI_GETITEMTEXT call to the explorer must be in 64 bits mode. This can't be done in VB6.

     

    I accomplished this by writing the 'kernel'of that program in C++ using the 64-bits SDK from Microsoft. So effectively this is a 64 bits native program performing the LVI_GETITEMTEXT call.

    This does the job perfectly.

     

    Regards, Ton

  • Saturday, May 17, 2008 12:28 PMTon Dorland Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I found out what the problem is. VB6 operates in 32 bits mode, but the explorer in 64 bits mode. So the address mappings used in the call to LVI_GETITEMTEXT must be 64 bits, which can't be done in VB6.

     

    I solved this by writing the 'kernel'(that is the calls to LVI_GETITEMTEXT and the memory mapings) in VC++ using Microsoft's 64-bit SDK. This works perfectly.

     

    Regards,

     

    Ton

  • Sunday, May 18, 2008 9:06 AMNeoTrantor Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    First of all many thanks for the quick response! Thats indeed an obvious solution but I never thought about the pointer issue Wink I'm using C++ anyway so using a x64 compiler solves that problem as you said Smile Thanks again and have a nice weekend!
  • Wednesday, August 12, 2009 1:58 PMAdjutor Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hello Ton Dorland,

    I am facing the same problem, I am unable to get text with LVM_GETITEMTEXT on 64-bit machine.  You said "In the meantime I have find out what the issue is. The problem is that a VB program operates in 32-bit mode, but the explorer in 64 bits mode. Therefore the adresses used by the LVI_GETITEMTEXT call to the explorer must be in 64 bits mode. This can't be done in VB6."  What is the solution to this problem for VB.NET?

    Regards,
    Adjutor

  • Saturday, November 07, 2009 4:26 PMDerek Morin Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    This only applies for .NET but:

    I also had LVM_GETITEMTEXT problems.  I got around them by compiling as x86 instead of x64 or "anycpu".