Sendmessage lvi_getitemtext not working on 64-BITS?
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 LongPrivate 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 TypePrivate Const LVIF_TEXT = &H1
Private Const LVIF_IMAGE = &H2
Private Const LVIF_STATE = &H8Type POINTAPI
X As Long
Y As Long
End TypePrivate 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 EnumPrivate 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 EnumPrivate Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const CB_LVITEM As Long = 36 ' user-defined' CodePage
Private Const CP_ACP = 0 ' ANSI code pagePrivate 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 EnumPrivate 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 LongPrivate 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 LongPrivate 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 IfEnd Sub
Private Function ListView_GetItemCount(hWnd As Long) As Long
On Local Error Resume NextListView_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 FunctionPrivate 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 ' GetWindowThreadProcessIdEnd 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
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
All Replies
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
- Same Problem here, is there already a solution to this?
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
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
- First of all many thanks for the quick response! Thats indeed an obvious solution but I never thought about the pointer issue
I'm using C++ anyway so using a x64 compiler solves that problem as you said
Thanks again and have a nice weekend! 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- 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".


