本技术文档所用到的实例点击这里下载( 提取码:6cth),内含x86\x64不同处理器支持的版本。如果打开失败,使用另外一个版本:
记得网课期间使用的会议软件吗?课堂锁定\会议锁定 功能真的是。。。
要想实现这种功能其实并不难,有人会说使用ShowWindow再用SetFocus就可以了,其实不然,这样子做有些时候窗口还是会被覆盖。
如果用SetForegroundWindow函数将创建指定窗口的线程设置到前台,并且激活该窗口,也不是100%好用。
最后,也是几年前我发现User32有个函数SwitchToThisWindow可以实现选择到当前窗口,但前提是指定的窗口必须是Visable的。
SwitchToThisWindow函数原型:
WINAPI SwitchToThisWindow(
HWND dwHwnds, // 要切换到前列的窗口
BOOL bEnable // TRUE为切换
)
这个函数需要通过以下方法获得:
typedef void (WINAPI* PSWITCHTOTHISWINDOW) (HWND, BOOL);
PSWITCHTOTHISWINDOW SwitchToThisWindow;
HMODULE hUser32 = GetModuleHandle(TEXT("user32"));
SwitchToThisWindow = (PSWITCHTOTHISWINDOW)GetProcAddress(hUser32, "SwitchToThisWindow");
然后,这样写即可让想放在前台的窗口切到前台,而不改变后面的窗口顺序。
ShowWindow(hwnd, SW_SHOW);//或者SW_NORMAL
SwitchToThisWindow(hwnd, TRUE);// 切前台窗口
然而,要是我们的窗口能够锁定,即要知道什么窗口正处于最上方,判断这个窗口是不是我们要的窗口。
这就用到GetForegroundWindow()找到前台窗口句柄然后和我们的窗口句柄进行比较,不同就Switch,当然你也可以把前台窗口最小化,甚至可以来个弹窗警告。
一个简单的判断如下,写在线程中即可:
HWND fwnd = GetForgroundWindow();
if (fwnd != hwnd)
{
...
// 窗口没有处于前台,通过操作使hwnd成为前台,
// Do What You Want!
}
std::cout << "窗口已经锁定" << endl;//在线程中不要使用printf,否则提示会频繁显示
当然,对于特殊的窗口我们要注意:比如桌面、任务栏等不能最小化,警告框等不能错误识别为其他窗口,否则警告会一直弹出。
带警告模式:
然后就是指定的窗口菜单栏标题栏按钮的改写,可以使得窗口不能关闭或者最小化:
HMENU hmenu = GetSystemMenu(mhwnd, false);// 取菜单窗口句柄
RemoveMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);// 移除菜单项标识符,关闭按钮、最小化按钮
LONG style = GetWindowLong(mhwnd, GWL_STYLE);// 获取窗口风格
style &= ~(WS_MINIMIZEBOX);// 指定的风格为最小化按钮菜单
SetWindowLong(mhwnd, GWL_STYLE, style);// 重设窗口风格
SetWindowPos(mhwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
// 改变窗口的位置与状态,HWND_TOPMOST使窗口置顶
//SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(mhwnd, SW_SHOWNORMAL);// 窗口以正常大小显示
DestroyMenu(hmenu);// 该函数销毁命令行(hmenu)的菜单,并释放此菜单占用的存储器。
ReleaseDC(mhwnd, NULL);// 刷新DC数据,更新窗口
当然不建议在SetWindowPos中指定HWND_TOPMOST。
按照以上思路,尝试运行效果如下:
然而,这种消息窗口会被遮挡,解决方法就是发出消息框时候第一个参数设置为父窗口,也可以配合SetParent设置置顶窗口为子窗口。
然后,就是处理全屏效果:
/*以下函数用于计算具体的全屏大小,但不包括任务栏
* 这是仅当任务栏位于下方时有效。
调节高度lHeight,调节宽度pWidth
*/
HWND hProgMan, hTrayWnd, hReBarWnd;
int pWidth, pHeight, ctHeight, lHeight;
RECT rect;
// 取桌面窗口句柄,可以用GetDesktopWindow代替
hProgMan = FindWindow(L"ProgMan", NULL);
// 获取SHELL任务窗格(总窗口)
hTrayWnd = FindWindow(L"Shell_TrayWnd", NULL);
// 获取ReBarWindow32窗口句柄
/*HWND WINAPI FindWindowExA(
_In_opt_ HWND hWndParent,
_In_opt_ HWND hWndChildAfter,
_In_opt_ LPCSTR lpszClass,
_In_opt_ LPCSTR lpszWindow);
*/
hReBarWnd = FindWindowEx(hTrayWnd, NULL, L"ReBarWindow32", NULL);
// 获取屏幕宽高参数
pWidth = GetSystemMetrics(SM_CXSCREEN);
pHeight = GetSystemMetrics(SM_CYSCREEN);
// 获取ReBarWindow32窗口的客户区矩形
GetClientRect(hReBarWnd, &rect);
// 计算矩形高度,win10 默认任务栏客户区高度为40,小任务栏客户区高度为30
ctHeight = (rect.bottom - rect.top);
// 获得最终的高度参数
lHeight = (pHeight - ctHeight);
// 立即更新窗口大小
SetWindowPos(ConsolehWnd, nullptr, 0, 0, pWidth, lHeight, SWP_FRAMECHANGED);
::SetWindowPos(ConsolehWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // 置顶窗口
然后,就是窗口的处理部分,要排除消息框:
HWND frhwnd;
ULONG nProcessID;
ULONG mProcessID;
ConsolehWnd = FindWindow(NULL, L"EventCapturer");
// HWND manhWnd = FindWindow(NULL, _T("EventCapturer"));
msghWnd = FindWindow(NULL, L"EventCapturer-Event");
frhwnd = GetForegroundWindow();// 使frhwnd代表最前端的窗口
//id
::GetWindowThreadProcessId(frhwnd, &nProcessID);
::GetWindowThreadProcessId(ConsolehWnd, &mProcessID);
// ShowWindow(msghWnd, SW_HIDE);
if (nProcessID == mProcessID)
{
system("cls");
std::cout << "前台窗口为EventCapturer窗口" << endl;
SendMessage(msghWnd, 16, 0, 0);//关闭冗余消息窗口
MsgIDCall = 0;//消息值状态清零
Sleep(1000);
}else if (frhwnd == msghWnd)
{
system("cls");
std::cout << "请回复消息窗口" << endl;
SetParent(msghWnd, ConsolehWnd);
SwitchToThisWindow(msghWnd, TRUE);
Sleep(2000);
}
else
{
system("cls");
std::cout << "请回复消息窗口" << endl;
// ShowWindow(msghWnd, SW_HIDE);
ShowWindow(ConsolehWnd, SW_SHOW);
SwitchToThisWindow(ConsolehWnd, TRUE);
Sleep(100);
MsgIDCall = MessageBoxA(ConsolehWnd,
"禁止切换其他窗口!",
"EventCapturer-Event",
MB_OK | MB_ICONEXCLAMATION);
if (MsgIDCall == IDOK)
{
// 为避免活动窗口为桌面时,ShowWindow错误判断
// 导致程序窗口频繁重绘,使用User32的函数
// SwitchToThisWindow重置窗口
SwitchToThisWindow(ConsolehWnd, TRUE);
/*以下函数用于计算具体的全屏大小,但不包括任务栏
* 这是仅当任务栏位于下方时有效。
调节高度lHeight,调节宽度pWidth
*/
HWND hProgMan, hTrayWnd, hReBarWnd;
int pWidth, pHeight, ctHeight, lHeight;
RECT rect;
// 取桌面窗口句柄,可以用GetDesktopWindow代替
hProgMan = FindWindow(L"ProgMan", NULL);
// 获取SHELL任务窗格(总窗口)
hTrayWnd = FindWindow(L"Shell_TrayWnd", NULL);
// 获取ReBarWindow32窗口句柄
/*HWND WINAPI FindWindowExA(
_In_opt_ HWND hWndParent,
_In_opt_ HWND hWndChildAfter,
_In_opt_ LPCSTR lpszClass,
_In_opt_ LPCSTR lpszWindow);
*/
hReBarWnd = FindWindowEx(hTrayWnd, NULL, L"ReBarWindow32", NULL);
// 获取屏幕宽高参数
pWidth = GetSystemMetrics(SM_CXSCREEN);
pHeight = GetSystemMetrics(SM_CYSCREEN);
// 获取ReBarWindow32窗口的客户区矩形
GetClientRect(hReBarWnd, &rect);
// 计算矩形高度,win10 默认任务栏客户区高度为40,小任务栏客户区高度为30
ctHeight = (rect.bottom - rect.top);
// 获得最终的高度参数
lHeight = (pHeight - ctHeight);
// 立即更新窗口大小
SetWindowPos(ConsolehWnd, nullptr, 0, 0, pWidth, lHeight, SWP_FRAMECHANGED);
::SetWindowPos(ConsolehWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // 置顶窗口
return 0;//结束后退出,不执行下文
}else
{
//ShowWindow(msghWnd, SW_HIDE);
//SendMessage(msghWnd, 16, 0, 0);//关闭窗口
Sleep(5000);
}
return 0;//结束后退出
}
最后完整代码:
1.EventHandle.h 头文件:
// EventHandle.h 头文件,保留备用
#pragma once
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>
#include <winbase.h>
#include <tlhelp32.h>
#include <pthread.h>
bool runing = true;
HANDLE m_hThreadHandle = NULL;
// 创建命名空间
using namespace std;
using std::endl;
HWND ConsolehWnd = nullptr;
HWND msghWnd = nullptr;
int MsgIDCall = 0;
unsigned __stdcall ThreadFunction(LPVOID pThreadData)
{
while (runing)
{
std::cout << "@OnTime_Running" << endl;
Sleep(1000);
}
return 0;
}
unsigned __stdcall CheckThreadFunction(LPVOID pThreadData)
{
while (true)
{
Sleep(1000);
DWORD dw = WaitForSingleObject(m_hThreadHandle, 10);
if (dw == WAIT_TIMEOUT)
{
std::cout << "@TimeOut_ExitWait : " << dw << endl;
Sleep(500);
}
else if (dw == WAIT_OBJECT_0)
{
std::cout << "@ObjectEvent : " << dw << endl;
Sleep(500);
}
else if (WAIT_FAILED == dw)
{
std::cout << "@Failed : " << dw << endl;
Sleep(500);
}
else
{
std::cout << "ThreadOtherEvent:" << dw << endl;
Sleep(500);
}
}
return 0;
}
void* BeginThreadEX(void* args)
{
#include <process.h>
m_hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ThreadFunction, NULL, 0, NULL);//
HANDLE m_hThreadHandlecc = NULL;
m_hThreadHandlecc = (HANDLE)_beginthreadex(NULL, 0, CheckThreadFunction, NULL, 0, NULL);//
Sleep(5000);
runing = false;
Sleep(5000);
return 0;
};
void* EventCapturerCMsgr(void* args)
{
std::cout << "Msgr" << endl;
HWND frhwnd;
ULONG nProcessID;
ULONG mProcessID;
ConsolehWnd = FindWindow(NULL, L"EventCapturer");
// HWND manhWnd = FindWindow(NULL, _T("EventCapturer"));
msghWnd = FindWindow(NULL, L"EventCapturer-Event");
frhwnd = GetForegroundWindow();// 使frhwnd代表最前端的窗口
//id
::GetWindowThreadProcessId(frhwnd, &nProcessID);
::GetWindowThreadProcessId(ConsolehWnd, &mProcessID);
// ShowWindow(msghWnd, SW_HIDE);
if (nProcessID == mProcessID)
{
system("cls");
std::cout << "前台窗口为EventCapturer窗口" << endl;
SendMessage(msghWnd, 16, 0, 0);//关闭冗余消息窗口
MsgIDCall = 0;//消息值状态清零
Sleep(1000);
}else if (frhwnd == msghWnd)
{
system("cls");
std::cout << "请回复消息窗口" << endl;
SetParent(msghWnd, ConsolehWnd);
SwitchToThisWindow(msghWnd, TRUE);
Sleep(2000);
}
else
{
system("cls");
std::cout << "请回复消息窗口" << endl;
// ShowWindow(msghWnd, SW_HIDE);
ShowWindow(ConsolehWnd, SW_SHOW);
SwitchToThisWindow(ConsolehWnd, TRUE);
Sleep(100);
MsgIDCall = MessageBoxA(ConsolehWnd,
"禁止切换其他窗口!",
"EventCapturer-Event",
MB_OK | MB_ICONEXCLAMATION);
if (MsgIDCall == IDOK)
{
// 为避免活动窗口为桌面时,ShowWindow错误判断
// 导致程序窗口频繁重绘,使用User32的函数
// SwitchToThisWindow重置窗口
SwitchToThisWindow(ConsolehWnd, TRUE);
/*以下函数用于计算具体的全屏大小,但不包括任务栏
* 这是仅当任务栏位于下方时有效。
调节高度lHeight,调节宽度pWidth
*/
HWND hProgMan, hTrayWnd, hReBarWnd;
int pWidth, pHeight, ctHeight, lHeight;
RECT rect;
// 取桌面窗口句柄,可以用GetDesktopWindow代替
hProgMan = FindWindow(L"ProgMan", NULL);
// 获取SHELL任务窗格(总窗口)
hTrayWnd = FindWindow(L"Shell_TrayWnd", NULL);
// 获取ReBarWindow32窗口句柄
/*HWND WINAPI FindWindowExA(
_In_opt_ HWND hWndParent,
_In_opt_ HWND hWndChildAfter,
_In_opt_ LPCSTR lpszClass,
_In_opt_ LPCSTR lpszWindow);
*/
hReBarWnd = FindWindowEx(hTrayWnd, NULL, L"ReBarWindow32", NULL);
// 获取屏幕宽高参数
pWidth = GetSystemMetrics(SM_CXSCREEN);
pHeight = GetSystemMetrics(SM_CYSCREEN);
// 获取ReBarWindow32窗口的客户区矩形
GetClientRect(hReBarWnd, &rect);
// 计算矩形高度,win10 默认任务栏客户区高度为40,小任务栏客户区高度为30
ctHeight = (rect.bottom - rect.top);
// 获得最终的高度参数
lHeight = (pHeight - ctHeight);
// 立即更新窗口大小
SetWindowPos(ConsolehWnd, nullptr, 0, 0, pWidth, lHeight, SWP_FRAMECHANGED);
::SetWindowPos(ConsolehWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // 置顶窗口
return 0;//结束后退出,不执行下文
}else
{
//ShowWindow(msghWnd, SW_HIDE);
//SendMessage(msghWnd, 16, 0, 0);//关闭窗口
Sleep(5000);
}
return 0;//结束后退出
}
return 0;//退出
};
2.EventCapturer.cpp :
// EventCapturer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <winbase.h>
#include <tlhelp32.h>
#include <pthread.h>
#include "EventHandle.h"
#pragma comment(lib, "pthreadVCE2.lib")
#define NUM_THREADS_1 1
#define NUM_THREADS_2 1
using namespace std;
using std::endl;
HWND mhwnd = nullptr;
int main()
{
SetConsoleTitle(_T("EventCapturer"));
Sleep(1000);
mhwnd = FindWindow(NULL, L"EventCapturer");
HMENU hmenu = GetSystemMenu(mhwnd, false);// 取菜单窗口句柄
RemoveMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);// 移除菜单项标识符,关闭按钮、最小化按钮
LONG style = GetWindowLong(mhwnd, GWL_STYLE);// 获取窗口风格
//style &= ~(WS_MINIMIZEBOX);// 指定的风格为最小化按钮菜单
SetWindowLong(mhwnd, GWL_STYLE, style);// 重设窗口风格
SetWindowPos(mhwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);// 改变窗口的位置与状态,HWND_TOPMOST使窗口置顶
//SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(mhwnd, SW_SHOWNORMAL);// 窗口以正常大小显示
DestroyMenu(hmenu);// 该函数销毁命令行(hmenu)的菜单,并释放此菜单占用的存储器。
::ReleaseDC(mhwnd, NULL);// 刷新DC数据,更新窗口
/*以下函数用于计算具体的全屏大小,但不包括任务栏
* 这是仅当任务栏位于下方时有效。
调节高度lHeight,调节宽度pWidth
*/
HWND hProgMan, hTrayWnd, hReBarWnd;
int pWidth, pHeight, ctHeight, lHeight;
RECT rect;
// 取桌面窗口句柄,可以用GetDesktopWindow代替
hProgMan = FindWindow(L"ProgMan", NULL);
// 获取SHELL任务窗格(总窗口)
hTrayWnd = FindWindow(L"Shell_TrayWnd", NULL);
// 获取ReBarWindow32窗口句柄
/*HWND WINAPI FindWindowExA(
_In_opt_ HWND hWndParent,
_In_opt_ HWND hWndChildAfter,
_In_opt_ LPCSTR lpszClass,
_In_opt_ LPCSTR lpszWindow);
*/
hReBarWnd = FindWindowEx(hTrayWnd, NULL, L"ReBarWindow32", NULL);
// 获取屏幕宽高参数
pWidth = GetSystemMetrics(SM_CXSCREEN);
pHeight = GetSystemMetrics(SM_CYSCREEN);
// 获取ReBarWindow32窗口的客户区矩形
GetClientRect(hReBarWnd, &rect);
// 计算矩形高度,win10 默认任务栏客户区高度为40,小任务栏客户区高度为30
ctHeight = (rect.bottom - rect.top);
// 获得最终的高度参数
lHeight = (pHeight - ctHeight);
// 立即更新窗口大小
SetWindowPos(mhwnd, nullptr, 0, 0, pWidth, lHeight, SWP_FRAMECHANGED);
::SetWindowPos(mhwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // 置顶窗口
typedef void (WINAPI* PSWITCHTOTHISWINDOW) (HWND, BOOL);
PSWITCHTOTHISWINDOW SwitchToThisWindow;
HMODULE hUser32 = GetModuleHandle(_T("user32"));
SwitchToThisWindow = (PSWITCHTOTHISWINDOW)GetProcAddress(hUser32, "SwitchToThisWindow");
bool bEnable = true;
while (bEnable) {
//line2:
if (GetForegroundWindow() == mhwnd)
{
system("cls");
std::cout << "当前状态∶窗口已置顶" << endl;
bEnable = true;
}else
{
HWND msghWnd = FindWindow(NULL, _T("EventCapturer-Event"));
HWND frhwnd;
frhwnd = GetForegroundWindow();// 使frhwnd代表最前端的窗口
if (frhwnd == mhwnd)
{
system("cls");
std::cout << "当前状态∶用户返回了主窗口" << endl;
// 使用User32的函数SwitchToThisWindow重置窗口
SwitchToThisWindow(mhwnd, TRUE);
SetWindowPos(mhwnd, nullptr, 0, 0, pWidth, lHeight, SWP_FRAMECHANGED);
::SetWindowPos(mhwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // 置顶窗口
Sleep(2000);
}else
{
ShowWindow(frhwnd, SW_MINIMIZE);
ShowWindow(mhwnd, SW_SHOW);
Sleep(100);
// 定义线程的 id 变量,
pthread_t tids1[NUM_THREADS_1];
int ret1 = pthread_create(tids1, NULL, BeginThreadEX, NULL);// 线程
if (ret1 != 0)
{
// cout << "\n" << endl;
cout << "PTHREAD_CREATE ERROR: Position: BeginThreadEX; \nError_Code=" << ret1 << endl;
Sleep(2000);
}
}
pthread_t tids2[NUM_THREADS_2];
int ret2 = pthread_create(tids2, NULL, EventCapturerCMsgr, NULL);// 线程
if (ret2 != 0)
{
cout << "PTHREAD_CREATE ERROR: Position: EventCapturerCMsgr; \nError_Code=" << ret2 << endl;
Sleep(2000);
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
// pthread_exit(NULL);
bEnable = true;
}
// 为避免活动窗口为桌面时,ShowWindow错误判断导致程序窗口频繁重绘,使用User32的函数SwitchToThisWindow重置窗口
//SwitchToThisWindow(mhwnd, TRUE);
Sleep(1000);
//goto line2;
bEnable = true;
continue;
};
pthread_exit(NULL);
Sleep(2000);
}
要知道是否置顶,必须对窗口大小,窗口焦点进行监视。将消息框的处理作为子窗口,并写入了冗余消息提示的关闭处理,从而解决了之前存在的问题,切换其他窗口后窗口恢复不在延迟。
并且可以使用小任务栏存放子窗口(额,我承认这一点是为了完全模拟无限宝课后网,因为他的处理也差不多)
运行效果:
好,就到这里。