Retrieving item text from Win32 ListView using JNA

512 views
Skip to first unread message

Enrique Zudaire

unread,
Apr 18, 2014, 12:42:13 AM4/18/14
to jna-...@googlegroups.com

Hi All, 

 

I’m trying to retrieve item information (text would be enough) from a Win32 ListView control (SysListView32) in an external application. I’m using JNA’s sendMessage() to send a LVM_GETITEM message. SendMessage () takes a pointer to a LVITEM structure (http://msdn.microsoft.com/en-us/library/windows/desktop/bb774760(v=vs.85).aspx). Here is my Java implementation of the LVITEM structure.

 

public static class LVITEM extends Structure {

 

        public static class ByReference extends LVITEM implements Structure.ByReference {}

 

        public int mask;

        public int iItem; 

        public int iSubItem; 

        public int state; 

        public int stateMask; 

        public Pointer pszText;

        public int cchTextMax; 

        public int iImage; 

        public int lParam; 

        public int iIndent; 

 

        @Override

        protected List getFieldOrder() {

            return Arrays.asList(new String[] { 

                  "mask",  "iItem",  "iSubItem",  "state", "stateMask", "pszText", "cchTextMax",  "iImage", "lParam",  "iIndent" });

        }

    }

 

The size of the Java structure is 40 bytes which should match the size of the native structure.

 

After reading a bunch my understanding is that I need to allocate memory space in the process of the external application. I have got some ideas in other posts. Here is my code (stripped of non relevant lines).

 

This is my Kernel32:

 

static class Kernel32 {

       

        static { Native.register("kernel32"); }

 

        public static int PROCESS_QUERY_INFORMATION = 0x0400;

        public static int PROCESS_VM_OPERATION = 0x0008;

        public static int PROCESS_VM_WRITE = 0x0020;

        public static int PROCESS_VM_READ = 0x0010;

        public static int PAGE_READWRITE = 0x04;

        public static int MEM_RESERVE = 0x2000;

        public static int MEM_COMMIT = 0x1000;

       

       

        public static native int GetLastError();       

        public static native int OpenProcess(int dwDesiredAccess, boolean bInheritHandle, Pointer pointer);       

        public static native Pointer VirtualAllocEx(int ProcessToAllocateRamIn, int AddresToStartAt, int DesiredSizeToAllocate, int AllocationType, int  ProtectType);

        public static native int WriteProcessMemory(int hProcess, Pointer AdressToChange, Pointer ValuesToWrite,int nSize, IntByReference irgendwas);

       public static native int ReadProcessMemory (int hProcess, Pointer lpBaseAddress, Pointer lpBuffer, int nSize, IntByReference lpNumberOfBytesWritten);

 

}

 

My User32 looks like:

 

public interface User32 extends StdCallLibrary {

            User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);   

           

            int LVM_FIRST                      = 0x1000;           // ListView Messages

            int LVM_GETITEM                    = LVM_FIRST + 5;

          

            int LVIF_TEXT = 0x0001;           // ListView Item Features

          

            int MEM_COMMIT = 0x00001000;

            int PAGE_READWRITE = 0x04;           

                               

            int SendMessage (WinDef.HWND hWnd, int msg, int wparam, Pointer lvItem);

            int GetWindowThreadProcessId(HWND hWnd, PointerByReference pref);

}

 

 

And here is the function that aims to retrieve the info for an item in the ListView:

 

public static String GetListViewItem (HWND hWnd, int itemIdx){

 

        PointerByReference lngProcID; int lngProcHandle;

        LVITEM lvi; String strLvItem;

        Pointer lngVarPtr1; Pointer lngVarPtr2;

        Pointer lngMemVar1; Pointer lngMemVar2;

        int lngMemLen1; int lngMemLen2;

       

//Get the process Id

        lngProcID = new PointerByReference();

        user32.GetWindowThreadProcessId(hWnd, lngProcID);

                

//Open process and get handle

        lngProcHandle = Kernel32.OpenProcess(Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ, false, lngProcID.getValue());

      

 

        lvi = new LVITEM();

        StringBuilder sb = new StringBuilder();

        sb.setLength(255);

        strLvItem = sb.toString(); 

     

        lngVarPtr1 = new NativeString(strLvItem,true).getPointer();

        lngVarPtr2 = lvi.getPointer();

        lngMemLen1 = strLvItem.length();

        lngMemLen2 = lvi.size();

       

              

        //Reserve or commit a region of memory within the virtual address space of the external process.

        lngMemVar1 = Kernel32.VirtualAllocEx(lngProcHandle, 0, lngMemLen1, Kernel32.MEM_RESERVE|Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE);

        lngMemVar2 = Kernel32.VirtualAllocEx(lngProcHandle, 0, lngMemLen2, Kernel32.MEM_RESERVE|Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE);

       

        IntByReference byteswritten1 = new IntByReference();

        IntByReference byteswritten2 = new IntByReference();

       

//Initialize structure’s elements

        lvi.cchTextMax = 255;

        lvi.iItem = itemIdx;

        lvi.iSubItem = 0;

        lvi.mask = 1;

        lvi.pszText = lngMemVar1;

       

//Write data to the external process memory       

        int a = Kernel32.WriteProcessMemory(lngProcHandle, lngMemVar1, lngVarPtr1, lngMemLen1, byteswritten1);

        int b = Kernel32.WriteProcessMemory(lngProcHandle, lngMemVar2, lngVarPtr2, lngMemLen2, byteswritten2);

       

//Send LVM_GETITEM message

        int r = user32.SendMessage (hWnd, User32.LVM_GETITEM, 0, lngMemVar2);

 

//Read external process memory

        IntByReference bytesread = new IntByReference();

        b = Kernel32.ReadProcessMemory(lngProcHandle, lngMemVar1, lngVarPtr1, lngMemLen1, bytesread);

      

 

        return strLvItem;

    }

 

In summary, everything seems to run smoothly: the process Id is confirmed by spy++, I get a process handle and GetLastError() is 0 for VirtualAllocEx(),WriteProcessMemory() and ReadProcessMemory(). In WriteProcessMemory() byteswritten1= 255 and byteswritten2 = 40 as expected. In ReadProcessMemory (), bytesread= 255 as expected.

 

However, at the end, strLvItem contains 255 null characters. I don’t seem to be able to recover the name of the items in the ListView. I don’t understand why everything runs without errors but I get nothing in my strLvItem String.

 

I have been struggling with this for a couple of days and tried many things but nothing seems to work. I believe the problem is in passing the LVITEM structure to SendMessage() (although it runs without error).

 

Any thoughts would be much appreciated.

 

Thanks,

 

EZ

Daniel Doubrovkine

unread,
Apr 18, 2014, 8:48:32 AM4/18/14
to jna-...@googlegroups.com
I think the signature of ReadProcessMemory is incorrect. I could be wrong, but giving it a pointer doesn't actually copy the memory back, it probably needs something  ByReference?

Would appreciate if you contributed these signatures to JNA proper, with tests.

Thx
dB.


--
You received this message because you are subscribed to the Google Groups "Java Native Access" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

dB. | Moscow - Geneva - Seattle - New York
code.dblock.org - @dblockdotorg - artsy.net - github/dblock

Enrique Zudaire

unread,
Apr 18, 2014, 12:49:04 PM4/18/14
to jna-...@googlegroups.com
Hi dB, 

Thanks for your comment.
 
However, following your suggestion I have  tested a couple of different signatures. Consider this:

static native int ReadProcessMemory (int hProcess, Pointer lpBaseAddress, PointerByReference lpBuffer, int nSize, Pointer lpNumberOfBytesWritten);

and 

PointerByReference ptrbyreference = new PointerByReference(lngVarPtr1); (this would be void**)
IntByReference bytesread = new IntByReference();
        Pointer bytesreadPtr = bytesread.getPointer();


Before ReadProcessMemory, 

ptrbyreference.getValue()  = native@0x31795a8
bytesreadPtr.getInt(0) = 0

After b = Kernel32.ReadProcessMemory(lngProcHandle, lngMemVar1, ptrbyreference, lngMemLen1, bytesreadPtr);

ptrbyreference.getValue()  = null
bytesreadPtr.getInt(0) = 255

So ReadProcessMemory() copied the number of bytes read into p (a Pointer), but copied a "null" into the ptrbyreference (PointerByReference)

I also tried:

static native int ReadProcessMemory (int hProcess, Pointer lpBaseAddress, StringByReference lpBuffer, int nSize, Pointer lpNumberOfBytesWritten);

StringByReference strbyreference = new StringByReference(strLvItem);
(there is no StringByReference in JNA. got it from http://jnaexamples.blogspot.com/) 


b = Kernel32.ReadProcessMemory(lngProcHandle, lngMemVar1, strbyreference, lngMemLen1, bytesread2);

Before ReadProcessMemory()
strbyreference.getValue() =  
strbyreference.getValue().length() = 0 
strbyreference.getPointer() = allocated@0x31799d8 (256 bytes)
bytesreadPtr.getInt(0) = 0; 

b = Kernel32.ReadProcessMemory(lngProcHandle, lngMemVar1, strbyreference, lngMemLen1, bytesread2)
strbyreference.getValue() =  
strbyreference.getValue().length() = 0 
strbyreference.getPointer() = allocated@0x31799d8 (256 bytes)
bytesreadPtr.getInt(0) = 255; 

Any thoughts?

Also, I'm not sure I understand your request ("...contributed these signatures to JNA proper, with tests."). Can you explain?

Thank you
EZ


On Friday, April 18, 2014 8:48:32 AM UTC-4, Daniel Doubrovkine wrote:
I think the signature of ReadProcessMemory is incorrect. I could be wrong, but giving it a pointer doesn't actually copy the memory back, it probably needs something  ByReference?

Would appreciate if you contributed these signatures to JNA proper, with tests.

Thx
dB.

Daniel Doubrovkine

unread,
Apr 20, 2014, 8:51:28 AM4/20/14
to jna-...@googlegroups.com
Lets backup a bit. 

Do you have a working C++ sample where ReadProcessMemory works?

By contributing I mean https://github.com/twall/jna/blob/master/www/Contributing.md. I think you should start by contributing the implementation of ReadProcessMemory to JNA proper, write tests, make sure that that works, and then figure out what the deal with reading list items is. I have a suspicion after spending a bit of time googling that this may not be a problem in your code.

Enrique Zudaire

unread,
Apr 20, 2014, 9:34:00 PM4/20/14
to jna-...@googlegroups.com
Hi dB, 

I don't have a working C++ sample, but I have a working VB sample:

Function ListViewGetText(ByVal hwnd As Long, ByVal iSubItem As Integer, ByVal iItem As Integer) As String
    Dim lngProcID As Long, lngProcHandle As Long
    Dim typLvItem As LVITEM, strLvItem As String
    Dim lngVarPtr1 As Long, lngVarPtr2 As Long
    Dim lngMemVar1 As Long, lngMemVar2 As Long
    Dim lngMemLen1 As Long, lngMemLen2 As Long
    
    Call GetWindowThreadProcessId(hwnd, lngProcID)
    
    If lngProcID <> 0 Then
        lngProcHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, lngProcID)

        If lngProcHandle <> 0 Then
            strLvItem = String(255, vbNullChar)

            lngVarPtr1 = StrPtr(strLvItem)
            lngVarPtr2 = VarPtr(typLvItem)
            lngMemLen1 = LenB(strLvItem)
            lngMemLen2 = LenB(typLvItem)

            lngMemVar1 = VirtualAllocEx(lngProcHandle, 0, lngMemLen1, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
            lngMemVar2 = VirtualAllocEx(lngProcHandle, 0, lngMemLen2, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
            
            With typLvItem
                .cchTextMax = 255
                .iItem = iItem
                .iSubItem = iSubItem
                .mask = LVIF_TEXT
                .pszText = lngMemVar1
            End With
            
            Dim l1, l2 As Long
            l1 = WriteProcessMemory(lngProcHandle, ByVal lngMemVar1, ByVal lngVarPtr1, lngMemLen1, 0)
            l2 = WriteProcessMemory(lngProcHandle, ByVal lngMemVar2, ByVal lngVarPtr2, lngMemLen2, 0)
            
            Call SendMessage(hwnd, LVM_GETITEM, ByVal 0, ByVal lngMemVar2)
            
            Call ReadProcessMemory(lngProcHandle, ByVal lngMemVar1, ByVal lngVarPtr1, lngMemLen1, 0)
            
            strLvItem = StrConv(strLvItem, vbUnicode)
            strLvItem = Left(strLvItem, InStr(1, strLvItem, vbNullChar) - 1)
            Debug.Print "strLvItem= " & strLvItem
            ListViewGetText = strLvItem
            Call VirtualFreeEx(lngProcHandle, ByVal lngMemVar1, lngMemLen1, MEM_RELEASE)
            Call VirtualFreeEx(lngProcHandle, ByVal lngMemVar2, lngMemLen2, MEM_RELEASE)
            Call CloseHandle(lngProcHandle)
        End If
    End If
End Function

EZ

Any thoughts would be much appreciated. <u

...

Enrique Zudaire

unread,
Apr 23, 2014, 9:34:49 AM4/23/14
to jna-...@googlegroups.com
Hi, 

I got this to work in case anyone is interested: 

        PointerByReference lngProcID;

        int lngProcHandle;

        LVITEM lvi;

        int strSize = 255;

        int result = 0;

        IntByReference byteIO = new IntByReference();

        Pointer lngVarPtr1 = null;Pointer lngMemVar1 = null;

        Pointer lngVarPtr2 = null;Pointer lngMemVar2 = null;

        Pointer lviVarPtr = null;Pointer lviVar = null;

        int lngMemLen1; int lngMemLen2;

       

        lngProcID = new PointerByReference();

        int ThreadId = user32.GetWindowThreadProcessId(hWnd, lngProcID);

                

        lngProcHandle = Kernel32.OpenProcess(Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ, false, lngProcID.getValue());

          

        lvi = new LVITEM();

        lngMemLen1 = strSize;

        lngMemLen2 = lvi.size(); 

      

        lngMemVar2 = Kernel32.VirtualAllocEx(lngProcHandle, 0, lngMemLen2, Kernel32.MEM_RESERVE|Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE);        

       

        lvi.cchTextMax = strSize;

        lvi.iItem = itemIdx;

        lvi.iSubItem = 0;

        lvi.mask = User32.LVIF_TEXT;

        lvi.pszText = lngMemVar1;       

       

//      result  = Kernel32.WriteProcessMemory(lngProcHandle, lngMemVar1, lngVarPtr1, lngMemLen1, byteswritten1);

        result = Kernel32.WriteProcessMemory(lngProcHandle, lngMemVar2, lvi, lngMemLen2, byteIO);

       

        result = user32.SendMessage (hWnd, User32.LVM_GETITEM, 0, lngMemVar2);

       

       lngVarPtr1 = new Memory(strSize + 1);

       result = Kernel32.ReadProcessMemory(lngProcHandle, lngMemVar1, lngVarPtr1, lngMemLen1, byteIO);

            

        result = Kernel32.VirtualFreeEx (lngProcHandle, lngMemVar1, 0, Kernel32.MEM_RELEASE);

        result = Kernel32.VirtualFreeEx (lngProcHandle, lngMemVar2, 0, Kernel32.MEM_RELEASE);        

        result = Kernel32.CloseHandle(lngProcHandle);

               

        return lngVarPtr1.getWideString(0);

EZ

<p style="margin-botto
...

Daniel Doubrovkine

unread,
Apr 23, 2014, 10:07:22 AM4/23/14
to jna-...@googlegroups.com
Great. Can you please contribute Kernel32 functions to JNA?


(you'll have to write tests :)


--
You received this message because you are subscribed to the Google Groups "Java Native Access" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ricardo Malikoski

unread,
Sep 30, 2014, 4:16:38 PM9/30/14
to jna-...@googlegroups.com
Does anyone have a full example? I can't get text from ListViews ( SysListView32, TListView, etc).

Thanks.
Reply all
Reply to author
Forward
0 new messages