Win32 尝试使用3种方法,为透明窗口加上进度条

举报
福州司马懿 发表于 2022/11/22 16:27:17 2022/11/22
【摘要】 前情回顾 透明样式在前面的博客中写了,创建透明背景的方法有3种UpdateLayeredWindow 设置窗体的样式为透明SetLayeredWindowAttributes 设置窗体的透明属性SetWindowRgn 设置窗体显示的区域,其余均为透明 进度条至于进度条,我们采用的是Common Controls自带的控件和样式,使用CreateWindowEx创建 UpdateLayer...

前情回顾

透明样式

在前面的博客中写了,创建透明背景的方法有3种

  1. UpdateLayeredWindow 设置窗体的样式为透明
    图片.png
  2. SetLayeredWindowAttributes 设置窗体的透明属性
    图片.png
  3. SetWindowRgn 设置窗体显示的区域,其余均为透明
    图片.png

进度条

至于进度条,我们采用的是Common Controls自带的控件和样式,使用CreateWindowEx创建
图片.png

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;
}

图片.png

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;
}

图片.png

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;
}

图片.png

可以看到,如果不调用BitBlt进行绘制,窗口会显示白色

图片.png

总结

  • 如果透明窗体上还要显示其它控件,建议使用SetLayeredWindowAttributesSetWindowRgn
  • 如果使用UpdateLayeredWindow,后续不会触发WM_PAINT事件,所有绘制操作都必须由UpdateLayeredWindow完成
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。