title: Correct way to handle and redraw a layered window with a bitmap
body: <p>I am making a script that displays an image in a fully transparent window, so it is just the image itself on the screen, and you can move it around. Here is the result:</p>
  <p><a href="https://i.stack.imgur.com/337Ho.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/337Ho.png" alt="example" /></a></p>
  <p>The simplified code:</p>
  <pre><code>#include &lt;math.h&gt;
  #include &lt;windows.h&gt;
  #include &lt;windowsx.h&gt;
  #include &lt;objidl.h&gt;
  #include &lt;gdiplus.h&gt;
  using namespace Gdiplus;
  #pragma comment (lib,&quot;Gdiplus.lib&quot;)
  
  
  HWND hWnd;
  
  double imageZoom = 0.5;
  
  VOID OnPaint(HWND hWnd, HDC hdc)
  {
      Bitmap image(L&quot;example.png&quot;);
  
      // Get image dimensions
      int imageWidth = image.GetWidth();
      int imageHeight = image.GetHeight();
  
      // Update the dimensions using the image zoom
      imageWidth = static_cast&lt;int&gt;(floor((double)imageWidth * imageZoom));
      imageHeight = static_cast&lt;int&gt;(floor((double)imageHeight * imageZoom));
  
      // Make a DC on which to draw the image
      HDC drawingDC = CreateCompatibleDC(hdc);
      
      // Create a new bitmap for drawing on it
      HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
  
      // Select the new bitmap to draw on it and save the old one
      HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
      
      // Draw image to the newly created DC
      Graphics graphics(drawingDC);
      Rect point(0, 0, imageWidth, imageHeight);
      graphics.DrawImage(&amp;image, point);
  
      // Create a blend function
      BLENDFUNCTION blend = { 0 };
      blend.BlendOp = AC_SRC_OVER;
      blend.SourceConstantAlpha = 255;
      blend.AlphaFormat = AC_SRC_ALPHA;
  
      // Set window info
      RECT coord;
      GetWindowRect(hWnd, &amp;coord);
      POINT windowPosition = { coord.left, coord.top };
      SIZE windowSize = { imageWidth, imageHeight };
      POINT imagePosition = { 0, 0 };
  
      // Call UpdateLayeredWindow
      UpdateLayeredWindow(hWnd, hdc, &amp;windowPosition, &amp;windowSize, drawingDC, &amp;imagePosition, 0, &amp;blend, ULW_ALPHA);
  
      SelectObject(drawingDC, oldBitmap);
      DeleteObject(newBitmap);
      DeleteDC(drawingDC);
      ReleaseDC(NULL, hdc);
  }
  
  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  
  INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
  {
      // Initialize GDI+.
      GdiplusStartupInput gdiplusStartupInput;
      ULONG_PTR           gdiplusToken;
      GdiplusStartup(&amp;gdiplusToken, &amp;gdiplusStartupInput, NULL);
  
  
      MSG                 msg;
      WNDCLASS            wndClass;
  
      wndClass.style = CS_HREDRAW | CS_VREDRAW;
      wndClass.lpfnWndProc = WndProc;
      wndClass.cbClsExtra = 0;
      wndClass.cbWndExtra = 0;
      wndClass.hInstance = hInstance;
      wndClass.hIcon = 0;
      wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      wndClass.lpszMenuName = NULL;
      wndClass.lpszClassName = TEXT(&quot;Sticker&quot;);
  
      RegisterClass(&amp;wndClass);
  
      hWnd = CreateWindowEx(
          WS_EX_LAYERED,
          TEXT(&quot;Sticker&quot;),   // window class name
          TEXT(&quot;Sticker&quot;),  // window caption
          NULL,      // window style
          0,            // initial x position
          0,            // initial y position
          500,            // initial x size
          500,            // initial y size
          NULL,                     // parent window handle
          NULL,                     // window menu handle
          hInstance,                // program instance handle
          NULL);                    // creation parameters
  
      ShowWindow(hWnd, iCmdShow);
  
      while (GetMessage(&amp;msg, NULL, 0, 0))
      {
          TranslateMessage(&amp;msg);
          DispatchMessage(&amp;msg);
      }
  
      GdiplusShutdown(gdiplusToken);
      return msg.wParam;
  }  // WinMain
  bool holding = false;
  LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
      WPARAM wParam, LPARAM lParam)
  {
      HDC          hdc;
      PAINTSTRUCT  ps;
      
      switch (message)
      {
      case WM_CREATE:
          hdc = BeginPaint(hWnd, &amp;ps);
          OnPaint(hWnd, hdc);
          EndPaint(hWnd, &amp;ps);
          return 0;
      case WM_LBUTTONDOWN:
          // In short, this allows moving the image
          PostMessage(hWnd, WM_NCLBUTTONDOWN, 2, 0);
          return 0;
      case WM_PAINT:
          hdc = BeginPaint(hWnd, &amp;ps);
          OnPaint(hWnd, hdc);
          EndPaint(hWnd, &amp;ps);
          return 0;
      case WM_DESTROY:
          PostQuitMessage(0);
          return 0;
      case WM_MBUTTONDOWN:
          // Just to test the size changing
          imageZoom += 0.1;
          RedrawWindow(hWnd, NULL, NULL, RDW_INTERNALPAINT);
          return 0;
      case WM_RBUTTONDOWN:
          PostQuitMessage(0);
          return 0;
      default:
          return DefWindowProc(hWnd, message, wParam, lParam);
      }
  } // WndProc
  </code></pre>
  <p>I am not sure if this is the correct way of using DCs and <code>RedrawWindow()</code>. The problem is, if I <code>RedrawWindow()</code> with a new size of the image (I included a WM_MBUTTONDOWN message to test that), the image gets updated and drawn with the new size, but the size of the layered window seem to stay the same somehow. Here is what it looks like:
  <a href="https://i.stack.imgur.com/UgqJk.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/UgqJk.png" alt="example2" /></a></p>
  <p>I have no idea why this is happening, so maybe someone knows why.</p>
  <p>But the main question is, what is the correct way of creating, updating and rendering a layered window? I suppose this way is not good, but I couldn't find any other understandable example on the internet</p>
  <p><strong>Update:</strong></p>
  <p>I have updated the code. Now it doesn't use WM_PAINT message and only uses UpdateLayeredWindow when needed.</p>
  <pre><code>#include &lt;math.h&gt;
  #include &lt;windows.h&gt;
  #include &lt;windowsx.h&gt;
  #include &lt;objidl.h&gt;
  #include &lt;gdiplus.h&gt;
  using namespace Gdiplus;
  #pragma comment (lib,&quot;Gdiplus.lib&quot;)
  
  
  #include &lt;wincodec.h&gt;
  #include &lt;wincodecsdk.h&gt;
  #pragma comment(lib, &quot;WindowsCodecs.lib&quot;)
  
  
  HWND hWnd;
  
  Bitmap* image;
  
  double imageZoom = 0.5;
  
  int originalWidth;
  int originalHeight;
  
  void firstUpdateLayeredWindow()
  {
      image = Bitmap::FromFile(L&quot;example.png&quot;);
  
      // Get image dimensions
      originalWidth = image-&gt;GetWidth();
      originalHeight = image-&gt;GetHeight();
  
      // Update the dimensions using the image zoom
      int imageWidth = static_cast&lt;int&gt;(floor((double)originalWidth * imageZoom));
      int imageHeight = static_cast&lt;int&gt;(floor((double)originalHeight * imageZoom));
  
      HDC hdc = GetDC(NULL);
  
      // Make a DC which which hold the bitmap for drawing
      HDC drawingDC = CreateCompatibleDC(hdc);
  
      // Create a new bitmap for drawing on it
      HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
  
      // Select the new bitmap onto the drawing DC and save the old one
      HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
  
      // Draw image to the newly created DC
      Graphics graphics(drawingDC);
      Rect point(0, 0, imageWidth, imageHeight);
      graphics.DrawImage(image, point);
  
      // Create a blend function
      BLENDFUNCTION blend = { 0 };
      blend.BlendOp = AC_SRC_OVER;
      blend.SourceConstantAlpha = 255;
      blend.AlphaFormat = AC_SRC_ALPHA;
  
      // Set window info
      RECT coord;
      GetWindowRect(hWnd, &amp;coord);
      POINT windowPosition = { coord.left, coord.top };
      SIZE windowSize = { imageWidth, imageHeight };
      POINT imagePosition = { 0, 0 };
  
      // Call UpdateLayeredWindow
      UpdateLayeredWindow(hWnd, hdc, &amp;windowPosition, &amp;windowSize, drawingDC, &amp;imagePosition, 0, &amp;blend, ULW_ALPHA);
  
      SelectObject(drawingDC, oldBitmap);
      DeleteObject(newBitmap);
      DeleteDC(drawingDC);
      ReleaseDC(NULL, hdc);
  }
  
  void followingUpdateLayeredWindow()
  {
      // Update the dimensions using the image zoom
      int imageWidth = static_cast&lt;int&gt;(floor((double)originalWidth * imageZoom));
      int imageHeight = static_cast&lt;int&gt;(floor((double)originalHeight * imageZoom));
  
      HDC hdc = GetDC(NULL);
      
      // Make a DC on which to draw the image
      HDC drawingDC = CreateCompatibleDC(hdc);
  
      // Create a new bitmap for drawing on it
      HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
  
      // Select the new bitmap to draw on it and save the old one
      HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
  
      // Draw image to the newly created DC
      Graphics graphics(drawingDC);
      Rect point(0, 0, imageWidth, imageHeight);
      graphics.DrawImage(image, point);
      
  
      // Create a blend function
      BLENDFUNCTION blend = { 0 };
      blend.BlendOp = AC_SRC_OVER;
      blend.SourceConstantAlpha = 255;
      blend.AlphaFormat = AC_SRC_ALPHA;
  
      // Set window info
      RECT coord;
      GetWindowRect(hWnd, &amp;coord);
      POINT windowPosition = { coord.left, coord.top };
      SIZE windowSize = { imageWidth, imageHeight };
      POINT imagePosition = { 0, 0 };
  
      // Call UpdateLayeredWindow
      UpdateLayeredWindow(hWnd, hdc, &amp;windowPosition, &amp;windowSize, drawingDC, &amp;imagePosition, 0, &amp;blend, ULW_ALPHA);
  
      SelectObject(drawingDC, oldBitmap);
      DeleteObject(newBitmap);
      DeleteDC(drawingDC);
      ReleaseDC(NULL, hdc);
  }
  
  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  
  INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
  {
      // Initialize GDI+.
      GdiplusStartupInput gdiplusStartupInput;
      ULONG_PTR           gdiplusToken;
      GdiplusStartup(&amp;gdiplusToken, &amp;gdiplusStartupInput, NULL);
      
      MSG                 msg;
      WNDCLASS            wndClass;
  
      wndClass.style = CS_HREDRAW | CS_VREDRAW;
      wndClass.lpfnWndProc = WndProc;
      wndClass.cbClsExtra = 0;
      wndClass.cbWndExtra = 0;
      wndClass.hInstance = hInstance;
      wndClass.hIcon = 0;
      wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wndClass.lpszMenuName = NULL;
      wndClass.lpszClassName = TEXT(&quot;Sticker&quot;);
  
      RegisterClass(&amp;wndClass);
  
      hWnd = CreateWindowEx(
          WS_EX_LAYERED,
          TEXT(&quot;Sticker&quot;),   // window class name
          TEXT(&quot;Sticker&quot;),  // window caption
          WS_DLGFRAME,      // window style
          0,            // initial x position
          0,            // initial y position
          500,            // initial x size
          500,            // initial y size
          NULL,                     // parent window handle
          NULL,                     // window menu handle
          hInstance,                // program instance handle
          NULL);                    // creation parameters
  
  
      // Update the layered window for the first time, attaching a bitmap to it
      firstUpdateLayeredWindow();
  
      ShowWindow(hWnd, iCmdShow);
  
      while (GetMessage(&amp;msg, NULL, 0, 0))
      {
          TranslateMessage(&amp;msg);
          DispatchMessage(&amp;msg);
      }
  
      GdiplusShutdown(gdiplusToken);
      return msg.wParam;
  }  // WinMain
  bool holding = false;
  LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
      WPARAM wParam, LPARAM lParam)
  {
      switch (message)
      {
      case WM_LBUTTONDOWN:
          // In short, this allows moving the image
          PostMessage(hWnd, WM_NCLBUTTONDOWN, 2, 0);
          return 0;
      case WM_MBUTTONDOWN:
      {
          // Just to test the size changing
          imageZoom += 0.1;
  
          // Update the layered window with the function for updating it multiple times
          followingUpdateLayeredWindow();
      }
          return 0;
      case WM_RBUTTONDOWN:
          PostQuitMessage(0);
          return 0;
      case WM_DESTROY:
          PostQuitMessage(0);
          return 0;
      default:
          return DefWindowProc(hWnd, message, wParam, lParam);
      }
  } // WndProc
  </code></pre>
  <p>Unfortunately it still does not work correctly. The image is resized correctly, but the result is the same as on the previous screenshot (the image is clipped). It only happens when increasing the size of the image, which is why I assume that the problem lies in the size of the layered window itself. And an even more weird thing is that if I increase the imageZoom variable a LOT, and then move the window, it actually updates for some reason on its own and draws the whole image in its actual size, but that only happens after the size of the window passes a certain value or something.</p>
  <p>I even tried updating the size of the layered window with a separate <code>UpdateLayeredWindow()</code> function, after updating the bitmap attached to it.</p>
  <p><strong>UPDATE: Solved but no idea why</strong></p>
  <p>I solved the weird behavior where the image got clipped by including a WS_POPUP style to the layered window. Now it works perfectly, though I have no idea why</p>