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.
精彩评论