Задача состоит в получении содержимого элемента 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
", lvi->pszText);
else
printf("%d
", 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)); // Пусто!
};
}
Ответ
Нужно обнулять память для структуры 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
", i,j,itemText);
}
VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);