Windows核心编程_窗口蒙版效果

首先先看一下界面效果:

遮罩窗口:

模糊模态窗口:

保留特定控件

模糊保留特定控件:

遮罩+模糊效果:

扫描二维码关注公众号,回复: 2215926 查看本文章

遮罩模态效果:

怎么样是不是很炫酷,非常适合在提示用户新信息或者其它需要模态窗口时候展现的效果,那么接下来,博主就一步一步教大家如何实现这个效果!

  1. 创建win32工程
 #include "stdafx.h"
#include <windows.h>

HWND M_hWnd;	//progman

//消息函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	//判断消息ID
	switch (uMsg){
	
	case WM_DESTROY:    // 窗口销毁消息
		PostQuitMessage(0);   //  发送退出消息
		return 0;
	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}
// 3、注册窗口类型
BOOL RegisterWindow(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProc;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}
//创建窗口(lpClassName 一定是已经注册过的窗口类型)
HWND CreateMyWindow(LPCTSTR lpClassName, HINSTANCE hInstance)
{
	HWND hWnd = NULL;
	// 创建窗口
	hWnd = CreateWindow(lpClassName, "test", WS_OVERLAPPEDWINDOW^WS_THICKFRAME, 0, 0, 1000, 800, NULL, NULL, hInstance, NULL);
	return hWnd;
}
//显示窗口
void DisplayMyWnd(HWND hWnd)
{
		//移动窗口到指定的位置
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}

void doMessage()        // 消息循环处理函数
{
	MSG msg = { 0 };
	// 获取消息
	while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
	{
		DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
	}
}
LPCTSTR ClassName = "MyWnd";  // 注册窗口的名称
// 入口函数
int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nShowCmd)
{
	LPCTSTR lpClassName = "MyWnd";  // 注册窗口的名称
	RegisterWindow(lpClassName, hInstance);
	//获得屏幕尺寸,居中显示
	int scrWidth, scrHeight;
	scrWidth = GetSystemMetrics(SM_CXSCREEN);
	scrHeight = GetSystemMetrics(SM_CYSCREEN);
	M_hWnd = CreateWindow(ClassName, "窗口蒙板效果", WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX, (scrWidth - 700) / 2, (scrHeight - 500) / 2, 700, 500, NULL, NULL, hInstance, NULL);
	//屏幕宽度-窗口宽度在除去2即为窗口横中心坐标,减去窗口宽度是为了将窗口大小排除在外,否则获取中心坐标时会不对称,因为Windows在绘制时以点坐标为起始坐标,以中心点为起始坐标的话窗口会绘制到右下方
	DisplayMyWnd(M_hWnd);
	doMessage();
	return 0;
}

窗口创建代码已经完成,这里我们首先创建八个按钮,用来执行不同的效果:

先定义按钮ID和HWND句柄:

#define ID_BTO1 0x001
#define ID_BTO2 0x002
#define ID_BTO3 0x003
#define ID_BTO4 0x004
#define ID_BTO5 0x005
#define ID_BTO6 0x006
#define ID_BTO7 0x007
#define ID_BTO8 0x008
HWND hWnd1;
HWND hWnd2;
HWND hWnd3;
HWND hWnd4;
HWND hWnd5;
HWND hWnd6;
HWND hWnd7;
HWND hWnd8;

在WinMain函数里创建我们的按钮:

//创建按钮

hWnd1 = CreateWindow("Button", "遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 100, 230, 30, M_hWnd, (HMENU)ID_BTO1, NULL, NULL);
	hWnd2 = CreateWindow("Button", "销毁遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 200, 230, 30, M_hWnd, (HMENU)ID_BTO2, NULL, NULL);
	hWnd3 = CreateWindow("Button", "模态遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 100, 230, 30, M_hWnd, (HMENU)ID_BTO3, NULL, NULL);
	hWnd4 = CreateWindow("Button", "保留特定控件", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 200, 230, 30, M_hWnd, (HMENU)ID_BTO4, NULL, NULL);
	hWnd5 = CreateWindow("Button", "模糊窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 300, 230, 30, M_hWnd, (HMENU)ID_BTO5, NULL, NULL);
	hWnd6 = CreateWindow("Button", "模糊窗口保留特定控件", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 300, 230, 30, M_hWnd, (HMENU)ID_BTO6, NULL, NULL);
	hWnd7 = CreateWindow("Button", "模糊窗口遮罩效果", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 400, 230, 30, M_hWnd, (HMENU)ID_BTO7, NULL, NULL);
	hWnd8 = CreateWindow("Button", "模糊窗口模态效果", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 400, 230, 30, M_hWnd, (HMENU)ID_BTO8, NULL, NULL);

运行效果:

遮罩窗口实现思路:创建一个具有透明属性的窗口,然后使其透明化,并且将坐标挪移到要显示遮罩窗口的地方!

首先在定义一个窗口类,用于表示遮罩窗口,注意这里不能使用父类的窗口类,这样的话就要和父类共享消息,而且无法不具有移植性和打包成动态库的性质,所以这里我们要处理自己的消息就是注册窗口类和自己的消息处理函数!

LPCTSTR lpClassName = "MASKWnd";  // 注册窗口的名称遮罩

在声明一个变量用于表示遮罩窗口句柄

HWND maskhwnd;	//遮罩窗口句柄
//消息函数遮罩窗口
LRESULT CALLBACK WindowProcMASK(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	//判断消息ID
	switch (uMsg){
	
	
	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

这里在编写一个注册函数和创建窗口的函数:

BOOL RegisterWindow_MASK(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProcMASK;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}

创建窗口,创建一个具有透明化的窗口:

HWND CreateMaskWindow(HWND hWndHENDL/*父窗口*/,LPCTSTR lpClassName/*类名*/, HINSTANCE hInstance/*资源实列句柄*/,BOOL MT/*是否模态*/)
{	
	HWND hWnd = NULL;
	//风格设置
	LONG dsyte;
	if (MT == TRUE){
		dsyte = WS_EX_LAYERED;
	}
	else
	{//WS_EX_TRANSPARENT鼠标穿透
		dsyte = WS_EX_LAYERED | WS_EX_TRANSPARENT;
	}
	//创建具有扩展风格的窗口
	hWnd = CreateWindowEx(dsyte,lpClassName, NULL, WS_POPUP, 0, 0, 0, 0, hWndHENDL, NULL, hInstance, NULL);
	if (MT == TRUE){
		EnableWindow(hWnd, FALSE);	//将窗口设置成不可用状态
	}
	//透明化
	SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 155, LWA_ALPHA);
	return hWnd;
}

具有透明化后,我们要让窗口背景改成黑色的!

我们编写一个函数负责让绘制窗口背景

//绘制背景
void SetbkColoR(){
	//绘制矩形填充窗口背景
	HDC hdc;
	hdc = GetDC(maskhwnd);
	HBRUSH hb;
	hb = CreateSolidBrush(RGB(0, 0, 0));//黑色画刷
	FillRect(hdc, &rect, hb);
	DeleteDC(hdc);
	DeleteObject(hb);
	UpdateWindow(maskhwnd);
}

除了绘制背景,我们还需要让遮罩窗口与父窗口坐标大小一致,所以需要一个移动遮罩窗口的函数:

//定义两个坐标结构体,用于表示屏幕坐标和客户区坐标:

RECT rect;	//客户区坐标
POINT PO;	//屏幕坐标
void MOVE_win(HWND hWnd){
	//获取窗口的客户区坐标
	GetClientRect(hWnd, &rect);	//对于客户区左上角为0
	PO.x = rect.left;		//0
	PO.y = rect.top;		//0
	//屏幕坐标转换,转换前需要给PO结构体赋予客户区坐标,也就是给PO结构体赋予初始值Windows才能转换
	ClientToScreen(hWnd, &PO);
	//移动窗口
	if (maskhwnd!=NULL){
		MoveWindow(maskhwnd, PO.x, PO.y, rect.right - rect.left, rect.bottom - rect.top, TRUE);
	}
}

我们还需要一个创建遮罩窗口的函数,方便调用:

在此之前需要一个子类化父类的消息:

子类化消息:

long OldWindowProc;	//保存旧的属性
//新的子类化消息
LRESULT NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

	if (message == WM_MOVE)//随父窗口移动

	{
		MOVE_win(hWnd);

	}
	if (message == WM_SIZE)//size

	{
		MOVE_win(hWnd);

	}
	return CallWindowProc((WNDPROC)OldWindowProc, hWnd, message, wParam, lParam);//交给旧的消息处理函数
}
//创建函数
void setmaskwindow(HWND hwnd/*父窗口句柄*/,BOOL MT/*是否模态*/){
	//注册窗口
	RegisterWindow_MASK(lpClassName, GetModuleHandle(NULL));
	//创建遮罩窗口
	maskhwnd = CreateMaskWindow(hwnd,lpClassName, GetModuleHandle(NULL), MT);
	//移动遮罩窗口
	MOVE_win(hwnd);
	//子类化父类消息
	OldWindowProc = GetWindowLong(hwnd, GWL_WNDPROC/* -4 */);	//保存窗口原风格
	WNDPROC lpfnOldProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, 			(DWORD)NewWndProc);	//setwindowlong会返回设置后的新风格
	DisplayMyWnd(maskhwnd);
	//设置父窗口焦点
	HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
	SetForegroundWindow(fORhwnd);	//激活

}

有了这些还需要一个销毁函数

//销毁遮罩窗口
void quitmaskwindow(){
	//设置回原始的消息处理函数,否则下次子类化会导致消息死循环
	HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
	WNDPROC lpfnOldProc = (WNDPROC)SetWindowLong(fORhwnd, GWL_WNDPROC, (DWORD)OldWindowProc);	//setwindowlong会返回设置后的新风格
	DestroyWindow(maskhwnd);	//关闭窗口
}
那么到此一步基本上就写完了,我们在普通窗口的处理函数里,挂接按钮1事件:
//判断消息ID
	switch (uMsg){
	case WM_COMMAND:
	{
					   switch (LOWORD(wParam))
					   {
					   case ID_BTO1:
						   //设置遮罩窗口
						   setmaskwindow(M_hWnd, FALSE);
						   break;
						}
}

运行效果:

无论移动还是修改大小窗口都会显示一致!

那么接下来就是保留特定控件效果,这里使用CreateRectRgn,CombineRgn,SetWindowRgn,来设置窗口绘制区域:

CreateRectRgn创建窗口绘制区域

CombineRgn合并两个绘制区域

SetWindowRgn设置新的绘制区域

可以使用CreateRectRgn来创建两个绘制区域,第一个是全部窗口,第二个是控件区域!

在利用CombineRgn特性,CombineRgn会将第三个参数视为不绘制区域然后合并到第二个参数里,在将合并结果放在第一个参数里!

在使用SetWindowRgn来设置新的区域即可,后面会详细介绍这些函数!

那么我们要给设置遮罩窗口函数设置新的参数:

void setmaskwindow(HWND hwnd/*父窗口句柄*/,HWND hWnd_l/*保留控件句柄*/
,BOOL MT/*是否模态*/){
//注册窗口
	RegisterWindow_MASK(lpClassName, GetModuleHandle(NULL));
	//创建遮罩窗口
	maskhwnd = CreateMaskWindow(hwnd,lpClassName, GetModuleHandle(NULL), MT);
	//移动遮罩窗口
	MOVE_win(hwnd);
	//子类化父类消息
	OldWindowProc = GetWindowLong(hwnd, GWL_WNDPROC/* -4 */);	//保存窗口原风格
	WNDPROC lpfnOldProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, 			(DWORD)NewWndProc);	//setwindowlong会返回设置后的新风格

	//判断是否设置保留控件句柄
		if (hWnd_l != NULL){
			//获取窗口矩形坐标
			RECT Winrect;
			GetWindowRect(hWnd_l, &Winrect);
			//屏幕坐标到客户区
			POINT pc;
			pc.x = Winrect.left;
			pc.y = Winrect.top;
			ScreenToClient(hwnd, &pc);
			//设置遮罩窗口新的绘图位置
			HRGN hrgn1 = CreateRectRgn(0, 0, rect.right - rect.left, rect.bottom - rect.top);
			//设置遮罩窗口放弃绘图位置
			HRGN hrgn2 = CreateRectRgn(pc.x, pc.y, pc.x + Winrect.right - Winrect.left, pc.y + Winrect.bottom - Winrect.top);
			//合并
			HRGN hrgn3 = CreateRectRgn(0, 0, 0, 0);
			//将新的绘图位置上设置放弃绘图的位置,并将设置后的新的绘图位置保存到hrgn3中
			CombineRgn(hrgn3, hrgn1, hrgn2, 3);
			//设置新的绘图位置
			SetWindowRgn(ghhwnd, hrgn3, TRUE);
			DisplayMyWnd(maskhwnd);
			//设置父窗口焦点
			HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
			SetForegroundWindow(fORhwnd);	//激活
}

运行结果:

注意我们的遮罩窗口关闭消息里不可以结束消息队列,否则整个窗口都会结束,还有重绘消息别忘记把绘制函数放进去:

case WM_PAINT:
		SetbkColoR();
		break;
	case WM_DESTROY:    // 窗口销毁消息
		return DefWindowProc(hwnd, uMsg, wParam, lParam);	//让系统帮我们关闭窗口,这里不能调用PostQuitMessage,因为我们只有一个消息循环函数

我们遮罩窗口没有调用消息循环函数是因为普通窗口的消息循环函数getmessage第二个参数是NULL即截获当前进程下所有线程的消息,而DispatchMessage会根据getmessage截获的消息分发给指定线程窗口的消息处理函数!

还差最后一个模糊窗口!

模糊窗口实现方法:

先将窗口dc保存到本地图片,在用opencv将其模糊化,在加载到内存dc中去,并且支持鼠标穿透,博主本来想使用模糊算法,可是效率不佳没有opencv的效率高,并且也可以使用gdi平滑处理,但是各种效果下来只有opencv效果最好!

LPCTSTR ghClassName = "ghushWnd";  // 注册窗口的名称模糊
HWND ghhwnd;	//模糊窗口句柄
#define GHIMAGE “5.bmp"//模糊dc-image
//消息函数模糊窗口
int gu = 0;	//此值用于判断模糊图片是否已经生成
HBITMAP hbmp;// 位图绘制对象句柄,模糊图像
HDC mdc;
LRESULT CALLBACK WindowProcGUSH(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	
	//判断消息ID
	switch (uMsg){
	case WM_PAINT:
		if (gu!=0){
			PAINTSTRUCT ps;
			HDC hdc;
			hdc = BeginPaint(ghhwnd, &ps);
			// TODO: 在此处添加使用 hdc 的任何绘图代码...
			//获取窗口的客户区坐标
			HWND fORhwnd;
			fORhwnd = GetParent(hwnd);	//获取父窗口句柄
			GetClientRect(hwnd, &rect);
			// 将缓存DC中的位图复制到窗口DC上
			BitBlt(
				hdc, // 目的DC
				rect.left, rect.top, // 目的DC的 x,y 坐标
				rect.right - rect.left, rect.bottom - rect.top,
				mdc, // 缓存DC
				0, 0, // 缓存DC的x,y坐标
				SRCCOPY // 粘贴方式
				);
			//内存dc不能删除,因为内存dc是全局的,否则需要重复加载文件
			//DeleteObject(hbmp);
			//DeleteDC(mdc);
			EndPaint(ghhwnd, &ps);
		}
		else{	//没有生成则将父窗口的dc绘制到模糊窗口上,便于保存
			HWND fORhwnd;
			fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
			HDC hdc;
			hdc = GetDC(fORhwnd);
			PrintWindow(hwnd, hdc, PW_CLIENTONLY);
		}
		break;
	
	case WM_DESTROY:    // 窗口销毁消息
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
		break;

	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}
//注册模糊窗口
BOOL RegisterWindoGUSHw(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProcGUSH;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}

还需要一个dc到文件的函数,此函数来源于csdn,但是只能用于mfc,博主经过修改在win32上,并且增加了注释:

//dc到图像文件,实现思路:利用PrintWindow将窗口dc输出到内存dc里,在将dc内存保存到本地文件中,注意printf会触发WM_PAINT消息,所以这也是为什么我们在模糊窗口里也要调用PrintfWindow,不然的话重绘了之后窗口里就是一片空白!

void SaveHwndToBmpFile(HWND hWnd, LPCTSTR lpszPath)
{
	HDC hDC = ::GetWindowDC(hWnd);//获取dc
	HDC hMemDC = ::CreateCompatibleDC(hDC);//内存dc
	RECT rc;
	::GetWindowRect(hWnd, &rc);//获取窗口尺寸
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);//创建一个bit图像
	HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBitmap);//关联
	::PrintWindow(hWnd, hMemDC, 0);	//将窗口dc输出到内存dc中
	BITMAP bitmap = { 0 };
	::GetObject(hBitmap, sizeof(BITMAP), &bitmap);	//获取对象
	BITMAPINFOHEADER bi = { 0 };
	BITMAPFILEHEADER bf = { 0 };
	//dc到文件
	CONST int nBitCount = 24;
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = bitmap.bmWidth;
	bi.biHeight = bitmap.bmHeight;
	bi.biPlanes = 1;
	bi.biBitCount = nBitCount;
	bi.biCompression = BI_RGB;
	DWORD dwSize = ((bitmap.bmWidth * nBitCount + 31) / 32) * 4 * bitmap.bmHeight;//bmp四字节对齐
	HANDLE hDib = GlobalAlloc(GHND, dwSize + sizeof(BITMAPINFOHEADER));
	LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
	*lpbi = bi;
	//获取图像内存dc数据
	GetDIBits(hMemDC, hBitmap, 0, bitmap.bmHeight, (BYTE*)lpbi + sizeof(BITMAPINFOHEADER), (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
	//写入文件
	FILE* file;
	fopen_s(&file, lpszPath, "wb+");
	bf.bfType = 0x4d42;	//BM=十六进制的0x4d42
	dwSize += sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	bf.bfSize = dwSize;
	bf.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	fwrite((BYTE*)&bf, sizeof(BITMAPFILEHEADER), 1,file);
	fwrite((BYTE*)lpbi , dwSize, 1, file);
	fclose(file);
	GlobalUnlock(hDib);
	GlobalFree(hDib);
	SelectObject(hMemDC, hOldBmp);
	DeleteObject(hBitmap);
	DeleteObject(hMemDC);
	ReleaseDC(hWnd, hDC);
}

增加模糊代码:

//模糊处理
	if (Gh == TRUE){
		//注册模糊窗口
		RegisterWindoGUSHw(ghClassName, GetModuleHandle(NULL));
		LONG dsyte;
		if (MT != TRUE){
			//当窗体具有WS_EX_LAYERED风格时,窗口的paint消息将被系统重载
			dsyte = WS_EX_LAYERED | WS_EX_TRANSPARENT;
		}
		else{
			dsyte = WS_EX_TRANSPARENT;	//失效,WS_EX_TRANSPARENT只有在具有WS_EX_LAYERED风格的窗口上有效,并且这个窗口必须被透明化过,WS_EX_LAYERED只是具有扩展风格,必须使用SetLayeredWindowAttributes或其他透明函数,才可以让此风格有效
		}
		//创建模糊窗口
		ghhwnd = CreateWindowEx(dsyte, ghClassName, NULL, WS_POPUP, 0, 0, 0, 0, hwnd, NULL, GetModuleHandle(NULL), NULL);
		//透明化
		//SetLayeredWindowAttributes可以让具有WS_EX_LAYERED风格的窗口paint不被系统重载
		SetLayeredWindowAttributes(ghhwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
		//移动遮罩窗口
		MOVE_win(hwnd);
		//显示模糊窗口
		DisplayMyWnd(ghhwnd);
		//将父窗口数据绘制到模糊窗口上
		HDC hdc = GetDC(ghhwnd);
		PrintWindow(hwnd, hdc, PW_CLIENTONLY);
		//将内存位图保存至保存到当前目录下
		SaveHwndToBmpFile(ghhwnd, GHIMAGE);
		//SetFileAttributes(GHIMAGE, FILE_ATTRIBUTE_READONLY);
		//设置标识符
		gu = 5;
		//利用opencv进行模糊处理
		IplImage *img = cvLoadImage(GHIMAGE);
		//隐藏文件
		//SetFileAttributes("3.bmp",  FILE_ATTRIBUTE_READONLY);
		//高斯模糊
		IplImage *out = cvCreateImage(
			cvGetSize(img),//当前图像大小
			IPL_DEPTH_8U,//各通道每个像素点的类型
			3//通道总数
			);
		//高斯模糊
		cvSmooth(img, out, CV_GAUSSIAN, 11, 0);
		cvSaveImage(GHIMAGE, out);
		//清除垃圾
		cvReleaseImage(&out);
		cvReleaseImage(&img);
		//设置模糊属性
		//可以加载模糊dc图像了
		/*
		// 加载图片到位图绘制对象hbmp中,此方法已废弃,LoadImage加载的图像会在调用函数结束系统自动释放
			hbmp = (HBITMAP)LoadImage(
			NULL, // 模块实例句柄
			GHIMAGE, // 位图路径。注意双斜杠,单斜杠表示转义,此时文件会加载不成功!!!
			IMAGE_BITMAP, // 图片类型
			rect.right - rect.left,
			rect.bottom - rect.top,
			LR_LOADFROMFILE // 从路径处加载图片
			);
		//删除dc位图
		DeleteFile(GHIMAGE);
		*/
		//兼容内存dc的方式,使用内存dc的方式将图像加载至内存dc中
		mdc = CreateCompatibleDC(hdc); // 创建兼容的缓存DC对象
		//加载位图
		hbmp = (HBITMAP)LoadImage(
			NULL, // 模块实例句柄
			GHIMAGE, // 位图路径。注意双斜杠,单斜杠表示转义,此时文件会加载不成功!!!
			IMAGE_BITMAP, // 图片类型
			rect.right - rect.left,
			rect.bottom - rect.top,
			LR_LOADFROMFILE // 从路径处加载图片
			);
		// 缓存DC(mdc)选择位图绘制对象(可以理解为将图片保存到mdc中)
		SelectObject(mdc, hbmp);
		//删除模糊图像
		//DeleteFile(GHIMAGE);
		//判断是否设置保留控件句柄
		if (hWnd_l != NULL){
			//获取窗口矩形坐标
			RECT Winrect;
			GetWindowRect(hWnd_l, &Winrect);
			//屏幕坐标到客户区
			POINT pc;
			pc.x = Winrect.left;
			pc.y = Winrect.top;
			ScreenToClient(hwnd, &pc);
			//设置遮罩窗口新的绘图位置
			HRGN hrgn1 = CreateRectRgn(0, 0, rect.right - rect.left, rect.bottom - rect.top);
			//设置遮罩窗口放弃绘图位置
			HRGN hrgn2 = CreateRectRgn(pc.x, pc.y, pc.x + Winrect.right - Winrect.left, pc.y + Winrect.bottom - Winrect.top);
			//合并
			HRGN hrgn3 = CreateRectRgn(0, 0, 0, 0);
			//将新的绘图位置上设置放弃绘图的位置,并将设置后的新的绘图位置保存到hrgn3中
			CombineRgn(hrgn3, hrgn1, hrgn2, 3);
			//设置新的绘图位置
			SetWindowRgn(ghhwnd, hrgn3, TRUE);
		}
		//重绘
		InvalidateRect(ghhwnd, NULL, TRUE);//设置窗口无效区域
		UpdateWindow(ghhwnd);	//立即重绘
		//这里不能使用send来发送paint消息,因为优先级的原因,所以我们要将窗口设置无效区域然后重绘
		
	}
	//显示窗口
	if (Gh == TRUE){//判断是否遮罩模糊
		if (Gh_Mask_Window == TRUE)
		{
			DisplayMyWnd(maskhwnd);
		}
	}
	else
	{
		DisplayMyWnd(maskhwnd);
	}
	//设置父窗口焦点
	HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
	SetForegroundWindow(fORhwnd);	//激活

这里来说一下sendmessage优先级,当我们使用sendmessage或post发送绘图消息时此函数会检查消息队列里是否有其它消息如果有则不发送!

而InvalidateRect则是设置窗口无效区域,当系统检测到有窗口具有无效区域时会等待窗口的消息队列里没有其它消息时则发送重绘消息,而updatewindow会检查窗口是否具有无效区域,如果有立即发送重绘消息!

注意为了防止重复调用,这里我们要增加判断是否重复调用的函数:

//判断是否重复调用

if (maskhwnd != NULL){

gu = 0; //必须设置为0,不然第二次调用模糊的话,保存dc图像时会触发WM_PAINT消息的话,就会加载内存dc的数据,但是调用quitmaskwindow之后内存dc数据就是空,所以绘显示空白绘图数据!

quitmaskwindow(); //必须清空,因为子类化原因,我们必须将子类化消息设置回系统默认,不然的话重复调用此函数回导致系统消息死循环

}

这里来解释一下子类化原因导致消息死循环:

一当我们子类化消息之后,下面会调用

return CallWindowProc((WNDPROC)OldWindowProc, hWnd, message, wParam, lParam);//交给旧的消息处理函数

如果不设置回去的话,那么下次子类化的时候,旧的消息还是新子类化的消息,那么在调用CallWindowProc交给旧的子类化消息就是当前子类化消息就会一直消息死循环,而导致中断!

那么做挂接一次按钮事件:

//判断消息ID
	switch (uMsg){
	case WM_COMMAND:
	{
					   switch (LOWORD(wParam))
					   {
					   case ID_BTO1:
						   //设置遮罩窗口
						   setmaskwindow(M_hWnd, NULL, FALSE, FALSE,FALSE);
						   break;
					   case ID_BTO2:
						   //销毁遮罩窗口
						   quitmaskwindow();
						   break;
					   case ID_BTO3:
						   //模态遮罩窗口
						   setmaskwindow(M_hWnd, NULL, FALSE, FALSE, TRUE);
						   MessageBox(hwnd, "DD", "DD", 0);
						   quitmaskwindow();
						   break;
						   break;
					   case ID_BTO4:
						   //保留特定控件
						   setmaskwindow(M_hWnd, hWnd4, FALSE, FALSE, FALSE);
						   break;
					   case ID_BTO5:
						   //模糊窗口
						   setmaskwindow(M_hWnd, NULL, TRUE, FALSE, FALSE);
						   break;
					   case ID_BTO6:
						   //模糊窗口保留特定控件
						   setmaskwindow(M_hWnd, hWnd6, TRUE, FALSE, FALSE);
						   break;
					   case ID_BTO7:
						   //模糊窗口遮罩效果
						   setmaskwindow(M_hWnd, NULL, TRUE, TRUE, FALSE);
						   break;
					   case ID_BTO8:
						   //模糊窗口模态
						   setmaskwindow(M_hWnd, NULL, TRUE, FALSE, TRUE);
						   MessageBox(hwnd, "DD", "DD", 0);
						   quitmaskwindow();
						   break;
					   default:
						   break;
					   }
	}

完整代码:

#include "stdafx.h"
#include <windows.h>
LPCTSTR lpClassName = "MASKWnd";  // 注册窗口的名称遮罩
LPCTSTR ghClassName = "ghushWnd";  // 注册窗口的名称模糊
HWND maskhwnd;	//遮罩窗口句柄
HWND ghhwnd;	//模糊窗口句柄
RECT rect;	//客户区坐标
POINT PO;	//屏幕坐标
HWND M_hWnd;	//父窗口句柄
#define GHIMAGE "5.bmp"//模糊dc-image

#define ID_BTO1 0x001
#define ID_BTO2 0x002
#define ID_BTO3 0x003
#define ID_BTO4 0x004
#define ID_BTO5 0x005
#define ID_BTO6 0x006
#define ID_BTO7 0x007
#define ID_BTO8 0x008
HWND hWnd1;
HWND hWnd2;
HWND hWnd3;
HWND hWnd4;
HWND hWnd5;
HWND hWnd6;
HWND hWnd7;
HWND hWnd8;
extern void SetbkColoR();
void setmaskwindow(HWND hwnd/*父窗口句柄*/, HWND hWnd_l/*保留控件句柄*/, BOOL Gh/*是否模糊*/, BOOL Gh_Mask_Window/*模糊遮罩*/, BOOL MT/*是否模态*/);
extern void quitmaskwindow();
//消息函数遮罩窗口
LRESULT CALLBACK WindowProcMASK(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	
	//判断消息ID
	switch (uMsg){
	case WM_PAINT:
		SetbkColoR();
		break;
	case WM_DESTROY:    // 窗口销毁消息
		return DefWindowProc(hwnd, uMsg, wParam, lParam);	//让系统帮我们关闭窗口,这里不能调用PostQuitMessage,因为我们只有一个消息循环函数
	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}
//消息函数普通窗口
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

	//判断消息ID
	switch (uMsg){
	case WM_COMMAND:
	{
					   switch (LOWORD(wParam))
					   {
					   case ID_BTO1:
						   //设置遮罩窗口
						   setmaskwindow(M_hWnd, NULL, FALSE, FALSE,FALSE);
						   break;
					   case ID_BTO2:
						   //销毁遮罩窗口
						   quitmaskwindow();
						   break;
					   case ID_BTO3:
						   //模态遮罩窗口
						   setmaskwindow(M_hWnd, NULL, FALSE, FALSE, TRUE);
						   MessageBox(hwnd, "DD", "DD", 0);
						   quitmaskwindow();
						   break;
						   break;
					   case ID_BTO4:
						   //保留特定控件
						   setmaskwindow(M_hWnd, hWnd4, FALSE, FALSE, FALSE);
						   break;
					   case ID_BTO5:
						   //模糊窗口
						   setmaskwindow(M_hWnd, NULL, TRUE, FALSE, FALSE);
						   break;
					   case ID_BTO6:
						   //模糊窗口保留特定控件
						   setmaskwindow(M_hWnd, hWnd6, TRUE, FALSE, FALSE);
						   break;
					   case ID_BTO7:
						   //模糊窗口遮罩效果
						   setmaskwindow(M_hWnd, NULL, TRUE, TRUE, FALSE);
						   break;
					   case ID_BTO8:
						   //模糊窗口模态
						   setmaskwindow(M_hWnd, NULL, TRUE, FALSE, TRUE);
						   MessageBox(hwnd, "DD", "DD", 0);
						   quitmaskwindow();
						   break;
					   default:
						   break;
					   }
	}
		break;
	case WM_DESTROY:    // 窗口销毁消息
		PostQuitMessage(0);
		return 0;
		break;
	
	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}
//消息函数模糊窗口
int gu = 0;
HBITMAP hbmp;// 位图绘制对象句柄,模糊图像
HDC mdc;
LRESULT CALLBACK WindowProcGUSH(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	
	//判断消息ID
	switch (uMsg){
	case WM_PAINT:
		if (gu!=0){
			PAINTSTRUCT ps;
			HDC hdc;
			hdc = BeginPaint(ghhwnd, &ps);
			// TODO: 在此处添加使用 hdc 的任何绘图代码...
			//获取窗口的客户区坐标
			HWND fORhwnd;
			fORhwnd = GetParent(hwnd);	//获取父窗口句柄
			GetClientRect(hwnd, &rect);
			// 将缓存DC中的位图复制到窗口DC上
			BitBlt(
				hdc, // 目的DC
				rect.left, rect.top, // 目的DC的 x,y 坐标
				rect.right - rect.left, rect.bottom - rect.top,
				mdc, // 缓存DC
				0, 0, // 缓存DC的x,y坐标
				SRCCOPY // 粘贴方式
				);
			//内存dc不能删除,因为内存dc是全局的,否则需要重复加载文件
			//DeleteObject(hbmp);
			//DeleteDC(mdc);
			EndPaint(ghhwnd, &ps);
		}
		else{
			HWND fORhwnd;
			fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
			HDC hdc;
			hdc = GetDC(fORhwnd);
			PrintWindow(hwnd, hdc, PW_CLIENTONLY);
		}
		break;
	
	case WM_DESTROY:    // 窗口销毁消息
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
		break;

	}
	// 其他的消息调用缺省的消息处理程序
	return DefWindowProc(hwnd, uMsg, wParam, lParam);

}
// 3、注册窗口类型
//遮罩窗口
BOOL RegisterWindow_MASK(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProcMASK;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}
//普通窗口
BOOL RegisterWindow(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProc;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}
//模糊窗口
BOOL RegisterWindoGUSHw(LPCSTR lpcWndName, HINSTANCE hInstance)
{
	ATOM nAtom = 0;
	// 构造创建窗口参数
	WNDCLASS wndClass = { 0 };
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowProcGUSH;      // 指向窗口过程函数
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = NULL;
	wndClass.hCursor = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
	nAtom = RegisterClass(&wndClass);
	return TRUE;
}
//创建遮罩窗口
HWND CreateMaskWindow(HWND hWndHENDL/*父窗口*/,LPCTSTR lpClassName/*类名*/, HINSTANCE hInstance/*资源实列句柄*/,BOOL MT/*是否模态*/)
{
	HWND hWnd = NULL;
	//风格设置
	LONG dsyte;
	if (MT == TRUE){
		dsyte = WS_EX_LAYERED;
	}
	else
	{//WS_EX_TRANSPARENT鼠标穿透
		dsyte = WS_EX_LAYERED | WS_EX_TRANSPARENT;
	}
	//创建具有扩展风格的窗口
	hWnd = CreateWindowEx(dsyte,lpClassName, NULL, WS_POPUP, 0, 0, 0, 0, hWndHENDL, NULL, hInstance, NULL);
	if (MT == TRUE){
		EnableWindow(hWnd, FALSE);	//将窗口设置成不可用状态
	}
	//透明化
	SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 155, LWA_ALPHA);
	return hWnd;
}
//绘制背景
void SetbkColoR(){
	//绘制矩形填充窗口背景
	HDC hdc;
	hdc = GetDC(maskhwnd);
	HBRUSH hb;
	hb = CreateSolidBrush(RGB(0, 0, 0));	//黑色画刷
	FillRect(hdc, &rect, hb);
	DeleteDC(hdc);
	DeleteObject(hb);
	UpdateWindow(maskhwnd);
}
//显示窗口
void DisplayMyWnd(HWND hWnd)
{
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
}
//移动遮罩窗口
void MOVE_win(HWND hWnd){
	//获取窗口的客户区坐标
	GetClientRect(hWnd, &rect);
	//屏幕坐标转换,转换前需要给PO结构体赋予客户区坐标,也就是给PO结构体赋予初始值Windows才能转换
	PO.x = rect.left;
	PO.y = rect.top;
	//屏幕坐标转换
	ClientToScreen(hWnd, &PO);
	//移动窗口
	if (maskhwnd!=NULL){
		MoveWindow(maskhwnd, PO.x, PO.y, rect.right - rect.left, rect.bottom - rect.top, TRUE);
	}
	if (ghhwnd != NULL){
		MoveWindow(ghhwnd, PO.x, PO.y, rect.right - rect.left, rect.bottom - rect.top, TRUE);
	}
}

void doMessage()        // 消息循环处理函数
{
	MSG msg = { 0 };
	// 获取消息
	while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
	{
		DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
	}
}
long OldWindowProc;	//保存旧的属性
//新的子类化消息
LRESULT NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

	if (message == WM_MOVE)//随父窗口移动

	{
		MOVE_win(hWnd);

	}
	if (message == WM_SIZE)//size

	{
		MOVE_win(hWnd);

	}
	return CallWindowProc((WNDPROC)OldWindowProc, hWnd, message, wParam, lParam);//交给旧的消息处理函数
}
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>  
#include <cv.hpp>
//dc到图像文件
void SaveHwndToBmpFile(HWND hWnd, LPCTSTR lpszPath)
{
	HDC hDC = ::GetWindowDC(hWnd);//获取dc
	HDC hMemDC = ::CreateCompatibleDC(hDC);//内存dc
	RECT rc;
	::GetWindowRect(hWnd, &rc);//获取窗口尺寸
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);//创建一个bit图像
	HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBitmap);//关联
	::PrintWindow(hWnd, hMemDC, 0);	//将窗口dc输出到内存dc中
	BITMAP bitmap = { 0 };
	::GetObject(hBitmap, sizeof(BITMAP), &bitmap);	//获取对象
	BITMAPINFOHEADER bi = { 0 };
	BITMAPFILEHEADER bf = { 0 };
	//dc到文件
	CONST int nBitCount = 24;
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = bitmap.bmWidth;
	bi.biHeight = bitmap.bmHeight;
	bi.biPlanes = 1;
	bi.biBitCount = nBitCount;
	bi.biCompression = BI_RGB;
	DWORD dwSize = ((bitmap.bmWidth * nBitCount + 31) / 32) * 4 * bitmap.bmHeight;//bmp四字节对齐
	HANDLE hDib = GlobalAlloc(GHND, dwSize + sizeof(BITMAPINFOHEADER));
	LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
	*lpbi = bi;
	//获取图像内存dc数据
	GetDIBits(hMemDC, hBitmap, 0, bitmap.bmHeight, (BYTE*)lpbi + sizeof(BITMAPINFOHEADER), (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
	//写入文件
	FILE* file;
	fopen_s(&file, lpszPath, "wb+");
	bf.bfType = 0x4d42;	//BM=十六进制的0x4d42
	dwSize += sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	bf.bfSize = dwSize;
	bf.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	fwrite((BYTE*)&bf, sizeof(BITMAPFILEHEADER), 1,file);
	fwrite((BYTE*)lpbi , dwSize, 1, file);
	fclose(file);
	GlobalUnlock(hDib);
	GlobalFree(hDib);
	SelectObject(hMemDC, hOldBmp);
	DeleteObject(hBitmap);
	DeleteObject(hMemDC);
	ReleaseDC(hWnd, hDC);
}
//设置遮罩窗口
void setmaskwindow(HWND hwnd/*父窗口句柄*/, HWND hWnd_l/*保留控件句柄*/, BOOL Gh/*是否模糊*/, BOOL Gh_Mask_Window/*模糊遮罩*/, BOOL MT/*是否模态*/){
	//判断是否重复调用
		if (maskhwnd != NULL){

gu = 0; //必须设置为0,不然第二次调用模糊的话,保存dc图像时会触发WM_PAINT消息的话,就会加载内存dc的数据,但是调用quitmaskwindow之后内存dc数据就是空,所以绘显示空白绘图数据!

quitmaskwindow(); //必须清空,因为子类化原因,我们必须将子类化消息设置回系统默认,不然的话重复调用此函数回导致系统消息死循环

	}
	//注册窗口
	RegisterWindow_MASK(lpClassName, GetModuleHandle(NULL));
	//创建遮罩窗口
	maskhwnd = CreateMaskWindow(hwnd,lpClassName, GetModuleHandle(NULL), MT);
	//移动遮罩窗口
	MOVE_win(hwnd);
	//子类化父类消息
	OldWindowProc = GetWindowLong(hwnd, GWL_WNDPROC/* -4 */);	//保存窗口原风格
	WNDPROC lpfnOldProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)NewWndProc);	//setwindowlong会返回设置后的新风格
	//判断是否设置保留控件句柄
	if (hWnd_l != NULL){
		//获取窗口矩形坐标
		RECT Winrect;
		GetWindowRect(hWnd_l, &Winrect);
		//屏幕坐标到客户区
		POINT pc;
		pc.x = Winrect.left;
		pc.y = Winrect.top;
		ScreenToClient(hwnd, &pc);
		//设置遮罩窗口新的绘图位置
		HRGN hrgn1 = CreateRectRgn(0, 0, rect.right - rect.left, rect.bottom - rect.top);
		//设置遮罩窗口放弃绘图位置
		HRGN hrgn2 = CreateRectRgn(pc.x, pc.y, pc.x + Winrect.right - Winrect.left, pc.y + Winrect.bottom - Winrect.top);
		//合并
		HRGN hrgn3 = CreateRectRgn(0, 0, 0, 0);
		//将新的绘图位置上设置放弃绘图的位置,并将后的新的绘图位置保存到hrgn3中
		CombineRgn(hrgn3, hrgn1, hrgn2, 3);
		//设置新的绘图位置
		SetWindowRgn(maskhwnd, hrgn3, TRUE);
	}

	//模糊处理
	if (Gh == TRUE){
		//注册模糊窗口
		RegisterWindoGUSHw(ghClassName, GetModuleHandle(NULL));
		LONG dsyte;
		if (MT != TRUE){
			//当窗体具有WS_EX_LAYERED风格时,窗口的paint消息将被系统重载
			dsyte = WS_EX_LAYERED | WS_EX_TRANSPARENT;
		}
		else{
			dsyte = WS_EX_TRANSPARENT;	//失效,WS_EX_TRANSPARENT只有在具有WS_EX_LAYERED风格的窗口上有效,并且这个窗口必须被透明化过,WS_EX_LAYERED只是具有扩展风格,必须使用SetLayeredWindowAttributes或其他透明函数,才可以让此风格有效
		}
		//创建模糊窗口
		ghhwnd = CreateWindowEx(dsyte, ghClassName, NULL, WS_POPUP, 0, 0, 0, 0, hwnd, NULL, GetModuleHandle(NULL), NULL);
		//透明化
		//SetLayeredWindowAttributes可以让具有WS_EX_LAYERED风格的窗口paint不被系统重载
		SetLayeredWindowAttributes(ghhwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
		//移动遮罩窗口
		MOVE_win(hwnd);
		//显示模糊窗口
		DisplayMyWnd(ghhwnd);
		//将父窗口数据绘制到模糊窗口上

		HDC hdc = GetDC(ghhwnd);
		PrintWindow(hwnd, hdc, PW_CLIENTONLY);
		//将内存位图保存至保存到当前目录下
		SaveHwndToBmpFile(ghhwnd, GHIMAGE);
		//SetFileAttributes(GHIMAGE, FILE_ATTRIBUTE_READONLY);
		//设置标识符
		gu = 5;
		//利用opencv进行模糊处理
		IplImage *img = cvLoadImage(GHIMAGE);
		//隐藏文件
		//SetFileAttributes("3.bmp",  FILE_ATTRIBUTE_READONLY);
		//高斯模糊
		IplImage *out = cvCreateImage(
			cvGetSize(img),//当前图像大小
			IPL_DEPTH_8U,//各通道每个像素点的类型
			3//通道总数
			);
		//高斯模糊
		cvSmooth(img, out, CV_GAUSSIAN, 11, 0);
		cvSaveImage(GHIMAGE, out);
		//清除垃圾
		cvReleaseImage(&out);
		cvReleaseImage(&img);
		//设置模糊属性
		//可以加载模糊dc图像了
		/*
		// 加载图片到位图绘制对象hbmp中,此方法已废弃,LoadImage加载的图像会在调用函数结束系统自动释放
			hbmp = (HBITMAP)LoadImage(
			NULL, // 模块实例句柄
			GHIMAGE, // 位图路径。注意双斜杠,单斜杠表示转义,此时文件会加载不成功!!!
			IMAGE_BITMAP, // 图片类型
			rect.right - rect.left,
			rect.bottom - rect.top,
			LR_LOADFROMFILE // 从路径处加载图片
			);
		//删除dc位图
		DeleteFile(GHIMAGE);
		*/
		//兼容内存dc的方式,使用内存dc的方式将图像加载至内存dc中
		mdc = CreateCompatibleDC(hdc); // 创建兼容的缓存DC对象
		//加载位图
		hbmp = (HBITMAP)LoadImage(
			NULL, // 模块实例句柄
			GHIMAGE, // 位图路径。注意双斜杠,单斜杠表示转义,此时文件会加载不成功!!!
			IMAGE_BITMAP, // 图片类型
			rect.right - rect.left,
			rect.bottom - rect.top,
			LR_LOADFROMFILE // 从路径处加载图片
			);
		// 缓存DC(mdc)选择位图绘制对象(可以理解为将图片保存到mdc中)
		SelectObject(mdc, hbmp);
		//删除模糊图像
		//DeleteFile(GHIMAGE);
		//判断是否设置保留控件句柄
		if (hWnd_l != NULL){
			//获取窗口矩形坐标
			RECT Winrect;
			GetWindowRect(hWnd_l, &Winrect);
			//屏幕坐标到客户区
			POINT pc;
			pc.x = Winrect.left;
			pc.y = Winrect.top;
			ScreenToClient(hwnd, &pc);
			//设置遮罩窗口新的绘图位置
			HRGN hrgn1 = CreateRectRgn(0, 0, rect.right - rect.left, rect.bottom - rect.top);
			//设置遮罩窗口放弃绘图位置
			HRGN hrgn2 = CreateRectRgn(pc.x, pc.y, pc.x + Winrect.right - Winrect.left, pc.y + Winrect.bottom - Winrect.top);
			//合并
			HRGN hrgn3 = CreateRectRgn(0, 0, 0, 0);
			//将新的绘图位置上设置放弃绘图的位置,并将设置后的新的绘图位置保存到hrgn3中
			CombineRgn(hrgn3, hrgn1, hrgn2, 3);
			//设置新的绘图位置
			SetWindowRgn(ghhwnd, hrgn3, TRUE);
		}
		//重绘
		InvalidateRect(ghhwnd, NULL, TRUE);//设置窗口无效区域
		UpdateWindow(ghhwnd);	//立即重绘
		//这里不能使用send来发送paint消息,因为优先级的原因,所以我们要将窗口设置无效区域然后重绘
		
	}
	//显示窗口
	if (Gh == TRUE){//判断是否遮罩模糊
		if (Gh_Mask_Window == TRUE)
		{
			DisplayMyWnd(maskhwnd);
		}
	}
	else
	{
		DisplayMyWnd(maskhwnd);
	}
	//设置父窗口焦点
	HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
	SetForegroundWindow(fORhwnd);	//激活
}

//销毁遮罩窗口
void quitmaskwindow(){

	//删除dc
	DeleteObject(hbmp);
	DeleteDC(mdc);
	//设置回原始的消息处理函数,否则下次子类化会导致消息死循环
	HWND fORhwnd = GetParent(maskhwnd);	//获取父窗口句柄
	WNDPROC lpfnOldProc = (WNDPROC)SetWindowLong(fORhwnd, GWL_WNDPROC, (DWORD)OldWindowProc);	//setwindowlong会返回设置后的新风格
	DestroyWindow(maskhwnd);	//关闭窗口
	DestroyWindow(ghhwnd);	//关闭窗口
}
LPCTSTR ClassName = "MYWnd";  // 注册窗口的名称
// 入口函数
int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nShowCmd)
{
	//注册窗口
	RegisterWindow(ClassName, hInstance);
	//创建普通窗口
	//居中窗口
	int scrWidth, scrHeight;
	//获得屏幕尺寸,居中显示
	scrWidth = GetSystemMetrics(SM_CXSCREEN);
	scrHeight = GetSystemMetrics(SM_CYSCREEN);
	M_hWnd = CreateWindow(ClassName, "窗口蒙板效果", WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX, (scrWidth - 700) / 2, (scrHeight - 500) / 2, 700, 500, NULL, NULL, hInstance, NULL);
	//创建按钮
	hWnd1 = CreateWindow("Button", "遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 100, 230, 30, M_hWnd, (HMENU)ID_BTO1, NULL, NULL);
	hWnd2 = CreateWindow("Button", "销毁遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 200, 230, 30, M_hWnd, (HMENU)ID_BTO2, NULL, NULL);
	hWnd3 = CreateWindow("Button", "模态遮罩窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 100, 230, 30, M_hWnd, (HMENU)ID_BTO3, NULL, NULL);
	hWnd4 = CreateWindow("Button", "保留特定控件", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 200, 230, 30, M_hWnd, (HMENU)ID_BTO4, NULL, NULL);
	hWnd5 = CreateWindow("Button", "模糊窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 300, 230, 30, M_hWnd, (HMENU)ID_BTO5, NULL, NULL);
	hWnd6 = CreateWindow("Button", "模糊窗口保留特定控件", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 300, 230, 30, M_hWnd, (HMENU)ID_BTO6, NULL, NULL);
	hWnd7 = CreateWindow("Button", "模糊窗口遮罩效果", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 50, 400, 230, 30, M_hWnd, (HMENU)ID_BTO7, NULL, NULL);
	hWnd8 = CreateWindow("Button", "模糊窗口模态效果", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON
		, 350, 400, 230, 30, M_hWnd, (HMENU)ID_BTO8, NULL, NULL);
	//显示普通窗口
	DisplayMyWnd(M_hWnd);
	//消息循环
	doMessage();
	return 0;
}

如果有什么不懂的地方可以在下方留言,我会一一解答!

猜你喜欢

转载自blog.csdn.net/bjbz_cxy/article/details/81086506