由於 ListView 內容儲存在擁有進程的記憶體中,外部進程需在目標進程配置記憶體緩衝區,發送 LVM_GETITEMTEXT 訊息讓目標進程填入緩衝區,再讀回本地。
MyGetListViewItemvoid MyGetListViewItem(HWND hWindow, TStrings *strlist)
{
const int nMaxLen = 1023;
char szBuf[nMaxLen+1];
int nLVItemCount;
DWORD dwProcessID;
HANDLE hProcess;
LVITEM lvItemLocal;
DWORD dwBytesRead, dwBytesWrite;
bool bSuccess, bWriteOK;
GetWindowThreadProcessId(hWindow, &dwProcessID);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);
if (!hProcess) return;
// 在目標進程配置緩衝區
LPVOID lpTextRemote = VirtualAllocEx(hProcess, NULL, nMaxLen+1, MEM_COMMIT, PAGE_READWRITE);
LPVOID lpListItemRemote = VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
if (!lpTextRemote || !lpListItemRemote) return;
nLVItemCount = ListView_GetItemCount(hWindow);
strlist->Add("Welcome to www.ccrun.com");
strlist->Add("ListView的Item總數: " + String(nLVItemCount));
strlist->Add("---------------------------");
for (int i = 0; i < nLVItemCount; i++) {
ZeroMemory(szBuf, nMaxLen+1);
bWriteOK = WriteProcessMemory(hProcess, lpTextRemote, szBuf, nMaxLen+1, &dwBytesWrite);
if (!bWriteOK) return;
lvItemLocal.iItem = i;
lvItemLocal.iSubItem = 0;
lvItemLocal.mask = LVIF_TEXT;
lvItemLocal.cchTextMax = nMaxLen;
lvItemLocal.pszText = (LPTSTR)lpTextRemote;
dwBytesWrite = 0;
bWriteOK = WriteProcessMemory(hProcess, lpListItemRemote, &lvItemLocal, sizeof(LVITEM), &dwBytesWrite);
if (!bWriteOK) return;
SendMessage(hWindow, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)lpListItemRemote);
bSuccess = ReadProcessMemory(hProcess, lpTextRemote, szBuf, nMaxLen+1, &dwBytesRead);
if (!bSuccess) return;
strlist->Add(AnsiString(szBuf));
}
VirtualFreeEx(hProcess, lpListItemRemote, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, lpTextRemote, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// 0x000100DC 為 Spy++ 取得的桌面 ListView 控制代碼 (Win2000)
MyGetListViewItem((HWND)0x000100DC, Memo1->Lines);
}
PROCESS_ALL_ACCESS 權限,受限於系統安全策略 (Vista 後需提權)。iSubItem=0);若為 vsReport 模式需額外讀取表頭與子項目。1024 字元足應付一般項目文字。