假设需要读取的进程为Exe可执行程序A
1、根据A的实际打开程序名:A.exe,来找到这个程序的窗口句柄(和进程句柄不同)
typedef struct tagWNDINFO
{
DWORD dwProcessId;
HWND hWnd;
} WNDINFO, *LPWNDINFO;
BOOL CALLBACK MyEnumProc(HWND hWnd,LPARAM lParam)//枚举所有进程
{
DWORD dwProcId;
GetWindowThreadProcessId(hWnd, &dwProcId);
LPWNDINFO pInfo=(LPWNDINFO)lParam;
if(dwProcId==pInfo->dwProcessId)
{
// 在此添加更多限制条件
CString strTmp;
GetClassName(hWnd, strTmp.GetBuffer(200), 200);
strTmp.ReleaseBuffer();
if (!strTmp.Compare("LButton")) // 按钮, 编辑框TEdit等, 具体可使用Spy++查看
{
pInfo->hWnd = hWnd;
return FALSE;
}
if (IsWindowVisible(hWnd)) // 当前窗口是否可见
{
pInfo->hWnd = hWnd; // 获取到第一个窗口句柄
return FALSE;
}
}
return TRUE;
}
HWND GetProcessHwnd(DWORD proccessId)
{
WNDINFO wi;
wi.dwProcessId = proccessId;
wi.hWnd = NULL;
EnumWindows(MyEnumProc,(LPARAM)&wi);
EnumChildWindows(wi.hWnd, MyEnumProc, (LPARAM)&wi); // 枚举窗口的子窗口句柄, MFC中的控作等
return wi.hWnd;
}
void GetProcessInfo(CString processName)
{
//创建进程快照(TH32CS_SNAPPROCESS表示创建所有进程的快照)
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
//PROCESSENTRY32进程快照的结构体
PROCESSENTRY32 pe;
//实例化后使用Process32First获取第一个快照的进程前必做的初始化操作
pe.dwSize = sizeof(PROCESSENTRY32);
//下面的IF效果同:
//if(hProcessSnap == INVALID_HANDLE_VALUE) 无效的句柄
if(!Process32First(hSnapShot,&pe))
{
return;
}
processName.MakeLower();
BOOL clearprocess = FALSE;
//如果句柄有效 则一直获取下一个句柄循环下去
while (Process32Next(hSnapShot,&pe))
{
//pe.szExeFile获取当前进程的可执行文件名称
CString scTmp = pe.szExeFile;
scTmp.MakeLower();
char modPath[MAX_PATH] = {0};
if(!scTmp.Compare(processName))
{
//从快照进程中获取该进程的PID(即任务管理器中的PID)
DWORD dwProcessID = pe.th32ProcessID;
//获取进程的句柄
HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE,FALSE,dwProcessID);
//获取第一个可视窗口的句柄
HWND hWnd = GetProcessHwnd(dwProcessID);
}
}
::CloseHandle(hSnapShot);
}
上面的这一段代码可以完美获取程序的主窗口句柄和子窗口句柄!再一次强调不是进程句柄哦!
2、通过窗口句柄,获取窗口的内容(主文档标题,编辑框内容,静态框内容)。但是如果要获取比较复杂的窗口的文本内容,就会麻烦一些。
//获取简单窗口的内容
GetWindowText(hWnd,strText.GetBuffer(256), 256);
比如获取一个TreeView的内容,就比较麻烦。总体思路就是通过发消息获取。
//从快照进程中获取该进程的PID(即任务管理器中的PID)
DWORD dwProcessID = pe.th32ProcessID;
//获取第一个可视窗口的句柄
HWND hWnd = GetProcessHwnd(dwProcessID);
//获取进程的句柄
HANDLE hProcess = ::OpenProcess(PROCESS_VM_OPERATION |PROCESS_VM_READ | PROCESS_VM_WRITE |PROCESS_QUERY_INFORMATION,FALSE,dwProcessID);
//----------------
//给_lvi,_item分配进程级的虚拟内存空间
TVITEM tvi, *_tvi;
char *_item;
char item[256];
CTreeCtrl* pTreeCtrl = ((CTreeCtrl*)CWnd::FromHandle((HWND)hWnd));
int nItemNum = pTreeCtrl->GetCount();
HTREEITEM Testitem1;
Testitem1=pTreeCtrl->GetRootItem();
long ret = (long)Testitem1;
_tvi=(TVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(TVITEM), MEM_COMMIT, PAGE_READWRITE);
_item=(char*)VirtualAllocEx(hProcess, NULL, 256, MEM_COMMIT, PAGE_READWRITE);
tvi.cchTextMax=256;
tvi.hItem=(HTREEITEM)ret;
tvi.mask=TVIF_TEXT;
tvi.pszText=_item;
//把_lvi 写入进程
WriteProcessMemory(hProcess, _tvi, &tvi, sizeof(TVITEM), NULL);
//发消息得到数据
::SendMessage(hWnd, TVM_GETITEM, 0 , (LPARAM)_tvi);
//从进程中读出数据
BOOL bRead = ReadProcessMemory(hProcess, _item, item, 256, NULL);
char filestr[256];
//copy data
memcpy(filestr,item,256);
strTitle.Format("%s",filestr);
//free memory
VirtualFreeEx(hProcess, _tvi, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, _item, 0, MEM_RELEASE);
执行这段代码的前提是已经获取了该程序进程的ID和对应窗口的句柄。网上也有很多这样的内容,也可查看其它文章,对照理解。
3、至此,就完成了从其他进程中读取文本内容的方法。但是有个注意事项,本篇文章所给出的方法只针对于32位程序。如果你要读取的程序是64位的,可将里面的一些函数和变量换成64位的。可能替换过程还需要再进行一些查阅资料。但实现的整理思路是想通的。本文的实现也是经过验证的。
4、spy++是对这种分析极为有效的工具。是微软编译工具的其中一个,善加利用。我的资源里有下载,需要的可以去看看。
附spy++ 下载链接:
SPY++下载