Welcome to MSDN Blogs Sign in | Join | Help

Vista Aero DWM seems to optimize out GDI paint calls

In this post: Foxpro Menu items, combo boxes not refreshing selected item under Aero in Vista I describe a problem in Foxpro where menu and list items that are supposed to be non-selected aren’t painted correctly. I described a workaround: call the GdiSetBatchLimit API, which limits the GDI paint functions that are batched. Apparently some calls are being optimized out or not executed.

 

The problem occurs whether run as Administrator or not, but requires Desktop Composition enabled.

(Vista Aero can have DwmIsCompositionEnabled enabled or disabled. See Control Panel->System and Maintenance->System->Advanced System Settings->Advanced->Performance->Settings->Visual Effects->Enable desktop composition)

 

On Windows XP, the program runs as expected.

 

I’ve managed to create a standalone simple C++ program that demonstrates the behavior. The entire VS 2005 project is available here

The program displays a dozen items with the first item selected (inverted colors). The user can hit the down or up arrows to move the selection.  If you hit the down arrow once, the first item is painted unselected, and the next item is painted selected. That’s fine. If you hit down arrow a few more times, the prior item doesn’t get repainted unselected.

 

I’ve put a call to GdiSetBatchLimit on Left or Right Arrow. Left will set it to 1, Right to 20 (the default). If you hit the left arrow, then the list selection behaves normally (you don’t even have to restart the app). Right arrow reproduces the problem behavior.

 

The problem seems related to calling SelectClipRgn

 

See also: Windows Vista Aero BorderStyle Paint problem as non Administrator

 

To create the sample project:

Start VS 2005: choose File->New Project-> Visual C++ Win32 Win32 Project. Call it VistaItems

In the Win32 Application Wizard, just choose all the defaults and Finish.

 

 

In the WndProc, add this case in the Switch to handle the WM_CREATE message:

 

      case WM_CREATE:

            {

                  RECT rect;

                  GetClientRect(hWnd,&rect);

                  rect.right/=2;

                  rect.left= 50;

                  CMenuWin *pCMenuWin = new (CMenuWin);

                  HWND hwndChild =

                  pCMenuWin->Create(hWnd, &rect,L"CMenuWin",

                        WS_VISIBLE | WS_CLIPCHILDREN | WS_POPUP

                        , WS_EX_NOPARENTNOTIFY);

                  if (hwndChild == 0)

                  {

                        int n = GetLastError();

                        DebugBreak();

                       

                  }

                  SetParent(hwndChild, hWnd);

                  g_hwndChild = hwndChild;

            }

            break;

      case WM_KEYDOWN:

            return SendMessage(g_hwndChild, message, wParam, lParam);

        break;

 

Add these lines after this line #include "VistaItems.h"

 

#include "atlbase.h"

#include "atlwin.h"

 

#define NUMITEMS 12

#define MAXITEMSIZE 10

 

struct DATA

{

      TCHAR strText[MAXITEMSIZE];

      int nState; // 0 or 1: selected or not

} Data[NUMITEMS] = {L"One",1,L"Two",0,L"Three",0,L"Four",0,

                              L"Five",0,L"Six",0,L"Seven",0,L"Eight",0,

                              L"Nine",0,L"Ten",0,L"Eleven",0,L"Twelve",0

                              };

 

 

int g_nCurItem = 0;

HDC g_hdc=0;

 

void GetRect(int nIndex, RECT * rect)

{

  rect->left=10;

  rect->right = 400;

  rect->top  = nIndex * 20;

  rect->bottom = rect->top + 20;

 

}

 

void ShowItems(HWND hWnd, int nIndex, HDC hdc)

{

        RECT rect;

        GetRect(nIndex, &rect);

 

        HFONT hFont = (HFONT)GetStockObject(ANSI_VAR_FONT);

        COLORREF clr, clrold;

        Data[nIndex].nState = 1 - Data[nIndex].nState ;     // invert state

        if (Data[nIndex].nState == 0)

        {

                  clr = RGB(255,0,0);

                  SetBkColor(hdc, RGB(0,0,0));

        } else

        {

                  clr = RGB(0,0,255);

                  SetBkColor(hdc, RGB(255,255,255));

        }

        clrold = SetTextColor(hdc, clr);

 

        ExtTextOut(hdc,

                          rect.left, rect.top, ETO_CLIPPED | ETO_OPAQUE ,

                          &rect, Data[nIndex].strText,(int)wcslen(Data[nIndex].strText), 0);

 

        SetTextColor(hdc, clrold);

}

 

 

HRGN g_hRgn = 0;

HWND g_hwndChild = 0;

 

class CMenuWin : public CWindowImpl<CMenuWin>

{

public:

//    DECLARE_WND_CLASS_EX(L"CMenuWin", 0 /*CS_OWNDC*/, COLOR_MENUHILIGHT)    // backcolor

      BEGIN_MSG_MAP(CMenuWin)

            MESSAGE_HANDLER(WM_KEYDOWN,OnKeyDown)

            MESSAGE_HANDLER(WM_PAINT,OnPaint)

 

      END_MSG_MAP()

      LRESULT OnKeyDown(UINT uMsg,WPARAM wParam, LPARAM lParam, BOOL & bHandled)

      {

            HDC hdc;

        hdc = GetDC();

            if (g_hRgn) {

                  DeleteObject(g_hRgn);

                  g_hRgn = 0;

            }

            RECT rect;

            GetRect(g_nCurItem, &rect);

            g_hRgn = CreateRectRgnIndirect(&rect);

            SelectClipRgn(hdc,g_hRgn);

 

        ShowItems(m_hWnd, g_nCurItem, hdc);

            switch(wParam)

            {

            case VK_LEFT:           // hit left arrow

                  GdiSetBatchLimit(1);    // this will make it behave as expected

                  break;

            case VK_RIGHT:          // hit right arrow

                  GdiSetBatchLimit(20);   // this will show the broken behavior

                  break;

            case VK_DOWN:                 // down arrow

                  if (++g_nCurItem == NUMITEMS)

                  {

                    g_nCurItem=0;

                  }

                  break;

            case VK_UP:

                  if (g_nCurItem-- == 0)

                  {

                          g_nCurItem=NUMITEMS-1;

                  }

                  break;

            }

            if (g_hRgn) {

                  DeleteObject(g_hRgn);

                  g_hRgn = 0;

            }

            GetRect(g_nCurItem, &rect);

            g_hRgn = CreateRectRgnIndirect(&rect);

            SelectClipRgn(hdc,g_hRgn);

        ShowItems(m_hWnd, g_nCurItem, hdc);

            ReleaseDC(hdc);

            return 0;

      }

      LRESULT OnPaint(UINT uMsg,WPARAM wParam, LPARAM lParam, BOOL & bHandled)

      {

            PAINTSTRUCT ps;

 

        HDC hdc = BeginPaint(&ps);

        for (int i = 0 ; i < NUMITEMS ; i++)

        {

              ShowItems(m_hWnd, i, hdc);

        }

            EndPaint(&ps);

            return 0;

      }

};

 

End of code

Published Thursday, May 03, 2007 4:50 PM by Calvin_Hsia

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Vista Aero DWM seems to optimize out GDI paint calls

Thursday, May 03, 2007 8:43 PM by Dean Harding

It might have something to do with painting stuff outside of the WM_PAINT handler. In the WM_KEYDOWN handler, try just updating state doing an InvalidateRect to force a WM_PAINT and see if that fixes it.

The problem is, with the DWM, it needs to know when it has to copy the in-memory bitmap into the texture it actually draws to the screen. The most obvious place is after a WM_PAINT (but there are other times that it does it -- the rules are complex) and this can often lead to such problems.

The most common thing I've noticed is apps which draw directly to a DC during input handlers (to show things like selections and so on) and this looks exactly like what you've done here.

# re: Vista Aero DWM seems to optimize out GDI paint calls

Friday, May 04, 2007 12:25 PM by Calvin_Hsia

Dean: thanks for your comments.

Adding an InvalidateRect causes low priority WM_PAINT messages to cause the rect to redraw, but if the user holds down the down arrow key, the highlight behaves very sluggishly because input messages have higher priority: several items appear highlighted at the same time.

Adding a GdiFlush() after painting the deselected item causes the problem to go away.

This code has been working for around 15 years in various versions of Windows: no doubt many apps are still working in older versions of Foxpro.

# re: Vista Aero DWM seems to optimize out GDI paint calls

Wednesday, May 09, 2007 12:46 PM by Grady McCue

Forgive my ignorance, but where is Vista Aero. I cannot find it in Vista Ultimate nor have I found it on the MS web pages. Please send a hint to foxunc@telus.net.

Thanks and keep sneaking fox stuff into VB.net 10

Grady

# re: Vista Aero DWM seems to optimize out GDI paint calls

Thursday, May 24, 2007 1:36 PM by Bruno Maddalozzo

Please try this code

DEFINE POPUP _popShortcutMenu ;

FROM MROW(),MCOL() ;

MARGIN ;

RELATIVE ;

SHORTCUT

DEFINE POPUP _SubMenu ;

SHORTCUT RELATIVE

DEFINE BAR 1 OF _popShortcutMenu PROMPT "Item 1"

DEFINE BAR 2 OF _popShortcutMenu PROMPT "Item 2 With sub"

DEFINE BAR 3 OF _popShortcutMenu PROMPT "Item 3"

ON BAR 2 OF _popShortcutMenu ACTIVATE Popup _SubMenu

Define Bar 1 Of _SubMenu Prompt "SubMenu 1"

Define Bar 2 Of _SubMenu Prompt "SubMenu 2"

ACTIVATE POPUP _popShortcutMenu

*RELEASE POPUP _popShortcutMenu

*RELEASE POPUP _SubMenu

WinXP shows correctly the submenu at the right side of the bar "Item 2 With sub".

Try the same code with Win Vista with Aero interface.

The submenu is always shown at the LEFT SIDE of the bar (which is ugly), and, if the shortcut menu is activated near the left side of the monitor, it becomes unusable, since the user can't read the submenu itself.

Any workaround?

# re: Vista Aero DWM seems to optimize out GDI paint calls

Wednesday, July 18, 2007 6:24 PM by Jack Chiou

ya, my FoxPro 8.0 application is doing the same thing, In Vista it will pop up to the left side even if it's running out of space but work perfectly in WinXP.

Did you find any workaround on this case? Thanks!!

# VFP9 SP2 / SEDNA USEFUL LINKS

Saturday, August 11, 2007 12:12 AM by Cesar Chalom

Here are some of the links that I often visit regarding VFP9 SP2 and Sedna. The official Microsoft Visual...

# re: Vista Aero DWM seems to optimize out GDI paint calls

Monday, June 30, 2008 9:41 AM by prafulla

Hi

We are facing problem with image filling in List View in VB6. It works very well with XP; but with vista - images are disproportionate in List View. We create DIB and apply wrapper behind the image (to make them all of uniform size) and load them into imagelist. Imagelist is bound to List View.

Any solutions ..if someone can offer around gdi+, will be great.

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker