LVM_GETCOLUMN returns 无结果

LVM_GETCOLUMN returns no result

我试图监视一个 syslistview32 元素。阅读内容效果很好,但我无法获取列 headers 的文本。

这是"my"代码(我不想用借来的羽毛来装饰自己-大部分代码来自https://konradn.wordpress.com/2012/02/21/read-listviewitem-content-from-another-process/):

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct LV_COLUMN
{
    public System.Int32 mask;
    public System.Int32 fmt;
    public System.Int32 cx;
    public System.IntPtr pszText;
    public System.Int32 cchTextMax;
    public System.Int32 iSubItem;
    public System.Int32 iImage;
    public System.Int32 iOrder;
}

    public static string GetListViewColumn(System.IntPtr hwnd, uint processId, int Column)
    {
        const int dwBufferSize = 2048;
        const int LVM_FIRST = 0x1000;
        const int LVM_GETCOLUMNA = LVM_FIRST + 25;
        const int LVM_GETCOLUMNW = LVM_FIRST + 95;
        const int LVCF_FMT = 0x00000001;

        int bytesWrittenOrRead = 0;
        LV_COLUMN lvCol;
        string retval;
        bool bSuccess;
        System.IntPtr hProcess = System.IntPtr.Zero;
        System.IntPtr lpRemoteBuffer = System.IntPtr.Zero;
        System.IntPtr lpLocalBuffer = System.IntPtr.Zero;

        try
        {
            lvCol = new LV_COLUMN();
            lpLocalBuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(dwBufferSize);
            hProcess = OpenProcess(Win32ProcessAccessType.AllAccess, false, processId);
            if (hProcess == System.IntPtr.Zero)
                throw new System.ApplicationException("Failed to access process!");

            lpRemoteBuffer = VirtualAllocEx(hProcess, System.IntPtr.Zero, dwBufferSize, Win32AllocationTypes.MEM_COMMIT, Win32MemoryProtection.PAGE_READWRITE);
            if (lpRemoteBuffer == System.IntPtr.Zero)
                throw new System.SystemException("Failed to allocate memory in remote process");

            lvCol.mask = LVCF_FMT;
            lvCol.pszText = (System.IntPtr)(lpRemoteBuffer.ToInt32() + System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_COLUMN)));
            lvCol.cchTextMax = 500;
            lvCol.iOrder = Column;

            bSuccess = WriteProcessMemoryGETCOLUMN(hProcess, lpRemoteBuffer, ref lvCol, (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_COLUMN)), out bytesWrittenOrRead);
            if (!bSuccess)
                throw new System.SystemException("Failed to write to process memory");


            SendMessage(hwnd, LVM_GETCOLUMNA, System.IntPtr.Zero, lpRemoteBuffer);

            bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize, out bytesWrittenOrRead);

            if (!bSuccess)
                throw new System.SystemException("Failed to read from process memory");

            retval = System.Runtime.InteropServices.Marshal.PtrToStringUni((System.IntPtr)(lpLocalBuffer.ToInt32() + System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_COLUMN))));
        }
        finally
        {
            if (lpLocalBuffer != System.IntPtr.Zero)
                System.Runtime.InteropServices.Marshal.FreeHGlobal(lpLocalBuffer);
            if (lpRemoteBuffer != System.IntPtr.Zero)
                VirtualFreeEx(hProcess, lpRemoteBuffer, 0, Win32AllocationTypes.MEM_RELEASE);
            if (hProcess != System.IntPtr.Zero)
                CloseHandle(hProcess);
        }
        return retval;
    }

无论我为列提供哪个值,结果总是string.Empty

获取列表视图内容的函数非常好用,非常相似:

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct LV_ITEM
{
    public System.Int32 mask;
    public System.Int32 iItem;
    public System.Int32 iSubItem;
    public System.Int32 state;
    public System.Int32 stateMask;
    public System.IntPtr pszText;
    public System.Int32 cchTextMax;
    public System.Int32 iImage;
    public System.Int32 lParam;
    public System.Int32 iIndent;
}

    public static string GetListViewItem(System.IntPtr hwnd, uint processId, int item, int subItem, out bool Valid)
    {
        const int dwBufferSize = 2048;
        const int LVM_FIRST = 0x1000;
        const int LVM_GETITEMW = LVM_FIRST + 75;
        const int LVM_GETITEM = LVM_FIRST + 5;
        const int LVIF_TEXT = 0x00000001;

        int bytesWrittenOrRead = 0;
        LV_ITEM lvItem;
        string retval;
        bool bSuccess;
        System.IntPtr hProcess = System.IntPtr.Zero;
        System.IntPtr lpRemoteBuffer = System.IntPtr.Zero;
        System.IntPtr lpLocalBuffer = System.IntPtr.Zero;

        try
        {
            lvItem = new LV_ITEM();
            lpLocalBuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(dwBufferSize);
            hProcess = OpenProcess(Win32ProcessAccessType.AllAccess, false, processId);
            if (hProcess == System.IntPtr.Zero)
                throw new System.ApplicationException("Failed to access process!");

            lpRemoteBuffer = VirtualAllocEx(hProcess, System.IntPtr.Zero, dwBufferSize, Win32AllocationTypes.MEM_COMMIT, Win32MemoryProtection.PAGE_READWRITE);
            if (lpRemoteBuffer == System.IntPtr.Zero)
                throw new System.SystemException("Failed to allocate memory in remote process");

            lvItem.mask = LVIF_TEXT;
            lvItem.iItem = item;
            lvItem.iSubItem = subItem;
            lvItem.pszText = (System.IntPtr)(lpRemoteBuffer.ToInt32() + System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_ITEM)));
            lvItem.cchTextMax = 500;

            bSuccess = WriteProcessMemoryGETITEM(hProcess, lpRemoteBuffer, ref lvItem, (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_ITEM)), out bytesWrittenOrRead);
            if (!bSuccess)
                throw new System.SystemException("Failed to write to process memory");


            SendMessage(hwnd, LVM_GETITEMW, System.IntPtr.Zero, lpRemoteBuffer);
            int Result = (int)SendMessage(hwnd, LVM_GETITEM, System.IntPtr.Zero, lpLocalBuffer);

            if(Result == 0) { Valid = false; }else { Valid = true; }

            bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize, out bytesWrittenOrRead);

            if (!bSuccess)
                throw new System.SystemException("Failed to read from process memory");

            retval = System.Runtime.InteropServices.Marshal.PtrToStringUni((System.IntPtr)(lpLocalBuffer.ToInt32() + System.Runtime.InteropServices.Marshal.SizeOf(typeof(LV_ITEM))));
        }
        finally
        {
            if (lpLocalBuffer != System.IntPtr.Zero)
                System.Runtime.InteropServices.Marshal.FreeHGlobal(lpLocalBuffer);
            if (lpRemoteBuffer != System.IntPtr.Zero)
                VirtualFreeEx(hProcess, lpRemoteBuffer, 0, Win32AllocationTypes.MEM_RELEASE);
            if (hProcess != System.IntPtr.Zero)
                CloseHandle(hProcess);
        }
        return retval;
    }

有没有人知道我做错了什么。

非常感谢您的帮助。

此致, 一月

Ps.: 我知道UI Automation,我喜欢UI Automation,我知道UI Automation可以解决这个问题,但是我不想用在这种情况下。

你只是要求 LVCF_FMT,你需要在掩码中包含 LVCF_TEXT 并设置 iSubItem 成员。