GDI+ erroneous drawing under Windows explorer window - winapi

I am using GDI* plus to do custom drawing, but I have a drawing error when my window gets drawn under a windows explorer window, it looks like this:
As u can see just under the explorer window.. the colors are weird.. the top right are buttons and the checkbox is also a button .. "Are you.." is a static control.. they are all inherited and implemented as custom controls.. when receiving WM_PAINT. I also use a buffered image in WM_PAINT.. anyway.. I can't explain this, any ideas? It works fine when not under windows explorer window, as u can see in the left site of the window.
The checkbox is a button control, use a MSG_OCM_DRAWITEM(OnPaintImpl) handler in which I get the DC as so:
LRESULT OnPaintImpl(UINT ctrlID, LPDRAWITEMSTRUCT lpDIS)
{
ATLASSERT(GdiPlus::IsInitialized());
OnPaintGdiPlus(lpDIS->hDC, lpDIS->rcItem, lpDIS->itemState);
return S_OK;
}
and in my OnPaintGdiPlus(HDC hDC, CRect rc, UINT nState) I do this:
CMemoryDC dcMem(hDC, rc);
Graphics graphics(dcMem);
Rect rcClient = GdiPlus::GetRect(rc);
Everything else is just calling basic drawing functions from the graphics.
In the dialog I get WM_PAINT and handle it here:
LRESULT OnPaintImpl(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(&ps);
if (ps.rcPaint.right || ps.rcPaint.bottom) // draw rect is defined
{
if (GdiPlus::IsInitialized())
OnPaintGdiPlus(hDC, ps, GetClientRect(m_hWnd));
else
::MessageBox(m_hWnd, L"Graphics mode not initialized properly!", L"Graphics", MB_OK | MB_ICONWARNING);
}
EndPaint(&ps);
return S_OK;
}
Based on this hDC I create a Graphics object and paint using it.
Any other stuff I should add here?

Related

WTL ScrollWindow efficient painting

With a class derived from CScrollWindowImpl
void Scroll::DoPaint(CDCHandle hDC)
{
if ( _MemDC==NULL)
return;
RECT r;
//I'd like to update r with rcPaint from the DC's PAINTSTRUCT here
hDC.BitBlt(r.left, r.top, r.right-r.left, r.bottom-r.top,
*_MemDC, r.left, r.top, SRCCOPY);
}
What is the most efficient way of painting the window contents with a WTL ScrollWindow?
The CScrollImpl WM_PAINT doesn't pass the CPaintDC to the derived class OnPaint, which has the PAINTSTRUCT m_ps member with the update RECT rcPaint member.
LRESULT CScrollImpl::OnPaint(UINT, WPARAM wParam, LPARAM, BOOL&) {
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
if(wParam != NULL) { // The HDC is sometimes passed in
CDCHandle dc = (HDC)wParam;
dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
pT->DoPaint(dc);
}
else {
CPaintDC dc(pT->m_hWnd);
dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
pT->DoPaint(CDCHandle(dc));
}
return 0;
}
So I've handled the WM_PAINT message and the best I've achieved so far is BitBlt the whole _MemDC when scrolled, but during an unscrolled redraw to only BitBlt the invalidated rectangle.
UPDATE:
Sometimes rcPaint is larger than the rectangle of the MemDc so the increase in efficiency is negligible and buggy.
Is it really the most efficient or not, however note that WTL Samples include Samples\BmpView project which features CScrollWindowImpl use in CBitmapView class, which displays a visible part of a [supposedly large] image. Specifically, it overrides background erase and paint handlers and demostrates how to do BitBlt for the requested for painting part only.

Preventing an edit control from rendering

I've got an edit control where I'd like to disable any rendering, but leave it as interactive (similar to an owner-drawn control). Right now, I have subclassed the Window and captured WM_PAINT, such as
SetWindowSubclass(box, [](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR, DWORD_PTR) -> LRESULT {
if (msg != WM_PAINT)
return DefSubclassProc(hwnd, msg, wparam, lparam);
PAINTSTRUCT paint;
BeginPaint(hwnd, &paint);
EndPaint(hwnd, &paint);
return 0;
}, 0, 0);
This has worked fine to disable almost all of the edit box's rendering. However, when interacting with it, it still occasionally clears the area where it would render, causing airspace issues when I am rendering in that space with Direct3D.
How can I complete the subclass such that the edit box never renders anything?

Problem with drawing custom Windows controls

I'm playing around with drawing my own custom controls using the uxTheme library in Windows, and I can't work out why my control doesn't look like the regular Windows control that (supposedly) uses the same theme I'm using:
The above image shows a standard Windows ComboBox (top) and my custom control drawn using the ComboBox theme (bottom). What I can't work out is why the border from my control is a different shape and colour to the standard control.
In my class constructor I open the theme data:
mComboTheme = OpenThemeData( hwnd, L"COMBOBOX" );
And then in the handler for WM_PAINT I'm just drawing two parts of the ComboBox components:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
RECT client;
if( GetUpdateRect( hwnd, &ps.rcPaint, false ))
{
hdc = BeginPaint( hwnd, &ps );
GetClientRect( hwnd, &client );
if( IsThemeBackgroundPartiallyTransparent( mComboTheme, CP_BACKGROUND, CBXS_HOT ))
{
DrawThemeParentBackground( hwnd, hdc, &ps.rcPaint );
}
DrawThemeBackground( mComboTheme, hdc, CP_BACKGROUND, CBXS_HOT, &client, &ps.rcPaint );
client.left = client.right - 20;
DrawThemeBackground( mComboTheme, hdc, CP_DROPDOWNBUTTONRIGHT, CBXSR_HOT, &client, ps.rcPaint );
EndPaint( *this, &ps );
}
break;
}
Any suggestions as to why these two controls don't look the same would be greatly appreciated.
Thanks,
James
You called DrawThemeBackground with CP_BACKGROUND and CP_DROPDOWNBUTTONRIGHT. Perhaps you should also call it with CP_BORDER if you want the border to match the standard combobox?

(Windows API) WM_PAINT Mouse problems

I created a window with the following flags to overlay a d3d application:
WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_LAYERED
I proceeded with colorkeying the window for transperacy and all worked well.
However once I began drawing on it using GDI an unforeseen problem occurred:
For some reason the mouse events (especially movement) are not passed correctly through the window when WM_PAINT is in progress, and so it appears as though the mouse and the keyboard for that matter lag. the FPS is fine, this is some API problem, I suspect that for some reason the keyboard/mouse messages are not handled as they should while the WM_PAINT is in progress, because the slower the timer is set to the less jerking there is.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
{
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
break;
}
case WM_CREATE:
{
SetTimer(hwnd, ID_TIMER, 10, NULL);
break;
}
case WM_TIMER:
{
InvalidateRect(hwnd, 0, 1);
break;
}
case WM_PAINT:
{
paint(hwnd);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
and
void paint (HWND hwnd)
{
PAINTSTRUCT Ps;
HDC hdc = BeginPaint(hwnd, &Ps);
SetBkColor(hdc, RGB(0,0,0));
SetBkMode(hdc, TRANSPARENT);
LOGBRUSH log_brush;
log_brush.lbStyle = BS_NULL;
HBRUSH handle_brush = CreateBrushIndirect(&log_brush);
SelectObject(hdc, handle_brush);
..........................................
DeleteObject(font);
DeleteObject(pen);
DeleteObject(handle_brush);
EndPaint(hwnd, &Ps);
}
Thank you for any help you may be able to give.
WM_PAINT messages are never delivered to your window unless someone calls UpdateWindow or there are no keyboard or mouse messages in your input queue.
Once you begin processing WM_PAINT, if a keyboard or mouse message arrives, it just sits in your queue until you are done with WM_PAINT. So What you are describing isn't possible.
If your WM_PAINT code takes a long time to execute, that could cause jerkiness, but you say that's not a problem so perhaps it's your handling of WM_ERASEBKGND? I don't see that code, but I do see that when you InvalidateRect, you are passing TRUE as the last parameter which means that you want the background to be erased.
If you don't handle WM_ERASEBKGND, then DefWindowProc will do it for you erasing your entire window with the brush from from your window class. This could result in windows thinking that no part of your window is transparent.
If you want mouse messages to pass through your window, a more reliable way is to handle the WM_NCHITTEST message and return HTTRANSPARENT where you want the mouse to pass through.
This is basically what how the WS_EX_TRANSPARENT style works. like this
case WM_NCHITTEST:
{
lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (HTCLIENT == lRet)
lRet = HTTRANSPARENT;
}
If your window has no non-client area, then you can skip the call to DefWindowProc.
WndProc() is not always re-entrant. I believe with the main message pump, the mouse and keyboard messages are queued up and waiting for you to finish the prior WM_PAINT message. Conversely, if you were to call SendMessage() from WndProc(), then you are looking at re-entrace. The other case is PostMessage() which would add the message to the queue. Maybe look at using DirectInput for mouse and keyboard input if this is an issue. Otherwise, look for ways to speed up your drawing.

Overlay Window not drawing correctly when hooking

The requirement is to draw my information in side of another application's window.
To take care of z order and so forth hooking WH_GETMESSAGE and draw on WM_PAINT seem good.
However some WM_PAINT are intended for the window area of my concern, but other WM_PAINT are for something completely different, like a context menu or button.
Example Notepad is hooked with an overlay writing "Hello" into the Notepad screen. This works fine. However when right clicking Notepad the context menu gets overlay with Hello. Basically the context menu is destroyed.
Is there an elegant way of determining what WM_PAINT is context menu?
LRESULT CALLBACK overlayHook(int code, WPARAM wParam, LPARAM lParam) {
//Try and be the LAST responder to WM_PAINT messages;
LRESULT retCode = CallNextHookEx(hhk, code, wParam, lParam);
//Per GetMsgProc documentation, don't do anything fancy
if(code < 0) {
return retCode;
}
//Assumes that target application only draws when WM_PAINT message is
//removed from input queue.
if(wParam == PM_NOREMOVE) {
return retCode;
}
MSG* message = (MSG*)lParam;
if(message->message != WM_PAINT) {
//Ignore everything that isn't a paint request
return retCode;
}
PAINTSTRUCT psPaint;
BeginPaint(message->hwnd, &psPaint);
HDC hdc = psPaint.hdc;
RECT r = psPaint.rcPaint;
TextOut(hdc, 10, 10, "Hello", 4);
EndPaint(message->hwnd, &psPaint);
return retCode;
}
It is not enough to test for the draw update region, because the context menu could be anywhere and contain the area of my concern.
I don't know of any elegant way to do this, but you could use GetWindowLong() to get the window's style or GetClassName() to get its class name, and then base your filtering decisions on those values.

Resources