Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get ListView items from other windows

I'm doing some project on c#. I need to get i item from ListView window, handle of it I got by doing something like this

IntPtr par_hWnd = API.FindWindow(null, "Form1");
IntPtr child1 = API.FindWindowEx(par_hWnd, (IntPtr)0, null, null);

API is my static class with lots of dllimports from "user32.dll" I am able to get count of items in this ListView:

IntPtr count = API.SendMessage(child1, API.LVM_GETITEMCOUNT, 0, 0);

Now i need to get the text of item, but the result somehow must be placed in the LVITEM Structure, I don't know how to call SendMessage correctly, and how to implement LVITEM in c#. Can't find examples for c#. Any help?

like image 516
b0xer Avatar asked Feb 01 '11 00:02

b0xer


People also ask

What is ListViewItem?

The ListViewItem class defines the appearance, behavior, and data associated with an item that is displayed in the ListView control. ListViewItem objects can be displayed in the ListView control in one of four different views. Items can be displayed as large or small icons or as small icons in a vertical list.

How to get selected value in ListView in Vb net?

In ListBox we can get selected item by a simple line of code: listbox1. SelectedItem .

How to remove items from ListView?

Use the RemoveAt or Clear method of the Items property. The RemoveAt method removes a single item; the Clear method removes all items from the list.


1 Answers

To retrieve the content of a list view in a foreign process is a complicated thing. Because the list view is in another process, and the LVM_GETITEM message requires you to send a pointer of an LVITEM structure, which must be allocated in the remote process's memory heap.

Here is the code:

The Implementation

// firstly we have the handle to the list view:
var listViewPtr = this.GetListViewHandle();

// get the ID of the process who owns the list view
WinAPI.GetWindowThreadProcessId(listViewPtr, out var processId);

// open the process
var processHandle = WinAPI.OpenProcess(
    WinAPI.ProcessAccessFlags.VirtualMemoryOperation
    | WinAPI.ProcessAccessFlags.VirtualMemoryRead
    | WinAPI.ProcessAccessFlags.VirtualMemoryWrite,
    false,
    processId);

// allocate buffer for a string to store the text of the list view item we wanted
var textBufferPtr = WinAPI.VirtualAllocEx(
    processHandle,
    IntPtr.Zero,
    WinAPI.MAX_LVMSTRING,
    WinAPI.AllocationType.Commit,
    WinAPI.MemoryProtection.ReadWrite);

var itemId = 0; // the item (row) index
var subItemId = 1; // the subitem (column) index

// this is the LVITEM we need to inject
var lvItem = new WinAPI.LVITEM
{
    mask = (uint)WinAPI.ListViewItemFilters.LVIF_TEXT,
    cchTextMax = (int)WinAPI.MAX_LVMSTRING,
    pszText = textBufferPtr,
    iItem = itemId,
    iSubItem = subItemId
};

// allocate memory for the LVITEM structure in the remote process
var lvItemSize = Marshal.SizeOf(lvItem);
var lvItemBufferPtr = WinAPI.VirtualAllocEx(
    processHandle,
    IntPtr.Zero,
    (uint)lvItemSize,
    WinAPI.AllocationType.Commit,
    WinAPI.MemoryProtection.ReadWrite);

// to inject the LVITEM structure, we have to use the WriteProcessMemory API, which does a pointer-to-pointer copy. So we need to turn the managed LVITEM structure to an unmanaged LVITEM pointer
// first allocate a piece of unmanaged memory ...
var lvItemLocalPtr = Marshal.AllocHGlobal(lvItemSize);

// ... then copy the managed object into the unmanaged memory
Marshal.StructureToPtr(lvItem, lvItemLocalPtr, false);

// and write into remote process's memory
WinAPI.WriteProcessMemory(
    processHandle,
    lvItemBufferPtr,
    lvItemLocalPtr,
    (uint)lvItemSize,
    out var _);

// tell the list view to fill in the text we desired
WinAPI.SendMessage(listViewPtr, (int)WinAPI.ListViewMessages.LVM_GETITEMTEXT, itemId, lvItemBufferPtr);

// read the text. we allocate a managed byte array to store the retrieved text instead of AllocHGlobal-ing a piece of unmanaged memory, because CLR knows how to marshal between a pointer and a byte array
var localTextBuffer = new byte[WinAPI.MAX_LVMSTRING];
WinAPI.ReadProcessMemory(
    processHandle,
    textBufferPtr,
    localTextBuffer,
    (int)WinAPI.MAX_LVMSTRING,
    out var _);

// convert the byte array to a string. assume the remote process uses Unicode
var text = Encoding.Unicode.GetString(localTextBuffer);
// the trailing zeros are not cleared automatically
text = text.Substring(0, text.IndexOf('\0'));

// finally free all the memory we allocated, and close the process handle we opened
WinAPI.VirtualFreeEx(processHandle, textBufferPtr, 0, WinAPI.AllocationType.Release);
WinAPI.VirtualFreeEx(processHandle, lvItemBufferPtr, 0, WinAPI.AllocationType.Release);
Marshal.FreeHGlobal(lvItemLocalPtr);

WinAPI.CloseHandle(processHandle);

Appendix: Minimal Windows API Declarations

static class WinAPI
{

    public enum ListViewMessages
    {
        LVM_GETITEMTEXT = 0x104B
    }

    public enum ListViewItemFilters : uint
    {
        LVIF_TEXT = 0x0001,
    }

    public const uint MAX_LVMSTRING = 255;

    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct LVITEM
    {
        public uint mask;
        public int iItem;
        public int iSubItem;
        public uint state;
        public uint stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        public IntPtr lParam;
    }

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);


    [Flags]
    public enum ProcessAccessFlags : uint
    {
        VirtualMemoryOperation = 0x0008,
        VirtualMemoryRead = 0x0010,
        VirtualMemoryWrite = 0x0020,
    }

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

    [Flags]
    public enum AllocationType
    {
        Commit = 0x1000,
        Release = 0x8000,
    }

    [Flags]
    public enum MemoryProtection
    {
        ReadWrite = 0x0004,
    }

    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle,  uint processId);

    [DllImport("kernel32.dll")]
    public static extern bool CloseHandle(IntPtr hHandle);

    [DllImport("kernel32.dll")]
    public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, out int lpNumberOfBytesWritten);

    [DllImport("kernel32.dll")]
    public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] buffer, int dwSize, out IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll")]
    public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType dwFreeType);
}
like image 80
hillin Avatar answered Oct 11 '22 21:10

hillin