Win32 使用 UpdateLayeredWindow 创建透明窗体
函数原型
UpdateLayeredWindow函数更新一个分层窗口的位置,大小,形状,内容和半透明度。函数原型如下
WINUSERAPI
BOOL
WINAPI
UpdateLayeredWindow(
_In_ HWND hWnd,
_In_opt_ HDC hdcDst,
_In_opt_ POINT* pptDst,
_In_opt_ SIZE* psize,
_In_opt_ HDC hdcSrc,
_In_opt_ POINT* pptSrc,
_In_ COLORREF crKey,
_In_opt_ BLENDFUNCTION* pblend,
_In_ DWORD dwFlags);
pblend
指向指定合成分层窗口时使用的透明度结构的指针。
dwFlags
可以是以下值之一。
如果hdcSrc指定为NULL,dwFlags应该指定为0。
dwFlags | 值 | 含义 |
---|---|---|
ULW_ALPHA | 0x02 | 使用参数pblend作为混合函数,如果显示模式为256色或低于256色,使用这个值实现的效果和使用ULW_OPAQUE的效果相同 |
ULW_COLORKEY | 0x01 | 使用参数crKey值作为透明颜色 |
ULW_OPAQUE | 0x04 | 绘制一个不透明的分层窗口 |
返回值
类型:BOOL
- 如果函数执行成功,返回非零值。
- 如果函数执行失败,返回值为0。调用GetLastError函数获取更多错误信息。
准备工作
首先新建 ——> Window桌面向导 ——> Windows桌面应用程序
给程序起个名字,然后创建项目
单击绿色三角形按钮(运行),运行后是一个空白窗体
代码瘦身
初始代码如下
// ShapedWindow.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "ShapedWindow.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_SHAPEDWINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SHAPEDWINDOW));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SHAPEDWINDOW));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SHAPEDWINDOW);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
1.删除菜单栏按钮响应事件
默认的程序有个菜单栏,菜单栏上“文件”和“帮助”。
这两个按钮的响应,在 WndProc
函数的 WM_COMMAND
事件里。这里用不到,所以,删除下面代码
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
2.删除“关于”对话框
DialogBox是一个Windows API函数。它的作用是从一个对话框资源中创建一个模态对话框。该函数直到指定的回调函数通过调用EndDialog函数中止模态的对话框才能返回控制。该函数通过调用DialogBoxParam函数来实现
因此删除关于对话框相关代码
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
3. 删除菜单栏
RegisterClassEx是一个计算机函数,该函数为随后在调用Createwindow函数和CreatewindowEx函数中使用的窗口注册一个窗口类
菜单栏会在创建窗口类时,传递给系统,因此要删除菜单栏相关代码wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SHAPEDWINDOW);
4.最终代码
// ShapedWindow.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "ShapedWindow.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_SHAPEDWINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SHAPEDWINDOW));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SHAPEDWINDOW));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
运行一下,现在窗口张这样
制作异形图片
这里详见我的另一篇博客 https://bbs.huaweicloud.com/blogs/383366
创建异形窗口
将 CreateWindow
改为如下代码,你就可以看到一个“不带标题栏的空白窗口”
int width = 1024;
int height = 768;
HWND hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE, // dwExStyle
szWindowClass, // Classname
szTitle, // Title Text
WS_POPUP, // dwStyle
CW_USEDEFAULT, // Windows decides the position
CW_USEDEFAULT, // where the window ends up on the screen
width, // The programs width
height, // and height in pixels
HWND_DESKTOP, // The window is a child-window to desktop,or write nullptr
nullptr, // No menu
hInstance, // Program Instance handler
nullptr // No Window Creation data
);
接着还是在 InitInstance
函数中,设置bmp图像的覆盖模式,新增的代码只能在 ShowWindow
之后被调用
ShowWindow(hWnd, nCmdShow);
//UpdateWindow(hWnd);
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//提取整个屏幕的DC
HDC screenDC = GetDC(NULL);
POINT ptSrc = { 0,0 };
SIZE wndSize;
wndSize.cx = bmp.bmWidth;
wndSize.cy = bmp.bmHeight;
BLENDFUNCTION blendFunction;
//当AlphaFormat参数的值是AC_SRC_ALPHA,那么源位图必须是32位深,否则的话,AlphaBland函数将调用失败
blendFunction.AlphaFormat = AC_SRC_ALPHA;
blendFunction.BlendFlags = 0;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.SourceConstantAlpha = 255;
//更新一个分层窗口的位置,大小,形状,内容和半透明度
UpdateLayeredWindow(hWnd, screenDC, &ptSrc, &wndSize,
hdcMem, &ptSrc, 0, &blendFunction, ULW_ALPHA);
为了优化用户体验,要加两样东西
- 鼠标按下允许拖动窗口
- 按下 Esc 键关闭窗口
为此需要修改消息函数 WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
//WM_LBUTTONDOWN是在左击客户区时响应;WM_NCLBUTTONDOWN是在左击非客户区时响应
//发送该事件能让窗口变得可拖动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) exit(0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
最后效果如下,可发现一点很奇怪的是顶部空白并没有透明,但鼠标挪过去却能直接点击到后面的软件内容,这点很奇怪。但起码说明透明确实是实现了,只不过显示为透明的白色
修复透明白
一直百思不得其解,想了一晚上。后面觉得C++在处理这个分层函数时,会不会是为了复用旧版处理RGB的函数,将透明通道,与RGB通道分开处理
猜想:
- 所有的像素点,本质上都是一串字节数组。初始值通过
memset
将其全设置为0,也就是黑色 - 而白色有是RGB三种颜色达到满值,叠加而来的。
- bmp图像是XRGB模式,即使带了透明通道,也不会被大部分看图软件识别为透明
推测:
- 用透明度来判断事件能否透过当前窗口
- 逐像素判断,如果非黑则上色
因此,就有了上一篇博客,将透明图片的背景改为黑色,于是顺利实现透明效果
作图方法,详见我的另一篇博客 https://bbs.huaweicloud.com/blogs/383366
注意事项
注意结构体size的赋值方式,用错了将导致窗口不显示
正确
SIZE wndSize;
wndSize.cx = bmp.bmWidth;
wndSize.cy = bmp.bmHeight;
错误1
SIZE wndSize = { width, height };
错误2
SIZE wndSize = { (LONG)width, (LONG)height };
错误3
SIZE wndSize;
wndSize.cx = width;
wndSize.cy = height;
完整代码如下
// ShapedWindow.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "ShapedWindow.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_SHAPEDWINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SHAPEDWINDOW));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SHAPEDWINDOW));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
//wcex.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
/*
hWnd hWnd = CreateWindowW(
szWindowClass, // Classname
szTitle, // Title Text
WS_OVERLAPPEDWINDOW, // dwStyle
CW_USEDEFAULT, // Windows decides the position
0, // where the window ends up on the screen
CW_USEDEFAULT, // The programs width
0, // and height in pixels
nullptr, // The window is a child-window to desktop
nullptr, // No menu
hInstance, // Program Instance handler
nullptr // No Window Creation data
);
*/
LPCSTR bgPath = "update4.bmp";
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, bgPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
int width = bmp.bmWidth;
int height = bmp.bmHeight;
HWND hWnd = CreateWindowEx(
WS_EX_LAYERED, // dwExStyle
szWindowClass, // Classname
szTitle, // Title Text
WS_POPUP, // dwStyle
CW_USEDEFAULT, // Windows decides the position
CW_USEDEFAULT, // where the window ends up on the screen
width, // The programs width
height, // and height in pixels
HWND_DESKTOP, // The window is a child-window to desktop,填null也可以
nullptr, // No menu
hInstance, // Program Instance handler
nullptr // No Window Creation data
);
if (!hWnd)
{
return FALSE;
}
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//提取整个屏幕的DC
HDC screenDC = GetDC(NULL);
POINT ptSrc = { 0,0 };
SIZE wndSize;
wndSize.cx = bmp.bmWidth;
wndSize.cy = bmp.bmHeight;
BLENDFUNCTION blendFunction;
//当AlphaFormat参数的值是AC_SRC_ALPHA,那么源位图必须是32位深,否则的话,AlphaBland函数将调用失败
blendFunction.AlphaFormat = AC_SRC_ALPHA;
blendFunction.BlendFlags = 0;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.SourceConstantAlpha = 255;
//更新一个分层窗口的位置,大小,形状,内容和半透明度
UpdateLayeredWindow(hWnd, screenDC, &ptSrc, &wndSize,
hdcMem, &ptSrc, 0, &blendFunction, ULW_ALPHA);
return TRUE;
}
//
// 函数: WndProc(hWnd, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
//WM_LBUTTONDOWN是在左击客户区时响应;WM_NCLBUTTONDOWN是在左击非客户区时响应
//发送该事件能让窗口变得可拖动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) exit(0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
- 点赞
- 收藏
- 关注作者
评论(0)