הערה
הגישה לדף זה מחייבת הרשאה. באפשרותך לנסות להיכנס או לשנות מדריכי כתובות.
הגישה לדף זה מחייבת הרשאה. באפשרותך לנסות לשנות מדריכי כתובות.
Question
Saturday, December 7, 2013 6:59 AM
Below is my code where I am trying to send LVM_SETITEMSTATE message to a Listview Control. But there is no resonse in the Target Application. I tried to investigate the issue with Spy++ where I could see the variable values of LV_ITEM struct received by the Application is Zero. But in my code I am actually sending values in the LV_ITEM struct which are not getting passed as zeros to the target application. I am not sure why this happens.Could anyone Please help me with this issue?
Actual Code:
'Declarations and Struct Definition
Public Const LVM_FIRST = &H1000
Public Const LVM_SETITEMSTATE = (LVM_FIRST + 43)
Public Const LVM_GETITEMSTATE As Long = (LVM_FIRST + 44)
Public Const LVM_GETSELECTEDCOUNT = (LVM_FIRST + 50)
Public Const LVIS_SELECTED = &H2
Public Const LVN_ITEMACTIVATE As Long = (LVM_FIRST + 14)
Public Const NM_DBLCLK As Long = (LVM_FIRST + 3)
Public Const LVIF_STATE = &H8
Public Const LVIS_STATEIMAGEMASK As Long = &HF000
Public Type LV_ITEM
mask As Long
iItem As Long
iSubItem As Long
state As Long
stateMask As Long
pszText As String
cchTextMax As Long
iImage As Long
lParam As Long
iIndent As Long
End Type
'Actual program sending the LVM_ SETITEMSTATE Message
Sub test3()
Dim LV As LV_ITEM
** 'I tried this buffer with 40b ytes size also which is the total size of my LV_ITEM struct but still not working.**
'I am assigning this buffer to the pszText in the LV_ITEM struct.
szString = Space(0)
With LV
.mask = LVIF_STATE
.iItem = 2
.iSubItem = 0
.state = 0
.cchTextMax = 0
.pszText = szString
.stateMask = LVIS_SELECTED
End With
'2952362 is the hwnd for the Listview control and I am trying to access the second item
Call SendMessage(2952364, LVM_SETITEMSTATE, 2, LV)
End Sub
All replies (5)
Monday, December 9, 2013 7:14 PM ✅Answered
Thanks Razerz. I am now able to select the listview item by following your code logic.
Below is the VBA version of your logic .Thanks for the solution. This would help a lot of people as only limited info is available on this in web. Nothing like the sample working code as given in this thread.
VBA Code to do LVM_SETITEMSTATE:
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, lpBuffer As Any, ByVal nSize As Byte, ByVal lpNumberOfBytesWritten As Long) 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 Byte, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Type LUID
UsedPart As Long
IgnoredForNowHigh42BitPart As Long
End Type
Public Const LVM_FIRST = &H1000
Public Const LVM_SETITEMSTATE = (LVM_FIRST + 43)
Public Const LVM_GETITEMSTATE As Long = (LVM_FIRST + 44)
Public Const LVM_GETSELECTEDCOUNT = (LVM_FIRST + 50)
Public Const LVIS_SELECTED = &H2
Public Const LVN_ITEMACTIVATE As Long = (LVM_FIRST + 14)
Public Const NM_DBLCLK As Long = (LVM_FIRST + 3)
Public Const LVIF_STATE = &H8
Public Const LVIS_STATEIMAGEMASK As Long = &HF000
Public Const VMWRITE = &H20
Public Const PAGE_READWRITE = &H4
Public Const PROCESS_ALL_ACCESS = &H1F0FFF
Public Const PROCESS_VM_WRITE = &H20
Public Const PROCESS_VM_OPERATION = &H8
Public Type LV_ITEM
mask As Long
iItem As Long
iSubItem As Long
state As Long
stateMask As Long
pszText As String
cchTextMax As Long
iImage As Long
lParam As Long
iIndent As Long
End Type
Private Const MEM_COMMIT = &H1000
Sub select_listitem()
Dim LV As LV_ITEM
Dim numSelected As Long
Dim t As Long
Dim lhwndProcess As Long
Dim lExitCode As Long
Dim lRetVal As Long
Dim lhThisProc As Long
Dim lhTokenHandle As Long
Dim tLuid As LUID
Dim tTokenPriv As TOKEN_PRIVILEGES, tTokenPrivNew As TOKEN_PRIVILEGES
Dim lBufferNeeded As Long
Dim lpid as Long
Const PROCESS_ALL_ACCESS = &H1F0FFF, PROCESS_TERMINATE = &H1
Const ANYSIZE_ARRAY = 1, TOKEN_ADJUST_PRIVILEGES = &H20
Const TOKEN_QUERY = &H8, SE_DEBUG_NAME As String = "SeDebugPrivilege"
Const SE_PRIVILEGE_ENABLED = &H2
'hWnd = 2100726
szString = Space(0)
With LV
.mask = LVIF_STATE
'.iItem = 2
'.iSubItem = 0
.state = &HF
'.cchTextMax = 0
'.pszText = szString
.stateMask = LVIS_SELECTED
End With
hwnd= XXXX'put the window handle of the listviewcontrol here
k = Len(LV)
lRetVal = GetWindowThreadProcessId(hWnd, lpid)
lhwndProcess = OpenProcess(PROCESS_ALL_ACCESS, False, lpid)
vpoint = VirtualAllocEx(lhwndProcess, 0, 40, MEM_COMMIT, PAGE_READWRITE)
result = WriteProcessMemory(lhwndProcess, vpoint, LV, Len(LV), 0)
Call SendMessage(hWnd, LVM_SETITEMSTATE, 2, vpoint) 'The second item in the list view is selected
End Sub
Saturday, December 7, 2013 2:44 PM
Hi,
The first thing you need to do is get the VB.NET signatures for the functions, structures, and constants. I listed some of them below. Also i see you are using (2952364) as the handle of the listview. The listview handle will change every time you open and close the window that has the listview on it so you will need to use FindWindow or FindWindowEx to get the Parent window handle and then use FindWindowEx to search threw the child windows of it to get the handle of the listview each time this is executed.
Now the bad news is that it is not as easy as just getting the listview handle and sending a message to it to select an item. It requires getting the processes ID, allocating unmanaged memory, and reading and writing to the processes memory.
I have tried this in the past and was never fully successful at doing this. There is a lot of similar questions on the net but, there is not a working example anywhere that i have found yet. Perhaps there is a specific reason that you need to select an item in the listview that may be able to be done a different way such as, if you just want to open an explorer window with a certain folder/file selected or something similar?
Imports System.Runtime.InteropServices
Public Class Form1
Public Const LVM_FIRST As Integer = &H1000
Public Const LVM_SETITEMSTATE As Integer = (LVM_FIRST + 43)
Public Const LVM_GETITEMSTATE As Integer = (LVM_FIRST + 44)
Public Const LVM_GETSELECTEDCOUNT As Integer = (LVM_FIRST + 50)
Public Const LVN_ITEMACTIVATE As Integer = (LVM_FIRST + 14)
Public Const NM_DBLCLK As Integer = (LVM_FIRST + 3)
Public Const LVIS_SELECTED As Integer = &H2
Public Const LVIF_STATE As Integer = &H8
Public Const LVIS_STATEIMAGEMASK As Integer = &HF000
<StructLayout(LayoutKind.Sequential)> _
Public Structure LVITEM
Public mask As Integer
Public iItem As Integer
Public iSubItem As Integer
Public state As Integer
Public stateMask As Integer
<MarshalAs(UnmanagedType.LPTStr)> Public pszText As String
Public cchTextMax As Integer
Public iImage As Integer
Public lParam As Integer
Public iIndent As Integer
Public iGroupId As Integer
Public cColumns As Integer
Public puColumns As Integer
Public piColFmt As Integer
Public iGroup As Integer
End Structure
<DllImport("user32.dll", EntryPoint:="SendMessageW")> _
Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As LVITEM) As Integer
End Function
<DllImport("user32.dll", EntryPoint:="SendMessageW")> _
Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
End Class
Sunday, December 8, 2013 9:48 AM
Hello IronRazers,
Thanks for your reply.I would like to add more details on what I am trying to do.
I am trying to automate the Listview Control in a Tool called IBM ISA using legacy VBA Macros. I am not using VB.NET through Visual studio. Even if you can give me a working code for this in VB.NET , it is fine. I will be able to convert it accordingly.
As you said the window handle is expected to change but I am capturing it successfully using UIAutomation feature.But UIAutomation does not seem to be working properly with some controls especially when executing the control methods. Hence I am using Win32 API concepts in my automation script to invoke controls. Could you please help me on how to pass the LV_ITEM struct properly to the Target Application. I need some guidelines on the memory management part.
Previously I was able to invoke menu control items using heapalloc feature in the current process and then creating the struct on it, but the same concept is not working in the case of Listviewitem. In the menu controls I actually sent the struct to get menuitem info. But here in Listview item I am actually passing the struct data to set the values in the Target Application. Any Thoughts on how to accomplish LVM_SETITEMSTATE??
Monday, December 9, 2013 1:03 AM
Hi,
I finally figured it out so here is an example that shows how to select or un-select a specific item in an external program`s SysListView32 by its index. Don`t be shocked if you tell it to select item 1 (the 2nd item) and it appears to select the wrong item because, it depends on how the external program arranges the items in the listview.
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
Private Const LVM_FIRST As UInteger = &H1000
Private Const LVM_GETITEMCOUNT As UInteger = (LVM_FIRST + 4)
Private Const LVM_SETITEM As UInteger = (LVM_FIRST + 6)
Private Const LVM_SETITEMSTATE As UInteger = (LVM_FIRST + 43)
Private Const LVIS_SELECTED As UInteger = &H2
Private Const LVIF_STATE As UInteger = &H8
Private Const MEM_COMMIT As UInteger = &H1000
Private Const MEM_RELEASE As UInteger = &H8000
Private Const PAGE_READWRITE As UInteger = &H4
Private Const PROCESS_VM_READ As UInteger = &H10
Private Const PROCESS_VM_WRITE As UInteger = &H20
Private Const PROCESS_VM_OPERATION As UInteger = &H8
<DllImport("kernel32.dll", SetLastError:=True)> Private Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, ByVal bInheritHandle As Boolean, ByVal dwProcessId As Integer) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal message As UInteger, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
End Function
<DllImport("kernel32.dll", SetLastError:=True)> Private Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal flAllocationType As UInteger, ByVal flProtect As UInteger) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)> Private Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal dwFreeType As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> Private Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByRef lpBuffer As LV_ITEM, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", SetLastError:=True)> Private Shared Function GetWindowThreadProcessId(<InAttribute()> ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True)> Private Shared Function FindWindowExW(ByVal hwndParent As IntPtr, ByVal hwndChildAfter As IntPtr, <InAttribute(), MarshalAs(UnmanagedType.LPTStr)> ByVal lpszClass As String, <InAttribute(), MarshalAs(UnmanagedType.LPTStr)> ByVal lpszWindow As String) As IntPtr
End Function
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure LV_ITEM
Public mask As UInteger
Public iItem As Integer
Public iSubItem As Integer
Public state As UInteger
Public stateMask As UInteger
Public pszText As IntPtr
Public cchTextMax As Integer
Public iImage As Integer
Public lParam As IntPtr
Public iIndent As Integer
Public iGroupId As Integer
Public cColumns As Integer
Public puColumns As IntPtr
Public piColFmt As IntPtr
Public iGroup As Integer
End Structure
Private Sub Button_SelectItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_SelectItem.Click
SelectItem(1, True) 'Sets the 2nd item in the listview to the Selected state if 2 items exist in the listview
End Sub
Private Sub Button_UnSelectItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_UnSelectItem.Click
SelectItem(1, False) 'Sets the 2nd item in the listview to the Un-Selected state if 2 items exist in the listview
End Sub
Private Sub SelectItem(ByVal iIndex As Integer, ByVal selState As Boolean)
Dim hLstVw As IntPtr = GetListViewHandle() 'Get the listview`s handle
If Not hLstVw.Equals(IntPtr.Zero) Then 'Check if the listview handle was found
Dim itemCount As Integer = SendMessage(hLstVw, LVM_GETITEMCOUNT, 0, IntPtr.Zero)
If itemCount < iIndex Then Exit Sub 'If the item index to be set is higher than the listview`s item count then exit the sub now
Dim procId As Integer = Nothing
GetWindowThreadProcessId(hLstVw, procId) 'Get the listview`s process Id
'Open the process id and set access rights to read and write to the process`s memory
Dim hProc As IntPtr = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, procId)
Dim lvi As New LV_ITEM 'Create a new LV_ITEM structure set to select or unselect the item depending on the selState parameter
lvi.mask = LVIF_STATE
lvi.stateMask = LVIS_SELECTED
If selState Then
lvi.state = &HF
Else
lvi.state = 0
End If
'Reserve space in the processes memory and write the new LV_ITEM structure`s data to it
Dim LvItemPtr As IntPtr = VirtualAllocEx(hProc, IntPtr.Zero, Marshal.SizeOf(lvi), MEM_COMMIT, PAGE_READWRITE)
WriteProcessMemory(hProc, LvItemPtr, lvi, Marshal.SizeOf(lvi), 0)
SendMessage(hLstVw, LVM_SETITEMSTATE, iIndex, LvItemPtr) 'Send the message to set the items state
VirtualFreeEx(hProc, LvItemPtr, 0, MEM_RELEASE) 'Release the processes memory used for the LV_ITEM structure`s data
CloseHandle(hProc)
Else
MessageBox.Show("ListView handle not found.")
End If
End Sub
'This is the function i used to get the SysListView32 handle of an explorer window
Private Function GetListViewHandle() As IntPtr
Dim hParent As IntPtr = FindWindowExW(IntPtr.Zero, IntPtr.Zero, "CabinetWClass", Nothing)
If Not hParent.Equals(IntPtr.Zero) Then
Dim hChld_1 As IntPtr = FindWindowExW(hParent, IntPtr.Zero, "SHELLDLL_DefView", Nothing)
If Not hChld_1.Equals(IntPtr.Zero) Then
Dim hChld_2 As IntPtr = FindWindowExW(hChld_1, IntPtr.Zero, "DUIViewWndClassName", Nothing)
If Not hChld_2.Equals(IntPtr.Zero) Then
Dim hChld_3 As IntPtr = FindWindowExW(hChld_2, IntPtr.Zero, "DirectUIHWND", Nothing)
If Not hChld_3.Equals(IntPtr.Zero) Then
Dim hChld_4 As IntPtr = FindWindowExW(hChld_3, IntPtr.Zero, "CtrlNotifySink", Nothing)
If Not hChld_4.Equals(IntPtr.Zero) Then
Dim hChld_5 As IntPtr = FindWindowExW(hChld_4, IntPtr.Zero, "SysListView32", "FolderView")
If Not hChld_5.Equals(IntPtr.Zero) Then
Return hChld_5 'Return the handle to the SysListView32
End If
End If
End If
End If
End If
End If
Return IntPtr.Zero
End Function
End Class
Monday, December 9, 2013 9:29 PM
Hi,
Glad to here it helped. :)
No there is not many examples of interacting with an external listview on the net and i bet i tried most if not all of them but, it seemed there was one problem or another with all of them.