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

Comments (7)

  1. Dean Harding says:

    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.

  2. Calvin_Hsia says:

    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.

  3. Grady McCue says:

    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

  4. Bruno Maddalozzo says:

    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?

  5. Jack Chiou says:

    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!!

  6. Cesar Chalom says:

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

  7. prafulla says:

    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.

Skip to main content