Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DwmExtendFrameIntoClientArea strange behaviour on Windows 10

I'm having some trouble extending the window frames using DwmExtendFrameIntoClientArea on Windows 10. The images below show the behaviour I'm getting:

enter image description here

The white titlebar colour is extended from the top, while from the sides and the bottom it extends the coloured edge of the window.

If I set the margins all to -1 to extend the frames all the way, the window is filled with white and loses its coloured edge altogether:

enter image description here

This result is very inconsistent, I was expecting the white colour to be extended on all sides of the window, similar to the way the coloured frame is extended in Windows 8, or the glass is extended in Windows 7 and Vista.

I've tried searching online, but I haven't been able to find any similar issues.

Here is the code I'm using:

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

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int main(int argc, char **argv)
{

    HINSTANCE hInstance = GetModuleHandle(NULL);
    MSG  msg;    
    HWND hwnd;
    WNDCLASSW wc;
    int message;

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.lpszClassName = L"Window";
    wc.hInstance     = hInstance;
    wc.hbrBackground = GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpfnWndProc   = WndProc;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassW(&wc);
    hwnd = CreateWindowW(wc.lpszClassName, L"Window",
                         WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                         100, 100, 350, 250, NULL, NULL, hInstance, NULL);  

    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    while(1) {
        message = GetMessageW(&msg, NULL, 0, 0);
        if(message == -1)
        {
            char x[100];
            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), x, 100, NULL);
            puts(x);
            abort();
        }
        else if(message == 0) break;

        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg) 
    {
        case WM_ACTIVATE:
        {
            MARGINS m = {50, 50, 50, 50};
            HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &m);
            if(!SUCCEEDED(hr))
            {
                char x[100];
                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 
                              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), x, 100, NULL);
                puts(x);
                abort();
            }
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;      
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

Am I doing something wrong or is this just an issue with Windows 10? Thanks in advance for any help!

Edit: The code I posted works perfectly with both Aero Lite and the high contrast themes on Windows 10, but not with the default Windows 10 theme.

like image 562
YmFzZTY0 Avatar asked Dec 22 '15 11:12

YmFzZTY0


1 Answers

When the frame has been extended into the client area, you need to make sure that your client area paint procedure draws pure black anywhere the glass should be.

From MSDN:

The easiest way to ensure that the extended frames are visible is to paint the entire client region black. To accomplish this, initialize the hbrBackground member of your WNDCLASS or WNDCLASSEX structure to the handle of the stock BLACK_BRUSH. The following image shows the same standard frame (left) and extended frame (right) shown previously. This time, however, hbrBackground is set to the BLACK_BRUSH handle obtained from the GetStockObject function.

enter image description here

Edit: I tried to reproduce your scratch program as closely as possible:

program ScratchProgram;

uses
  Windows,
  Messages,
  DwmApi,
  UxTheme;

{ Window Procedure }
function WndProc(hWnd: HWND; uiMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
    m: TMargins;
begin
    case uiMsg of
    WM_ACTIVATE:
        begin
            m.cxLeftWidth := 50;
            m.cxRightWidth := 50;
            m.cyTopHeight := 50;
            m.cyBottomHeight := 50;
            DwmExtendFrameIntoClientArea(hWnd, m);
        end;
    WM_DESTROY:
        begin
            PostQuitMessage(0);
            Result := 0;
            Exit;
        end;
    end;

    Result := DefWindowProc(hWnd, uiMsg, wParam, lParam);
end;

function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nShowCmd: Integer): Integer; stdcall;
var
    wc: WNDCLASS;
    msg: TMSG;
    hWindow: HWND;
    instance: HINST;
begin
    instance := GetModuleHandle(nil);

    wc.style := CS_HREDRAW or CS_VREDRAW;
    wc.cbClsExtra := 0;
    wc.cbWndExtra := 0;
    wc.lpszClassName := 'Window';
    wc.hInstance := instance;
    wc.hbrBackground := GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName := nil;
    wc.lpfnWndProc := @WndProc;
    wc.hCursor := LoadCursor(0, IDC_ARROW);
    wc.hIcon := LoadIcon(0, IDI_APPLICATION);

    RegisterClass(wc);

    hWindow := CreateWindow(
            wc.lpszClassName,                  // Class Name
            'Window',                          // Title
            WS_OVERLAPPEDWINDOW or WS_VISIBLE, // Style
            100, 100,                          // Position
            350, 250,                          // Size
            0,                                 // Parent
            0,                                 // No menu
            instance,                          // Instance
            nil);                              // No special parameters

    ShowWindow(hWindow, SW_SHOW);

    while (GetMessage(msg, 0, 0, 0)) do
    begin
        TranslateMessage(msg);
        DispatchMessage(msg);
    end;

    Result := 0;
end;

begin
    WinMain(hInstance, hPrevInst, CmdLine, CmdShow);
end.

And it works for me:

enter image description here

Whatever the problem is, the code doesn't look conceptually wrong.

Perhaps calling conventions, or a failure where you don't expect it (RegisterClass for example, or the use of GetModuleHandle over the instance handle passed to WinMain, or calling DwmExtendFrameIntoClientArea even when the form is being deactivated).

like image 93
Ian Boyd Avatar answered Nov 15 '22 01:11

Ian Boyd