前面说到了 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,向大家推荐一下)