如果说,读者的开发是基于WINDOWS操作系统的话,那么不管是QT的程序还是什么的程序,都离不开WINDOWS的消息机制和窗口机制。所有的对话框都被WINDOWS操作系统认为是一种资源对象,只要有了对话框他的句柄,隐藏该对话框某个控件是一件很容易就能做到的事情。
在开始之前我们需要先学习几个WINAPI
和操作。
有的加密软件在运行过程中会禁止用户运行一些调试、跟踪或监视软件,以防止自身被破解。要查看系统中是否正在运行敏感软件,可以枚举系统中正在运行的进程列表,还可以枚举所有顶级窗口并获取窗口标题
。
重叠窗口和弹出窗口(我们做的对话框)都可以是顶级窗口,顶级窗口的父窗口为桌面窗口
,因此可以通过枚举桌面窗口的所有子窗口的方式来实现窗口枚举,例如下面的代码∶
void CHelloMFCDlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
//查桌面窗口的第一个子窗口
HWND hwnd = ::GetWindow(::GetDesktopWindow(),GW_CHILD);
TCHAR szTitle[MAX_PATH];
TCHAR szClassName[MAX_PATH];
while (hwnd!= NULL)
{
::GetWindowText(hwnd, szTitle,_countof(szTitle));
::GetClassName(hwnd, szClassName, _countof(szClassName));
//找到我们MFC对话框窗口的句柄
if (!_tcsicmp(szTitle, _T("OpenSaveFile")))
{
CString info;
info.Format(_T("szTitle: %s szClassName: %s"), szTitle, szClassName);
MessageBox(info);
}
//获取桌面窗口的下一个子窗口
hwnd = ::GetWindow(hwnd,GW_HWNDNEXT);
}
}
可以看到,成功弹出了消息,并输出了我们对话框窗口的标题的类名。
打开Spy++的窗口列表,可以看到该工具同时枚举了顶级窗口的子窗口,但是上面的代码只能枚举顶级窗口。
与FindWindow()
函数相同,FindWindowEx()
函数也用于查找具有指定窗口类名和窗口标题的窗口的窗口句柄,但是FindWindowEx()
函数只查找指定父窗口中的子窗口∶
HwND FindWindowEx
(
_ln_opt_ HWNDhWndParent,//父窗口句柄,设置为NULL表示函数使用桌面窗口作为父窗口
_ln_opt_ HWNDhWndChildAfter,//子窗口句柄,函数将从该子窗口以后开始搜索
_ln_opt_LPCTSTR lpszClass,//窗口类名
_ln_opt_LPCTSTR lpszWindow //窗口标题
);
hWndParent
参数指定父窗口句柄,设置为NULL表示函数使用桌面窗口作为父窗口。
hWndChildAfter
参数指定一个子窗口句柄,函数将从该子窗口以后开始查找,如果该参数设置为NULL,则表示从hwndParent
的第一个子窗口开始查找。
如果hwndParent和hwndChildAfter参数均指定为NULL,函数将查找所有顶级窗口。
lpszClass
参数指定窗口类名。
lpszWindow
参数指定窗口标题。
如果函数执行成功,则返回值是具有指定类和窗口名称的窗口句柄﹔如果函数执行失败,则返回值为NULL。
上面枚举顶级窗口的代码,在循环中处理完一个顶级窗口句柄以后,可以继续调用下面的代码枚举该窗口中的子窗口∶
void CHelloMFCDlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
//查桌面窗口的第一个子窗口
HWND hwnd = ::GetWindow(::GetDesktopWindow(),GW_CHILD);
TCHAR szTitle[MAX_PATH];
TCHAR szClassName[MAX_PATH];
while (hwnd!= NULL)
{
::GetWindowText(hwnd, szTitle,_countof(szTitle));
::GetClassName(hwnd, szClassName, _countof(szClassName));
//找到我们MFC对话框窗口的句柄
if (!_tcsicmp(szTitle, _T("OpenSaveFile")))
{
//注意,这里继续查MFC对话框窗口的子窗口
HWND hwndChild = NULL;
while (hwndChild = ::FindWindowEx(hwnd,hwndChild,NULL,NULL))
{
//获取其他子窗口的标题应该发送WM_GETTEXT消息,而不是GetWindowText
::SendMessage(hwndChild, WM_GETTEXT,_countof(szTitle),(LPARAM)szTitle);
::GetClassName(hwndChild,szClassName,_countof(szClassName));
CString info;
info.Format(_T("szTitle: %s szClassName: %s"), szTitle, szClassName);
MessageBox(info);
}
}
//获取桌面窗口的下一个子窗口
hwnd = ::GetWindow(hwnd,GW_HWNDNEXT);
}
}
要枚举窗口,还有一个更简单的函数EnumWindows。调用GetWindow函数时,可能会陷入死循环或引用已被销毁的窗口句柄,所以EnumWindows更可靠一些。
EnumWindows函数声明如下∶
BOOL EnumWindows(
_In_ WNDENUMPROC lpEnumFunc,//回调函数
_ln__LPARAM lParam //传递给回调函数的参数);
回调函数的定义格式如下∶
BOOL CALLBACK EnumWindowsProc(
_ln_ HWND hwnd,//顶级窗口窗口句柄
_ln__ LPARAM lParam //传递过来的参数);
回调函数可以根据窗口句柄参数hwnd获取有关窗口的各种信息,处理完后应该返回TRUE表示继续枚举,如果想停止枚举可以返回FALSE。
EnumWindows()函数只能枚举顶级窗口,要枚举顶级窗口中的子窗口可以使用EnumChildWindows()函数∶
BOOL EnumChildWindows(
_ln_opt_ HWND hWndParent,//父窗口句柄,设置为NULL则该函数等效于EnumWindows
_ln_ WNDENUMPROC IpEnumFunc,//回调函数,定义格式和用法与EnumWindows函数的回调函数相同
_ln_ LPARAMlParam //传递给回调函数的参数);
至于,GetForegroundWindow()
检索前台窗口(用户当前正在使用的窗口)的句柄。系统为创建前台窗口的线程分配的优先级略高于为其他线程分配的优先级。总之就是拿到当前用户正在操作使用的窗口。
这里提供一个简单的案例实现,做到基于某个情况下(这里是点击按钮的情况),比如说,点击按钮可以做到把所有的button
控件隐藏掉。
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 HelloMFC.rc 使用
//
#define IDM_ABOUTBOX 0x0010
#define IDS_ABOUTBOX 101
#define IDD_HELLOMFC_DIALOG 102
#define IDD_MAIN 102
#define IDR_MAINFRAME 128
#define IDR_MENU 131
#define IDC_BUTTON1 1005
#define IDC_BUTTON2 1006
#define IDC_BUTTON3 1007
#define IDC_BUTTON4 1008
#define IDC_EDIT1 1009
#define IDC_BUTTON6 1010
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 132
#define _APS_NEXT_COMMAND_VALUE 32775
#define _APS_NEXT_CONTROL_VALUE 1012
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
#pragma once
#include <winnt.h>
class CHelloMFCDlg : public CDialogEx
{
public:
CHelloMFCDlg(CWnd* pParent = nullptr);
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_HELLOMFC_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
HICON m_hIcon;
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
private:
public:
afx_msg void OnBnClickedButton6();
};
// HelloMFCDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "HelloMFC.h"
#include "HelloMFCDlg.h"
#include "afxdialogex.h"
#include <strsafe.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CFindReplaceDialog* g_findReplaceDialog = NULL;
TCHAR g_szFindWhat[80] = { 0 };
TCHAR g_szReplaceWith[80] = { 0 };
BEGIN_MESSAGE_MAP(CHelloMFCDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON6, &CHelloMFCDlg::OnBnClickedButton6)
END_MESSAGE_MAP()
CHelloMFCDlg::CHelloMFCDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_HELLOMFC_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CHelloMFCDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BOOL CHelloMFCDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
::SetDlgItemText(m_hWnd,IDC_BUTTON6,_T("一键隐藏控件"));
return TRUE;
}
void CHelloMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
CDialogEx::OnSysCommand(nID, lParam);
}
void CHelloMFCDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CHelloMFCDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//枚举窗口子进程 回调函数
BOOL CALLBACK EnumChildProc(_In_ HWND hwnd,_In_ LPARAM lParam);
void CHelloMFCDlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
HWND hwnd = ::GetForegroundWindow();
CString info;
info.Format(_T("最顶端的窗口的句柄:%d 当前窗口的句柄:%d"), hwnd, this->m_hWnd);
MessageBox(info);
EnumChildWindows(hwnd, EnumChildProc,NULL);
}
BOOL CALLBACK EnumChildProc(_In_ HWND hwnd, _In_ LPARAM lParam)
{
TCHAR szBuf[MAX_PATH] = { 0 };
//查控件名称
int resourceId = ::GetDlgCtrlID(hwnd);
//查窗口标题
::GetWindowText(hwnd, szBuf,_countof(szBuf));
//输出
CString info;
info.Format(_T("控件名称:%s 控件资源ID:%d"), szBuf, resourceId);
::AfxMessageBox(info);
//#define IDC_BUTTON1 1005
//#define IDC_BUTTON2 1006
//#define IDC_BUTTON3 1007
//#define IDC_BUTTON4 1008
//根据控件ID做过滤,不建议用控件名称,比如说输入框控件,控件名称是用户输入的文本值,这样会给匹配带来很大问题
if (resourceId == 1005 || resourceId == 1006 || resourceId == 1007 || resourceId == 1008)
{
//发一个消息让控件(窗口子类) 隐藏
::ShowWindow(hwnd, SW_HIDE);
}
return TRUE;
}