Win32 使用 UpdateLayeredWindow 创建透明窗体

举报
福州司马懿 发表于 2022/11/15 23:57:06 2022/11/15
【摘要】 准备工作首先新建 ——> Window桌面向导 ——> Windows桌面应用程序给程序起个名字,然后创建项目单击绿色三角形按钮(运行),运行后是一个空白窗体 代码瘦身初始代码如下// ShapedWindow.cpp : 定义应用程序的入口点。//#include "framework.h"#include "ShapedWindow.h"#define MAX_LOADSTRING 1...

函数原型

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桌面应用程序

图片.png

给程序起个名字,然后创建项目

图片.png

单击绿色三角形按钮(运行),运行后是一个空白窗体

图片.png

代码瘦身

初始代码如下

// 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.删除菜单栏按钮响应事件

默认的程序有个菜单栏,菜单栏上“文件”和“帮助”。

图片.png
图片.png

这两个按钮的响应,在 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;
}

运行一下,现在窗口张这样

图片.png

制作异形图片

这里详见我的另一篇博客 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);

为了优化用户体验,要加两样东西

  1. 鼠标按下允许拖动窗口
  2. 按下 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;
}

最后效果如下,可发现一点很奇怪的是顶部空白并没有透明,但鼠标挪过去却能直接点击到后面的软件内容,这点很奇怪。但起码说明透明确实是实现了,只不过显示为透明的白色

图片.png

修复透明白

一直百思不得其解,想了一晚上。后面觉得C++在处理这个分层函数时,会不会是为了复用旧版处理RGB的函数,将透明通道,与RGB通道分开处理

猜想:

  • 所有的像素点,本质上都是一串字节数组。初始值通过 memset 将其全设置为0,也就是黑色
  • 而白色有是RGB三种颜色达到满值,叠加而来的。
  • bmp图像是XRGB模式,即使带了透明通道,也不会被大部分看图软件识别为透明

推测:

  • 用透明度来判断事件能否透过当前窗口
  • 逐像素判断,如果非黑则上色

因此,就有了上一篇博客,将透明图片的背景改为黑色,于是顺利实现透明效果

图片.png

作图方法,详见我的另一篇博客 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;
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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