Listview of another Process

delphi package - easy access to kernel objects etc.
Post Reply
neji
Posts: 155
Joined: Wed Mar 09, 2005 11:39 am
Contact:

Listview of another Process

Post by neji »

Is there a way with madCollection, to get the Text and the index of each Item of a Listview in another process?
shellBullet
Posts: 18
Joined: Thu Apr 28, 2005 12:57 am

Post by shellBullet »

create a inject dll for the target process, in the dll find the target window that contains the listview:

Code: Select all

handleToTargetWnd:=FindWindowEx(handleToParentWnd, 0, 'SysListView32',nil)
from there, use ListView_GetItemCount (number of rows) and ListView_GetItemText to find the desired text...
neji
Posts: 155
Joined: Wed Mar 09, 2005 11:39 am
Contact:

Post by neji »

thanks , and when I found the Item that I have searched for, how can I then select this item?
shellBullet
Posts: 18
Joined: Thu Apr 28, 2005 12:57 am

Post by shellBullet »

neji,im not quite sure what u mean.. do u want to get a TListItem at every row... or just the text of every row... anyway.. try this...

Code: Select all

var    
    text        : array[1..100] of AnsiChar;
    aFile  : TextFile;
    i           : Integer;
    j           : Integer;
    rowCount    : Integer;
   begin
   // assingn a file path to the text file then find your parent window and 
  // do the following
         hTargetWnd:=FindWindowEx(hParentWnd, 0, 'SysListView32',nil);
         if hTargetWnd<> 0 then
         begin
             rowCount := ListView_GetItemCount(hTargetWnd);

             for i:=0 to rowCount-1 do
             begin

                  for j:=0 to  rowCount-1 do
                  begin
                       ListView_GetItemText(hTargetWnd,i,j,@text,100);
                               
                      Write(aFile,Trim(String(text)));
                      
                  end;
                  
             end;
   end.
 
u can manipulate i and j for formatting in the file .. or use ipc to send the text
directly to your main app
shellBullet
Posts: 18
Joined: Thu Apr 28, 2005 12:57 am

Post by shellBullet »

ps: neji, in last post i'm assumin number of row = number of column if they are different just put the disired value in for loop for j if u know how many column your foregein listview has...
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

shellBullet is right. The easiest way to solve the problem is to inject a dll into the process which owns the target list view and then use the list view messages / APIs from inside the injected dll.

There's another possibility, though:

The only reason why some of the list view messages / APIs don't work for list views of other processes is that those non working list view messages work with pointers. And as you know, pointers are valid in your own process, only. You can work around that, though, by allocating memory in the target process and send a pointer to that buffer to the target list view. You can use AllocMemEx and FreeMemEx for this purpose. You'll have to use WriteProcessMemory and ReadProcessMemory to access this buffer. madShell is using this method internally to get access to the desktop items.
neji
Posts: 155
Joined: Wed Mar 09, 2005 11:39 am
Contact:

Post by neji »

thanks for the reply. I tried to do that by allocating memory and using Read/Writeprocessmemory.

The Code, I have used to find the index of the item I looked for :

Code: Select all

function GetItemIndex(aText : String; const CaseSensitive : Boolean = FALSE) : Integer;
  function GetListView : Cardinal;
  begin
    result := FindWindow(nil,'Form1');
    result := FindWindowEx(result, 0, 'TListView', nil);
  end;
var
  ListView: HWND;
  ProcessId: DWORD;
  Process: THandle;
  Size: Cardinal;
  MemLocal: Pointer;
  MemRemote: Pointer;
  NumBytes: Cardinal;
  IconIndex: Integer;
  IconLabel: string;
begin
  result := 0;
  ProcessId := 0;
  ListView := GetListView;
  GetWindowThreadProcessId(ListView, @ProcessId);
  Process := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
    PROCESS_VM_WRITE, False, ProcessId);
  if (Process <> 0) then
  try
    Size := SizeOf(TLVItem) + SizeOf(Char) * MAX_PATH + 1;
    MemLocal := VirtualAlloc(nil, Size, MEM_RESERVE or MEM_COMMIT,
      PAGE_READWRITE);
    MemRemote := VirtualAllocEx(Process, nil, Size, MEM_RESERVE or MEM_COMMIT,
      PAGE_READWRITE);
    if Assigned(MemLocal) and Assigned(MemRemote) then
    try
      for IconIndex := 0 to SendMessage(ListView, LVM_GETITEMCOUNT, 0, 0) - 1 do
      begin
        ZeroMemory(MemLocal, SizeOf(TLVItem));
        with PLVItem(MemLocal)^ do
        begin
          mask := LVIF_TEXT;
          iItem := IconIndex;
          pszText := LPTSTR(Cardinal(MemRemote) + Cardinal(SizeOf(TLVItem)));
          cchTextMax := MAX_PATH;
        end;
        NumBytes := 0;
        if WriteProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes) and
          Boolean(SendMessage(ListView, LVM_GETITEM, 0, LPARAM(MemRemote))) and
          ReadProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes) then
        begin
          IconLabel := string(
            PChar(Cardinal(MemLocal) + Cardinal(SizeOf(TLVItem))));
          if CaseSensitive then
          begin
            IconLabel := LowerCase(IconLabel);
            aText     := LowerCase(aText);
          end;

          if IconLabel = aText then
          begin
            Result := IconIndex;
            exit;
          end;
        end;
      end;
    except
    end;
    if Assigned(MemRemote) then
      VirtualFreeEx(Process, MemRemote, 0, MEM_RELEASE);
    if Assigned(MemLocal) then
      VirtualFree(MemLocal, 0, MEM_RELEASE);
  finally
    CloseHandle(Process);
  end;
end;
It worked well, but I'm not done yet :(
I still need to know, how I can "select" this item (the selection that appears when clicking an item with the mouse) with code. Does Anybody know how that can be done?
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

There's surely a list view message for selecting items. Don't know it from the top of my had. But just look through the list view messages. Should be easy to find...
neji
Posts: 155
Joined: Wed Mar 09, 2005 11:39 am
Contact:

Post by neji »

I looked through the Listview messages and the only one I think it could be it is then LVM_SETITEMSTATE message, am I right?
The LVM_SETITEMSTATE message changes the state of an item in a list view control. You can explicitly send this message or by using the ListView_SetItemState macro.
Anyway, I tried it with

Code: Select all

ListView_SetItemState(mein_handle, itemIndex, LVIS_FOCUSED + LVIS_SELECTED,LVIS_FOCUSED + LVIS_SELECTED);
ListView_EnsureVisible(mein_handle,itemIndex,TRUE);
but it didn't work... if an Item is selected before sending the message, it get's unselected...but my wanted item isnt selected then
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

I recommend to use the messages instead of the APIs. When using the APIs you can't really see what's going on. The APIs internally use the messages. When using the ListView_SetItemState API it looks like no pointers would be involved. However, check out the message documentation and suddenly you'll see that setting item states does involve pointers. So you need to do the AllocMemEx etc trick here, too.
neji
Posts: 155
Joined: Wed Mar 09, 2005 11:39 am
Contact:

Post by neji »

The LVM_SETITEMSTATE message doesn't even work in my own projects with my own listviews.... :(
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Sorry, I can't help here. I'd have to experiment myself. I guess you didn't fill the structure correctly.
Post Reply