在 C++ Win32 中创建透明窗口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3970066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:09:43  来源:igfitidea点击:

Creating a transparent window in C++ Win32

c++winapigdi+transparencyalphablending

提问by adoss

I'm creating what should be a very simple Win32 C++ app who's sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.

我正在创建一个非常简单的 Win32 C++ 应用程序,它的唯一目的是仅显示半透明的 PNG。窗口不应该有任何镀铬,并且所有的不透明度都应该在 PNG 本身中进行控制。

My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.

我的问题是当窗口下的内容更改时窗口不会重新绘制,因此 PNG 的透明区域“卡住”了应用程序最初启动时窗口下的内容。

Here's the line where I setup the new window:

这是我设置新窗口的行:

hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);

For the call to RegisterClassEx, I have this set for the background:

对于对 RegisterClassEx 的调用,我为背景设置了以下设置:

wcex.hbrBackground = (HBRUSH)0;

Here is my handler for WM_PAINT message:

这是我的 WM_PAINT 消息处理程序:

 case WM_PAINT:
 {
   hdc = BeginPaint(hWnd, &ps);
   Gdiplus::Graphics graphics(hdc);
   graphics.DrawImage(*m_pBitmap, 0, 0);
   EndPaint(hWnd, &ps);
   break;
 }

One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.

需要注意的一件事是应用程序始终停靠在屏幕左侧并且不会移动。但是,当用户打开、关闭或移动应用程序下方的窗口时,应用程序下方的内容可能会发生变化。

When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).

当应用程序第一次启动时,它看起来很完美。PNG 的透明(和半透明)部分完美地显示出来。但是,当应用程序下面的背景发生变化时,背景不会更新,它只是与应用程序首次启动时保持不变。事实上,WM_PAINT(或 WM_ERASEBKGND 在背景改变时不会被调用)。

I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.

我已经玩了很长一段时间,并且已经接近 100% 正确,但还没有完全正确。例如,我尝试将背景设置为 (HBRUSH) NULL_BRUSH,并且尝试处理 WM_ERASEBKGND。

What can be done to get the window to repaint when the contents under it changes?

当窗口下的内容发生变化时,如何让窗口重新绘制?

采纳答案by adoss

I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

通过使用本系列第 1 部分和第 2 部分中的代码,我能够完全按照自己的意愿去做:http: //code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindowfunction with the proper BLENDFUNCTIONparameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:

那些博客文章讨论的是在 Win32 C++ 中显示启动画面,但它几乎与我需要做的相同。我相信我缺少的部分是,我需要使用UpdateLayeredWindow具有正确BLENDFUNCTION参数的函数,而不是仅使用 GDI+ 将 PNG 绘制到窗口。我将粘贴下面的 SetSplashImage 方法,该方法可以在上面链接的第 2 部分中找到:

void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
  // get the size of the bitmap
  BITMAP bm;
  GetObject(hbmpSplash, sizeof(bm), &bm);
  SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };

  // get the primary monitor's info
  POINT ptZero = { 0 };
  HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
  MONITORINFO monitorinfo = { 0 };
  monitorinfo.cbSize = sizeof(monitorinfo);
  GetMonitorInfo(hmonPrimary, &monitorinfo);

  // center the splash screen in the middle of the primary work area
  const RECT & rcWork = monitorinfo.rcWork;
  POINT ptOrigin;
  ptOrigin.x = 0;
  ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;

  // create a memory DC holding the splash bitmap
  HDC hdcScreen = GetDC(NULL);
  HDC hdcMem = CreateCompatibleDC(hdcScreen);
  HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);

  // use the source image's alpha channel for blending
  BLENDFUNCTION blend = { 0 };
  blend.BlendOp = AC_SRC_OVER;
  blend.SourceConstantAlpha = 255;
  blend.AlphaFormat = AC_SRC_ALPHA;

  // paint the window (in the right location) with the alpha-blended bitmap
  UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
      hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);

  // delete temporary objects
  SelectObject(hdcMem, hbmpOld);
  DeleteDC(hdcMem);
  ReleaseDC(NULL, hdcScreen);
}

回答by Simon Steele

Use SetLayeredWindowAttributes, this allows you to set a mask color that will become transparent, thus allowing the background through.

使用 SetLayeredWindowAttributes,这允许您设置将变为透明的遮罩颜色,从而允许背景通过。

http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx

http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx

You will also need to configure your window with the layered flag, e.g.

您还需要使用分层标志配置您的窗口,例如

SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

After that it's fairly simple:

之后就相当简单了:

// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);

When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:

当您的 PNG 包含要与背景混合的半透明像素时,这会变得更加复杂。您可以尝试查看这篇 CodeProject 文章中的方法:

Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above

带有适用于 Windows 2000 及更高版本的标准控件的酷炫、半透明和异形对话框