winapi: reading listview contents

Just starting out? Need help? Post your questions and find answers here.
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

winapi: reading listview contents

Post by thefool »

Hey!

I'm having a little trouble at work with a program that has no interface (of course it has a GUI, but you know what i mean). It stores some information in what looks like a standard listview (with 3 columns) and i need to get that information out for processing. I'll be spending more time on this myself of course, but i'm wondering if anyone here has done something like this before with success and can give me a hint?

Best regards :)
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: winapi: reading listview contents

Post by IdeasVacuum »

well, it displays the info in a list view, but maybe it is reading from a database? If so, then there is another maybe - collect the info from the db.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: winapi: reading listview contents

Post by netmaestro »

Of course it depends upon whether it's actually a SysListview32 (similar to PB ListIconGadget) or a ListBox (similar to PB ListviewGadget). Those are the classnames to look for. If it's a SysListview32, get its hwnd and send it the LVM_GETITEMCOUNT followed by a loop with LVM_GETITEMTEXT for each item. For a ListBox, send LB_GETCOUNT and LB_GETTEXT. Good hunting!
BERESHEIT
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Re: winapi: reading listview contents

Post by thefool »

IdeasVacuum wrote:well, it displays the info in a list view, but maybe it is reading from a database? If so, then there is another maybe - collect the info from the db.
Sadly not, its being collected realtime and i need to read it (almost) realtime. I could ask it to write to the disk very often (eventually a virtual ram disk or flash drive etc) but i think the other solution is better in this case.
netmaestro wrote:Of course it depends upon whether it's actually a SysListview32 (similar to PB ListIconGadget) or a ListBox (similar to PB ListviewGadget). Those are the classnames to look for. If it's a SysListview32, get its hwnd and send it the LVM_GETITEMCOUNT followed by a loop with LVM_GETITEMTEXT for each item. For a ListBox, send LB_GETCOUNT and LB_GETTEXT. Good hunting!
Great! Just what i need. Thanks :)
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

so how to select some item in SysListview32?
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

i made it! finaly! at last! i found how to right read items, and how to know what item is selected and select and focus another item :)

Code: Select all

Procedure GetItem(ID) 

Define LVItem.LVITEM
LVItem\StateMask = #LVIS_DROPHILITED
LVItem\State     = #LVIS_DROPHILITED

lvi.LVITEM 
_lvi.LVITEM 
temp.s 
Item.l 
Count=SendMessage_(ID,#LVM_GETITEMCOUNT,0,0)
GetWindowThreadProcessId_(ID, @pid)  
If pid 
 proc = OpenProcess_(#PROCESS_VM_OPERATION|#PROCESS_VM_READ|#PROCESS_VM_WRITE|#PROCESS_QUERY_INFORMATION, #False, pid) 
 If proc 
   *_lvi.LVITEM=VirtualAllocEx_(proc, 0, SizeOf(LVITEM), #MEM_COMMIT, #PAGE_READWRITE) 
   Item=VirtualAllocEx_(proc, 0, 255, #MEM_COMMIT, #PAGE_READWRITE) 
   temp=Space(255) 
   lvi\cchTextMax=255 
   null = 0 
         For i=0 To Count-1 
           *Buffer1=AllocateMemory(512) 
           If *Buffer1 
             lvi\iSubItem = 0 
             lvi\pszText = Item 
             lvi\cchTextMax = 255 
             WriteProcessMemory_(proc, *_lvi, @lvi, SizeOf(LVITEM), null) 
             LenString=SendMessage_(ID, #LVM_GETITEMTEXT, i, *_lvi)
             ReadProcessMemory_(proc, item, @temp, 255, null) 
             Debug temp
             If SendMessage_(ID,#LVM_GETITEMSTATE, i, #LVIS_SELECTED)
                Debug "selected was " + temp  
             EndIf
           EndIf 
         Next i
         
      VirtualFreeEx_(proc, *_lvi, 0, #MEM_RELEASE); 
      VirtualFreeEx_(proc, item, 0, #MEM_RELEASE) 
      CloseHandle_(proc)
  EndIf 
EndIf 
EndProcedure 



If OpenWindow(0, 10, 10, 200, 100, "for findwindow", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
   ListIconGadget(0, 10, 10, 180, 80, "for findwindow ex", 170, #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
   
   AddGadgetItem(0, -1, "first select")
   AddGadgetItem(0, -1, "second select")
   AddGadgetItem(0, -1, "third select")

   SetGadgetState(0, 0)
   
   SetActiveGadget(0)
   
   Repeat
      mainwind = FindWindow_(0, "for findwindow")
      Delay(100)
   Until mainwind
   
   Debug mainwind
   
   Repeat
      exwind = FindWindowEx_(mainwind, 0, "SysListView32", 0)
      Delay(100)
   Until exwind
   
   GetItem(exwind) 

;{ select and focus third item
   State.LVITEM 
   State\mask = #LVIF_STATE
   State\state = #LVIS_SELECTED | #LVIS_FOCUSED
   State\stateMask = #LVIS_SELECTED | #LVIS_FOCUSED
   SendMessage_(exwind, #LVM_SETITEMSTATE, 2, @State)
  ;exwind handle of SysListView32 class window.
  ;2 it is number of item in list. numbers begin from 0. 0 = 1 item, 1 = 2 item, 2 = 3 item, and etc.
;}

   Repeat
       Event = WaitWindowEvent()
   Until Event = #PB_Event_CloseWindow 
EndIf
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

why this code work so unstable? i try to apply this code to read some iexplore window with select certificate, but or didnt work, or double select:
Image

how to fix it? where is thin spot in this code? what the secret?
(XP, explorer 6 or 7 or 8, threadsafe and unicode checks in compiler)

i think problem is in delay. and some time code start to work fine with debug. if remove debug command - the same code - not working.

and after select need to press enter:

Code: Select all

PostMessage_(exwind,#WM_KEYDOWN,13,0)
PostMessage_(exwind,#WM_KEYUP,13,0)
but it is too fast - window no time to select enter aready pressed.
if i put delay betweeen select and enter - programm create double select. the same with sleep_(). how to make some kind of pause between select and pressing enter?
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

i see SendMessage_(exwind, #LVM_SETITEMSTATE, 2, @State) not enough. it need to open memory iexplore and many operation. who can convert to PureBasic this code?

Code: Select all

int SelectList(HWND hList, int iIndex)

{

    /* get process Id, that has ListView */

    GetWindowThreadProcessId(hList, pid);

 

    if(!pid){

 

        /* open process with rights */

        HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION, TRUE, pid);

 

        if(!hProcess){



            /* allocate memory in target process */

            LPVOID lpMem = VirtualAllocEx(hProcess, NULL, sizeof(LV_ITEM), MEM_COMMIT, PAGE_READWRITE);

 

            if(!lpMem){

 

                /* fill structure  */

                LV_ITEM LV;

                LV.state = LVIS_SELECTED;

                LV.mask = LVIS_SELECTED;

 

                SIZE_T cdWrite;

 

                /* copy LV to target process */

                if (WriteProcessMemory(hProcess, lpMem, &LV, sizeof(LV_ITEM), &cdWrite))

                {

                    /* send message to select */

                    SendMessage(hList, LVM_SETITEMSTATE, (WPARAM)iIndex, (LPARAM)lpMem);

                }

 

                /* free allocate memory */

                VirtualFreeEx(hProcess, lpMem, 0, MEM_RELEASE);

            }

            /* close handle*/

            CloseHandle(hProcess);

        }

    }

}
i try to do this, but nothing... didnt work.

Code: Select all

Procedure SelectList(hList.i, iIndex.i)
  
  ;/* get process Id, that has ListView */
  GetWindowThreadProcessId_(hList, @pid)
  
  If pid
    
    ;/* open process With rights */  
    hProcess = OpenProcess_(#PROCESS_VM_WRITE|#PROCESS_VM_OPERATION, #True, pid);
    If hProcess
    
      ;/* allocate memory in target process */
      lpMem = VirtualAllocEx_(hProcess, #Null, SizeOf(LVITEM), #MEM_COMMIT, #PAGE_READWRITE)
      If lpMem
        
        ;/* fill Structure  */
        LV.LVITEM
        LV\state = #LVIS_SELECTED;
        LV\mask = #LVIS_SELECTED;
        
        ;/* copy LV To target process */
        If WriteProcessMemory_(hProcess, lpMem, @LV, SizeOf(LVITEM), @cdWrite)

          ;/* send message To Select */
          SendMessage_(hList, #LVM_SETITEMSTATE, iIndex, @lpMem);
          
        EndIf
        
        ;/* free allocate memory */
        VirtualFreeEx_(hProcess, lpMem, 0, #MEM_RELEASE);
        
      EndIf
      
      ;/* close handle*/
      CloseHandle_(hProcess);
      
    EndIf 
    
  EndIf
   
EndProcedure


SelectList(8192804, 3)
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: winapi: reading listview contents

Post by netmaestro »

Give this one a try:

Code: Select all

Procedure SelectList(hList, iIndex)
  
  ;/* get process Id, that has ListView */
  GetWindowThreadProcessId_(hList, pid)
  
  If pid
    
    ;/* open process With rights */
    hProcess = OpenProcess_(#PROCESS_VM_WRITE|#PROCESS_VM_OPERATION, #True, pid)
    
    If hProcess
      
      ;/* allocate memory in target process */
      lpMem = VirtualAllocEx_(hProcess, #Null, SizeOf(LV_ITEM), #MEM_COMMIT, #PAGE_READWRITE)
       
      If lpMem
         
        ;/* fill Structure  */
        With LV.LV_ITEM;
          \state = #LVIS_SELECTED
          \mask = #LVIS_SELECTED
        EndWith
        
        ;/* copy LV To target process */
        If (WriteProcessMemory_(hProcess, lpMem, @LV, SizeOf(LV_ITEM), @cdWrite.l))
          
          ;/* send message To Select */
          SendMessage_(hList, #LVM_SETITEMSTATE, iIndex, lpMem)
          
        EndIf

        ;/* free allocate memory */
        VirtualFreeEx_(hProcess, lpMem, 0, #MEM_RELEASE)
        
      EndIf
      
      ;/* close handle*/
      CloseHandle_(hProcess)
      
    EndIf
    
  EndIf
  
EndProcedure
BERESHEIT
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

no, didnt work.

try to change GetWindowThreadProcessId_(hList, pid) to a GetWindowThreadProcessId_(hList, @pid) :)

and

Code: Select all

        With LV.LV_ITEM;
          \state = #LVIS_SELECTED
          \mask = #LVIS_SELECTED
        EndWith
to

Code: Select all

        With LV.LV_ITEM;
          \state = #LVIS_SELECTED|#LVIS_FOCUSED
          \mask = #LVIF_STATE
          \stateMask = #LVIS_SELECTED|#LVIS_FOCUSED
        EndWith
now looks goood :)
SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

for example another, not my programm, have some kind of this:

Code: Select all

Enumeration
  #Window
  
  #Container
  #ListIcon
EndEnumeration

If OpenWindow(#Window, 100, 100, 300, 105, "Window for cathc", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
   ContainerGadget(#Container, 5, 5, 290, 95, #PB_Container_Raised)

   ListIconGadget(#ListIcon, 5, 5, 270, 75, "Name", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #LVS_NOCOLUMNHEADER | #PB_ListIcon_CheckBoxes)
   AddGadgetItem(#ListIcon, -1, "Harry Rannit")
   AddGadgetItem(#ListIcon, -1, "Ginger Brokeit")
   AddGadgetItem(#ListIcon, -1, "3 par")
   AddGadgetItem(#ListIcon, -1, "num four")
   
   CloseGadgetList()
   Repeat
     Event = WaitWindowEvent()
   Until Event = #PB_Event_CloseWindow
 EndIf
save it with some "testwindow.exe" file, launch. then start code below.

i know how to read it. and maybe select one - by press "space" after select. i mean checkbox. but how to return checkbox is check or not check?

Code: Select all

Procedure.i SelectItem(pid.i, hwnd.i, toselectitem.i)
  ;/* get process Id, that has ListView */
  ;GetWindowThreadProcessId_(hList, @pid)
  If pid
    ;/* open process With rights */
    hProcess = OpenProcess_(#PROCESS_VM_WRITE|#PROCESS_VM_OPERATION, #True, pid)
    If hProcess
      ;/* allocate memory in target process */
      lpMem = VirtualAllocEx_(hProcess, #Null, SizeOf(LV_ITEM), #MEM_COMMIT, #PAGE_READWRITE)
      If lpMem
        ;/* fill Structure  */
        With LV.LV_ITEM;
          \state = #LVIS_SELECTED|#LVIS_FOCUSED
          \mask = #LVIF_STATE
          \stateMask = #LVIS_SELECTED|#LVIS_FOCUSED
        EndWith
        ;/* copy LV To target process */
        If (WriteProcessMemory_(hProcess, lpMem, @LV, SizeOf(LV_ITEM), @cdWrite.l))
          ;/* send message To Select */
          ret = SendMessage_(hwnd, #LVM_SETITEMSTATE, toselectitem, lpMem)          
        EndIf
        ;/* free allocate memory */
        VirtualFreeEx_(hProcess, lpMem, 0, #MEM_RELEASE)        
      EndIf      
      ;/* close handle*/
      CloseHandle_(hProcess)      
    EndIf    
  EndIf
  
  ProcedureReturn ret
  
EndProcedure

Procedure.i NumberItem(pid.i, hwnd.i, selectitem$)

  toselectitem = -2
  
  lvi.LVITEM 
  _lvi.LVITEM 
  
  temp.s 
  Item.l 
  
  Count = SendMessage_(hwnd,#LVM_GETITEMCOUNT,0,0)
  
  proc = OpenProcess_(#PROCESS_ALL_ACCESS, #False, pid)

  If proc

    *_lvi.LVITEM = VirtualAllocEx_(proc, 0, SizeOf(LVITEM), #MEM_COMMIT, #PAGE_READWRITE) 
    Item         = VirtualAllocEx_(proc, 0,            255, #MEM_COMMIT, #PAGE_READWRITE)

    temp = Space(255) 

    lvi\cchTextMax=255 

    For i=0 To Count-1 
      
      lvi\iSubItem = 0 
      lvi\pszText = Item 
      lvi\cchTextMax = 255 
      WriteProcessMemory_(proc, *_lvi, @lvi, SizeOf(LVITEM), 0) 
      
      ; i need same as #LVM_GETITEMTEXT, but for checkbox?
      SendMessage_(hwnd, #LVM_GETITEMTEXT, i, *_lvi) 
      ReadProcessMemory_(proc, item, @temp, 255, 0)
      Debug temp
        
      If temp = selectitem$
        toselectitem = i
        Break               
      EndIf
      
    Next i
    VirtualFreeEx_(proc, *_lvi, 0, #MEM_RELEASE); 
    VirtualFreeEx_(proc, item, 0, #MEM_RELEASE)
    CloseHandle_(proc)
  EndIf

  ProcedureReturn toselectitem
    
EndProcedure

Prototype ProcessFirst(Snapshot, Process)
Prototype ProcessNext(Snapshot, Process)
Global ProcessFirst.ProcessFirst
Global ProcessNext.ProcessNext

Procedure GetPidProcess2(Name.s) ; Returns 'pid' of the first Process found if it exists / 0 if it doesn't exist
  
  
  Protected ProcLib
  Protected ProcName.s
  Protected Process.PROCESSENTRY32
  Protected x
  Protected retval=#False
  Name=UCase(Name.s)
  ProcLib= OpenLibrary(#PB_Any, "Kernel32.dll") 
  If ProcLib
    CompilerIf #PB_Compiler_Unicode
      ProcessFirst           = GetFunction(ProcLib, "Process32FirstW") 
      ProcessNext            = GetFunction(ProcLib, "Process32NextW") 
    CompilerElse
      ProcessFirst           = GetFunction(ProcLib, "Process32First") 
      ProcessNext            = GetFunction(ProcLib, "Process32Next") 
    CompilerEndIf
    If  ProcessFirst And ProcessNext 
      Process\dwSize = SizeOf(PROCESSENTRY32) 
      Snapshot =CreateToolhelp32Snapshot_(#TH32CS_SNAPALL,0)
      If Snapshot 
        ProcessFound = ProcessFirst(Snapshot, Process) 
        x=1
        While ProcessFound 
          ProcName=PeekS(@Process\szExeFile)
          ProcName=GetFilePart(ProcName)
          If UCase(ProcName)=UCase(Name) 
            ret=Process\th32ProcessID
            Break
          EndIf
          
          ProcessFound = ProcessNext(Snapshot, Process) 
          x=x+1  
        Wend 
      EndIf 
      CloseHandle_(Snapshot) 
    EndIf 
    CloseLibrary(ProcLib) 
  EndIf 
  ProcedureReturn ret
  
EndProcedure

 


pid = GetPidProcess2("testwindow.exe") ; from droopy

hwndmain = FindWindow_(0, "Window for cathc")
If hwndmain

  SetForegroundWindow_(hwndmain)
  
  hwndpanel = FindWindowEx_(hwndmain, 0, "PureContainer", 0)

  If hwndpanel

    hwnd = FindWindowEx_(hwndpanel, 0, "SysListView32", 0)
    
    selectnum = NumberItem(pid, hwnd, "Ginger Brokeit")

    If selectnum > -2
      If SelectItem(pid, hwnd, selectnum)
        Debug "select done"
        
        ; space button       
        Delay(100)
        PostMessage_(hwnd, #WM_KEYDOWN, 32, 0) 
        Delay(100)
        PostMessage_(hwnd, #WM_KEYUP, 32, 0)
        
      EndIf
    EndIf
    
  EndIf
EndIf

SeregaZ
Enthusiast
Enthusiast
Posts: 628
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: winapi: reading listview contents

Post by SeregaZ »

i found some ListView_GetCheckState macro https://msdn.microsoft.com/en-us/librar ... s.85).aspx
but probably it have no windows XP support.
Minimum supported client Windows Vista [desktop apps only]

how i can get state check or not check of this checkboxes of another application?
Post Reply