좋아, 그래서 나는 γηράσκω δ 'αεί 통합하기 위해 노력하고, 좀 더 조사 πολλά διδασκόμε와 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;
}
* WM_NCCALCSIZE * MSDN :의 wParam이 TRUE 인 경우 * 단순히 클라이언트 영역은 창틀 포함하는 윈도우의 크기에 크기를 변경시킬 것이다 NCCALCSIZE_PARAMS 사각형을 처리하지 않고 0을 반환. ** ** 창 프레임과 캡션 항목을 창에서 제거 ** 클라이언트 영역 만 남겨두기 ** ** –
@ γηράσκωδ'εείπολλάδιδασκόμε 예, 링크 된 MSDN 페이지를 살펴보십시오 : 'pncsp-> rgrc [0]'을 변경하지 않고 자신에게 보내고,'DefWIndowProc()'를 호출하지 않고'WndProc()'에 0을 반환하도록 명령한다. 미묘한 것이 없다면 (pncsp-> rgrc [0]'자체를 설정하는 것은 아무것도하지 않는 것과 같지 않습니까?)? – andlabs
* hbrBackground *의 색상을 검정색으로 바꿉니다. –