LVM_GETCOLUMN returns no result

为君一笑 提交于 2019-12-25 18:37:46

问题


I try to spy an syslistview32 element. Reading the content is working quite well but I'm not able to get the text of the column headers.

This is "my" code (I don't want to adorn myself with borrowed plumes - most of the code is from 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;
    }

Whether which value for Column I provide, the result is always string.Empty.

The function to get the content of the listview works greate and is very similary:

[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;
    }

Does anybody has an idea, what I'm doing wrong.

Thank you very much for your help.

Regards, Jan

Ps.: I know UI Automation, I like UI Automation, I know that UI Automation can solve this problem, but I don't want to use it in this case.


回答1:


You are just asking for LVCF_FMT, you need to include LVCF_TEXT in the mask and set the iSubItem member.



来源:https://stackoverflow.com/questions/48476648/lvm-getcolumn-returns-no-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!