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