MFC-windows桌面窗口句柄的关系
windows桌面层次
首先要知道windows的桌面是由哪个窗口显示的:
打开spy++工具。窗口列表翻到最下面有一个 类名是Progman窗口 标题是Program Manager的窗口。这个窗口下面还有3个子窗口如下:
注意:3个子窗口用FindWindowEx查找,不要用FindWindow
CString str; HWND hProgman = NULL; HWND hWndDesktop = NULL; HWND hShellDefView = NULL; HWND hSysHeader32 = NULL; HWND hWnd = NULL; void CsDlg::OnBnClickedButton1() { hProgman = ::FindWindow(_T("Progman"), NULL);//程序管理器窗口句柄 //用于显示桌面背景、图标和壁纸 str.Format(_T("hProgman=%X\r\n"), hProgman); ::OutputDebugString(str); if (hProgman) { HWND hShellDefView = ::FindWindowEx(hProgman, NULL, _T("SHELLDLL_DefView"), NULL); str.Format(_T("hShellDefView=%X\r\n"), hShellDefView); ::OutputDebugString(str); if (hShellDefView) { hWndDesktop = ::FindWindowEx(hShellDefView, NULL, _T("SysListView32"), NULL); str.Format(_T("hWndDesktop=%X\r\n"), hWndDesktop); ::OutputDebugString(str); if (hWndDesktop) { hSysHeader32= ::FindWindowEx(hWndDesktop, NULL, _T("SysHeader32"), NULL); str.Format(_T("hSysHeader32=%X\r\n"), hSysHeader32); ::OutputDebugString(str); } } } hWnd = ::GetDesktopWindow();//获取当前桌面窗口 //这个窗口是所有窗口的父窗口 //hProgman 也是 hWnd 的子窗口; 证明:先点击窗口句柄按钮,再点击父子关系按钮 str.Format(_T("hWnd=%X\r\n"), hWnd); ::OutputDebugString(str); } void CsDlg::OnBnClickedButton2() { ::SendMessage(hSysHeader32, WM_CLOSE, 0, 0);//向窗口发送close信号 /* 执行后刷新一下窗口,才能看到效果 执行后图标下面的名字不见了,所以这个窗口就是显示桌面图标名字的 */
代码输出的句柄和SPY++显示的句柄完全相同
桌面各个窗口层作用
类名SysHeader32窗口
::SendMessage(hSysHeader32, WM_CLOSE, 0, 0);//向窗口发送close信号
执行这条指令后,刷新桌面,你会发现图标下面的名都不见了
所以这个窗口就是显示桌面图标名字的
类名SysListView32窗口
::SendMessage(hWndDesktop, WM_CLOSE, 0, 0);
发现图标没有了,所以这个窗口是显示图标的还有图标的一系列操作
类名SHELLDLL_DefView窗口
可能是管理壁纸的窗口
类名Progman窗口
关闭这个窗口时,会出现关机窗口,说明这个窗口是管理窗口,桌面最底层的窗口
各个窗口作用图
int count = (int)::SendMessage(SysListView32的句柄, LVM_GETITEMCOUNT, (WPARAM)0, (LPARAM)0);//获取桌面图标的数量
SysListView32窗口属性
1.这个窗口是列表视图控件
2.每一项的0列就是图标名称
3.文本数据类型是ANSI的char字符串,所以工程的字符集尽量用多字节字符集
4.32位系统可以用LVITEM项结构,如果是64位系统不要用LVITEM项结构,结构中有的数据类型改变了,下面我给出了64位系统自定义项结构
typedef struct tagLVITEM64A { UINT mask; int iItem; //行号 int iSubItem; //列号 UINT state; UINT stateMask; INT64 pszText; //文本 int cchTextMax; int iImage; LPARAM lParam; int iGroupId; UINT cColumns; // tile view columns PUINT puColumns; } LVITEM64A, * LPLVITEM64A;
实例:获取QQ在桌面中的位置,双击打开QQ
//=====================我的程序=================== CString str; CString str1; HWND hProgman = NULL; HWND hWndDesktop = NULL; HWND hShellDefView = NULL; HWND hSysHeader32 = NULL; HWND hWnd = NULL; CString strIconName = _T("腾讯QQ"); //我要寻找的图标名称 RECT rect; //图标位置 POINT pt; //图标位置 typedef struct tagLVITEM64A { UINT mask; int iItem; int iSubItem; UINT state; UINT stateMask; INT64 pszText; int cchTextMax; int iImage; LPARAM lParam; int iGroupId; UINT cColumns; // tile view columns PUINT puColumns; } LVITEM64A, * LPLVITEM64A; void CsuanjiDlg::OnBnClickedButton1() { //=============获取句柄 hProgman = ::FindWindow(_T("Progman"), NULL);//程序管理器窗口句柄 //用于显示桌面背景、图标和壁纸 //看:https://www.cnblogs.com/liming19680104/p/17342378.html str.Format(_T("hProgman=%X\r\n"), hProgman); ::OutputDebugString(str); if (hProgman) { HWND hShellDefView = ::FindWindowEx(hProgman, NULL, _T("SHELLDLL_DefView"), NULL); str.Format(_T("hShellDefView=%X\r\n"), hShellDefView); ::OutputDebugString(str); if (hShellDefView) { hWndDesktop = ::FindWindowEx(hShellDefView, NULL, _T("SysListView32"), NULL); str.Format(_T("hWndDesktop=%X\r\n"), hWndDesktop); ::OutputDebugString(str); if (hWndDesktop) { hSysHeader32 = ::FindWindowEx(hWndDesktop, NULL, _T("SysHeader32"), NULL); str.Format(_T("hSysHeader32=%X\r\n"), hSysHeader32); ::OutputDebugString(str); } } } int count = (int)::SendMessage(hWndDesktop, LVM_GETITEMCOUNT, (WPARAM)0, (LPARAM)0);//获取桌面图标的数量 str.Format(_T("图标的数量count=%d\r\n"), count); ::OutputDebugString(str); unsigned long pid; ::GetWindowThreadProcessId(hWndDesktop, &pid);//获取进程ID str.Format(_T("进程ID:pid=%d\r\n"), pid); ::OutputDebugString(str); HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION, FALSE, pid); //打开进程权限,并返回进程的句柄 /* 参数1:DWORD dwDesiredAccess 渴望得到的访问权限 PROCESS_ALL_ACCESS //所有能获得的权限 PROCESS_CREATE_PROCESS //需要创建一个进程 PROCESS_CREATE_THREAD //需要创建一个线程 PROCESS_DUP_HANDLE //重复使用DuplicateHandle句柄 PROCESS_QUERY_INFORMATION //获得进程信息的权限,如它的退出代码、优先级 PROCESS_QUERY_LIMITED_INFORMATION /*获得某些信息的权限,如果获得了PROCESS_QUERY_INFORMATION,也拥有PROCESS_QUERY_LIMITED_INFORMATION权限 PROCESS_SET_INFORMATION //设置某些信息的权限,如进程优先级 PROCESS_SET_QUOTA //设置内存限制的权限,使用SetProcessWorkingSetSize PROCESS_SUSPEND_RESUME //暂停或恢复进程的权限 PROCESS_TERMINATE //终止一个进程的权限,使用TerminateProcess PROCESS_VM_OPERATION //操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory) PROCESS_VM_READ //读取进程内存空间的权限,可使用ReadProcessMemory PROCESS_VM_WRITE //读取进程内存空间的权限,可使用WriteProcessMemory SYNCHRONIZE //等待进程终止 参数2:BOOL bInheritHandle 得到的进程句柄是否可以被继承 参数3:DWORD dwProcessId 被打开的进程ID 返回值:如成功,返回值为指定进程的句柄。如失败,返回值为NULL */ LVITEM64A lvi; //列表视图控件的项结构 lvi.cchTextMax = 512; lvi.mask = LVIF_TEXT; LVITEM64A* pLVITEM = (LVITEM64A*)VirtualAllocEx(process, NULL, sizeof(LVITEM64A), MEM_COMMIT, PAGE_READWRITE);//在指定进程的虚拟地址空间中保留或开辟一段区域 /* 在指定进程的虚拟地址空间中保留或开辟一段区域..除非MEM_RESET被使用,否则这个函数将会初始化那段内存为0 【个人理解:给进程申请一块内存物理空间】 参数1:HANDLE hProcess 申请内存所在的进程句柄 【OpenProcess的返回值】 参数2:LPVOID lpAddress 保留页面的内存地址;一般用NULL自动分配 参数3:SIZE_T dwSize 欲分配的内存大小,字节单位;注意实际分配的内存大小是页内存大小的整数倍 参数4:DWORD flAllocationType 内存分配的类型,这个参数必须包含以下其中一种 MEM_COMMIT=0x1000 分配指定的物理内存或在磁盘上的内存分页文件 MEM_PHYSICAL=0x400000 分配一段具有读写权限的物理内存(仅用于地址窗口扩展内存) 这个值必须为MEM_RESERVE不能为其他值 MEM_RESERVE=0x2000 在指定进程的虚拟地址空间中保留一段在物理内存或磁盘上的内存分页文件中的内存区域 MEM_RESET=0x80000 指明在内存中由参数lpAddress和dwSize指定的数据无效 当你使用MEM_RESET时,VirtualAllocEx函数将会忽略fProtect的值,但你扔必须设置fProtect的值 MEM_TOP_DOWN=0x100000 在尽可能高的地址上分配内存(Windows 98忽略此标志) MEM_WRITE_WATCH:必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98) 参数5:DWORD flProtect 在分配的内存页区域中进行内存保护的属性 PAGE_READONLY: 该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访 PAGE_READWRITE 区域可被应用程序读写 PAGE_EXECUTE: 区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝 PAGE_EXECUTE_READ :区域包含可执行代码,应用程序可以读该区域 PAGE_EXECUTE_READWRITE: 区域包含可执行代码,应用程序可以读写该区域 PAGE_GUARD: 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限 PAGE_NOACCESS: 任何访问该区域的操作将被拒绝 PAGE_NOCACHE: RAM中的页映射到该区域时将不会被微处理器缓存(cached) 返回值:执行成功就返回分配内存的首地址,不成功就是NULL */ char* pszText = (char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT, PAGE_READWRITE); RECT* pItemRc = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE); RECT rc; if (pItemRc != NULL || pLVITEM != NULL) { LVITEM64A LVITEM; LVITEM.mask = LVIF_TEXT; LVITEM.cchTextMax = 512; LVITEM.pszText = (INT64)pszText; char ItemBuf[512]; int nCount = ::SendMessage(hWndDesktop, LVM_GETITEMCOUNT, 0, 0);//获取图标总数(项总数) for (int iItem = 0; iItem < nCount; iItem++) { LVITEM.iItem = iItem; LVITEM.iSubItem = 0; //将设置好的结构插入目标进程 WriteProcessMemory(process, pLVITEM, &LVITEM, sizeof(LVITEM), NULL);//将数据写入指定进程中的内存区域 /* 参数1:HANDLE hProcess 由OpenProcess返回的进程句柄 句柄必须具有对进程的 PROCESS_VM_WRITE 和 PROCESS_VM_OPERATION 访问权限 如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程 参数2:LPVOID lpBaseAddress 要写的内存首地址 【VirtualAllocEx返回的地址】 在数据传输之前,系统会验证指定大小的基地址和内存中的所有数据是否都可以进行写访问,如果不可访问,则该功能失败 参数3:LPCVOID lpBuffer 指向要写入的数据的指针 参数4:SIZE_T nSize 要写入的字节数 参数5:SIZE_T *lpNumberOfBytesWritten 用来保存实际传输字节数的变量的指针 该参数是可选的。如果为NULL,则忽略该参数 返回值:非零值代表成功 */ BOOL r = (BOOL)::SendMessage(hWndDesktop, LVM_GETITEMTEXTA, iItem, (LPARAM)pLVITEM);//获取列表视图文本 /* 参数3:列表视图项的索引 参数4:指向 LVITEM 结构的指针。保存获取的内容 若要检索项文本,请将 iSubItem 设置为零。 若要检索子项的文本,请将 iSubItem 设置为子项的索引。 pszText 成员指向接收文本的缓冲区。 cchTextMax 成员指定缓冲区中的字符数 返回值:如果显式发送此消息,它将返回 LVITEM 结构的 pszText 成员中的字符数 */ BOOL b1=ReadProcessMemory(process, pszText, ItemBuf, 512, NULL); /* 参数1:HANDLE hProcess 进程句柄 【OpenProcess的返回值】 句柄必须具有对进程的PROCESS_VM_READ访问权限 参数2:LPCVOID lpBaseAddress 指向要从中读取的指定进程中基址的指针 参数3:LPVOID lpBuffer 指向从指定进程的地址空间接收内容的缓冲区的指针 参数4:SIZE_T nSize 要从指定进程读取的字节数 参数5:SIZE_T *lpNumberOfBytesRead 指向接收传输到指定缓冲区的字节数的变量的指针。 如果为NULL,则忽略参数 返回值:如果该函数成功,则返回值为非零值 如果函数失败,则返回值为 0 (零) */ str.Format(_T("%s\r\n"), &ItemBuf); str1.Format(_T("%s"), &ItemBuf); if (str1== strIconName) { BOOL b2=::SendMessage(hWndDesktop, LVM_GETITEMRECT, iItem, (LPARAM)pItemRc);//获取列表视图中项的RECT /* 获取图标位置 参数3:列表视图项的索引 参数4:指向接收边界矩形的 RECT 结构的指针 返回值:如果成功,则返回 TRUE ,否则返回 FALSE 注意:RECT值相对于客户区 */ b1=ReadProcessMemory(process, pItemRc, &rc, sizeof(RECT), NULL); memcpy(&rect, &rc, sizeof(RECT)); break; } OutputDebugString(str); } VirtualFreeEx(process, pLVITEM, 0, MEM_RELEASE); VirtualFreeEx(process, pszText, 0, MEM_RELEASE); BOOL b3=VirtualFreeEx(process, pItemRc, 0, MEM_RELEASE);//释放进程内存 /* 参数1:HANDLE 由OpenProcess返回的进程句柄 参数2:LPVOID lpAddress 指向要释放的虚拟内存空间首地址的指针 如果 dwFreeType 为 MEM_RELEASE, 则该参数必须为VirtualAllocEx的返回值 参数3:SIZE_T dwSize 虚拟内存空间的字节数 如果 dwFreeType 为 MEM_RELEASE,则 dwSize 必须为0 . 按 VirtualAllocEx申请时的大小全部释放 如果dwFreeType 为 MEM_DECOMMIT, 则释放从lpAddress开始的一个或多个字节 ,即 lpAddress+dwSize 参数4:DWORD dwFreeType 释放类型,取值见下表: MEM_DECOMMIT=0x00004000 取消提交页面的指定区域。 操作后,内存空间不可用,内存页还将存在 MEM_RELEASE=0x00008000 这种方式 很彻底,完全回收 返回值:成功: 为非 0 值(零) 失败: 为 0 (零) */ CloseHandle(process); } pt.x = rect.left+ (rect.right - rect.left) / 2; pt.y = rect.top+ (rect.bottom - rect.top) / 2; ::ClientToScreen(hWndDesktop, &pt); SetCursorPos(pt.x, pt.y);//把光标移到屏幕的指定位置 int cx_screen = ::GetSystemMetrics(SM_CXSCREEN); //屏幕宽 int cy_screen = ::GetSystemMetrics(SM_CYSCREEN); //屏幕高 int real_x = 65535 * pt.x / cx_screen; //像素坐标转换成绝对坐标 int real_y = 65535 * pt.y / cy_screen; INPUT inputs[4] = {};//双击就是两次单击 inputs[0].type = INPUT_MOUSE; inputs[1].type = INPUT_MOUSE; inputs[2].type = INPUT_MOUSE; inputs[3].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; inputs[2].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[3].mi.dwFlags = MOUSEEVENTF_LEFTUP; UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); }
实例工程下载:
链接:https://pan.baidu.com/s/1JyMm7cPcgDEBmT-qm7afew
提取码:6666