Fix confusion with LOGFONT{A,W} parameters to MSW GetTheme[Sys]Font().
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 30 Oct 2010 23:50:22 +0000 (23:50 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 30 Oct 2010 23:50:22 +0000 (23:50 +0000)
Change the signature of GetThemeFont() and GetThemeSysFont() methods of
wxUxThemeEngine to take an artificial wxUxThemeFont::Ptr type instead of
LOGFONT which allows the broken code to compile correctly and even work in
Unicode builds but crashed in ANSI ones under Windows Vista/7 as these
functions expect a LOGFONTW and not LOGFONTA even in non-Unicode build under
these systems.

This generalizes the previous fix/workaround for the same problem in
wxStaticBox so remove it now and use wxUxThemeFont both there and in
wxMenuItem to avoid crashes when using owner-drawn menus in ANSI build.

Closes #12364.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65955 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/msw/uxtheme.h
src/msw/menuitem.cpp
src/msw/statbox.cpp

index aa03f8522e7f42bdbfa3186c604890f7a450c312..934fbb2949d4c73152a19a7d649f084a24b9d365 100644 (file)
 #include "wx/msw/private.h"     // we use GetHwndOf()
 #include "wx/msw/uxthemep.h"
 
+// Amazingly, GetThemeFont() and GetThemeSysFont() functions use LOGFONTA under
+// XP but LOGFONTW (even in non-Unicode build) under later versions of Windows.
+// If we declare them as taking LOGFONT below, the code would be able to
+// silently pass LOGFONTA to them in ANSI build and would crash at run-time
+// under Windows Vista/7 because of a buffer overrun (LOGFONTA being smaller
+// than LOGFONTW expected by these functions). If we we declare them as taking
+// LOGFONTW, the code wouldn't work correctly under XP. So we use a special
+// wxUxThemeFont class to encapsulate this and intentionally change the LOGFONT
+// output parameters of the theme functions to take it instead.
+
+class wxUxThemeFont
+{
+public:
+    // Trivial default ctor.
+    wxUxThemeFont() { }
+
+    // Just some unique type.
+    struct Ptr { };
+
+#if wxUSE_UNICODE
+    // In Unicode build we always use LOGFONT anyhow so this class is
+    // completely trivial.
+    Ptr *GetPtr() { return reinterpret_cast<Ptr *>(&m_lfW); }
+    const LOGFONTW& GetLOGFONT() { return m_lfW; }
+#else // !wxUSE_UNICODE
+    // Return either LOGFONTA or LOGFONTW pointer as required by the current
+    // Windows version.
+    Ptr *GetPtr()
+    {
+        return UseLOGFONTW() ? reinterpret_cast<Ptr *>(&m_lfW)
+                             : reinterpret_cast<Ptr *>(&m_lfA);
+    }
+
+    // This method returns LOGFONT (i.e. LOGFONTA in ANSI build and LOGFONTW in
+    // Unicode one) which can be used with other, normal, Windows or wx
+    // functions. Internally it may need to transform LOGFONTW to LOGFONTA.
+    const LOGFONTA& GetLOGFONT()
+    {
+        if ( UseLOGFONTW() )
+        {
+            // Most of the fields are the same in LOGFONTA and LOGFONTW so just
+            // copy everything by default.
+            memcpy(&m_lfA, &m_lfW, sizeof(m_lfA));
+
+            // But the face name must be converted from Unicode.
+            WideCharToMultiByte(CP_ACP, 0, m_lfW.lfFaceName, -1,
+                                m_lfA.lfFaceName, sizeof(m_lfA.lfFaceName),
+                                NULL, NULL);
+        }
+
+        return m_lfA;
+    }
+
+private:
+    static bool UseLOGFONTW()
+    {
+        return wxGetWinVersion() >= wxWinVersion_Vista;
+    }
+
+    LOGFONTA m_lfA;
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+
+private:
+    LOGFONTW m_lfW;
+
+    wxDECLARE_NO_COPY_CLASS(wxUxThemeFont);
+};
+
 typedef HTHEME  (__stdcall *PFNWXUOPENTHEMEDATA)(HWND, const wchar_t *);
 typedef HRESULT (__stdcall *PFNWXUCLOSETHEMEDATA)(HTHEME);
 typedef HRESULT (__stdcall *PFNWXUDRAWTHEMEBACKGROUND)(HTHEME, HDC, int, int, const RECT *, const RECT *);
@@ -39,7 +107,7 @@ typedef HRESULT (__stdcall *PFNWXUGETTHEMEBOOL)(HTHEME, int, int, int, BOOL *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMEINT)(HTHEME, int, int, int, int *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMEENUMVALUE)(HTHEME, int, int, int, int *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMEPOSITION)(HTHEME, int, int, int, POINT *);
-typedef HRESULT (__stdcall *PFNWXUGETTHEMEFONT)(HTHEME, HDC, int, int, int, LOGFONT *);
+typedef HRESULT (__stdcall *PFNWXUGETTHEMEFONT)(HTHEME, HDC, int, int, int, wxUxThemeFont::Ptr *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMERECT)(HTHEME, int, int, int, RECT *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMEMARGINS)(HTHEME, HDC, int, int, int, RECT *, MARGINS *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMEINTLIST)(HTHEME, int, int, int, INTLIST*);
@@ -50,7 +118,7 @@ typedef COLORREF(__stdcall *PFNWXUGETTHEMESYSCOLOR)(HTHEME, int);
 typedef HBRUSH  (__stdcall *PFNWXUGETTHEMESYSCOLORBRUSH)(HTHEME, int);
 typedef BOOL    (__stdcall *PFNWXUGETTHEMESYSBOOL)(HTHEME, int);
 typedef int     (__stdcall *PFNWXUGETTHEMESYSSIZE)(HTHEME, int);
-typedef HRESULT (__stdcall *PFNWXUGETTHEMESYSFONT)(HTHEME, int, LOGFONT *);
+typedef HRESULT (__stdcall *PFNWXUGETTHEMESYSFONT)(HTHEME, int, wxUxThemeFont::Ptr *);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMESYSSTRING)(HTHEME, int, wchar_t *, int);
 typedef HRESULT (__stdcall *PFNWXUGETTHEMESYSINT)(HTHEME, int, int *);
 typedef BOOL    (__stdcall *PFNWXUISTHEMEACTIVE)();
index bc5bebea68de12721580d1335d273349ebb082b8..fb02feaadb624603f6cad2c18f63c78c8a3c228b 100644 (file)
@@ -367,9 +367,9 @@ void MenuDrawData::Init()
 
         Offset = -14;
 
-        wxNativeFontInfo fontInfo;
-        theme->GetThemeSysFont(hTheme, TMT_MENUFONT, &fontInfo.lf);
-        Font = wxFont(fontInfo);
+        wxUxThemeFont themeFont;
+        theme->GetThemeSysFont(hTheme, TMT_MENUFONT, themeFont.GetPtr());
+        Font = wxFont(themeFont.GetLOGFONT());
 
         Theme = true;
 
index 1058f381b91741d267ddba60b71d55c37a96d617..2047e8bd4c565aeea3529415a40ce880350547f9 100644 (file)
@@ -430,11 +430,7 @@ void wxStaticBox::PaintForeground(wxDC& dc, const RECT& rc)
             wxUxThemeHandle hTheme(this, L"BUTTON");
             if ( hTheme )
             {
-                // GetThemeFont() expects its parameter to be LOGFONTW and not
-                // LOGFONTA even in ANSI programs and will happily corrupt
-                // memory after the struct end if we pass a LOGFONTA (which is
-                // smaller) to it!
-                LOGFONTW lfw;
+                wxUxThemeFont themeFont;
                 if ( wxUxThemeEngine::Get()->GetThemeFont
                                              (
                                                 hTheme,
@@ -442,24 +438,10 @@ void wxStaticBox::PaintForeground(wxDC& dc, const RECT& rc)
                                                 BP_GROUPBOX,
                                                 GBS_NORMAL,
                                                 TMT_FONT,
-                                                (LOGFONT *)&lfw
+                                                themeFont.GetPtr()
                                              ) == S_OK )
                 {
-#if wxUSE_UNICODE
-                    // ok, no conversion necessary
-                    const LOGFONT& lf = lfw;
-#else // !wxUSE_UNICODE
-                    // most of the fields are the same in LOGFONTA and LOGFONTW
-                    LOGFONT lf;
-                    memcpy(&lf, &lfw, sizeof(lf));
-
-                    // but the face name must be converted
-                    WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1,
-                                        lf.lfFaceName, sizeof(lf.lfFaceName),
-                                        NULL, NULL);
-#endif // wxUSE_UNICODE/!wxUSE_UNICODE
-
-                    font.Init(lf);
+                    font.Init(themeFont.GetLOGFONT());
                     if ( font )
                         selFont.Init(hdc, font);
                 }