*References:
가끔 다른 프로세스의 SysListView32를 읽고싶을 때가 있다.
그런데 막상 해보니까 쉬울줄 알았던 일이 잘 안돼서 고생을 좀 하다가.. 검색을 통해 해결했다.
우선 다음과 같은 코드를 생각해보자.
#define WIN32_LEAN_AND_MEAN // compile faster
#include <stdio.h>
#include <locale.h>
#include <windows.h>
#include <commctrl.h>
const int COLS(5), SZBUF(512);
int main(void)
{
setlocale(LC_ALL, ""); // 한글 출력을 위해 기본 로케일 설정.
HWND hParent = FindWindow(NULL, L"SW 찾기");
HWND hListView = FindWindowEx(hParent, NULL, L"SysListView32", NULL);
int numItems=(int)SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
LVITEM lvi;
wchar_t subItems[COLS][SZBUF] = {};
lvi.cchTextMax=SZBUF; // 텍스트 버퍼 사이즈를 설정
for(int i = 0; i < numItems; i++) {
for (int j = 0; j < COLS; j++) {
// j번째 column의 값을 얻는다.
lvi.iSubItem = j;
lvi.pszText = subItems[j];
wcscpy(subItems[j], L"끼야아앙");
SendMessage(hListView, LVM_GETITEMTEXTW, (WPARAM)i, (LPARAM)&lvi);
printf("%s%ls", j > 0 ? ";" : "", subItems[j]);
// column1;column2;column3 형식으로 한 줄씩 출력.
}
puts("");
}
return 0;
}
완벽해보이...지만 실행해보면?
C:\>a.exe 끼야아앙;끼야아앙;끼야아앙;끼야아앙;끼야아앙 끼야아앙;끼야아앙;끼야아앙;끼야아앙;끼야아앙 C:\> |
실제로 버퍼에 아무것도 쓰이지 않은 것을 확인할 수 있다.
원인이 뭘까 고민해보다가 reference에서 그 답을 찾았다.
요약하자면 "ListView를 가진 프로세스는 a.exe의 메모리에 접근 권한이 없음!" 이라고..
그러므로 일단 ListView를 가진 프로세스에 메모리를 할당하고,
우리는 ReadProcessMemory/WriteProcessMemory를 이용해서 해당 메모리에 접근하면 된다.
덕분에 코드는 더 지저분해졌지만, 어쨌든 돌아는 간다 :D
최종 결과물
#define WIN32_LEAN_AND_MEAN // compile faster
#include <stdio.h>
#include <locale.h>
#include <windows.h>
#include <commctrl.h>
const int COLS(5), SZBUF(512);
int main(void)
{
setlocale(LC_ALL, ""); // 한글 출력을 위해 기본 로케일 설정.
HWND hParent = FindWindow(NULL, L"SW 찾기");
HWND hListView = FindWindowEx(hParent, NULL, L"SysListView32", NULL);
int numItems=(int)SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
LVITEM lvi, *_lvi = NULL;
wchar_t subItems[COLS][SZBUF] = {};
wchar_t *_subItems[COLS] = {};
unsigned long pid = -1;
HANDLE process = NULL;
// hWnd로부터 pid를 얻는다.
GetWindowThreadProcessId(hListView, &pid);
// 해당 pid를 가진 프로세스에 대해 VM조작 권한을 얻는다.
process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
_lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
MEM_COMMIT, PAGE_READWRITE);
// ListView를 가지고 있는 프로세스에 LVITEM 메모리를 할당한다.
for (int i = 0; i < COLS; i++) {
_subItems[i]=(wchar_t*)VirtualAllocEx(process, NULL,
SZBUF * sizeof(wchar_t),
MEM_COMMIT,
PAGE_READWRITE);
// ListView를 가지고 있는 프로세스에 wchar[] 메모리를 할당한다.
}
lvi.cchTextMax=SZBUF; // 텍스트 버퍼 사이즈를 설정
for(int i = 0; i < numItems; i++) {
for (int j = 0; j < COLS; j++) {
// j번째 column의 값을 얻는다.
lvi.iSubItem = j;
lvi.pszText = _subItems[j];
// 반환되는 텍스트는 타겟 프로세스의 wchar[] 버퍼에 써진다.
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
// 타겟 프로세스의 LVITEM에 lvi의 설정값을 쓴다(복사).
SendMessage(hListView, LVM_GETITEMTEXTW, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _subItems[j], subItems[j], SZBUF, NULL);
// 타겟 프로세스의 wchar[] 버퍼를 읽는다.
printf("%s%ls", j > 0 ? ";" : "", subItems[j]);
// column1;column2;column3 형식으로 한 줄씩 출력.
}
puts("");
}
// 타겟 프로세스에 할당한 메모리를 해제한다.
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
for (int j = 0; j < COLS; j++) {
VirtualFreeEx(process, _subItems[j], 0, MEM_RELEASE);
}
return 0;
}