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

发布于 2024-10-04 01:32:31 字数 391 浏览 6 评论 0原文

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

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

有谁知道我可以暂时暂停窗户绘画的方法吗?

谢谢!

I am writing a global hook DLL that needs to do some drawing using GDI+ on a window in response to an event. My problem is that the window that is being drawn keeps repainting itself, so what I draw gets erased before I want it to. Is there any way I can prevent the window from painting anything for as long as I need to?

My hook currently is a WH_CALLWNDPROC hook. The drawing is done using GDI+ in response to the message WM_SIZING. I drawing using GDI+ onto the window's DC (i.e. GetWindowDC). What I am drawing is drawn correctly, but gets erased almost instantly as the window client area gets repainted. The program that created the window I am drawing on is Notepad. As the cursor blinks, what I have drawn gets erased.

Does anyone know of a way I can temporarily suspend painting of the window?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

千仐 2024-10-11 01:32:31

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

I would suggest putting your graphics in a layered window overlapping the target window. That seems to be the cleanest way. After all, at some level of conception, this is the purpose of the window manager :)

回忆那么伤 2024-10-11 01:32:31

我认为 tenfour 的答案是最好,但他/她必须解释如何做到这一点,而不仅仅是 > 告诉做什么,所以现在将解释如何:

注意:如果您想要 >主要思想在我的解决方案中,然后您可以跳到下面的最后一步9(开始从下到上搜索,直到找到它)。

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

第 1 步: 创建新的 WNDCLASSEX 结构,然后使用 将其 cbSize 成员设置为此结构的大小(以字节为单位) >sizeof 关键字,并将 lpszClassName 成员设置为您想要的任何内容。同时将 hbrBackground 成员设置为 either GetStockObject 函数的返回值,以获取 either 黑色、or 白色< strong>或灰色画笔,ORCreateSolidBrush函数来获取你想要的任何颜色的画笔。选择所有绘图都使用的颜色非常重要!不仅仅是为了您的快乐!

例如:

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

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

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

在其上方切换uMsg参数,并至少添加WM_PAINT案例。
在这种情况下,定义新变量 PAINTSTRUCT,并调用 BeginPaintEndPaint 函数。在这两个函数中,如果 psPAINTSTRUCT 变量的名称,则可以引用 &ps 等变量。

调用BeginPaint函数后,在目标窗口上添加所有你想要的绘图函数,但要确保所有绘图函数中的HDC都是属于目标窗口,而是属于您在窗口过程的first参数中拥有其句柄的窗口,即hWnd! !
首先调用GetDC或GetWindowDC函数来检索hWnd的hDC。

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

不要忘记在 switch 语句块内添加以下代码行:default: return DefWindowProc(hWnd, uMsg, wParam, lParam);

第 4 步:使用 RegisterClassEx 函数注册类。

例如:RegisterClassEx(&wcex);

第 5 步:在您的应用程序中,使用 CreateWindowEx< 创建新的分层窗口/代码> 功能。
非常重要,将此函数的first参数设置为WS_EX_LAYERED,以声明您创建的新窗口是分层的.

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

注意WS_POPUP 将创建没有边框的新窗口。这就是我忽略 wcexhIcon 成员以及将 third 参数设置为 NULL 的原因
您可以将 WS_POPUP 替换为 WS_POPUPWINDOW。结果将是相同的,但 WS_POPUPWINDOW 还会绘制轮廓灰色矩形,整个客户区域的厚度只有 1 个像素。与 WS_POPUPWINDOW 不同,WS_POPUP 不会绘制任何内容,只是删除边框。

我还设置了 WS_VISIBLE 标志来显示窗口。如果不设置此标志,则必须在CreateWindowEx之后调用ShowWindow函数,才能显示分层窗口。

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

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

注意该参数的数据类型是COLORREF,而不是HBRUSH,即它接受实心画笔,但只是一种颜色!

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

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

如果您遵循我上面的示例,并且您拥有类型为 HBRUSHbackground_brush 变量,用于存储 wcex 的 hbrBackground 成员的实心画笔,并且您没有crKey 参数提供任何正确的 COLORREF,那么:

为了获取类型为 HBRUSH 的实心画笔的颜色> 到 COLORREF 结构,然后:

可以使用GetObject函数来获取LOGBRUSH信息
笔刷,其 lbColor 组件将给出颜色值
颜色参考。您可以使用 GetRValue、GetGValue 和 GetBValue 函数来
也获取各个颜色分量。

Pravin 对那些必须知道如何获取 HBRUSHCOLORREF 作为实体画笔的人的回答。

以下是更多信息的链接:

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

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

然后,只需调用 GetDCBrushColor 函数即可获取 SetLayeredWindowAttributes 函数的 crKey 参数的 COLORREF 结构。

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

第7步:调用任一 FindWindow FindWindowEx函数来检索窗口句柄目标窗口(如果您还没有)。

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

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

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

首先创建新的 POINT 结构,并将其坐标设置为 (0; 0)
(将其 xy 成员设置为 0)。

然后调用ClientToScreen函数。将first参数设置为目标窗口的句柄(从FindWindowFindWindowEx函数返回),并在second< /strong> 参数引用您之前创建的 POINT 结构。

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

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

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

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

获得目标窗口客户端的位置(位置和大小)后,现在可以通过调用 任一 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 函数来做到这一点。

例如:(

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.

您也可以选择并复制上面的示例代码,然后将其粘贴到您的代码中):

示例代码可以位于消息循环中,这意味着分层窗口将每时每刻 >,但是如果您想要每时每刻,但每次您做某事或每次发生某事时,目标窗口不属于您的应用程序(例如记事本),那么您将拥有调用SetWindowsHookEx函数。您可以在 MSDN 站点和网络上的其他站点上获取有关该功能的更多信息。

如果您希望这种情况每时每刻都发生,但不是在消息循环中发生,那么您可以在其他线程的其他 while 循环中执行此操作,但您必须为其创建一个函数并调用 CreateThread 函数。同样,您还可以在 MSDN 站点和网络上的其他站点上获取有关该函数的信息

确保当您的应用程序进程退出或<时,线程的 while 循环结束/strong> 终止。

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

tenfour's answer is best in my opinion, but he/she also had to explain how to do it, not just to tell what to do, so now I will explain how:

NOTE: If you want the main idea in my solution, then you can skip to the last step 9 much below (begin search for it from bottom to top until you find it).

Sorry if I exaggerated with my answer and explained and detailed too much, but I promise if you will read properly, you will understand and be satisfied.

Step 1: Create new WNDCLASSEX structure, and then set its cbSize member to the size of this structure, in bytes, by using the sizeof keyword, and set lpszClassName member to whatever you want. Also set the hbrBackground member to the return value of either GetStockObject function to obtain either the black, or white or gray brush, OR CreateSolidBrush function to obtain a brush of any color you want. It is very important to choose the color that all your drawings do not use at all!!! And not just for your pleasure!

For example:

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

Step 2: Define a new function that is a window procedure, and then create new function pointer of it. Then set the lpfnWndProc member of wcex to that function pointer.

Step 3: In the new window procedure function definition, add return 0; which is the last line of code.

Above it switch uMsg parameter, and at least add the WM_PAINT case.
In that case, define new variable of PAINTSTRUCT, and call the BeginPaint and EndPaint functions. In both functions, you reference the variable like &ps, if ps is the name of the PAINTSTRUCT variable.

After you call BeginPaint function, add all the drawing functions you want on the target window, but make sure that the HDC in all drawing functions are not belong to the target window, but to the window which you have its handle in the first parameter of the window procedure, i.e. hWnd!!!
Call either GetDC or GetWindowDC function to retrieve hWnd's hDC first of all.

NOTE: You don't need to retrieve the target window's hDc at all!

Don't forget to add the following code line: default: return DefWindowProc(hWnd, uMsg, wParam, lParam); inside the block of the switch statement.

Step 4: Register the class using the RegisterClassEx function.

For example: RegisterClassEx(&wcex);

Step 5: In your application, create new layered window by using the CreateWindowEx function.
It is very important that you set the first parameter of this function to WS_EX_LAYERED to declare that the new window that you create is layered.

Set the second parameter to the name of the new registered class, for example "myNewClassName", ignore the third parameter (set it to NULL), and in the fourth parameter, set the following flags: WS_POPUP | WS_VISIBLE

NOTE: WS_POPUP will create the new window without border. This is the reason I ignored the hIcon member of wcex and also the third parameter set to NULL
You can replace WS_POPUP with WS_POPUPWINDOW instead. The result will be the same, but WS_POPUPWINDOW also draws outlined gray rectangle, thin with only 1 pixel thickness all over the client area. WS_POPUP does not draw anything in contrast to WS_POPUPWINDOW, just removes the border like it.

I also set the WS_VISIBLE flag to show the window. If you don't set this flag, then you will have to call ShowWindow function after CreateWindowEx, in order to show the layered window.

Step 6: Call SetLayeredWindowAttributes function after CreateWindowEx. Set the first parameter to the handle of the new created layered window returned from the CreateWindowEx function.

Set the second parameter to the background color of your window's client, i.e. to the color of your solid brush stored in the background_brush variable of the example, which you used to set the hbrBackground member of the wcex whose type is WNDCLASSEX.

Note that the data type of this parameter is COLORREF, and not HBRUSH, i.e. it does not accept solid brush, but simply a color!

If you know the color of the solid brush you chose in the GetStockObject function, then you know how to create its COLORREF structure.

If you called CreateSolidBrush function instead, then before you call it, define new variable of COLORREF structure that will store the solid brush's color and the crKey in the future. Later you can use the variable in both functions: CreateSolidBrush and SetLayeredWindowAttributes (in the second parameter, i.e. crKey).

If you followed my example above, and you only have the background_brush variable whose type is HBRUSH that stores the solid brush for the hbrBackground member of wcex, and you do not have any proper COLORREF for the crKey parameter, then:

In order to obtain the color of the solid brush whose type is HBRUSH to COLORREF structure then:

You can use GetObject function to get the LOGBRUSH information of the
brush, lbColor component of which will give the colour value in
COLORREF. You can use GetRValue, GetGValue and GetBValue functions to
get the individual colour components as well.

Pravin's answer to someone that had to know how to get COLORREF of HBRUSH as solid brush.

Here is the link for more information:

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

If you want another way to retrieve the proper COLORREF structure for the crKey parameter of SetLayeredWindowAttributes function, then you can try the GetBkColor function after you have retrieved the hDc of the new created layered window first.
or call SelectObject function instead. Set first parameter to the new layered window's dc, and set second parameter to the hbrBackground member of wcex or bakcground_brush of the example above.

Then just call the GetDCBrushColor function to get the COLORREF structure for the crKey parameter of the SetLayeredWindowAttributes function.

Ignore the third parameter (set bAlpha to NULL), and in the fourth parameter set only the LWA_COLORKEY flag.
crKey is the color of hbrBackground, the entire client of the layered window won't be drawn, except other pixels that their color is not crKey.
Now all what you have to do is to bound the layered window on the target window's client.

Step 7: Call either FindWindow or FindWindowEx function to retrieve the handle of the target window, if you don't have it yet.

Step 8: Write the message loop (or select and copy the piece of code below and paste it to yours):

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Step 9 (last): In the message loop (inside the block of the while loop) no matter where (before TranslateMessage or after DispatchMessage or between them), write another piece of code that will every moment bound the layered window to the target window's client:

First of all create new POINT structure, and set its coordinates to (0; 0)
(Set its x and y members to 0).

Then call ClientToScreen function. Set the first parameter to the handle of the target window (returned from the FindWindow or FindWindowEx function), and in the second parameter reference the POINT structure, which you created before.

After the call to ClientToScreen function, your POINT structure stores the coordinates of the most LEFT TOP edge of the target window (in screen coordinates, measured in pixels unit).

Now you have to retrieve the size of the target window's client. To do so next define new variable whose type is the RECT structure.

Then call GetClientRect function. Set the first parameter to the handle of the target window, and in the second parameter reference the variable whose type is the RECT structure.

After the call to GetClientRect function, the RECT variable stores the width and the height of the target window's client, measured in pixels unit. The right member stores the width, and the bottom member stores the height. Ignore the left and top
members of the variable whose type is the RECT structure. They are not used and not set, and always will have their default value, i.e. 0 only in this case.

After you have the position of the target window's client (the location and the size), now you can bound your layered window to the target window's client by calling either MoveWindow or SetWindowPos function, like this:

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.

If you decided to use the MoveWindow function, and you have problem that you don't see your paintings on the target window, that is because the layered window is below the target window. You will have to change the call to SetWindowPos function instead to solve this problem then.
In the second parameter I invoked the system that I want to place the bounded layered window above the target window. If it still doesn't work, you can change this parameter and set it to either HWND_TOP or HWND_TOPMOST flag.

I am sure after that it should work fine.
Don't forget sometimes to invalidate or redraw or update the layered window to update your drawings on the target window. You can call either InvalidateRect or RedrawWindow or UpdateWindow function to do that.

For example:

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.

(You can select and copy the example code above and paste it to yours too):

The example code can be in the message loop, it means that the layered window will be bound and updated every moment, but if you do not want every moment, but everytime you do something, or everytime something is happening, with the target window, which not belongs to your application, like Notepad, then you'll have to make call to the SetWindowsHookEx function. You can get more information about that function on MSDN site and other sites on the web.

If you want that to be happen every moment, but not in the message loop, then you can do it in other while loop of other thread, but you will have make a function for it and call CreateThread function. Again, you can also get information about that function on MSDN site and other sites on the web

Make sure that the while loop of the thread ends when the process of your application is either exited or terminated.

You can also try either IsWindow or IsWindowVisible function in the while loop condition to determine to stop when the layered window or/and the target window is/are being either destroyed or closed (finished, not going to be minimized or iconic).

黎夕旧梦 2024-10-11 01:32:31

不。

相反,为什么不挂钩 WM_PAINT,这样你就可以在他们绘制时绘制呢?

No.

Instead, why not hook WM_PAINT, so you draw when they draw?

起风了 2024-10-11 01:32:31

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

不过,这是一个相当侵入性的解决方案。

You can try sending WM_SETREDRAW messages to the window, with wParam set to FALSE to suspend painting, then set to TRUE to restore normal behavior.

That's quite an intrusive solution, though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文