2016-12-12 1 views
2

나는 MSDN's guide to using the DWM API to extend the frame into the client area을 따라 왔지만 다시 시도해 볼 수 있으므로 실험 해 볼 수 있습니다.왜 내 DwmExtendFrameIntoClientArea() 창에서 DWM 테두리를 그리지 않습니까?

그러나 Visual Studio 2013 x64 네이티브 도구 명령 줄에서 cl wincompositiontest.cpp을 사용하여 빌드하면 프로그램에서 창 테두리를 그립니다. 표준 창 단추는 작동합니다. Windows 7 단추의 광선과 도구 설명을 볼 수 있으며 단추를 클릭하면 해당 작업이 수행되므로이 ​​창을 닫을 수 있습니다. 그러나 다른 아무것도 작동 : 나는 이동하거나 가장자리에 창 크기를 조정하고, 테두리 대신 흰색 그리기, 그리기하지 않습니다 수 없습니다

Window

DwmExtendFrameIntoClientArea() 반환 S_OK합니다.

무슨 일 이니? 이것은 VirtualBox에서 실행되는 Windows 7 x64에 있습니다. 실제 하드웨어에서 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이 TRUE 인 경우 * 단순히 클라이언트 영역은 창틀 포함하는 윈도우의 크기에 크기를 변경시킬 것이다 NCCALCSIZE_PARAMS 사각형을 처리하지 않고 0을 반환. ** ** 창 프레임과 캡션 항목을 창에서 제거 ** 클라이언트 영역 만 남겨두기 ** ** –

+0

@ γηράσκωδ'εείπολλάδιδασκόμε 예, 링크 된 MSDN 페이지를 살펴보십시오 : 'pncsp-> rgrc [0]'을 변경하지 않고 자신에게 보내고,'DefWIndowProc()'를 호출하지 않고'WndProc()'에 0을 반환하도록 명령한다. 미묘한 것이 없다면 (pncsp-> rgrc [0]'자체를 설정하는 것은 아무것도하지 않는 것과 같지 않습니까?)? – andlabs

+0

* hbrBackground *의 색상을 검정색으로 바꿉니다. –

답변

2

Windows 10과의 호환성을 위해 왼쪽/오른쪽/아래쪽 여백은 0이어야합니다.

일반적으로 제목 표시 줄 영역 만 변경해야합니다. 따라서 margins.cyTopHeight의 값을 변경하십시오.

AdjustWindowRectEx을 사용하여 지정된 창 스타일의 기본 테두리 너비를 계산하고이 값을 WM_NCCALCSIZE에 전달하면 기본 테두리가 표시됩니다.

DwmDefWindowProc은 (하지만 어쩌면 당신이 그것을 설정 한 방법은 미래의 호환성을 위해 더 나은)

당신은 또한 시스템 버튼이 제대로 표시 할 WM_PAINT을 처리해야 WM_NCHITTESTWM_NCMOUSELEAVE에 응답해야합니다. 아마도 시스템 버튼을 숨기고 버튼을 그리는 것이 더 쉬울 것이므로 제목 표시 줄은 자체 배경색으로 완전히 맞춤 설정되어 있습니다.

다음은 Windows 10의 예입니다. Windows 7에서 작동해야합니다. 응용 프로그램은 DPI에서 인식해야합니다. 그렇지 않으면 테두리에 작은 표시 문제가 있습니다. 윈도우 10에서

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); 
} 

결과 :

enter image description here

승 7 :

enter image description here

+0

'DwmDefWindowProc()'에 대한 문서에서는 WM_NCMOUSELEAVE에 대해서도 호출해야한다고 말하고 있습니다. 아마도 Windows 7의 글로우 오라가 사라지는 메시지이기 때문입니다. 무엇이 제로 rect에서'AdjustWindowRectEx()'를 전달한 결과와 다른 0을 전달하게할까요? 그러면 꼭 필요한 insets가 될까요? 그리고 Windows 10은 그게 잘못 그릴 수 있습니까? 내가 무슨 일이 일어날 지 알아야 할 코드를 테스트하기 위해 지금 Windows 10으로 부팅 할 것이지만, 정기적으로 사용하지 않기 때문에 나는 뭔가를 놓칠 수있다 ... – andlabs

+0

나는 WM_NCMOUSMOVE가 아니라'WM_NCMOUSELEAVE'를 작성하려고했다. 그것을 정정했다. 당신이 원하는만큼 AdjustWindowRectEx를위한 계산을 할 수 있습니다. 0으로 놓으면, AdjustWindowRectEx는 작은 음의 값을 되돌려주고, 그 값을 무효로하여 테두리 두께를 찾습니다. –

+0

아, 알았어요. Windows 10에서 특별하게 설정해야하는 테두리가 무엇인지 알 수 있습니다. 'AdjustWindowRectEx()'는 여전히 Windows 7/8 값을 반환합니다. 하지만 잠깐, 매니페스트로 덮어 쓸 수있는 호환성 설정이 아니 었나요? 나는 과거에 여기서 토론을 떠올리게하는 것을 상기 한 것 같다. 그래도 캡션 버튼을 잘못 만드는 이유는 아직도 모르겠다. (또한 FWIW 내가 게시 한 코드에서하고있는 모든 것은'wndproc()'의 맨 위에'DwmDefWindowProc()'을 호출하는 것을 포함하여 링크 된 MSDN 페이지에서 가져온 것입니다. 왜 검은 붓인지 모르겠다.) – andlabs

1

좋아, 그래서 나는 γηράσκω δ 'αεί 통합하기 위해 노력하고, 좀 더 조사 πολλά διδασκόμε와 Barmak Shemirani의 제안은 내가 처음에 (MSDN에 기반한) 무엇을 가지고 왔는지에 관한 것입니다 미래를 보장 할 수있는 방법으로 모든 사건을 처리하는 것처럼 보입니다. 철저한 테스트는 아래 코드가 Windows 10의 이상한 테두리 마우스 오버 동작을 완벽하게 처리한다는 것을 보여줍니다 (정상적인 윈도우에서도 파란색 가장자리에서만 트리거되며 다른 것과 약간 다름이 아님). Windows 10, Windows 8.1 및 Windows 7에서 올바르게 나타납니다. 그리고 최대화는 이제 제대로 작동합니다 (또는 제대로 작동하는 것처럼 보입니다. 누락 된 부분과 미묘한 차이가 있는지 확실하지 않습니다)!

Barmak 코드의 가장 큰 차이점은 WM_NCCALCSIZE 결과를 DefWindowProc()에서 가져 와서 상단 결과를 필터링하여 상단 가장자리를 제어하고 나머지는 얼마나 큰지를 결정하도록 허용한다는 것입니다. 또한 Barmak처럼 border_thickness을 추적 할 필요가 없음을 의미합니다. 또한, WM_PAINT로 알아 낸 버그를 정리하고 테두리가 겹치지 않게 창 크기를 조정합니다. 그러나 이유 또는 방법을 알지 못합니다.

defWindowProcFirst 변수는 사용 된 동작을 제어합니다. FALSE으로 설정하면 Windows 10 불일치가있는 Barmak 이전 동작이 발생합니다.

몇 가지 추가가주의하는 것이 있습니다

  • 는 아래 아직 WM_PRINTCLIENT을 처리하지 않습니다.
  • 다음은 실제 클라이언트 영역에 대해 HTCLIENT를 반환하지 않습니다. 그
  • WM_NCCALCSIZE에서 DefWindowProcW() 반환 값이 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; 
} 
관련 문제