wxpython skin bug(弹出菜单,内存错误)

通过使用GetMenuString方法替代GetMenuItemInfo方法,成功解决了wxPython皮肤使用后导致的程序崩溃问题。实现了使用SkinSharp皮肤后,wxPython程序能够正常运行,并展示了如何在wxPython中使用自定义皮肤,以及测试过程中的优化方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面说到了 wxpython skin的问题(使用皮肤后弹出目录,程序崩溃)

http://blog.youkuaiyun.com/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,向大家推荐一下)






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值