2016-12-12 5 views
2

Я слежу за MSDN's guide to using the DWM API to extend the frame into the client area, но переделал, чтобы экспериментировать с ним, когда у меня что-то работает.Почему мое окно DwmExtendFrameIntoClientArea() не создает границы DWM?

Однако, когда я его создаю, используя cl wincompositiontest.cpp в Visual Studio 2013 x64 Нативная команда командной строки, программа не рисует границы окна. Стандартные кнопки окна работают: я вижу свечение кнопок и подсказок кнопки Windows 7, и нажатие на кнопки делает их соответствующие действия (поэтому я могу закрыть это окно). Но ничего не работает: Я не могу переместить или изменить размер окна с краями, а границы не рисовать, а рисовать белый:

Window

DwmExtendFrameIntoClientArea() возвращается S_OK.

Что происходит? Это работает в Windows 7 x64, работающем в VirtualBox; Я передал код другу, работающему под управлением Windows 7 на реальном оборудовании, и получил те же результаты. Благодарю.

// 12 december 2016 
#define UNICODE 
#define _UNICODE 
#define STRICT 
#define STRICT_TYPED_ITEMIDS 
// get Windows version right; right now Windows Vista 
// unless otherwise stated, all values from Microsoft's sdkddkver.h 
// TODO is all of this necessary? how is NTDDI_VERSION used? 
// TODO plaform update sp2 
#define WINVER   0x0600 /* from Microsoft's winnls.h */ 
#define _WIN32_WINNT  0x0600 
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ 
#define _WIN32_IE   0x0700 
#define NTDDI_VERSION  0x06000000 
#include <windows.h> 
#include <commctrl.h> 
#include <uxtheme.h> 
#include <windowsx.h> 
#include <shobjidl.h> 
#include <d2d1.h> 
#include <d2d1helper.h> 
#include <dwrite.h> 
#include <usp10.h> 
#include <msctf.h> 
#include <textstor.h> 
#include <olectl.h> 
#include <shlwapi.h> 
#include <dwmapi.h> 
#include <stddef.h> 
#include <stdint.h> 
#include <string.h> 
#include <wchar.h> 
#include <stdarg.h> 
#include <stdio.h> 
#include <math.h> 
#include <float.h> 
#include <inttypes.h> 
#include <vector> 
#include <map> 
#include <string> 

#pragma comment(linker, \ 
    "\"/manifestdependency:type='Win32' " \ 
    "name='Microsoft.Windows.Common-Controls' " \ 
    "version='6.0.0.0' " \ 
    "processorArchitecture='*' " \ 
    "publicKeyToken='6595b64144ccf1df' " \ 
    "language='*'\"") 
#pragma comment(lib, "user32.lib") 
#pragma comment(lib, "kernel32.lib") 
#pragma comment(lib, "gdi32.lib") 
#pragma comment(lib, "comctl32.lib") 
#pragma comment(lib, "uxtheme.lib") 
#pragma comment(lib, "dwmapi.lib") 

#define HR(call) printf("%s -> 0x%I32X\n", #call, call) 

LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    RECT r; 
    MARGINS margins; 
    BOOL dwmHandled; 
    LRESULT lResult; 

    dwmHandled = DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult); 
    switch (uMsg) { 
    case WM_CREATE: 
     GetWindowRect(hwnd, &r); 
     SetWindowPos(hwnd, NULL, 
      r.left, r.top, 
      r.right - r.left, r.bottom - r.top, 
      SWP_FRAMECHANGED); 
     // TODO if we pass SWP_NOOWNERZORDER || SWP_NOZORDER, the default frame is not correctly inhibited 
     break; 
    case WM_ACTIVATE: 
     margins.cxLeftWidth = 8; 
     margins.cxRightWidth = 8; 
     margins.cyBottomHeight = 20; 
     margins.cyTopHeight = 27; 
     HR(DwmExtendFrameIntoClientArea(hwnd, &margins)); 
     break; 
    case WM_NCCALCSIZE: 
     if (wParam != (WPARAM) FALSE) 
      return 0; 
     break; 
    case WM_CLOSE: 
     PostQuitMessage(0); 
     break; 
    } 
    if (dwmHandled) 
     return lResult; 
    return DefWindowProcW(hwnd, uMsg, wParam, lParam); 
} 

int main(void) 
{ 
    WNDCLASSW wc; 
    HWND mainwin; 
    MSG msg; 

    ZeroMemory(&wc, sizeof (WNDCLASSW)); 
    wc.lpszClassName = L"mainwin"; 
    wc.lpfnWndProc = wndproc; 
    wc.hInstance = GetModuleHandle(NULL); 
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); 
    RegisterClassW(&wc); 

    mainwin = CreateWindowExW(0, 
     L"mainwin", L"Main Window", 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, CW_USEDEFAULT, 
     400, 400, 
     NULL, NULL, GetModuleHandle(NULL), NULL); 

    ShowWindow(mainwin, SW_SHOWDEFAULT); 
    UpdateWindow(mainwin); 

    while (GetMessageW(&msg, NULL, 0, 0)) { 
     TranslateMessage(&msg); 
     DispatchMessageW(&msg); 
    } 
    return 0; 
} 
+1

Для * WM_NCCALCSIZE * MSDN: * Когда WPARAM ИСТИНА, просто возвращая значение 0 без обработки прямоугольников NCCALCSIZE_PARAMS заставит область клиента, чтобы изменить размер до размера окна, в том числе оконной рамы. Это будет ** удалять ** окно и элементы заголовка из вашего окна **, оставляя только отображаемую область клиента **. * –

+0

@ γηράσκωδ'αείπολλάδιδασκόμε да, но посмотрите на страницу MSDN, которую я связал: она просто устанавливает 'pncsp-> rgrc [0]' для себя, не меняя его, затем инструктирует 'WndProc()' возвращать 0 без вызова 'DefWIndowProc()'. Если есть что-то тонкое, что мне не хватает (установка 'pncsp-> rgrc [0]' сама по себе не то же самое, что ничего не делать?)? – andlabs

+0

Измените цвет * hbrBackground * на черный –

ответ

2

Для совместимости с Windows 10 край слева/справа/снизу должен быть равен нулю.

Обычно вам нужно изменить область заголовка. Таким образом, только изменить значение margins.cyTopHeight

Расчет границ по умолчанию ширины для данного стиля окна с помощью AdjustWindowRectEx в и передать эти значения WM_NCCALCSIZE, это покажет границы по умолчанию.

DwmDefWindowProc требуется в ответ на WM_NCHITTEST и WM_NCMOUSELEAVE только (но, может быть, как вы установили его лучше для будущей совместимости)

Вы также должны обрабатывать WM_PAINT для кнопок системы для корректного отображения. Скорее всего, проще скрыть системные кнопки и нарисовать собственные кнопки, поэтому панель заголовков полностью настраивается с собственным цветом фона.

Ниже приведен пример для Windows 10, он должен работать для Windows 7. Приложение должно быть распознано DPI, иначе будут проблемы с отображением с границами.

void paint_caption(HWND hWnd, HDC hdc, int caption_height) 
{ 
    RECT rc; 
    GetClientRect(hWnd, &rc); 
    rc.bottom = caption_height; 

    HDC memdc = CreateCompatibleDC(hdc); 
    BITMAPINFOHEADER bmpInfoHdr = 
     { sizeof(BITMAPINFOHEADER), rc.right, -rc.bottom, 1, 32 }; 
    HBITMAP hbitmap = 
     CreateDIBSection(memdc, (BITMAPINFO*)(&bmpInfoHdr), DIB_RGB_COLORS, 0, 0, 0); 
    HGDIOBJ oldbitmap = SelectObject(memdc, hbitmap); 

    //Note, GDI functions don't support alpha channel, they can't be used here 
    //Use GDI+, BufferedPaint, or DrawThemeXXX functions 

    BitBlt(hdc, 0, 0, rc.right, caption_height, memdc, 0, 0, SRCCOPY); 
    SelectObject(memdc, oldbitmap); 
    DeleteObject(hbitmap); 
    DeleteDC(memdc); 
} 

LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    //static MARGINS margins = { -1,-1,100,-1 }; 
    static MARGINS margins = { 0,0,100,0 }; 
    static RECT border_thickness = { 0 }; 

    switch(uMsg) 
    { 
    case WM_CREATE: 
     if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME) 
     { 
      AdjustWindowRectEx(&border_thickness, 
       GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL); 
      border_thickness.left *= -1; 
      border_thickness.top *= -1; 
     } 
     else if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) 
     { 
      border_thickness = { 1,1,1,1 }; 
     } 

     DwmExtendFrameIntoClientArea(hwnd, &margins); 
     SetWindowPos(hwnd, NULL, 0, 0, 0, 0, 
      SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); 

     break; 

    case WM_NCCALCSIZE: 
     if(lParam) 
     { 
      NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam; 
      sz->rgrc[0].left += border_thickness.left; 
      sz->rgrc[0].right -= border_thickness.right; 
      sz->rgrc[0].bottom -= border_thickness.bottom; 
      return 0; 
     } 
     break; 

    case WM_PAINT: 
    { 
     PAINTSTRUCT ps; 
     HDC hdc = BeginPaint(hwnd, &ps); 

     //paint caption area 
     paint_caption(hwnd, hdc, margins.cyTopHeight); 

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

    case WM_NCHITTEST: 
    { 
     //handle close/minimize/maximize/help button 
     LRESULT lResult; 
     if (DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult)) 
      return lResult; 

     //do default processing, except change the result for caption area 
     lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); 
     if(lResult == HTCLIENT) 
     { 
      POINT pt = { LOWORD(lParam), HIWORD(lParam) }; 
      ScreenToClient(hwnd, &pt); 
      if(pt.y < border_thickness.top) return HTTOP; 
      if(pt.y < margins.cyTopHeight) return HTCAPTION; 
     } 

     return lResult; 
    } 

    case WM_NCMOUSELEAVE: 
    { 
     LRESULT lResult; 
     if(DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult)) 
      return lResult; 
     break; 
    } 

    case WM_CLOSE: 
     PostQuitMessage(0); 
     break; 
    } 

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

Результат в Windows 10:

enter image description here

Win 7:

enter image description here

+0

Документация для 'DwmDefWindowProc()' говорит, что она также должна быть вызвана для 'WM_NCMOUSELEAVE', предположительно потому, что это сообщение, в котором аура свечения на Windows 7 уходит. Что делает передачу 0 отличной от результата передачи 'AdjustWindowRectEx()' на нулевом прямоугольнике, что приведет к необходимости вставки? А как насчет Windows 10 неправильно рисует? Я загружу в Windows 10 сейчас, чтобы проверить код, который я должен видеть, что происходит, но поскольку я не использую его регулярно, я мог бы что-то упустить ... – andlabs

+0

Я хотел написать 'WM_NCMOUSELEAVE', а не' WM_NCMOUSMOVE'. Исправлено. Вы можете сделать расчет для 'AdjustWindowRectEx' в зависимости от того, что вы хотите. Вы можете положить в ноль, затем 'AdjustWindowRectEx' возвращает небольшие отрицательные значения, а затем отрицает эти значения, чтобы найти толщину границы. –

+0

А, ладно; Я вижу, что вы подразумеваете под границами, которые должны быть установлены специально в Windows 10; 'AdjustWindowRectEx()' все еще возвращает значения Windows 7/8. Но подождите, разве это не параметр совместимости, который можно переопределить манифестом? Кажется, я вспоминаю, что в прошлом здесь обсуждались. Я все еще не знаю, что делает кнопки с надписью неправильными. (Также FWIW все, что я делаю в опубликованном мной кодеке, происходит на странице MSDN, с которой я связан, включая вызов 'DwmDefWindowProc()' вверху 'wndproc()'. Я уже расширил приведенный выше код. 't знаю, почему черная кисть, хотя.) – andlabs

1

Хорошо, так что я исследовал немного больше, пытаясь включить γηράσκω δ»αεί предложения πολλά διδασκόμε и Barmak Shemirani с тем, что я первоначально (основанный на MSDN), чтобы прийти с чем-то, что, по-видимому, справляется со всеми делами в будущем. Исчерпывающее тестирование показывает, что нижеприведенный код отлично справляется с необычным поведением мыши на клавиатуре Windows 10 (даже верхняя граница, которая даже в обычных окнах запускается только на синем краю, а не слегка выключена, как и остальные). Он выглядит корректно на Windows 10, Windows 8.1 и Windows 7. И более того, максимизация теперь работает правильно (или, кажется, работает правильно, я не уверен, есть ли тонкая разница, которую я пропустил или нет)!

Самое большое отличие от кода Бармака в том, что я вытаскиваю WM_NCCALCSIZE результаты из DefWindowProc() и просто отфильтровываю верхний результат, позволяя мне взять верх над верхним краем и позволить Windows решить, насколько велика остальная ситуация. Это также означает, что мне не нужно отслеживать border_thickness, как это делает Бармак. Кроме того, он очищает ошибку, которую я заметил с помощью WM_PAINT, и размер окна меньше перекрывается в границах, но я не знаю, почему и как ...

Переменная defWindowProcFirst - это то, что контролирует, какое поведение используется. Если вы установите его на FALSE, вы получите пред-бармакское поведение, в котором были несоответствия Windows 10.

Есть несколько дополнительных вещей, чтобы отметить:

  • Ниже не обрабатывает WM_PRINTCLIENT еще.
  • Ниже не возвращается HTCLIENT для реальной клиентской области; что не должно быть слишком трудно из затруднительного ...
  • Значение DefWindowProcW() возвращения из WM_NCCALCSIZE не используется, что означает rgrc[1], rgrc[2] и lppos никогда не трогали, и мы могли бы упустить некоторые оптимизации; Мне нужно, чтобы понять, как справиться с этими
  • Кучу остаточному несделанного а

Но учитывая все это, кажется, работает нормально :) я должен вернуться и проверить код MSDN неизмененной, хотя; Я полагаю, что это даст мне аналогичные результаты defWindowProcFirst = FALSE, хотя ...

Спасибо, тем временем!

// 12 december 2016 
#define UNICODE 
#define _UNICODE 
#define STRICT 
#define STRICT_TYPED_ITEMIDS 
// get Windows version right; right now Windows Vista 
// unless otherwise stated, all values from Microsoft's sdkddkver.h 
// TODO is all of this necessary? how is NTDDI_VERSION used? 
// TODO plaform update sp2 
#define WINVER   0x0600 /* from Microsoft's winnls.h */ 
#define _WIN32_WINNT  0x0600 
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ 
#define _WIN32_IE   0x0700 
#define NTDDI_VERSION  0x06000000 
#include <windows.h> 
#include <commctrl.h> 
#include <uxtheme.h> 
#include <windowsx.h> 
#include <shobjidl.h> 
#include <d2d1.h> 
#include <d2d1helper.h> 
#include <dwrite.h> 
#include <usp10.h> 
#include <msctf.h> 
#include <textstor.h> 
#include <olectl.h> 
#include <shlwapi.h> 
#include <dwmapi.h> 
#include <stddef.h> 
#include <stdint.h> 
#include <string.h> 
#include <wchar.h> 
#include <stdarg.h> 
#include <stdio.h> 
#include <math.h> 
#include <float.h> 
#include <inttypes.h> 
#include <vector> 
#include <map> 
#include <string> 

#pragma comment(linker, \ 
    "\"/manifestdependency:type='Win32' " \ 
    "name='Microsoft.Windows.Common-Controls' " \ 
    "version='6.0.0.0' " \ 
    "processorArchitecture='*' " \ 
    "publicKeyToken='6595b64144ccf1df' " \ 
    "language='*'\"") 
#pragma comment(lib, "user32.lib") 
#pragma comment(lib, "kernel32.lib") 
#pragma comment(lib, "gdi32.lib") 
#pragma comment(lib, "comctl32.lib") 
#pragma comment(lib, "uxtheme.lib") 
#pragma comment(lib, "dwmapi.lib") 

#define HR(call) printf("%s -> 0x%I32X\n", #call, call) 

struct metrics { 
    RECT windowRect; 
    MARGINS resizeFrameInsets; 
    MARGINS nonclientInsets; 
    MARGINS realNonclientInsets; 
    RECT effectiveClientRect; 
    RECT relativeClientRect; 
}; 

BOOL defWindowProcFirst = TRUE; 

// TODO this is incorrect when maximized 
void getMetrics(HWND hwnd, struct metrics *m) 
{ 
    RECT r; 

    GetWindowRect(hwnd, &(m->windowRect)); 

    // get the margins of the resize frame 
    ZeroMemory(&r, sizeof (RECT)); 
    AdjustWindowRectEx(&r, 
     GetWindowStyle(hwnd) & ~WS_CAPTION, 
     FALSE, 
     GetWindowExStyle(hwnd)); 
    m->resizeFrameInsets.cxLeftWidth = -r.left; 
    m->resizeFrameInsets.cyTopHeight = -r.top; 
    m->resizeFrameInsets.cxRightWidth = r.right; 
    m->resizeFrameInsets.cyBottomHeight = r.bottom; 

    // get non-client insets 
    ZeroMemory(&r, sizeof (RECT)); 
    AdjustWindowRectEx(&r, 
     GetWindowStyle(hwnd), 
     FALSE, 
     GetWindowExStyle(hwnd)); 
    m->nonclientInsets.cxLeftWidth = -r.left; 
    m->nonclientInsets.cyTopHeight = -r.top; 
    m->nonclientInsets.cxRightWidth = r.right; 
    m->nonclientInsets.cyBottomHeight = r.bottom; 
    if (defWindowProcFirst) { 
     m->nonclientInsets.cxLeftWidth = 0; 
     m->nonclientInsets.cxRightWidth = 0; 
     m->nonclientInsets.cyBottomHeight = 0; 
    } 

    // give the top 2.5x the room so we can shove stuff in there 
    m->realNonclientInsets = m->nonclientInsets; 
    m->realNonclientInsets.cyTopHeight *= 2.5; 

    // compute the effective client rect 
    m->effectiveClientRect = m->windowRect; 
    m->effectiveClientRect.left += m->realNonclientInsets.cxLeftWidth; 
    m->effectiveClientRect.top += m->realNonclientInsets.cyTopHeight; 
    m->effectiveClientRect.right -= m->realNonclientInsets.cxRightWidth; 
    m->effectiveClientRect.bottom -= m->realNonclientInsets.cyBottomHeight; 

    // and compute it relative to the window's real client rect 
    m->relativeClientRect = m->effectiveClientRect; 
    MapWindowRect(NULL, hwnd, &(m->relativeClientRect)); 

#if 0 
// for debugging 
printf("***\n"); 
#define PRINTRECT(r) ((int)((r).left)), ((int)((r).top)), ((int)((r).right)), ((int)((r).bottom)) 
printf("window rect %d %d %d %d\n", PRINTRECT(m->windowRect)); 
    ZeroMemory(&r, sizeof (RECT)); 
    AdjustWindowRectEx(&r, 
     GetWindowStyle(hwnd), 
     FALSE, 
     GetWindowExStyle(hwnd)); 
r.left=-r.left;r.top=-r.top; 
printf("edge insets %d %d %d %d\n", PRINTRECT(r)); 
HR(DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &r, sizeof (RECT))); 
printf("DWMWA_EXTENDED_FRAME_BOUNDS %d %d %d %d\n", PRINTRECT(r)); 
{HMODULE m; 
m=LoadLibraryW(L"kernel32.dll"); 
if(m == NULL)printf("unknown os\n"); 
// TODO this doesn't work; apparently the function is really in one of the api-ms-core* DLLs... 
else if(GetProcAddress(m,"VirtualAllocFromApp")!=NULL)printf("windows 10\n"); 
else if(GetProcAddress(m,"GetPackageApplicationIds")!=NULL)printf("windows 8.1\n"); 
else if(GetProcAddress(m,"GetSystemTimePreciseAsFileTime")!=NULL)printf("windows 8\n"); 
else printf("windows 7\n");} 
printf("\n"); 
#endif 
} 

HWND rebarHost; 
HWND rebar; 

const char *htnames[] = { 
    "HTERROR", 
    "HTTRANSPARENT", 
    "HTNOWHERE", 
    "HTCLIENT", 
    "HTCAPTION", 
    "HTSYSMENU", 
    "HTGROWBOX", 
    "HTMENU", 
    "HTHSCROLL", 
    "HTVSCROLL", 
    "HTMINBUTTON", 
    "HTMAXBUTTON", 
    "HTLEFT", 
    "HTRIGHT", 
    "HTTOP", 
    "HTTOPLEFT", 
    "HTTOPRIGHT", 
    "HTBOTTOM", 
    "HTBOTTOMLEFT", 
    "HTBOTTOMRIGHT", 
    "HTBORDER", 
    "HTOBJECT", 
    "HTCLOSE", 
    "HTHELP", 
}; 

LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    struct metrics m; 
    HDC dc; 
    PAINTSTRUCT ps; 
    BOOL dwmHandled; 
    LRESULT lResult; 

    dwmHandled = DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lResult); 
    getMetrics(hwnd, &m); 
    switch (uMsg) { 
    case WM_CREATE: 
     SetWindowPos(hwnd, NULL, 
      m.windowRect.left, m.windowRect.top, 
      m.windowRect.right - m.windowRect.left, m.windowRect.bottom - m.windowRect.top, 
      SWP_FRAMECHANGED); 
     // TODO if we pass SWP_NOOWNERZORDER || SWP_NOZORDER, the default frame is not correctly inhibited 

     rebarHost = CreateWindowExW(0, 
      L"rebarHost", L"", 
      WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 
      m.realNonclientInsets.cxLeftWidth, 
      m.nonclientInsets.cyTopHeight, 
      m.windowRect.right - m.windowRect.left - 
       m.realNonclientInsets.cxLeftWidth - m.realNonclientInsets.cxRightWidth, 
      m.realNonclientInsets.cyTopHeight - m.nonclientInsets.cyTopHeight, 
      hwnd, NULL, GetModuleHandle(NULL), NULL); 

     rebar = CreateWindowExW(0, 
      REBARCLASSNAMEW, L"", 
      WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | RBS_VARHEIGHT | CCS_NODIVIDER, 
      0, 0, 0, 0, 
      rebarHost, NULL, GetModuleHandle(NULL), NULL); 

     { 
      REBARBANDINFOW rb; 

      ZeroMemory(&rb, sizeof (REBARBANDINFOW)); 
      rb.cbSize = sizeof (REBARBANDINFOW); 
      rb.fMask = RBBIM_TEXT; 
      rb.lpText = L"This is a rebar"; 
      HR((HRESULT) SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rb))); 
     } 

     SendMessageW(rebar, RB_SETWINDOWTHEME, 0, 
      (LPARAM) L"NavbarComposited"); 

     break; 
    case WM_ACTIVATE: 
     HR(DwmExtendFrameIntoClientArea(hwnd, &(m.realNonclientInsets))); 
     break; 
    case WM_NCCALCSIZE: 
     if (wParam != (WPARAM) FALSE) { 
      NCCALCSIZE_PARAMS *op = (NCCALCSIZE_PARAMS *) lParam; 
      NCCALCSIZE_PARAMS np; 

      if (!defWindowProcFirst) 
       return 0; 
      np = *op; 
      DefWindowProcW(hwnd, uMsg, wParam, (LPARAM) (&np)); 
      printf("old %ld %ld %ld %ld\nnew %ld %ld %ld %ld\n", 
       op->rgrc[0].left, op->rgrc[0].top, op->rgrc[0].right, op->rgrc[0].bottom, 
       np.rgrc[0].left, np.rgrc[0].top, np.rgrc[0].right, np.rgrc[0].bottom); 
      op->rgrc[0].left = np.rgrc[0].left; 
      op->rgrc[0].right = np.rgrc[0].right; 
      op->rgrc[0].bottom = np.rgrc[0].bottom; 
      return 0; 
     } 
     break; 
    case WM_NCHITTEST: 
     if (dwmHandled) 
      return lResult; 
     // DWM did not handle it; we have to do it ourselves 
     if (defWindowProcFirst) { 
      lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam); 
      if (lResult != HTCLIENT) { 
       printf("them %s\n", htnames[lResult + 2]); 
       return lResult; 
      } 
     } 
     { 
      POINT p; 

      p.x = GET_X_LPARAM(lParam); 
      p.y = GET_Y_LPARAM(lParam); 

      lResult = HTNOWHERE; 
      if (p.y >= m.windowRect.top && p.y < (m.windowRect.top + m.resizeFrameInsets.cyTopHeight)) 
       lResult = HTTOP; 
      else if (p.y >= m.effectiveClientRect.bottom && p.y < m.windowRect.bottom) 
       lResult = HTBOTTOM; 

      if (p.x >= m.windowRect.left && p.x < m.effectiveClientRect.left) 
       switch (lResult) { 
       case HTNOWHERE: 
        lResult = HTLEFT; 
        break; 
       case HTTOP: 
        lResult = HTTOPLEFT; 
        break; 
       case HTBOTTOM: 
        lResult = HTBOTTOMLEFT; 
        break; 
       } 
      else if (p.x >= m.effectiveClientRect.right && p.x < m.windowRect.right) 
       switch (lResult) { 
       case HTNOWHERE: 
        lResult = HTRIGHT; 
        break; 
       case HTTOP: 
        lResult = HTTOPRIGHT; 
        break; 
       case HTBOTTOM: 
        lResult = HTBOTTOMRIGHT; 
        break; 
       } 

      if (lResult == HTNOWHERE) 
       if (p.y >= (m.windowRect.top + m.resizeFrameInsets.cyTopHeight) && p.y < m.effectiveClientRect.top) 
        lResult = HTCAPTION; 

      if (defWindowProcFirst) 
       printf("us %s\n", htnames[lResult + 2]); 
      if (lResult != HTNOWHERE) 
       return lResult; 
     } 
     // we can't handle it; give it to DefWindowProcW() and hope for the best 
     break; 
    case WM_SIZE: 
     // TODO if defWindowProcFirst == FALSE, this seems to be wrong when shrinking the size on the right or bottom edges 
     // TODO without this call, the WM_PAINT never fills new areas 
     // we may need to handle WM_WINDOWPOSCHANGED and compute new metrics from there 
     // TODO what happens with defWindowProcFirst == TRUE? do we need to do anything else special? is this needed? 
     InvalidateRect(hwnd, &(m.relativeClientRect), FALSE); 
     break; 
    case WM_PAINT: 
     dc = BeginPaint(hwnd, &ps); 
     FillRect(dc, &(m.relativeClientRect), (HBRUSH) (COLOR_BTNFACE + 1)); 
     EndPaint(hwnd, &ps); 
     break; 
    case WM_CLOSE: 
     PostQuitMessage(0); 
     break; 
    } 
    if (dwmHandled) 
     return lResult; 
    return DefWindowProcW(hwnd, uMsg, wParam, lParam); 
} 

int main(void) 
{ 
    INITCOMMONCONTROLSEX icc; 
    WNDCLASSW wc; 
    HWND mainwin; 
    MSG msg; 

    ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); 
    icc.dwSize = sizeof (INITCOMMONCONTROLSEX); 
    icc.dwICC = (ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_UPDOWN_CLASS | ICC_PROGRESS_CLASS | ICC_HOTKEY_CLASS | ICC_ANIMATE_CLASS | ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES | ICC_COOL_CLASSES | ICC_INTERNET_CLASSES | ICC_PAGESCROLLER_CLASS | ICC_NATIVEFNTCTL_CLASS | ICC_STANDARD_CLASSES | ICC_LINK_CLASS); 
    InitCommonControlsEx(&icc); 

    ZeroMemory(&wc, sizeof (WNDCLASSW)); 
    wc.lpszClassName = L"mainwin"; 
    wc.lpfnWndProc = wndproc; 
    wc.hInstance = GetModuleHandle(NULL); 
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); 
    RegisterClassW(&wc); 

    ZeroMemory(&wc, sizeof (WNDCLASSW)); 
    wc.lpszClassName = L"rebarHost"; 
    wc.lpfnWndProc = DefWindowProcW; 
    wc.hInstance = GetModuleHandle(NULL); 
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); 
    RegisterClassW(&wc); 

    mainwin = CreateWindowExW(0, 
     L"mainwin", L"Main Window", 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, CW_USEDEFAULT, 
     400, 400, 
     NULL, NULL, GetModuleHandle(NULL), NULL); 

    ShowWindow(mainwin, SW_SHOWDEFAULT); 
    UpdateWindow(mainwin); 

    while (GetMessageW(&msg, NULL, 0, 0)) { 
     TranslateMessage(&msg); 
     DispatchMessageW(&msg); 
    } 
    return 0; 
}