防止在C ++中重新绘制窗口

时间:2010-11-23 21:39:02

标签: c++ windows gdi+ hook repaint

我正在编写一个全局钩子DLL,需要在窗口上使用GDI +进行一些绘制以响应事件。我的问题是正在绘制的窗口不断重新绘制自己,所以我绘制的内容会在我想要之前被删除。有什么办法可以阻止窗户在我需要的时候画任何东西吗?

我的钩子目前是一个WH_CALLWNDPROC钩子。使用GDI +完成绘图以响应消息WM_SIZING。我使用GDI +绘制到窗口的DC(即GetWindowDC)。我正在绘制的内容是正确绘制的,但随着窗口客户区重新绘制,它几乎立即被删除。创建我正在使用的窗口的程序是记事本。当光标闪烁时,我绘制的内容会被删除。

有没有人知道我可以暂时暂停画窗的方式?

谢谢!

4 个答案:

答案 0 :(得分:5)

我建议将图形放在与目标窗口重叠的分层窗口中。这似乎是最干净的方式。毕竟,在某种程度的概念,这是窗口管理器的目的:)

答案 1 :(得分:3)

在我看来,tenfour的回答是最好,但是他/她必须解释如何来做到这一点,而不是只是告诉要做什么,所以现在会解释如何:

注意:如果您想在我的解决方案中使用主要想法,那么您可以跳到下面的最后一步9 (开始搜索)从底部到顶部直到找到它为止。

很抱歉,如果我夸大了我的回答并解释和详细说明,但我保证如果你能正确阅读,你会理解并感到满意。

步骤1:创建新的WNDCLASSEX结构,然后使用cbSize将其sizeof成员设置为此结构的大小(以字节为单位)关键字,并将lpszClassName成员设置为您想要的任何内容。同时将hbrBackground成员设置为 GetStockObject函数的返回值,以获得 黑色,白色灰色画笔, OR CreateSolidBrush功能,以获取您想要的任何颜色的画笔。 非常重要选择所有图纸完全使用的颜色!而且不只是为了你的快乐!

例如:

WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpszClassName = "myNewClassName";
HBRUSH background_brush = GetStockObject(BLACK_BRUSH);
wcex.hbrBackground = background_brush;

第2步:定义一个新窗口过程的函数,然后创建它的新函数指针。然后将lpfnWndProc wcex成员设置为该函数指针。

第3步:在新窗口过程函数定义中,添加return 0;,这是最后代码行。

在它上方切换uMsg参数,并至少添加WM_PAINT个案。 在这种情况下,定义PAINTSTRUCT的新变量,并调用BeginPaintEndPaint函数。在这两个函数中,如果&psps变量的名称,则引用PAINTSTRUCT之类的变量。

之后调用BeginPaint函数,在目标窗口中添加所需的所有绘图函数,但要确保所有绘图函数中的HDC都是属于目标窗口,而是属于窗口过程的第一个参数中处理的窗口,即hWnd! 调用 GetDC GetWindowDC函数来检索hWnd的hDC 首先

注意:您根本不需要检索目标窗口的hDc!

请勿忘记在default: return DefWindowProc(hWnd, uMsg, wParam, lParam);语句的块中添加以下代码行:switch

第4步:使用RegisterClassEx功能注册课程。

例如:RegisterClassEx(&wcex);

第5步:在您的应用中,使用CreateWindowEx功能创建新的分层窗口。 非常重要,您将此函数的第一个参数设置为WS_EX_LAYERED,以声明您创建的新窗口是分层。

第二个参数设置为新注册类的名称,例如"myNewClassName",忽略第三个参数(将其设置为{{1} }),并在第四个参数中设置以下标志:NULL

注意WS_POPUP | WS_VISIBLE将创建新窗口,不带边框。这就是我忽略了WS_POPUP的{​​{1}}成员以及设置为hIcon第三参数的原因 您可以将wcex替换为NULL。结果将是相同的,但WS_POPUP也会绘制轮廓灰色矩形,在整个客户区域内只有1像素的厚度。 WS_POPUPWINDOWWS_POPUPWINDOW相比,会绘制任何内容,只需就会删除边框。

我还设置了WS_POPUP标志来显示窗口。如果您没有设置此标记,则必须在WS_POPUPWINDOW之后调用WS_VISIBLE函数,以显示分层窗口。

第6步:ShowWindow之后调用CreateWindowEx函数。将第一个参数设置为从SetLayeredWindowAttributes函数返回的新创建的分层窗口的句柄。

第二个参数设置为窗口客户端的背景颜色,即存储在示例的CreateWindowEx变量中的实心画笔的颜色,用于设置类型为CreateWindowEx的wcex的hbrBackground成员。

注意,此参数的数据类型为background_brush,而不是WNDCLASSEX,即接受实体画笔,但只是一种颜色!

如果您知道在COLORREF函数中选择的实体画笔的颜色,那么您就知道如何创建其HBRUSH结构。

如果您改为调用GetStockObject函数,那么在调用它之前,请定义COLORREF结构的新变量,该变量将存储实体画笔的颜色和将来的crKey。之后您可以在两个函数中使用变量:CreateSolidBrushCOLORREF(在第二个参数中,即CreateSolidBrush)。

如果您按照上面的示例操作,并且具有类型为SetLayeredWindowAttributes的{​​{1}}变量,该变量存储wcex的hbrBackground成员的实体画笔,并且您crKey参数设置任何正确的COLORREF,然后:

为了获得类型为background_brushHBRUSH结构的实心画笔的颜色,那么:

  

您可以使用GetObject函数获取LOGBRUSH的信息   brush,lbColor组件将给出颜色值   COLORREF。您可以使用GetRValue,GetGValue和GetBValue函数   获得各个颜色组件。

Pravin回答那些必须知道如何crKey HBRUSH为实心画笔的人。

以下是获取更多信息的链接:

http://forums.codeguru.com/showthread.php?423605-Color-of-HBRUSH

如果您想要另一种方法来检索COLORREF函数的COLORREF参数的正确HBRUSH结构,那么您可以在检索到COLORREF函数后尝试crKey函数首先创建新创建的分层窗口的SetLayeredWindowAttributes代替调用GetBkColor函数。将第一个参数设置为新的分层窗口的dc,并将 second 参数设置为wcex的hDc成员或SelectObject的成员上面的例子。

然后只需调用hbrBackground函数即可获得bakcground_brush函数的GetDCBrushColor参数的COLORREF结构。

忽略第三个参数(将crKey设置为SetLayeredWindowAttributes),并在第四个参数中设置bAlpha标志。 crKey是hbrBackground的颜色,除了其颜色不是NULL的其他像素外,不会绘制分层窗口的整个客户端。 现在,您所要做的就是在目标窗口的客户端上绑定分层窗口。

第7步:调用 <{strong> LWA_COLORKEY crKey函数来检索目标窗口的句柄,如果你还没有。

第8步:编写消息循环(或选择并复制下面的代码并粘贴到您的代码中):

FindWindow

步骤9(最后):在消息循环中(在while循环块内),无论在哪里(在TranslateMessage之前或在DispatchMessage之后或之间),写下另一段代码每时每刻将分层窗口绑定到目标窗口的客户端:

首先创建新的POINT结构,并将其坐标设置为(0; 0) (将其FindWindowExMSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } 成员设置为0)。

然后调用x函数。将第一个参数设置为目标窗口的句柄(从yClientToScreen函数返回),并在第二个参数参考中POINT结构,您之前创建的。

调用FindWindow函数后,POINT结构存储目标窗口最左上边缘的坐标(以屏幕坐标为单位,以像素为单位测量)。

现在您必须检索目标窗口客户端的大小。为此,下一步定义类型为FindWindowEx结构的新变量。

然后调用ClientToScreen函数。将第一个参数设置为目标窗口的句柄,并在 second 参数中引用类型为RECT结构的变量。

调用GetClientRect函数后,RECT变量存储目标窗口客户端的宽度和高度,以像素为单位。 GetClientRect成员存储宽度RECT成员存储高度。忽略rightbottom 类型为left结构的变量的成员。它们未被使用且未被设置,并且始终具有其默认值,即仅在这种情况下为0。

获得目标窗口客户端的位置(位置和大小)后,现在可以通过调用 top RECT功能,如下所示:

MoveWindow

如果您决定使用SetWindowPos功能,并且您在目标窗口上看不到您的画作,那就是因为分层窗口 目标窗口。您必须将呼叫更改为MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE); //If you want, you can set the last parameter to FALSE. //OR SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL); //If you want, you can specify some flags in the last parameter. //Where hWnd is the handle of the layered window returned from CreateWindowEx function, and hTargetWnd is the handle of the target window returned from either FindWindow or FindWindowEx function. 功能,然后才能解决此问题。 在第二个参数中,我调用了我想要将有界分层窗口放在目标窗口上的系统。如果仍然无效,您可以更改此参数并将其设置为 MoveWindow SetWindowPos标记。

我确信它应该可以正常工作。 不要忘记有时无效或重绘或更新分层窗口以更新目标窗口上的图形。您可以调用 HWND_TOP HWND_TOPMOST InvalidateRect函数来执行此操作。

例如:

RedrawWindow

(您可以选择并复制上面的示例代码并将其粘贴到您的代码上):

示例代码可以在消息循环中,这意味着分层窗口将被绑定并更新每时每刻,但如果您想要每时每刻,但每当你做某事或每次发生某事时,使用目标窗口(不属于你的应用程序,如记事本),那么你必须调用UpdateWindow函数。您可以在MSDN站点和Web上的其他站点上获得有关该功能的更多信息。

如果你希望每时每刻都发生这种情况,而不是在消息循环中,那么你可以在其他线程的其他循环中执行它,但是你将为它创建一个函数并调用POINT point; ClientToScreen(hTargetWnd, &point); RECT rect; GetClientRect(hTargetWnd, &rect); MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE); //OR SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL); InvalidateRect(hWnd, NULL, TRUE); //If you want, you can set the third parameter to FALSE. 函数。同样,您还可以在MSDN站点和Web上的其他站点上获取有关该功能的信息

当应用程序 终止时,请确保线程的while循环结束。

您还可以在while循环条件中尝试SetWindowsHookExCreateThread函数,以确定在分层窗口或/和目标窗口 时停止销毁关闭(已完成,不会被最小化或标志性化)。

答案 2 :(得分:2)

没有

相反,为什么不挂钩WM_PAINT,所以你画画时画画?

答案 3 :(得分:2)

您可以尝试向窗口发送WM_SETREDRAW条消息,wParam设置为FALSE暂停绘画,然后设置为TRUE以恢复正常行为。

但这是一个非常具有侵入性的解决方案。