riceman0
asked on
Read items in a SysLstView32 in another app
Not having much luck lately with answers, maybe this one will work.
Hey, trying to read items out of a listbox in another application and use it in my VB.NET app. I can use FindWindowEx to get a handle to it just fine. Then I tried
Dim i As Integer = SendMessage(hndList, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
to get the number of items in it and that doesn't work. I wrote another app and dropped a listbox on it and ran it, and the above line of code DID work on that one. Then I used Spy++ and discovered the control they use in my intended target is not a Listbox, but a SysLstView32, I guess a standard ListView. I don't know I can use SendMessage to get data out of that, like I would a listbox, and if so what are the equivalents to LB_GETCOUNT, etc? And if not, is there any way to access that data??
Thanks very much.
Hey, trying to read items out of a listbox in another application and use it in my VB.NET app. I can use FindWindowEx to get a handle to it just fine. Then I tried
Dim i As Integer = SendMessage(hndList, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
to get the number of items in it and that doesn't work. I wrote another app and dropped a listbox on it and ran it, and the above line of code DID work on that one. Then I used Spy++ and discovered the control they use in my intended target is not a Listbox, but a SysLstView32, I guess a standard ListView. I don't know I can use SendMessage to get data out of that, like I would a listbox, and if so what are the equivalents to LB_GETCOUNT, etc? And if not, is there any way to access that data??
Thanks very much.
http://www.codeproject.com/threads/int64_memsteal.asp ("Stealing Program's Memory") illustrates how to do that properly.
ASKER
Having trouble translating that to .NET...
So why did you post that in "unmanaged C++"=
ASKER
Because I know the solution probably uses a lot of API calls. Hopefully that wasn't the primary zone. The question does say I said I need it in VB.NET...
Here's my translation so far (old code commented, adjacent). I'm passing a good handle, and I know it's looking at the right control because it gets the count right. Everything looks okay (non-zero handles, etc) until it gets to the first WriteProcessMemory command in the for loop, then I get an "Value does not fall within the expected range" error.
Looking at the parameters of the WriteProcessMemory command, process=1000, _lvi = 12124160, lvi_length=40, and the lvi structure only has two nonzero members, cchTxtMax = 255 and pszText = 12189696. Can't figure out which of these it's talking about through trial and error.
Private Structure LV_ITEMA
Dim mask As Integer
Dim iItem As Integer
Dim iSubItem As Integer
Dim state As Integer
Dim stateMask As Integer
Dim pszText As Integer
Dim cchTextMax As Integer
Dim iImage As Integer
Dim lParam As Integer
Dim iIndent As Integer
End Structure
Public Sub GetListInfo3(ByVal hndList As Integer)
Dim count As Integer = SendMessage(hndList, LVM_GETITEMCOUNT, 0, 0)
Dim lvi As New LV_ITEMA
', *_lvi;
Dim item As String
Dim subitem As String
Dim pid As Integer
Dim process As Integer
'char item[512], subitem[512];
'char *_item, *_subitem;
'unsigned long pid;
' HANDLE process;
GetWindowThreadProcessId(h ndList, pid)
process = OpenProcess(PROCESS_VM_OPE RATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_QUERY_INFORMATION, False, pid)
'lvi = VirtualAllocEx(process, vbnull, 20, MEM_COMMIT, PAGE_READWRITE);
Dim lvi_length As Integer = System.Runtime.InteropServ ices.Marsh al.SizeOf( lvi)
Dim _lvi As IntPtr
_lvi = VirtualAllocEx(process, 0&, lvi_length, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
Dim _item As IntPtr
Dim _subitem As IntPtr
_item = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
_subitem = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
'_lvi=(LVITEM*)VirtualAllo cEx(proces s, NULL, sizeof(LVITEM),
' MEM_COMMIT, PAGE_READWRITE);
'_item=(char*)VirtualAlloc Ex(process , NULL, 512, MEM_COMMIT,
' PAGE_READWRITE);
'_subitem=(char*)VirtualAl locEx(proc ess, NULL, 512, MEM_COMMIT,
' PAGE_READWRITE);
lvi.cchTextMax = 512
'lvi.cchTextMax=512;
For i As Integer = 0 To 511
'for(i=0; i<count; i++) {
lvi.iSubItem = 0
lvi.pszText = _item
' lvi.iSubItem=0;
' lvi.pszText=_item;
WriteProcessMemory(process , _lvi, lvi, lvi_length, 0&)
' WriteProcessMemory(process , _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
' SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
lvi.iSubItem = 1
lvi.pszText = _subitem
' lvi.iSubItem=1;
' lvi.pszText=_subitem;
WriteProcessMemory(process , _lvi, lvi, lvi_length, 0&)
' WriteProcessMemory(process , _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
' SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _item, item, 512, 0&)
ReadProcessMemory(process, _subitem, subitem, 512, 0&)
' ReadProcessMemory(process, _item, item, 512, NULL);
' ReadProcessMemory(process, _subitem, subitem, 512, NULL);
' printf("%s - %s\n", item, subitem);
'}
Next i
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE)
VirtualFreeEx(process, _item, 0, MEM_RELEASE)
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE)
'VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
'VirtualFreeEx(process, _item, 0, MEM_RELEASE);
'VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
'return 0;
End Sub
Here's my translation so far (old code commented, adjacent). I'm passing a good handle, and I know it's looking at the right control because it gets the count right. Everything looks okay (non-zero handles, etc) until it gets to the first WriteProcessMemory command in the for loop, then I get an "Value does not fall within the expected range" error.
Looking at the parameters of the WriteProcessMemory command, process=1000, _lvi = 12124160, lvi_length=40, and the lvi structure only has two nonzero members, cchTxtMax = 255 and pszText = 12189696. Can't figure out which of these it's talking about through trial and error.
Private Structure LV_ITEMA
Dim mask As Integer
Dim iItem As Integer
Dim iSubItem As Integer
Dim state As Integer
Dim stateMask As Integer
Dim pszText As Integer
Dim cchTextMax As Integer
Dim iImage As Integer
Dim lParam As Integer
Dim iIndent As Integer
End Structure
Public Sub GetListInfo3(ByVal hndList As Integer)
Dim count As Integer = SendMessage(hndList, LVM_GETITEMCOUNT, 0, 0)
Dim lvi As New LV_ITEMA
', *_lvi;
Dim item As String
Dim subitem As String
Dim pid As Integer
Dim process As Integer
'char item[512], subitem[512];
'char *_item, *_subitem;
'unsigned long pid;
' HANDLE process;
GetWindowThreadProcessId(h
process = OpenProcess(PROCESS_VM_OPE
'lvi = VirtualAllocEx(process, vbnull, 20, MEM_COMMIT, PAGE_READWRITE);
Dim lvi_length As Integer = System.Runtime.InteropServ
Dim _lvi As IntPtr
_lvi = VirtualAllocEx(process, 0&, lvi_length, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
Dim _item As IntPtr
Dim _subitem As IntPtr
_item = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
_subitem = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
'_lvi=(LVITEM*)VirtualAllo
' MEM_COMMIT, PAGE_READWRITE);
'_item=(char*)VirtualAlloc
' PAGE_READWRITE);
'_subitem=(char*)VirtualAl
' PAGE_READWRITE);
lvi.cchTextMax = 512
'lvi.cchTextMax=512;
For i As Integer = 0 To 511
'for(i=0; i<count; i++) {
lvi.iSubItem = 0
lvi.pszText = _item
' lvi.iSubItem=0;
' lvi.pszText=_item;
WriteProcessMemory(process
' WriteProcessMemory(process
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
' SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
lvi.iSubItem = 1
lvi.pszText = _subitem
' lvi.iSubItem=1;
' lvi.pszText=_subitem;
WriteProcessMemory(process
' WriteProcessMemory(process
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
' SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process,
ReadProcessMemory(process,
' ReadProcessMemory(process,
' ReadProcessMemory(process,
' printf("%s - %s\n", item, subitem);
'}
Next i
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE)
VirtualFreeEx(process, _item, 0, MEM_RELEASE)
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE)
'VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
'VirtualFreeEx(process, _item, 0, MEM_RELEASE);
'VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
'return 0;
End Sub
ASKER
Oh, and in case its important.
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Integer, _
ByVal lpBaseAddress As Object, ByVal lpBuffer As Object, ByVal nSize As Integer, ByVal lpNumberOfBytesWritten As Integer) As Integer
>>>> Dim i As Integer = SendMessage(hndList, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
The message to send is
SendMessage(hndList, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero)
Reading the VM_Memory is only needed if wanting to get text from the list control.
If the above antive C++ access works you may consider putting that to a C++ dll and call the dll from VB by providing a - big enough - string buffer that the dll simply has to fill up.
Regards, Alex
The message to send is
SendMessage(hndList, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero)
Reading the VM_Memory is only needed if wanting to get text from the list control.
If the above antive C++ access works you may consider putting that to a C++ dll and call the dll from VB by providing a - big enough - string buffer that the dll simply has to fill up.
Regards, Alex
ASKER
Oops, sorry already fixed that, my count is working correctly per my latest post. Thanks though.
I do need to read the items out of the listview.
That's a thought, but hopefully I don't need to create an external DLL. It seems like I might be pretty close, the translation is getting pretty far, just somehow I'm screwing up the parameters for that WriteProcessMemory.
ASKER
Occurs to me that might be too much code, here it is shortened up and annotated:
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Integer, _
ByVal lpBaseAddress As Object, ByVal lpBuffer As Object, ByVal nSize As Integer, ByVal lpNumberOfBytesWritten As Integer) As Integer
Private Structure LV_ITEMA
Dim mask As Integer
Dim iItem As Integer
Dim iSubItem As Integer
Dim state As Integer
Dim stateMask As Integer
Dim pszText As Integer
Dim cchTextMax As Integer
Dim iImage As Integer
Dim lParam As Integer
Dim iIndent As Integer
End Structure
Public Sub GetListInfo3(ByVal hndList As Integer)
' ***COUNT WORKS OKAY
Dim count As Integer = SendMessage(hndList, LVM_GETITEMCOUNT, 0, 0)
Dim lvi As New LV_ITEMA
Dim item As String
Dim subitem As String
Dim pid As Integer
Dim process As Integer
GetWindowThreadProcessId(h
process = OpenProcess(PROCESS_VM_OPE
Dim lvi_length As Integer = System.Runtime.InteropServ
Dim _lvi As IntPtr
_lvi = VirtualAllocEx(process, 0&, lvi_length, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
Dim _item As IntPtr
Dim _subitem As IntPtr
_item = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
_subitem = VirtualAllocEx(process, 0&, 512, MEM_COMMIT, PAGE_READWRITE)
' ******* ALL POINTERS SEEM TO BE NON_NULL, WHICH SEEMS TO MEAN IT'S WORKING UP TO HERE
lvi.cchTextMax = 512
For i As Integer = 0 To 511
lvi.iSubItem = 0
lvi.pszText = _item
'***** ERROR OCCURS ON NEXT LINE
WriteProcessMemory(process
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
lvi.iSubItem = 1
lvi.pszText = _subitem
WriteProcessMemory(process
SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)
ReadProcessMemory(process,
ReadProcessMemory(process,
Next i
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE)
VirtualFreeEx(process, _item, 0, MEM_RELEASE)
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE)
End Sub
One possible problem:
Dim lvi As New LV_ITEMA; // <--- lvi is a pointer
Dim lvi_length As Integer = System.Runtime.InteropServ ices.Marsh al.SizeOf( lvi)
// lvi_length is the length of a pointer (4 bytes)
WriteProcessMemory(process , _lvi, lvi, lvi_length, 0&)
This function will write 4 bytes. One reason I always pre-pend pointer variable names with the letter p; e.g., I'd use
Dim plvi As New LV_ITEMA; // <--- lvi is a pointer
or just
LV_ITEM lvi; // <--- not a pointer
...
WriteProcessMemory(process , _lvi, &lvi, lvi_length, 0&)
If you look at jkr's excellent reference,
http://www.codeproject.com/threads/int64_memsteal.asp
you'll see that the requirement is to initialize the entire LV_ITEM structure pre-fill it with zeros but set
lvi.pszText
to point to another buffer in the same external address space and set
lvi.cchTextMax
to the length of that buffer. BTW, a shorter version of this is to allocate enough room in the other process to contain both the LV_ITEM *and* the length of the buffer as one allocation, then preset the lvi.pszText to point into the same allocation (e.g., to one byte past the end of the structure).
Another example of how to do this in C++ is in MSJ, Sept 1997
http://www.microsoft.com/msj/0997/win320997.aspx
Note/Disclaimer: I have not tried this in a .NET environment or used the marshalling that is required. It is quite possible that this enviroment requires special permissions or other special handling to "steal" memory from another process.
-- Dan
Dim lvi As New LV_ITEMA; // <--- lvi is a pointer
Dim lvi_length As Integer = System.Runtime.InteropServ
// lvi_length is the length of a pointer (4 bytes)
WriteProcessMemory(process
This function will write 4 bytes. One reason I always pre-pend pointer variable names with the letter p; e.g., I'd use
Dim plvi As New LV_ITEMA; // <--- lvi is a pointer
or just
LV_ITEM lvi; // <--- not a pointer
...
WriteProcessMemory(process
If you look at jkr's excellent reference,
http://www.codeproject.com/threads/int64_memsteal.asp
you'll see that the requirement is to initialize the entire LV_ITEM structure pre-fill it with zeros but set
lvi.pszText
to point to another buffer in the same external address space and set
lvi.cchTextMax
to the length of that buffer. BTW, a shorter version of this is to allocate enough room in the other process to contain both the LV_ITEM *and* the length of the buffer as one allocation, then preset the lvi.pszText to point into the same allocation (e.g., to one byte past the end of the structure).
Another example of how to do this in C++ is in MSJ, Sept 1997
http://www.microsoft.com/msj/0997/win320997.aspx
Note/Disclaimer: I have not tried this in a .NET environment or used the marshalling that is required. It is quite possible that this enviroment requires special permissions or other special handling to "steal" memory from another process.
-- Dan
ASKER
Just trying this now, thanks. One thing, before I go off in the wrong direction: did you tell me to put a "&" before the parameter in the WriteProcessMemory command? You added a "&", but when I add that it .NET says "expression required."
Also, you made me wonder if the API declare should have ByRef or ByVal in the prototype. Changing it to ByRef didn't immediately fix the problem, but maybe that's a contributor.
Still chewing, but let me know if that "&" was deliberate if you see this... thanks...
Also, you made me wonder if the API declare should have ByRef or ByVal in the prototype. Changing it to ByRef didn't immediately fix the problem, but maybe that's a contributor.
Still chewing, but let me know if that "&" was deliberate if you see this... thanks...
ASKER
Dan, everything is zero in the LV_ITEM, except for the string and the string length, which I set just as he does.
The first link you send is the exact code I translated from, so I am initializing everything he is initializing in the same memory spaces he is... do you see something specific that he is doing that I am not?
I'm starting to think this is impossible in .NET. I've come to hate "marshalling."
The first link you send is the exact code I translated from, so I am initializing everything he is initializing in the same memory spaces he is... do you see something specific that he is doing that I am not?
I'm starting to think this is impossible in .NET. I've come to hate "marshalling."
In the example, the code created a local instance of the LVITEM structure
LVITEM lvi;
so, when you pass its address in any function, you need to use &
as opposed to your code which allocates one...
Dim lvi As New LV_ITEMA
I'm really not qualified to answer C# questions. Also, my experience with marshalling is very limited.
LVITEM lvi;
so, when you pass its address in any function, you need to use &
as opposed to your code which allocates one...
Dim lvi As New LV_ITEMA
I'm really not qualified to answer C# questions. Also, my experience with marshalling is very limited.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Oh, nice... how did I miss that in my searches? Checking that out...
ASKER
You rock, thanks!
ASKER
By the way I've posted follow-up questions here:
https://www.experts-exchange.com/questions/22912002/How-to-get-multiple-columns-from-a-ListView-in-another-app.html
https://www.experts-exchange.com/questions/22912061/Double-click-item-in-listview-in-another-app.html
Thanks again!
https://www.experts-exchange.com/questions/22912002/How-to-get-multiple-columns-from-a-ListView-in-another-app.html
https://www.experts-exchange.com/questions/22912061/Double-click-item-in-listview-in-another-app.html
Thanks again!