开发者

Prevent repainting of window in C++

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 drawin开发者_StackOverflow社区g 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!


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 :)


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).


No.

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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜