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

来源:互联网 发布:mysql大于符号 编辑:程序博客网 时间:2024/06/04 21:06

前面说到了 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_MENUSint 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_MENUSint 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 fixedWinStruct<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;elsebreak; } }}else // failed to get the menu text?{// it's not fatal, so don't show error, but still log itwxLogLastError(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,向大家推荐一下)






原创粉丝点击