]> git.saurik.com Git - wxWidgets.git/commitdiff
Fix fatal bug in wxGetWindowFromHWND() for radio buttons.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 30 Oct 2010 23:50:39 +0000 (23:50 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 30 Oct 2010 23:50:39 +0000 (23:50 +0000)
wxGetWindowFromHWND() could crash if it was called for a HWND of a radio
button which was not created by wxWidgets because it blindly dereferenced the
user data associated with the button expecting it to be a pointer to
wxRadioBox and crashed if it was something different. And this actually
happened when using the standard Windows printing dialog which can contain
radio buttons which obviously used their user data field for their own
purposes.

Fix this by maintaining a global hash map with radio buttons HWNDs as keys and
radio boxes as values. This ensures that we can always safely check whether
the given HWND is a radio button in one of our radio boxes or not.

Also change wxSpinCtrl which already did something similar in a different way
(using an array instead or a more efficient hash map) for consistency.

Closes #12083.

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

include/wx/msw/radiobox.h
include/wx/msw/spinctrl.h
src/msw/radiobox.cpp
src/msw/spinctrl.cpp
src/msw/window.cpp

index 01db4da7d86535a845ba29ada48224b42541a4cb..e6e1c1ae1060f335e982691ced7bb2da9af7e23f 100644 (file)
@@ -123,9 +123,14 @@ public:
     void SetLabelFont(const wxFont& WXUNUSED(font)) {}
     void SetButtonFont(const wxFont& font) { SetFont(font); }
 
+
     // implementation only from now on
     // -------------------------------
 
+    // This function can be used to check if the given radio button HWND
+    // belongs to one of our radio boxes. If it doesn't, NULL is returned.
+    static wxRadioBox *GetFromRadioButtonHWND(WXHWND hwnd);
+
     virtual bool MSWCommand(WXUINT param, WXWORD id);
     void Command(wxCommandEvent& event);
 
index 551eb064eb8b2559ad6157e8a021563e640006ec..dd178b6c828da2166c12f22923ddf5c85ce6a382 100644 (file)
@@ -142,10 +142,6 @@ protected:
     // Block text update event after SetValue()
     bool m_blockEvent;
 
-    // all existing wxSpinCtrls - this allows to find the one corresponding to
-    // the given buddy window in GetSpinForTextCtrl()
-    static wxArraySpins ms_allSpins;
-
 private:
     DECLARE_DYNAMIC_CLASS(wxSpinCtrl)
     DECLARE_EVENT_TABLE()
index 634d598476286bd7c2eb0f005397be7cc7a4d3aa..de869045c1114c5a63bb391dd69d3a23f7da0d90 100644 (file)
@@ -29,6 +29,7 @@
 #include "wx/radiobox.h"
 
 #ifndef WX_PRECOMP
+    #include "wx/hashmap.h"
     #include "wx/bitmap.h"
     #include "wx/brush.h"
     #include "wx/settings.h"
@@ -110,13 +111,34 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd,
 // global vars
 // ---------------------------------------------------------------------------
 
+namespace
+{
+
 // the pointer to standard radio button wnd proc
-static WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
+WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
+
+// Hash allowing to find wxRadioBox containing the given radio button by its
+// HWND. This is used by (subclassed) radio button window proc to find the
+// radio box it belongs to.
+WX_DECLARE_HASH_MAP(HWND, wxRadioBox *,
+                    wxPointerHash, wxPointerEqual,
+                    RadioBoxFromButton);
+
+RadioBoxFromButton gs_boxFromButton;
+
+} // anonymous namespace
 
 // ===========================================================================
 // implementation
 // ===========================================================================
 
+/* static */
+wxRadioBox* wxRadioBox::GetFromRadioButtonHWND(WXHWND hwnd)
+{
+    const RadioBoxFromButton::const_iterator it = gs_boxFromButton.find(hwnd);
+    return it == gs_boxFromButton.end() ? NULL : it->second;
+}
+
 // ---------------------------------------------------------------------------
 // wxRadioBox creation
 // ---------------------------------------------------------------------------
@@ -253,9 +275,23 @@ wxRadioBox::~wxRadioBox()
 {
     SendDestroyEvent();
 
+    // Unsubclass all the radio buttons and remove their soon-to-be-invalid
+    // HWNDs from the global map. Notice that we need to unsubclass because
+    // otherwise we'd need the entries in gs_boxFromButton for the buttons
+    // being deleted to handle the messages generated during their destruction.
+    for ( size_t item = 0; item < m_radioButtons->GetCount(); item++ )
+    {
+        HWND hwnd = m_radioButtons->Get(item);
+
+        wxSetWindowProc(hwnd, reinterpret_cast<WNDPROC>(s_wndprocRadioBtn));
+        gs_boxFromButton.erase(hwnd);
+    }
+
     delete m_radioButtons;
+
     if ( m_dummyHwnd )
         DestroyWindow((HWND)m_dummyHwnd);
+
     delete[] m_radioWidth;
     delete[] m_radioHeight;
 }
@@ -270,7 +306,8 @@ void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
         s_wndprocRadioBtn = (WXFARPROC)wxGetWindowProc(hwndBtn);
 
     wxSetWindowProc(hwndBtn, wxRadioBtnWndProc);
-    wxSetWindowUserData(hwndBtn, this);
+
+    gs_boxFromButton[hwndBtn] = this;
 }
 
 // ----------------------------------------------------------------------------
@@ -745,6 +782,10 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
                                            WPARAM wParam,
                                            LPARAM lParam)
 {
+
+    wxRadioBox * const radiobox = wxRadioBox::GetFromRadioButtonHWND(hwnd);
+    wxCHECK_MSG( radiobox, 0, wxT("Should have the associated radio box") );
+
     switch ( message )
     {
         case WM_GETDLGCODE:
@@ -760,10 +801,6 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
 
         case WM_KEYDOWN:
             {
-                wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
-
-                wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
-
                 bool processed = true;
 
                 wxDirection dir;
@@ -819,10 +856,6 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
         case WM_SETFOCUS:
         case WM_KILLFOCUS:
             {
-                wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
-
-                wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
-
                 // if we don't do this, no focus events are generated for the
                 // radiobox and, besides, we need to notify the parent about
                 // the focus change, otherwise the focus handling logic in
@@ -837,10 +870,6 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
 #ifndef __WXWINCE__
         case WM_HELP:
             {
-                wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
-
-                wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
-
                 bool processed = false;
 
                 wxEvtHandler * const handler = radiobox->GetEventHandler();
index bb867d36a80e5dd1da47e7a6c453e79d18198e8c..1ca1b0528b5721a636b8c634f1b2b4efb8f096f7 100644 (file)
@@ -29,6 +29,7 @@
 #include "wx/spinctrl.h"
 
 #ifndef WX_PRECOMP
+    #include "wx/hashmap.h"
     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
     #include "wx/event.h"
     #include "wx/textctrl.h"
@@ -129,12 +130,28 @@ END_EVENT_TABLE()
 // font size?)
 static const int MARGIN_BETWEEN = 1;
 
+
+// ---------------------------------------------------------------------------
+// global vars
+// ---------------------------------------------------------------------------
+
+namespace
+{
+
+// Global hash used to find the spin control corresponding to the given buddy
+// text control HWND.
+WX_DECLARE_HASH_MAP(HWND, wxSpinCtrl *,
+                    wxPointerHash, wxPointerEqual,
+                    SpinForTextCtrl);
+
+SpinForTextCtrl gs_spinForTextCtrl;
+
+} // anonymous namespace
+
 // ============================================================================
 // implementation
 // ============================================================================
 
-wxArraySpins wxSpinCtrl::ms_allSpins;
-
 // ----------------------------------------------------------------------------
 // wnd proc for the buddy text ctrl
 // ----------------------------------------------------------------------------
@@ -144,7 +161,7 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd,
                                             WPARAM wParam,
                                             LPARAM lParam)
 {
-    wxSpinCtrl *spin = (wxSpinCtrl *)wxGetWindowUserData(hwnd);
+    wxSpinCtrl * const spin = wxSpinCtrl::GetSpinForTextCtrl(hwnd);
 
     // forward some messages (mostly the key and focus ones) to the spin ctrl
     switch ( message )
@@ -170,7 +187,7 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd,
             spin->MSWWindowProc(message, wParam, lParam);
 
             // The control may have been deleted at this point, so check.
-            if ( !::IsWindow(hwnd) || wxGetWindowUserData(hwnd) != spin )
+            if ( !::IsWindow(hwnd) )
                 return 0;
             break;
 
@@ -198,13 +215,13 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd,
 /* static */
 wxSpinCtrl *wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy)
 {
-    wxSpinCtrl *spin = (wxSpinCtrl *)wxGetWindowUserData((HWND)hwndBuddy);
-
-    int i = ms_allSpins.Index(spin);
-
-    if ( i == wxNOT_FOUND )
+    const SpinForTextCtrl::const_iterator
+        it = gs_spinForTextCtrl.find(hwndBuddy);
+    if ( it == gs_spinForTextCtrl.end() )
         return NULL;
 
+    wxSpinCtrl * const spin = it->second;
+
     // sanity check
     wxASSERT_MSG( spin->m_hwndBuddy == hwndBuddy,
                   wxT("wxSpinCtrl has incorrect buddy HWND!") );
@@ -399,7 +416,8 @@ bool wxSpinCtrl::Create(wxWindow *parent,
     wxSpinButtonBase::SetRange(min, max);
 
     // subclass the text ctrl to be able to intercept some events
-    wxSetWindowUserData(GetBuddyHwnd(), this);
+    gs_spinForTextCtrl[GetBuddyHwnd()] = this;
+
     m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
                                                 wxBuddyTextWndProc);
 
@@ -441,24 +459,16 @@ bool wxSpinCtrl::Create(wxWindow *parent,
         m_oldValue = initial;
     }
 
-    // do it after finishing with m_hwndBuddy creation to avoid generating
-    // initial wxEVT_COMMAND_TEXT_UPDATED message
-    ms_allSpins.Add(this);
-
     return true;
 }
 
 wxSpinCtrl::~wxSpinCtrl()
 {
-    ms_allSpins.Remove(this);
-
-    // This removes spurious memory leak reporting
-    if (ms_allSpins.GetCount() == 0)
-        ms_allSpins.Clear();
-
     // destroy the buddy window because this pointer which wxBuddyTextWndProc
     // uses will not soon be valid any more
-    ::DestroyWindow(GetBuddyHwnd());
+    ::DestroyWindow( GetBuddyHwnd() );
+
+    gs_spinForTextCtrl.erase(GetBuddyHwnd());
 }
 
 // ----------------------------------------------------------------------------
index 64e7ce2c52beb294fb8d2c68c1b74ea2d1c5ba65..ff1861f06df7b97f5426deef25726a14e15f0464 100644 (file)
     #include "wx/caret.h"
 #endif // wxUSE_CARET
 
+#if wxUSE_RADIOBOX
+    #include "wx/radiobox.h"
+#endif // wxUSE_RADIOBOX
+
 #if wxUSE_SPINCTRL
     #include "wx/spinctrl.h"
 #endif // wxUSE_SPINCTRL
@@ -6547,7 +6551,7 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd)
             // do it as well, win would be already non NULL
             if ( ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON )
             {
-                win = (wxWindow *)wxGetWindowUserData(hwnd);
+                win = wxRadioBox::GetFromRadioButtonHWND(hwnd);
             }
             //else: it's a wxRadioButton, not a radiobutton from wxRadioBox
 #endif // wxUSE_RADIOBOX