所用函数:SetWindowDisplayAffinity,通过获取窗口句柄,设置第二个参数,如果第二个参数为WDA_MONITOR表示将开关打开,当前进程的窗体就会变黑。程序结束时,记得将参数恢复为WDA_NONE。
注意:
- 只对当前进程有效
- 有系统限制:
核心代码(MFC程序:有个按钮):
void CMFCApplication2Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
HWND hwnd = ::FindWindow(NULL, L"MFC");
if (hwnd != NULL)
{
CString tempstr;
GetDlgItem(IDC_BUTTON1)->GetWindowText(tempstr);
BOOL temp = FALSE;
if (tempstr == "打开") {
temp=SetWindowDisplayAffinity(hwnd, WDA_MONITOR);
GetDlgItem(IDC_BUTTON1)->SetWindowText(L"关闭");
}
else {
temp=SetWindowDisplayAffinity(hwnd, WDA_NONE);
GetDlgItem(IDC_BUTTON1)->SetWindowText(L"打开");
}
if(!temp) {
char szError[256];
DWORD dwError = GetLastError();
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
0,
(LPWSTR)szError,
sizeof(szError),
NULL);
//AfxMessageBox(szError);
::MessageBox(0, (LPCTSTR)szError, NULL, NULL);
}
}
}
实现效果:
将开关打开后:
还可以设置透明度,具体可以搜索函数用法。
因为只对当前进程有用,所以想对某个进程使用此函数,可以用dll注入的方式来实现。
64位和32位的都需要生成一下。
最好在虚拟机里面试验,我实验了一下,确实成功了,但是很卡
dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <windows.h>
#include <vector>
std::vector<HWND>ProtectedWindowsHwnd;
HHOOK h_msghook = NULL;
HHOOK h_callhook = NULL;
HINSTANCE ghInstance = NULL;
VOID SetWindowsDisplayStart()
{
//遍历可见窗口得到窗口句柄
HWND hwnd = NULL;
DWORD dwpid = 0;
hwnd = ::GetWindow(::GetDesktopWindow(), GW_CHILD);
if (NULL != hwnd)
{
hwnd = ::GetWindow(hwnd, GW_HWNDLAST);
while (hwnd)
{
if (::GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)//窗口必须可见
{
::GetWindowThreadProcessId(hwnd, &dwpid);
SetWindowDisplayAffinity(hwnd, WDA_MONITOR);
//没有存在过就加进去
if (std::find(ProtectedWindowsHwnd.begin(), ProtectedWindowsHwnd.end(), hwnd) == ProtectedWindowsHwnd.end())
ProtectedWindowsHwnd.push_back(hwnd);
}
hwnd = ::GetWindow(hwnd, GW_HWNDPREV);
}
}
}
VOID SetWindowsDisplayEnd()
{
std::vector<HWND>::iterator it;
for (it = ProtectedWindowsHwnd.begin(); it != ProtectedWindowsHwnd.end(); it++)
if (NULL != *it)
SetWindowDisplayAffinity(*it, WDA_NONE);
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
ghInstance = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
SetWindowsDisplayEnd();
//主动广播消息,用来触发消息钩子执行卸载操作
PostMessage(HWND_BROADCAST, WM_NULL, 0, 0);
break;
}
return TRUE;
}
LRESULT CALLBACK GetMsgProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
SetWindowsDisplayStart();
return CallNextHookEx(h_msghook, code, wParam, lParam);//继续传递消息
}
LRESULT CALLBACK CallWndProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
SetWindowsDisplayStart();
return CallNextHookEx(h_callhook, code, wParam, lParam);//继续传递消息
}
extern "C"
{
__declspec(dllexport) void HookStart()
{
h_msghook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, ghInstance, 0);//WH_GETMESSAGE Hook只拦截由GetMessage or PostMessage PeekMessage的队列消息。
h_callhook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, ghInstance, 0);//专门用来截获通过SendMessage()函数发送到窗口的消息
//同步:SendMessage函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。
//异步:而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回。
ProtectedWindowsHwnd.clear();
}
__declspec(dllexport) void HookStop()
{
if (h_msghook)
{
UnhookWindowsHookEx(h_msghook);
h_msghook = NULL;
}
if (h_callhook)
{
UnhookWindowsHookEx(h_callhook);
h_callhook = NULL;
}
ProtectedWindowsHwnd.clear();
}
}
需要加载器去加载:
// hookdlltext64.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
typedef void(*PFNHOOKSTART)();
typedef void(*PFNHOOKSTOP)();
int main()
{
std::cout << "输入回车开始" << std::endl;
getchar();
#ifdef _AMD64_
HMODULE hmod = ::LoadLibrary(L"C:\\Protect\\win64\\SetWindowDisplayAffityTest.dll");
#else
HMODULE hmod = ::LoadLibrary(L"C:\\Protect\\win32\\SetWindowDisplayAffityTest.dll");
#endif
if (NULL != hmod)
{
PFNHOOKSTART qHookStart = (PFNHOOKSTART)GetProcAddress(hmod, "HookStart");
PFNHOOKSTOP qHookStop = (PFNHOOKSTOP)GetProcAddress(hmod, "HookStop");
qHookStart();
std::cout << "输入回车结束" << std::endl;
getchar();
qHookStop();
FreeLibrary(hmod);
}
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
以上如果有误,欢迎指正。