What's the point of DeferWindowPos? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050706-26/?p=35023
Raymond Chen 在 2005年7月6日
DeferWindowPos 的作用是什么?
简要
文章讨论了
DeferWindowPos
函数的用途,即一次性移动多个子窗口,以减少窗口移动时发生的重绘数量。
正文
DeferWindowPos
函数的目的是一次性移动多个子窗口。这在一定程度上减少了窗口移动时发生的重绘数量。
拿几个月前的 DC 刷子示例,进行以下修改:
HWND g_hwndChildren[2];
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) {
const static COLORREF s_rgclr[2] = { RGB(255,0,0), RGB(0,255,0) };
for (int i = 0; i < 2; i++) {
g_hwndChildren[i] = CreateWindow(TEXT("static"), NULL,
WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
hwnd, (HMENU)IntToPtr(s_rgclr[i]), g_hinst, 0);
if (!g_hwndChildren[i]) return FALSE;
}
return TRUE;
}
注意我使用控件 ID 来保存所需的颜色。在选择背景颜色时,我们会检索它。
HBRUSH OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type) {
Sleep(500);
SetDCBrushColor(hdc, (COLORREF)GetDlgCtrlID(hwndChild));
return GetStockBrush(DC_BRUSH);
}
HANDLE_MSG(hwnd, WM_CTLCOLORSTATIC, OnCtlColor);
我加了一个半秒的睡眠,这样更容易观察绘制过程。
void OnSize(HWND hwnd, UINT state, int cx, int cy) {
int cxHalf = cx/2;
SetWindowPos(g_hwndChildren[0],
NULL, 0, 0, cxHalf, cy,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
SetWindowPos(g_hwndChildren[1],
NULL, cxHalf, 0, cx-cxHalf, cy,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
}
我们将两个子窗口并排放置在客户区域。首先,我们使用 SetWindowPos
函数来定位窗口。
编译并运行这个程序,一旦它启动,点击最大化按钮。仔细观察绿色矩形的哪些部分被重绘。
现在让我们将定位代码更改为使用 DeferWindowPos
函数。延迟窗口定位函数的使用模式如下:
HDWP hdwp = BeginDeferWindowPos(n);
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 1
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 2
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 3
…
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // n
if (hdwp) EndDeferWindowPos(hdwp);
这里有一些关键点:
你传递给
BeginDeferWindowPos
函数的值是你打算移动的窗口数量。即使这个值错了也没关系,但正确的值会减少内部重新分配的数量。
DeferWindowPos
返回的值存储回hdwp
,因为返回值不一定与最初传递的值相同。如果延迟预订需要执行重新分配,DeferWindowPos
函数返回指向新的延迟信息的句柄;旧的延迟信息不再有效。更重要的是,如果延迟失败,旧的延迟信息将被销毁。这与realloc
函数不同,如果重新分配失败,原始对象保持不变。模式p = realloc(p, …)
会导致一个内存泄漏,但模式hdwp = DeferWindowPos(hdwp, …)
不会。
第二点很重要,但许多人都弄错了。
现在你可能对这个函数感到害怕,让我们将重新定位代码更改为利用延迟窗口定位。它真的没有那么难。(将这些更改保存到一个新文件中,不过。我们将想要并排运行旧版本和新版本。)
void OnSize(HWND hwnd, UINT state, int cx, int cy) {
HDWP hdwp = BeginDeferWindowPos(2);
int cxHalf = cx/2;
if (hdwp) hdwp = DeferWindowPos(hdwp, g_hwndChildren[0],
NULL, 0, 0, cxHalf, cy,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if (hdwp) hdwp = DeferWindowPos(hdwp, g_hwndChildren[1],
NULL, cxHalf, 0, cx-cxHalf, cy,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if (hdwp) EndDeferWindowPos(hdwp);
}
编译并运行这个程序,再次启动后,最大化窗口并观察哪些区域重新绘制。你会发现新版本与旧版本相比,重绘的区域略有减少。