none
UpdateLayeredWindow not working, window invisible

    Question

  • Hello,

    while I did manage to use UpdateLayeredWindow and create windows from a generated bitmap with full alpha-channel transparency just fine in C# (with p/invoke), I cannot get it working in C++ now.

    I want to create the bitmaps within the application and then use them as window contents. Each bitmap will be one layered window. But when I try it, the window is created and appears in the taskbar, but there's nothing to see on the screen. Also, UpdateLayeredWindow returns with failure code (0), but GetLastError returns 0, telling me there was no error.

    I have a minimal sample application as VC++ 2008 project (C++, Win32 app, empty project, no MFC or anything) with a single .cpp code file (110 lines):

    #include <windows.h>  
     
    #define WINDOW_CLASS L"LayeredTestClass" 
     
    // Function declarations  
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);  
     
    // Entry function  
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)  
    {  
        // Register window class  
        WNDCLASSEX 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          = NULL;  
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
        wcex.lpszMenuName   = NULL;  
        int classLen = wcslen(WINDOW_CLASS) + 2;  
        wcex.lpszClassName = (LPCWSTR) malloc(classLen);  
        wcscpy_s((wchar_t *) wcex.lpszClassName, classLen, WINDOW_CLASS);  
        wcex.hIconSm        = NULL;  
        RegisterClassEx(&wcex);  
     
        int x, y, w, h;  
        x = 0;  
        y = 0;  
        w = 100;  
        h = 30;  
     
        // Create window  
        HWND hWnd;  
        hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST,  
            WINDOW_CLASS, NULL, WS_OVERLAPPEDWINDOW,  
            x, y, w, h, NULL, NULL, hInstance, NULL);  
        if (!hWnd)  
        {  
            MessageBox(NULL, L"Cannot create window.", L"Error", MB_ICONERROR);  
            return -1;  
        }  
        ShowWindow(hWnd, nCmdShow);  
     
        // Prepare bitmap  
        HDC screenDC = GetDC(NULL);  
        HBITMAP bmp;  
          
        HDC memDC = CreateCompatibleDC(screenDC);  
        bmp = CreateCompatibleBitmap(memDC, 100, 30);  
        SelectObject(memDC, bmp);  
        for (int x = 0; x < 100; x++)  
        {  
            for (int y = 0; y < 30; y++)  
            {  
                //SetPixel(memDC, x, y, (255 - (y * 255 / 30)) << 24);  
                SetPixel(memDC, x, y, 0xFF0000FF);  
            }  
        }  
     
        POINT topPos;  
        topPos.x = x;  
        topPos.y = y;  
        SIZE size;  
        size.cx = w;  
        size.cy = h;  
        POINT pointSource;  
        pointSource.x = 0;  
        pointSource.y = 0;  
        BLENDFUNCTION bf;  
        bf.BlendOp = AC_SRC_OVER;  
        bf.BlendFlags = 0;  
        bf.SourceConstantAlpha = 255;  
        bf.AlphaFormat = AC_SRC_ALPHA;  
        if (!UpdateLayeredWindow(hWnd, screenDC, &topPos, &size, memDC, &pointSource, 0, &bf, ULW_ALPHA))  
        {  
            DWORD err = GetLastError();  
            if (err)  
                MessageBox(NULL, L"UpdateLayeredWindow failed.", L"Error", MB_ICONERROR);  
            else 
                MessageBox(NULL, L"UpdateLayeredWindow failed with GetLastError() == 0.", L"Error", MB_ICONERROR);  
            return -1;  
        }  
     
        // Main message loop  
        MSG msg;  
        while (GetMessage(&msg, NULL, 0, 0))  
        {  
            TranslateMessage(&msg);  
            DispatchMessage(&msg);  
        }  
        return (int) msg.wParam;  
    }  
     
    // Window message handler  
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
    {  
        switch (message)  
        {  
            case WM_DESTROY:  
                PostQuitMessage(0);  
                return 0;  
            default:  
                return DefWindowProc(hWnd, message, wParam, lParam);  
        }  
    Thursday, January 29, 2009 10:12 AM

Answers

  • Problem solved.

    If you use CreateCompatibleBitmap, don't use memDC but screenDC instead. Otherwise it would use a wrong colour format and that seems to silently break UpdateLayeredWindow. (Kinda fragile, huh?)

    Also, never ever use SetPixel when dealing with RGBA (32 bpp) bitmaps. It will only produce pixel garbage. I now switched my code to use CreateBitmap instead of CreateCompatibleBitmap, specify width, height and colour depth - and a BYTE array of all pixel colours. This way I can modify each pixel and be sure that each colour/alpha component (each byte) ends up exactly where it belongs. (In my case I can use memset to initialise large areas of my bitmap memory.)

    Should anybody find this thread... (It seems there are exactly 3 people in the world using UpdateLayeredWindow. One am I and another one doesn't read the internet. ;-))

    PS: Did I tell I'm on Windows XP (SP3). If it matters. Maybe Vista does it differently.
    • Marked as answer by LonelyPixel Thursday, February 05, 2009 2:46 PM
    Thursday, February 05, 2009 2:45 PM

All replies

  • Problem solved.

    If you use CreateCompatibleBitmap, don't use memDC but screenDC instead. Otherwise it would use a wrong colour format and that seems to silently break UpdateLayeredWindow. (Kinda fragile, huh?)

    Also, never ever use SetPixel when dealing with RGBA (32 bpp) bitmaps. It will only produce pixel garbage. I now switched my code to use CreateBitmap instead of CreateCompatibleBitmap, specify width, height and colour depth - and a BYTE array of all pixel colours. This way I can modify each pixel and be sure that each colour/alpha component (each byte) ends up exactly where it belongs. (In my case I can use memset to initialise large areas of my bitmap memory.)

    Should anybody find this thread... (It seems there are exactly 3 people in the world using UpdateLayeredWindow. One am I and another one doesn't read the internet. ;-))

    PS: Did I tell I'm on Windows XP (SP3). If it matters. Maybe Vista does it differently.
    • Marked as answer by LonelyPixel Thursday, February 05, 2009 2:46 PM
    Thursday, February 05, 2009 2:45 PM
  • Hello, i have the same problem with UpdateLayeredWindow function. On Vista it works good, but on XP SP3
    i get an error when invoke UpdateLayeredWindow; I dont't understand what i do wrong. Can you help me.
    Here is my method on C#:
     private void UpdateLayeredWindow(Point point, Size size, byte alpha)
            {
                Bitmap bitmap1 = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
                using (Graphics graphics1 = Graphics.FromImage(bitmap1))
                {
                    Rectangle rectangle1;
                    SIZE size1;
                    POINT point1;
                    POINT point2;
    
                    rectangle1 = new Rectangle(0, 0, size.Width, size.Height);
                    OnPaint(new PaintEventArgs(graphics1, rectangle1));
                    IntPtr ptr1 = User32.GetDC(IntPtr.Zero);
                    IntPtr ptr2 = Gdi32.CreateCompatibleDC(ptr1);
                    IntPtr ptr3 = bitmap1.GetHbitmap(Color.FromArgb(0));
                    IntPtr ptr4 = Gdi32.SelectObject(ptr2, ptr3);
                    size1.cx = size.Width;
                    size1.cy = size.Height;
                    point1.x = point.X;
                    point1.y = point.Y;
                    point2.x = 0;
                    point2.y = 0;
    
                    BLENDFUNCTION blendfunction1 = new BLENDFUNCTION();
                    blendfunction1.BlendOp = 255;
                    blendfunction1.BlendFlags = 0;
                    blendfunction1.SourceConstantAlpha = alpha;
                    blendfunction1.AlphaFormat = 1;
    
                    if (User32.UpdateLayeredWindow(Handle, ptr1, ref point1, ref size1, ptr2, ref point2, 0, ref blendfunction1, 2) == 0)
    		{
    		    //Here i get a error
                        Debugger.Break();
    		}
                    Gdi32.SelectObject(ptr2, ptr4);
    
                    User32.ReleaseDC(IntPtr.Zero, ptr1);
                    Gdi32.DeleteObject(ptr3);
                    Gdi32.DeleteDC(ptr2);
                }
            }
    Thanks/
    • Proposed as answer by Kiryl Kovaliov Thursday, June 25, 2009 8:42 AM
    Wednesday, June 24, 2009 3:55 PM
  • Resolved.
        private void UpdateLayeredWindow(Point point, Size size, byte alpha)
            {
                Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    Rectangle rect = new Rectangle(0, 0, size.Width, size.Height);
                    OnPaint(new PaintEventArgs(graphics,rect));
                    IntPtr dC = User32.GetDC(IntPtr.Zero);
                    IntPtr hDC = Gdi32.CreateCompatibleDC(dC);
                    IntPtr zero = IntPtr.Zero;
                    IntPtr hObject = IntPtr.Zero;
                    try
                    {
                        zero = bitmap.GetHbitmap(Color.FromArgb(0));
                        hObject = Gdi32.SelectObject(hDC, zero);
                        SIZE psize = new SIZE(bitmap.Width, bitmap.Height);
                        POINT pprSrc = new POINT(0, 0);
                        POINT pptDst = new POINT(point.X, point.Y);
                        Win32.BLENDFUNCTION pblend = new Win32.BLENDFUNCTION();
                        pblend.BlendOp = 0;
                        pblend.BlendFlags = 0;
                        pblend.SourceConstantAlpha = alpha;
                        pblend.AlphaFormat = 1;
                        User32.UpdateLayeredWindow(Handle, dC, ref pptDst, ref psize, hDC, ref pprSrc, 0, ref pblend, 2);
                    }
                    finally
                    {
                        User32.ReleaseDC(IntPtr.Zero, dC);
                        if (zero != IntPtr.Zero)
                        {
                            Gdi32.SelectObject(hDC, hObject);
                            Gdi32.DeleteObject(zero);
                        }
                        Gdi32.DeleteDC(hDC);
                    }
                }
    }

    • Proposed as answer by Kiryl Kovaliov Thursday, June 25, 2009 8:43 AM
    Thursday, June 25, 2009 8:42 AM