绘制一个标准框架和透明内容的窗口

jtu*_*ney 7 windows winapi

对于xtow,我想绘制一个顶级窗口,其中包含标准的非客户区域,客户区域填充了一个具有alpha通道的位图.

我现在发现我在Windows 7上实现此方法的方法,但在Windows 8.1上无法正确呈现,在移动或最大化时留下窗口内容的图像.

为了调查,我做了一个简单的测试程序alpha测试,其中

  • 使用DwmEnableBlurBehindWindow()设置非交叉模糊区域,以便窗口中的alpha值得到尊重,而不会模糊.
  • 使用BitBlt()将带有alpha的位图复制到其中.
//
// g++ alpha-test.cc -o alpha-test -mwindows -lgdiplus -ldwmapi
//

#define  _WIN32_WINNT 0x0600

#include <assert.h>
#include <stdio.h>
#include <windows.h>
#include <gdiplus.h>
#include <dwmapi.h>

int width = 360;
int height = 360;
HBITMAP hBitmap;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
    {
    case WM_PAINT:
      {
        PAINTSTRUCT ps;
        HDC hdcUpdate = BeginPaint(hWnd, &ps);

        RECT rc;
        GetClientRect(hWnd, &rc);
        HBRUSH hbrush = CreateSolidBrush(RGB(0,0,0));
        FillRect(hdcUpdate, &rc, hbrush);
        DeleteObject(hbrush);

        HDC hdcMem = CreateCompatibleDC(hdcUpdate);
        HBITMAP hbmpold = (HBITMAP)SelectObject(hdcMem, hBitmap);

        if (!BitBlt(hdcUpdate, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, hdcMem, 0, 0, SRCCOPY))
          {
            printf("BitBlt failed: 0x%08x\n", (int)GetLastError());
          }

        SelectObject(hdcMem, hbmpold);
        DeleteDC(hdcMem);

        EndPaint(hWnd, &ps);
      }
      return 0;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
  ULONG_PTR gdiplusToken;
  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

  LPCTSTR szWindowClass = "TransparentClass";

  // Register class
  WNDCLASSEX wcex = {0};
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style          = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC;
  wcex.lpfnWndProc    = WndProc;
  wcex.cbClsExtra     = 0;
  wcex.cbWndExtra     = 0;
  wcex.hInstance      = hInstance;
  wcex.hIcon          = NULL;
  wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
  wcex.lpszClassName  = szWindowClass;
  wcex.hIconSm        = NULL;
  wcex.hbrBackground  = (HBRUSH)CreateSolidBrush(0x00000000);
  RegisterClassEx(&wcex);

  // Create window
  HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW,
                             szWindowClass,
                             "Transparent Window",
                             WS_OVERLAPPED | WS_SYSMENU,
                             CW_USEDEFAULT, CW_USEDEFAULT, width, height,
                             NULL, NULL, hInstance, NULL);

  Gdiplus::Bitmap *m_pImage = Gdiplus::Bitmap::FromFile(L"sample.png", FALSE);
  Gdiplus::Color bg(0,0,0,0);
  m_pImage->GetHBITMAP(bg, &hBitmap);
  assert(hBitmap);

  DWM_BLURBEHIND blurBehind = { 0 };
  blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
  blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
  blurBehind.fEnable = TRUE;
  blurBehind.fTransitionOnMaximized = FALSE;
  DwmEnableBlurBehindWindow(hWnd, &blurBehind);
  DeleteObject(blurBehind.hRgnBlur);

  ShowWindow(hWnd, SW_SHOW);

  // Main message loop
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

  return (int)msg.wParam;
}
Run Code Online (Sandbox Code Playgroud)

这真的坏了吗?我该如何修复我的代码?这是Windows的bug /限制吗?

是否有另一种方法可以实现我的目标,即将带有alpha的位图绘制到带边框的窗口中?

更新: 我使用Direct2D和Direct3D进行了一些测试,用位图填充客户区,但是它们以相同的方式错误渲染.

Jon*_*ter 1

DWM 不再进行模糊处理(此功能被认为太耗电,并在 Windows 8 中被删除),因此我猜测它不再正确地合成窗口的背景区域 - 因此您不会获得 Windows 7 中提供的“自动”Alpha 效果。

老实说,这是一种不寻常的绘制透明窗口的方法。使用UpdateLayeredWindow是“官方”方式,并且可以在 Windows 8 和 Windows 7 上工作。