Link to home
Start Free TrialLog in
Avatar of riceman0
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.
Avatar of jkr
jkr
Flag of Germany image

http://www.codeproject.com/threads/int64_memsteal.asp ("Stealing Program's Memory") illustrates how to do that properly.
Avatar of riceman0
riceman0

ASKER

Having trouble translating that to .NET...
So why did you post that in "unmanaged C++"=
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(hndList, pid)

        process = OpenProcess(PROCESS_VM_OPERATION 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.InteropServices.Marshal.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*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
        '                             MEM_COMMIT, PAGE_READWRITE);
        '_item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
        '                            PAGE_READWRITE);
        '_subitem=(char*)VirtualAllocEx(process, 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


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



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.  

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(hndList, pid)

        process = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_QUERY_INFORMATION, False, pid)

        Dim lvi_length As Integer = System.Runtime.InteropServices.Marshal.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)

' ******* 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, _lvi, lvi, lvi_length, 0&)
            SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)

            lvi.iSubItem = 1
            lvi.pszText = _subitem

            WriteProcessMemory(process, _lvi, lvi, lvi_length, 0&)

            SendMessage(hndList, LVM_GETITEMTEXT, i, _lvi)

            ReadProcessMemory(process, _item, item, 512, 0&)
            ReadProcessMemory(process, _subitem, subitem, 512, 0&)

        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.InteropServices.Marshal.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
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...
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."
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.

ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Oh, nice... how did I miss that in my searches?  Checking that out...
You rock, thanks!