Я вернулась чуть раньше =)
И пример притащила... Всё оказывается несколько сложнее, чем я изначально полагала! Так вот просто забрать содержимое ListView из чужого окна путем посылки ему сообщения LVM_GETITEMTEXT не получится. Нужно свершать дополнительные манипуляции по выделению памяти под текстовый буфер в чужом процессе.
Сразу говорю, что я воспользовалась этим примером (и авторство себе не присваиваю). Но я заточила тот пример под наши конкретные нужды и написала процедуру, считывающую всё содержимое ListView в объект StringGrid, который передается процедуре как параметр по ссылке. Это может быть как реально существующий на форме StringGrid, так и динамически создаваемый (runtime) с помощью конструктора Create. Просто, думаю, со StringGrid очень удобно работать в дальнейшем, анализируя содержимое.
Вот как выглядит процедура:
Код:
procedure TForm1.GetListViewData(LVHandle: HWND; ColumnCount: Integer;
var DataGrid: TStringGrid);
var
hProcess: THandle;
dwProcessID: DWORD;
dwBytesWriten: DWORD;
nItemCount: Integer;
i, j, nTextLength: Integer;
plviRemoteLVItem: PLVItem;
lviRemoteLVItem: LV_ITEM;
pszText: PChar;
svText: ShortString;
begin
if LVHandle = 0 then Exit;
dwProcessID := 0;
nItemCount := ListView_GetItemCount(LVHandle);
GetWindowThreadProcessId(LVHandle, @dwProcessID);
if dwProcessID = 0 then Exit;
hProcess := OpenProcess(PROCESS_ALL_ACCESS, True, dwProcessID);
if hProcess = 0 then Exit;
pszText := VirtualAllocEx(hProcess, nil, 255,
MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
if GetLastError <> 0 then Exit;
plviRemoteLVItem := VirtualAllocEx(hProcess, nil, SizeOf(LV_ITEM),
MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
if GetLastError <> 0 then Exit;
{ единицу прибавляю в том предположении,
что есть фиксированные строка и столбец }
DataGrid.RowCount := nItemCount + 1;
DataGrid.ColCount := ColumnCount + 1;
ZeroMemory(@lviRemoteLVItem, SizeOf(LV_ITEM));
lviRemoteLVItem.mask := LVIF_TEXT;
lviRemoteLVItem.pszText := pszText;
lviRemoteLVItem.cchTextMax := 255;
for i := 0 to nItemCount - 1 do
begin
lviRemoteLVItem.iSubItem := 0;
if not WriteProcessMemory(hProcess, plviRemoteLVItem, @lviRemoteLVItem,
SizeOf(LV_ITEM), dwBytesWriten) then Exit;
nTextLength := SendMessage(LVHandle, LVM_GETITEMTEXT,
i, Integer(plviRemoteLVItem));
ZeroMemory(@svText, 255);
ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
@svText[1], nTextLength, dwBytesWriten);
DataGrid.Cells[1, i + 1] := StrPas(PChar(@svText[1]));
for j := 1 to ColumnCount - 1 do
begin
lviRemoteLVItem.iSubItem := j;
if not WriteProcessMemory(hProcess, plviRemoteLVItem, @lviRemoteLVItem,
SizeOf(LV_ITEM), dwBytesWriten) then Exit;
nTextLength := SendMessage(LVHandle, LVM_GETITEMTEXT,
i, Integer(plviRemoteLVItem));
ZeroMemory(@svText, 255);
ReadProcessMemory(hProcess, lviRemoteLVItem.pszText,
@svText[1], nTextLength, dwBytesWriten);
DataGrid.Cells[j + 1, i + 1] := StrPas(PChar(@svText[1]));
end;
end;
VirtualFreeEx(hProcess, pszText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, plviRemoteLVItem, 0, MEM_RELEASE);
CloseHandle(hProcess);
end;
Ещё чуть-чуть поясню. Процедура получает следующие параметры: - Handle окна ListView (того самого, который имеет класс SysListView32). Этот дескриптор находите так же, как и раньше.
- Количество столбцов в этом ListView.
- Последний параметр, как уже сказано, — объект типа TStringGrid, который либо лежит на форме вашего собственного приложения, либо создается динамически.
Вот пример использования этой процедуры:
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
StrGrid: TStringGrid;
begin
StrGrid := TStringGrid.Create(Self);
try
GetListViewData(ListViewHandle, 4, StrGrid);
{ теперь таблица StrGrid содержит всё, что было в ListView.
Тут можно выполнять какие-то манипуляции с данными,
обращаясь к ним, например, так: StrGrid.Cells[3, 5] }
// Анализ и обработка...
finally
StrGrid.Free;
end;
end;
|