Win32 尝试使用3种方法,为透明窗口加上进度条
【摘要】 前情回顾 透明样式在前面的博客中写了,创建透明背景的方法有3种UpdateLayeredWindow 设置窗体的样式为透明SetLayeredWindowAttributes 设置窗体的透明属性SetWindowRgn 设置窗体显示的区域,其余均为透明 进度条至于进度条,我们采用的是Common Controls自带的控件和样式,使用CreateWindowEx创建 UpdateLayer...
前情回顾
透明样式
在前面的博客中写了,创建透明背景的方法有3种
UpdateLayeredWindow
设置窗体的样式为透明
SetLayeredWindowAttributes
设置窗体的透明属性
SetWindowRgn
设置窗体显示的区域,其余均为透明
进度条
至于进度条,我们采用的是Common Controls
自带的控件和样式,使用CreateWindowEx
创建
UpdateLayeredWindow + 进度条(失败,进度条无法绘制)
完整代码如下
// test.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "test.h"
#define MAX_LOADSTRING 100
#include <stdio.h>
#include <thread>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32") //InitCommonControls
#define IMG_PATH "alpha32_black.bmp" //升级背景图片
#define PROGRESS_STEP 0.05 //进度条每一步的步长
#define PROGRESS_SLEEP 100 //进度条每走一下,休息的时间(单位毫秒)
// 全局变量:
HINSTANCE hInst; // 当前实例
HWND gHwnd;
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_TEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
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_TEST));
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);
}
void ShowConsole()
{
AllocConsole();
FILE* stream;
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
}
DWORD WINAPI PBThreadProc(LPVOID lpParameter)
{
HWND hwndPB = (HWND)lpParameter; //进度条的窗口句柄
PBRANGE range; //进度条的范围
SendMessage(hwndPB, PBM_SETRANGE, //设置进度条的范围
(WPARAM)0, (LPARAM)(MAKELPARAM(0, 100)));
SendMessage(hwndPB, PBM_GETRANGE, //获取进度条的范围
(WPARAM)TRUE, //TRUE 表示返回值为范围的最小值,FALSE表示返回最大值
(LPARAM)&range);
while (TRUE)
{
printf("SendMessage progress\n");
SendMessage(hwndPB, PBM_DELTAPOS, //设置进度条的新位置为当前位置加上范围的1/20
(WPARAM)((range.iHigh - range.iLow) * PROGRESS_STEP), (LPARAM)0);
if (SendMessage(hwndPB, PBM_GETPOS, (WPARAM)0, (LPARAM)0) //取得进度条当前位置
== range.iHigh)
{
Sleep(PROGRESS_SLEEP); //当前进度要保留一定时间
SendMessage(hwndPB, PBM_SETPOS, (WPARAM)range.iLow, (LPARAM)0); //将进度条复位(复位后会立即改变进度条位置)
}
SendMessage(gHwnd, WM_PAINT, NULL, NULL);
Sleep(PROGRESS_SLEEP); //每次更改进度后,停留一定时间
}
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
//从项目目录下,加载图片文件
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, IMG_PATH, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//创建并获取图片信息
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
//获取图片宽高
int width = bmp.bmWidth;
int height = bmp.bmHeight;
//获取屏幕宽高
int scWidth = GetSystemMetrics(SM_CXSCREEN);
int scHeight = GetSystemMetrics(SM_CYSCREEN);
//计算窗口居中时,左上角的xy坐标
int x = (scWidth - width) / 2;
int y = (scHeight - height) / 2;
//创建窗口,并将窗体样式设置为无标题栏的弹窗
//HWND hWnd = CreateWindowW(szWindowClass, szTitle,
// //WS_OVERLAPPEDWINDOW,
// WS_POPUP,
// x, y, width, height, nullptr, nullptr, hInstance, nullptr);
//UpdateLayeredWindow只能用CreateWindowEx,用上面无法透明
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;
}
gHwnd = hWnd;
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(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;
//显示窗口
ShowWindow(hWnd, nCmdShow);
//绘制一定要在ShowWindow之后调用,否则绘图无效
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
UpdateWindow(hWnd);
//为样式添加附加层,用来支持透明样式
//LONG nExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
//nExStyle |= WS_EX_LAYERED;
//SetWindowLong(hWnd, GWL_EXSTYLE, nExStyle);
UpdateLayeredWindow(hWnd, screenDC, &ptSrc, &wndSize,
hdcMem, &ptSrc, 0, &blendFunction, ULW_ALPHA);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
ReleaseDC(hWnd, hdcMem);
int pbWidth = width * 0.7;
int pbHeight = 10;
int pbX = (width - pbWidth) / 2;
int pbY = height * 0.7;
HWND hwndPB = CreateWindowEx(
0,
PROGRESS_CLASS,
NULL,
WS_CHILD | WS_VISIBLE,
pbX, pbY, pbWidth, pbHeight,
hWnd,
NULL,
hInst,
NULL);
CreateThread( //操作进度条的线程
NULL,
0,
(LPTHREAD_START_ROUTINE)PBThreadProc,
hwndPB,
0,
0
);
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_CREATE:
//ShowConsole();
case WM_MOUSEMOVE:
printf("WM_MOUSEMOVE\n");
break;
case WM_NCMOUSEMOVE:
printf("WM_NCMOUSEMOVE\n");
break;
case WM_LBUTTONDOWN:
//模拟按下标题栏区域,让窗口可移动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 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;
}
SetLayeredWindowAttributes + 进度条(成功)
完整代码如下
// test.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "test.h"
#define MAX_LOADSTRING 100
#include <stdio.h>
#include <thread>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32") //InitCommonControls
#define IMG_PATH "alpha32_black.bmp" //升级背景图片
#define PROGRESS_STEP 0.05 //进度条每一步的步长
#define PROGRESS_SLEEP 100 //进度条每走一下,休息的时间(单位毫秒)
// 全局变量:
HINSTANCE hInst; // 当前实例
HWND gHwnd;
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_TEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
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_TEST));
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);
}
void ShowConsole()
{
AllocConsole();
FILE* stream;
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
}
DWORD WINAPI PBThreadProc(LPVOID lpParameter)
{
HWND hwndPB = (HWND)lpParameter; //进度条的窗口句柄
PBRANGE range; //进度条的范围
SendMessage(hwndPB, PBM_SETRANGE, //设置进度条的范围
(WPARAM)0, (LPARAM)(MAKELPARAM(0, 100)));
SendMessage(hwndPB, PBM_GETRANGE, //获取进度条的范围
(WPARAM)TRUE, //TRUE 表示返回值为范围的最小值,FALSE表示返回最大值
(LPARAM)&range);
while (TRUE)
{
printf("SendMessage progress\n");
SendMessage(hwndPB, PBM_DELTAPOS, //设置进度条的新位置为当前位置加上范围的1/20
(WPARAM)((range.iHigh - range.iLow) * PROGRESS_STEP), (LPARAM)0);
if (SendMessage(hwndPB, PBM_GETPOS, (WPARAM)0, (LPARAM)0) //取得进度条当前位置
== range.iHigh)
{
Sleep(PROGRESS_SLEEP); //当前进度要保留一定时间
SendMessage(hwndPB, PBM_SETPOS, (WPARAM)range.iLow, (LPARAM)0); //将进度条复位(复位后会立即改变进度条位置)
}
SendMessage(gHwnd, WM_PAINT, NULL, NULL);
Sleep(PROGRESS_SLEEP); //每次更改进度后,停留一定时间
}
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
//从项目目录下,加载图片文件
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, IMG_PATH, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//创建并获取图片信息
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
//获取图片宽高
int width = bmp.bmWidth;
int height = bmp.bmHeight;
//获取屏幕宽高
int scWidth = GetSystemMetrics(SM_CXSCREEN);
int scHeight = GetSystemMetrics(SM_CYSCREEN);
//计算窗口居中时,左上角的xy坐标
int x = (scWidth - width) / 2;
int y = (scHeight - height) / 2;
//创建窗口,并将窗体样式设置为无标题栏的弹窗
HWND hWnd = CreateWindowW(szWindowClass, szTitle,
//WS_OVERLAPPEDWINDOW,
WS_POPUP,
x, y, width, height, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
gHwnd = hWnd;
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//显示窗口
ShowWindow(hWnd, nCmdShow);
//绘制一定要在ShowWindow之后调用,否则绘图无效
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
ReleaseDC(hWnd, hdcMem);
//为样式添加附加层,用来支持透明样式
LONG nExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
nExStyle |= WS_EX_LAYERED;
SetWindowLong(hWnd, GWL_EXSTYLE, nExStyle);
//设置窗体属性
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
UpdateWindow(hWnd);
int pbWidth = width * 0.7;
int pbHeight = 10;
int pbX = (width - pbWidth) / 2;
int pbY = height * 0.7;
HWND hwndPB = CreateWindowEx(
0,
PROGRESS_CLASS,
NULL,
WS_CHILD | WS_VISIBLE,
pbX, pbY, pbWidth, pbHeight,
hWnd,
NULL,
hInst,
NULL);
CreateThread( //操作进度条的线程
NULL,
0,
(LPTHREAD_START_ROUTINE)PBThreadProc,
hwndPB,
0,
0
);
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_CREATE:
//ShowConsole();
case WM_MOUSEMOVE:
printf("WM_MOUSEMOVE\n");
break;
case WM_NCMOUSEMOVE:
printf("WM_NCMOUSEMOVE\n");
break;
case WM_LBUTTONDOWN:
//模拟按下标题栏区域,让窗口可移动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 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;
}
SetWindowRgn + 进度条(成功)
设置裁剪区域是个耗时操作,要在ShowWindow
之前调用,否则会先显示未裁剪过的窗口,然后过了一会才刷新成裁剪后的窗口。
这里无论是绘制完裁剪,还是裁剪完绘制,都可以,如果不绘制,窗口呈白色
// test.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "test.h"
#define MAX_LOADSTRING 100
#include <stdio.h>
#include <thread>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32") //InitCommonControls
#define IMG_PATH "alpha32_black.bmp" //升级背景图片
#define PROGRESS_STEP 0.05 //进度条每一步的步长
#define PROGRESS_SLEEP 100 //进度条每走一下,休息的时间(单位毫秒)
// 全局变量:
HINSTANCE hInst; // 当前实例
HWND gHwnd;
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_TEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
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_TEST));
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);
}
void ShowConsole()
{
AllocConsole();
FILE* stream;
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
}
DWORD WINAPI PBThreadProc(LPVOID lpParameter)
{
HWND hwndPB = (HWND)lpParameter; //进度条的窗口句柄
PBRANGE range; //进度条的范围
SendMessage(hwndPB, PBM_SETRANGE, //设置进度条的范围
(WPARAM)0, (LPARAM)(MAKELPARAM(0, 100)));
SendMessage(hwndPB, PBM_GETRANGE, //获取进度条的范围
(WPARAM)TRUE, //TRUE 表示返回值为范围的最小值,FALSE表示返回最大值
(LPARAM)&range);
while (TRUE)
{
printf("SendMessage progress\n");
SendMessage(hwndPB, PBM_DELTAPOS, //设置进度条的新位置为当前位置加上范围的1/20
(WPARAM)((range.iHigh - range.iLow) * PROGRESS_STEP), (LPARAM)0);
if (SendMessage(hwndPB, PBM_GETPOS, (WPARAM)0, (LPARAM)0) //取得进度条当前位置
== range.iHigh)
{
Sleep(PROGRESS_SLEEP); //当前进度要保留一定时间
SendMessage(hwndPB, PBM_SETPOS, (WPARAM)range.iLow, (LPARAM)0); //将进度条复位(复位后会立即改变进度条位置)
}
SendMessage(gHwnd, WM_PAINT, NULL, NULL);
Sleep(PROGRESS_SLEEP); //每次更改进度后,停留一定时间
}
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
//从项目目录下,加载图片文件
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, IMG_PATH, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//创建并获取图片信息
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
//获取图片宽高
int width = bmp.bmWidth;
int height = bmp.bmHeight;
//获取屏幕宽高
int scWidth = GetSystemMetrics(SM_CXSCREEN);
int scHeight = GetSystemMetrics(SM_CYSCREEN);
//计算窗口居中时,左上角的xy坐标
int x = (scWidth - width) / 2;
int y = (scHeight - height) / 2;
//创建窗口,并将窗体样式设置为无标题栏的弹窗
HWND hWnd = CreateWindowW(szWindowClass, szTitle,
//WS_OVERLAPPEDWINDOW,
WS_POPUP,
x, y, width, height, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
gHwnd = hWnd;
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//创建矩形区域(耗时操作,要在showWindow之前创建,不然会白个几秒)
HRGN hRgn = CreateRectRgn(0, 0, width, height);
COLORREF col;
HRGN rgnTemp;
//由于是逐像素遍历,所以非常的慢
for (int x = 0; x <= width; x++)
{
for (int y = 0; y <= height; y++)
{
//其中第一字节为 0 而且始终为 0,其它三个字节分别表示蓝色、绿色和红色,刚好和 RGB 的次序相反。
//这个结构体用起来挺别扭。对于COLORREF,我们通常使用宏RGB对其进行赋值。
col = GetPixel(hdcMem, x, y);
if ((col & 0x0000ff00) == 0)
{
// 创建一个矩形裁剪区域
rgnTemp = CreateRectRgn(x, y, x + 1, y + 1);
//合并两个区域
CombineRgn(hRgn, hRgn, rgnTemp, RGN_XOR);
//删除区域对象
DeleteObject(rgnTemp);
}
}
}
//显示窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//绘制一定要在ShowWindow之后调用,否则绘图无效
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
//设置剪裁区域
SetWindowRgn(hWnd, hRgn, TRUE);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
ReleaseDC(hWnd, hdcMem);
int pbWidth = width * 0.7;
int pbHeight = 10;
int pbX = (width - pbWidth) / 2;
int pbY = height * 0.7;
HWND hwndPB = CreateWindowEx(
0,
PROGRESS_CLASS,
NULL,
WS_CHILD | WS_VISIBLE,
pbX, pbY, pbWidth, pbHeight,
hWnd,
NULL,
hInst,
NULL);
CreateThread( //操作进度条的线程
NULL,
0,
(LPTHREAD_START_ROUTINE)PBThreadProc,
hwndPB,
0,
0
);
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_CREATE:
//ShowConsole();
case WM_MOUSEMOVE:
printf("WM_MOUSEMOVE\n");
break;
case WM_NCMOUSEMOVE:
printf("WM_NCMOUSEMOVE\n");
break;
case WM_LBUTTONDOWN:
//模拟按下标题栏区域,让窗口可移动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 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;
}
可以看到,如果不调用BitBlt
进行绘制,窗口会显示白色
总结
- 如果透明窗体上还要显示其它控件,建议使用
SetLayeredWindowAttributes
或SetWindowRgn
- 如果使用
UpdateLayeredWindow
,后续不会触发WM_PAINT
事件,所有绘制操作都必须由UpdateLayeredWindow
完成
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)