Страницы

Поиск по вопросам

понедельник, 16 декабря 2019 г.

Получение содержимого ListView другого окна

#cpp #listview


Задача состоит в получении содержимого элемента ListView другого окна. Однако, данный
код приводит к критическому завершению работы процесса - владельца окна. Как исправить это?

#include "stdafx.h"
#include "windows.h"
#include "Commctrl.h"

int main() {

    HWND hWnd = FindWindow(NULL, L"");
    HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL);

    unsigned long pid;
    GetWindowThreadProcessId(hSysListView32, &pid);
    HANDLE hProc = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_READ |
        PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION,
        FALSE,
        pid);

    LVITEM* lvi = new LVITEM;
    lvi->iSubItem = 0;
    lvi->cchTextMax = 256;

    LVITEM* lviAddr = (LVITEM*)VirtualAllocEx(hProc, NULL, sizeof LVITEM, MEM_COMMIT,
PAGE_READWRITE);
    WriteProcessMemory(hProc, lviAddr, lvi, sizeof LVITEM, NULL);

    if (SendMessage(hSysListView32, LVM_GETITEMTEXT, 0, (LPARAM)lviAddr) > 0)
        printf("%s\n", lvi->pszText);
    else
        printf("%d\n", GetLastError());

    system("pause");
    return 0;
}




Представляю рабочий код:

HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL);
HWND hSysHeader32 = FindWindowEx(hSysListView32, NULL, L"SysHeader32", NULL);

int itemsCount = SendMessage(hSysListView32, LVM_GETITEMCOUNT, 0, 0);
int colCount = SendMessage(hSysHeader32, HDM_GETITEMCOUNT, 0, 0);

DWORD dwProcessId;
GetWindowThreadProcessId(hSysListView32, &dwProcessId);
HANDLE hProcess = OpenProcess(
    PROCESS_VM_READ | PROCESS_VM_WRITE |
    PROCESS_VM_OPERATION,
    FALSE,
    dwProcessId);

if (!hProcess) return -1;

LVITEM* lviAddress = (LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof LVITEM, MEM_COMMIT,
PAGE_READWRITE);
LPTSTR pItemText = (LPTSTR)VirtualAllocEx(hProcess, NULL, sizeof TCHAR * 512, MEM_COMMIT,
PAGE_READWRITE);

for (int i = 0; i < itemsCount; i++)
{
    TCHAR itemText[512] = { 0 };

    LVITEM lvItem = { 0 };
    lvItem.mask = LVIF_TEXT;
    lvItem.iSubItem = 0;
    lvItem.iItem = i;
    lvItem.pszText = pItemText;
    lvItem.cchTextMax = 512;

    WriteProcessMemory(hProcess, lviAddress, &lvItem, sizeof(LVITEM), NULL);

    int nSymbolsCount = SendMessage(hSysListView32, LVM_GETITEMTEXT, i, (LPARAM)lviAddress);
    if (nSymbolsCount > 0)
        ReadProcessMemory(hProcess, pItemText, itemText, sizeof TCHAR * nSymbolsCount,
NULL);

    wcout << itemText << endl;
}

VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);


Однако, на этот раз проблема заключается в том, что не удаётся получить текст каждого
элемента каждого столбца. В SysListView32 программы, с которой я работаю, 10 столбцов,
но получить информацию о содержимом их элементов получается только для 0-2 столбцов.
Дальше - неполная...

Как это исправить? Хотя если делать перебор не в цикле, а в ручную (ну самому i задавать),
любой item любого column выдаёт. Странно...



Никто не запрещал использовать hotkeys. Это вариант решения показался мне жизнеспособным:

HWND hWnd = FindWindow(NULL, L"");

DWORD idAttach = GetWindowThreadProcessId(GetForegroundWindow(), 0);
DWORD idAttachTo = GetWindowThreadProcessId(hWnd, 0);
AttachThreadInput(idAttach, idAttachTo, TRUE);
SetFocus(hWnd);

INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wScan = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;

// CONTROL key down.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "A" key down.
input.ki.wVk = 'A';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "A" key up.
input.ki.wVk = 'A';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key up.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key down.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "C" key down.
input.ki.wVk = 'C';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "C" key up.
input.ki.wVk = 'C';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key up.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

AttachThreadInput(idAttach, idAttachTo, FALSE);
SetFocus(NULL);


Но куда без очередных проблем... Проект на шарпе. Возникли трудности с переводом
(не работает - пустой буфер обмена):

        const ushort VK_CONTROL = 0x11;
        const uint KEYEVENTF_KEYUP = 0x0002;

        enum INPUT_TYPE : uint {
            INPUT_MOUSE = 0,
            INPUT_KEYBOARD = 1,
            INPUT_HARDWARE = 2
        }

        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Explicit, Size = 28)]
        struct INPUT {
            [FieldOffset(0)] public INPUT_TYPE type;
            [FieldOffset(4)] public KEYBDINPUT ki;
        }

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern IntPtr FindWindow(
            [In, Optional, MarshalAs(UnmanagedType.LPTStr)] string lpszClass,
            [In, Optional, MarshalAs(UnmanagedType.LPTStr)] string lpszWindow);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern IntPtr GetMessageExtraInfo();

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern uint SendInput(
            [In] uint nInputs,
            [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs,
            [In] int cbSize);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool AttachThreadInput(
            [In] uint idAttach,
            [In] uint idAttachTo,
            [In] bool fAttach);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetFocus([In, Optional] IntPtr hWnd);

        static INPUT FormateKbdInputStruct(ushort vkCode, bool down) {
            return new INPUT() {
                type = INPUT_TYPE.INPUT_KEYBOARD,
                ki = new KEYBDINPUT() {
                    wScan = 0,
                    time = 0,
                    dwExtraInfo = GetMessageExtraInfo(),
                    wVk = vkCode,
                    dwFlags = down ? 0 : KEYEVENTF_KEYUP
                }
            };
        }

        public MainForm() {

            InitializeComponent();

            Shown += delegate {
                IntPtr hWnd = FindWindow(null, "");

                uint idAttach = GetWindowThreadProcessId(GetForegroundWindow(), 0);
                uint idAttachTo = GetWindowThreadProcessId(hWnd, 0);
                if (!AttachThreadInput(idAttach, idAttachTo, true)) return;

                INPUT[] inputs = new INPUT[] { FormateKbdInputStruct(VK_CONTROL, true) };
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('A', true);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('A', false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('C', true);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('C', false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct(VK_CONTROL, false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));

                SetFocus(hWnd);
                SetFocus(IntPtr.Zero);
                AttachThreadInput(idAttach, idAttachTo, false);

                Debug.WriteLine(Clipboard.GetData(DataFormats.UnicodeText)); // Пусто!
           };
        }

    


Ответы

Ответ 1



Нужно обнулять память для структуры LVITEM и массива символов перед каждым вызовом WriteProcessMemory / SendMessage. Данный код позволяет получить текст для всех столбцов ListView: HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL); DWORD dwProcessId; GetWindowThreadProcessId(hSysListView32, &dwProcessId); HANDLE hProcess = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId); if (!hProcess) return -1; LVITEM* lviAddress = (LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof LVITEM, MEM_COMMIT, PAGE_READWRITE); LPTSTR pItemText = (LPTSTR)VirtualAllocEx(hProcess, NULL, sizeof TCHAR * 512, MEM_COMMIT, PAGE_READWRITE); TCHAR itemText[512] = {0}; LVITEM lvItem = { 0 }; for (int i = 0; i < COUNT_ITEMS; i++) for(int j=0; j < COUNT_SUBITEMS; j++) { memset(&itemText,0,sizeof(itemText)); //обнуление памяти memset(&lvItem,0,sizeof(LVITEM)); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = j; lvItem.iItem = i; lvItem.pszText = pItemText; lvItem.cchTextMax = 512; WriteProcessMemory(hProcess, lviAddress, &lvItem, sizeof(LVITEM), NULL); int nSymbolsCount = SendMessage(hSysListView32, LVM_GETITEMTEXT, i, (LPARAM)lviAddress); if (nSymbolsCount > 0) ReadProcessMemory(hProcess, pItemText, itemText, sizeof TCHAR * nSymbolsCount, NULL); wprintf(L"Item %d, subitem %d: %s\n", i,j,itemText); } VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE); VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE); CloseHandle(hProcess);

Комментариев нет:

Отправить комментарий