Get ListView items from other windows

前端 未结 2 473
暖寄归人
暖寄归人 2020-12-03 22:59

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 pa         


        
相关标签:
2条回答
  • 2020-12-03 23:27

    I found a C# wrapper for WinAPIs that seems to provide access to the contents of an LV from any window.
    ManagedWinapi

    using ManagedWinapi.Windows;
    using System;
    
    namespace TestApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Create a SystemWindow object from the HWND of the ListView
                SystemWindow lvWindow = new SystemWindow((IntPtr)0x6d1d38);
    
                // Create a ListView object from the SystemWindow object
                var lv = SystemListView.FromSystemWindow(lvWindow);
    
                // Read text from a row
                var text = lv[0].Title;
            }
        }
    }
    

    Also, I have also forked mwapi here and am trying to add some new functionality - Mainly centred around colouring ListView rows, but also adding some missing p/invokes etc.

    0 讨论(0)
  • 2020-12-03 23:33

    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);
    }
    
    0 讨论(0)
提交回复
热议问题