wxpython skin bug(彈出菜單,內存錯誤)

前面說到了 wxpython skin的問題(使用皮膚後彈出目錄,程序崩潰)

http://blog.csdn.net/xugangjava/article/details/7763212

使用OllyDbg 調試 發現在wxwindow中的 handlermenuchar 報錯了,非法的地址訪問,


一下也看不出來是爲什麼,還好wxpython是開源的,網上下載源碼。

我下載的是2.9.4 最新版,我建議下載這個版本,官方的說法是development 版本的並非bug更多,只是文檔跟API未能跟上而已,

wxpython2.9編譯非常簡單,在vc 2008命令提示符 下運行 wxpython目錄中的 build_wxpython.py 就 OK了

詳細可以參考http://wxpython.org/BUILD.html


打開源碼查看 core 工程目錄下的 MSW Sources

打開windows.cpp 5805行

#if wxUSE_MENUS
int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel),
                                WXLPARAM WXUNUSED_IN_WINCE(lParam))
{
    // FIXME: implement GetMenuItemCount for WinCE, possibly
    // in terms of GetMenuItemInfo
#ifndef __WXWINCE__
    const HMENU hmenu = (HMENU)lParam;

    WinStruct<MENUITEMINFO> mii;

    // we could use MIIM_FTYPE here as we only need to know if the item is
    // ownerdrawn or not and not dwTypeData which MIIM_TYPE also returns, but
    // MIIM_FTYPE is not supported under Win95
    mii.fMask = MIIM_TYPE | MIIM_DATA;

    // find if we have this letter in any owner drawn item
    const int count = ::GetMenuItemCount(hmenu);
    for ( int i = 0; i < count; i++ )
    {
        // previous loop iteration could modify it, reset it back before
        // calling GetMenuItemInfo() to prevent it from overflowing dwTypeData
        mii.cch = 0;

        if ( ::GetMenuItemInfo(hmenu, i, TRUE, &mii) )
        {
            if ( mii.fType == MFT_OWNERDRAW )
            {
                //  dwItemData member of the MENUITEMINFO is a
                //  pointer to the associated wxMenuItem -- see the
                //  menu creation code
                wxMenuItem *item = (wxMenuItem*)mii.dwItemData;

                const wxString label(item->GetItemLabel());
                const wxChar *p = wxStrchr(label.t_str(), wxT('&'));
                while ( p++ )
                {
                    if ( *p == wxT('&') )
                    {
                        // this is not the accel char, find the real one
                        p = wxStrchr(p + 1, wxT('&'));
                    }
                    else // got the accel char
                    {
                        // FIXME-UNICODE: this comparison doesn't risk to work
                        // for non ASCII accelerator characters I'm afraid, but
                        // what can we do?
                        if ( (wchar_t)wxToupper(*p) == (wchar_t)chAccel )
                        {
                            return i;
                        }
                        else
                        {
                            // this one doesn't match
                            break;
                        }
                    }
                }
            }
        }
        else // failed to get the menu text?
        {
            // it's not fatal, so don't show error, but still log it
            wxLogLastError(wxT("GetMenuItemInfo"));
        }
    }
#endif
    return wxNOT_FOUND;
}

const wxString label(item->GetItemLabel()); 這裏報錯,內存衝突

看來mii.dwItemData強轉成wxMenuItem 調用GetItemLabel時造成了無效的內存訪問。

重新實現這一段代碼,使用GetMenuString來得到,Menu的 Text 然後跟用戶按鍵比較。

#if wxUSE_MENUS
int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel),
                                WXLPARAM WXUNUSED_IN_WINCE(lParam))
{
    // FIXME: implement GetMenuItemCount for WinCE, possibly
    // in terms of GetMenuItemInfo
#ifndef __WXWINCE__
    const HMENU hmenu = (HMENU)lParam;

	//xugangjava fixed
	WinStruct<MENUITEMINFO> mii;
	mii.fMask = MIIM_TYPE | MIIM_DATA;
	const int count=::GetMenuItemCount(hmenu);
	wchar_t itemText[255];
	for(int itemIdx=0;itemIdx<count;itemIdx++)
	{
		if ( ::GetMenuItemInfo(hmenu, itemIdx, TRUE, &mii) )
		{
			 if ( mii.fType == MFT_OWNERDRAW )
			 {
				 int textLen  =::GetMenuStringW(hmenu,itemIdx,itemText,255,MF_BYPOSITION);
				 if(!textLen)continue;
				 for(int strIdx=0;strIdx<textLen;strIdx++)
				 {
					if(itemText[strIdx]==L'&')
							continue;
					else if (itemText[strIdx] == (wchar_t)chAccel)
							return itemIdx;
					else
							break;
				 }
			 }
		}
		else // failed to get the menu text?
		{
			// it's not fatal, so don't show error, but still log it
			wxLogLastError(wxT("GetMenuItemInfo"));
		}
	}

	/* bug   
    // we could use MIIM_FTYPE here as we only need to know if the item is
    // ownerdrawn or not and not dwTypeData which MIIM_TYPE also returns, but
    // MIIM_FTYPE is not supported under Win95
    mii.fMask = MIIM_TYPE | MIIM_DATA;

    // find if we have this letter in any owner drawn item
    const int count = ::GetMenuItemCount(hmenu);
    for ( int i = 0; i < count; i++ )
    {
        // previous loop iteration could modify it, reset it back before
        // calling GetMenuItemInfo() to prevent it from overflowing dwTypeData
        mii.cch = 0;

        if ( ::GetMenuItemInfo(hmenu, i, TRUE, &mii) )
        {
            if ( mii.fType == MFT_OWNERDRAW )
            {
                //  dwItemData member of the MENUITEMINFO is a
                //  pointer to the associated wxMenuItem -- see the
                //  menu creation code
                wxMenuItem *item = (wxMenuItem*)mii.dwItemData;

                const wxString label(item->GetItemLabel());
                const wxChar *p = wxStrchr(label.t_str(), wxT('&'));
                while ( p++ )
                {
                    if ( *p == wxT('&') )
                    {
                        // this is not the accel char, find the real one
                        p = wxStrchr(p + 1, wxT('&'));
                    }
                    else // got the accel char
                    {
                        // FIXME-UNICODE: this comparison doesn't risk to work
                        // for non ASCII accelerator characters I'm afraid, but
                        // what can we do?
                        if ( (wchar_t)wxToupper(*p) == (wchar_t)chAccel )
                        {
                            return i;
                        }
                        else
                        {
                            // this one doesn't match
                            break;
                        }
                    }
                }
            }
        }
        else // failed to get the menu text?
        {
            // it's not fatal, so don't show error, but still log it
            wxLogLastError(wxT("GetMenuItemInfo"));
        }
	
    }
		*/
#endif
    return wxNOT_FOUND;
}

Ok 經過測試後 皮膚可以用了,沒有內存崩潰,

現在 Wxpython 也可以做出QQ 的效果了,雖然有些地方沒有渲染到位,不過整體還是可以的

(我用的是SkinSharp,向大家推薦一下)






發佈了72 篇原創文章 · 獲贊 5 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章