我正在编写一个全局钩子DLL,需要在窗口上使用GDI +进行一些绘制以响应事件。我的问题是正在绘制的窗口不断重新绘制自己,所以我绘制的内容会在我想要之前被删除。有什么办法可以阻止窗户在我需要的时候画任何东西吗?
我的钩子目前是一个WH_CALLWNDPROC
钩子。使用GDI +完成绘图以响应消息WM_SIZING
。我使用GDI +绘制到窗口的DC
(即GetWindowDC
)。我正在绘制的内容是正确绘制的,但随着窗口客户区重新绘制,它几乎立即被删除。创建我正在使用的窗口的程序是记事本。当光标闪烁时,我绘制的内容会被删除。
有没有人知道我可以暂时暂停画窗的方式?
谢谢!
答案 0 :(得分:5)
我建议将图形放在与目标窗口重叠的分层窗口中。这似乎是最干净的方式。毕竟,在某种程度的概念,这是窗口管理器的目的:)
答案 1 :(得分:3)
注意:如果您想在我的解决方案中使用主要想法,那么您可以跳到下面的最后一步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
的新变量,并调用BeginPaint
和EndPaint
函数。在这两个函数中,如果&ps
是ps
变量的名称,则引用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_POPUPWINDOW
与WS_POPUPWINDOW
相比,不会绘制任何内容,只需就会删除边框。
我还设置了WS_POPUP
标志来显示窗口。如果您没有设置此标记,则必须在WS_POPUPWINDOW
之后调用WS_VISIBLE
函数,以显示分层窗口。
第6步:在ShowWindow
之后调用CreateWindowEx
函数。将第一个参数设置为从SetLayeredWindowAttributes
函数返回的新创建的分层窗口的句柄。
将第二个参数设置为窗口客户端的背景颜色,即存储在示例的CreateWindowEx
变量中的实心画笔的颜色,用于设置类型为CreateWindowEx
的wcex的hbrBackground成员。
注意,此参数的数据类型为background_brush
,而不是WNDCLASSEX
,即不接受实体画笔,但只是一种颜色!
如果您知道在COLORREF
函数中选择的实体画笔的颜色,那么您就知道如何创建其HBRUSH
结构。
如果您改为调用GetStockObject
函数,那么在调用它之前,请定义COLORREF
结构的新变量,该变量将存储实体画笔的颜色和将来的crKey。之后您可以在两个函数中使用变量:CreateSolidBrush
和COLORREF
(在第二个参数中,即CreateSolidBrush
)。
如果您按照上面的示例操作,并且仅具有类型为SetLayeredWindowAttributes
的{{1}}变量,该变量存储wcex的hbrBackground成员的实体画笔,并且您不为crKey
参数设置任何正确的COLORREF,然后:
为了获得类型为background_brush
到HBRUSH
结构的实心画笔的颜色,那么:
您可以使用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)
(将其FindWindowEx
和MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
成员设置为0)。
然后调用x
函数。将第一个参数设置为目标窗口的句柄(从y
或ClientToScreen
函数返回),并在第二个参数参考中POINT结构,您之前创建的。
调用FindWindow
函数后,POINT结构存储目标窗口最左上边缘的坐标(以屏幕坐标为单位,以像素为单位测量)。
现在您必须检索目标窗口客户端的大小。为此,下一步定义类型为FindWindowEx
结构的新变量。
然后调用ClientToScreen
函数。将第一个参数设置为目标窗口的句柄,并在 second 参数中引用类型为RECT
结构的变量。
调用GetClientRect
函数后,RECT
变量存储目标窗口客户端的宽度和高度,以像素为单位。 GetClientRect
成员存储宽度,RECT
成员存储高度。忽略right
和bottom
类型为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循环条件中尝试SetWindowsHookEx
或CreateThread
函数,以确定在分层窗口或/和目标窗口 时停止销毁或关闭(已完成,不会被最小化或标志性化)。
答案 2 :(得分:2)
没有
相反,为什么不挂钩WM_PAINT
,所以你画画时画画?
答案 3 :(得分:2)
您可以尝试向窗口发送WM_SETREDRAW条消息,wParam
设置为FALSE
暂停绘画,然后设置为TRUE
以恢复正常行为。
但这是一个非常具有侵入性的解决方案。