您的位置:首页 > 其它

VC中单文档框架删除菜单的调试经历

2007-01-06 13:49 471 查看
我的技术博客已搬家至: http://www.kai-zhou.com, 其他博客已停止更新,欢迎访问查看文章的最新版本.

最近想在VC中单文档框架中删除菜单,状态栏,工具栏 。状态栏,工具栏在CMainFrame::OnCreate中就可以注释调,但是怎么样将菜单删掉呢?百度了一下,不太好找到。只好依靠现有知识,自己找到办法了。通过跟踪调试MFC的源代码居然让我找到了办法。

首先,我们先想办法达到在单文档界面中不显示菜单的效果。

通过现有知识,我们知道MFC是在CMainFrame::OnCreate中生成状态栏,工具栏的。代码如下:


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)


{


if (CFrameWnd::OnCreate(lpCreateStruct) == -1)


return -1;






if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP


| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||


!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))


{


TRACE0("未能创建工具栏
");


return -1; // 未能创建


}




if (!m_wndStatusBar.Create(this) ||


!m_wndStatusBar.SetIndicators(indicators,


sizeof(indicators)/sizeof(UINT)))


{


TRACE0("未能创建状态栏
");


return -1; // 未能创建


}




// TODO: 如果不需要工具栏可停靠,则删除这三行


m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);


EnableDocking(CBRS_ALIGN_ANY);


DockControlBar(&m_wndToolBar);


return 0;


}

所以如果不想要状态栏,工具栏,直接在CMainFrame::OnCreate中将相关代码注释调就行了。那么,菜单相关的代码在哪呢?遍历一遍CMainFrame类,没找到。根据代码相关性,既然状态栏,工具栏是在 CMainFrame::OnCreate中生成的,那么菜单估计也是在这个函数里面生成的。

首先在 if (CFrameWnd::OnCreate(lpCreateStruct) == -1) 处下断点,进入函数内部,代码如下:


int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)


{


ENSURE_ARG(lpcs != NULL);


CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;


return OnCreateHelper(lpcs, pContext);


}

继续进入OnCreateHelper函数,代码如下:


int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)


{


if (CWnd::OnCreate(lpcs) == -1)


return -1;




// create special children first


if (!OnCreateClient(lpcs, pContext))


{


TRACE(traceAppMsg, 0, "Failed to create client pane/view for frame.
");


return -1;


}




// post message for initial message string


PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);




// make sure the child windows have been properly sized


RecalcLayout();




return 0; // create ok


}

下一步进入OnCreateClient,代码如下:


BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)


{


// default create client will create a view if asked for it


if (pContext != NULL && pContext->m_pNewViewClass != NULL)


{


if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)


return FALSE;


}


return TRUE;


}

进入CreateView,代码如下:


CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)


{


ASSERT(m_hWnd != NULL);


ASSERT(::IsWindow(m_hWnd));


ENSURE_ARG(pContext != NULL);


ENSURE_ARG(pContext->m_pNewViewClass != NULL);




// Note: can be a CWnd with PostNcDestroy self cleanup


CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();


if (pView == NULL)


{


TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed.
",


pContext->m_pNewViewClass->m_lpszClassName);


return NULL;


}


ASSERT_KINDOF(CWnd, pView);




// views are always created with a border!


if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,


CRect(0,0,0,0), this, nID, pContext))


{


TRACE(traceAppMsg, 0, "Warning: could not create view for frame.
");


return NULL; // can't continue without a view


}




if (pView->GetExStyle() & WS_EX_CLIENTEDGE)


{


// remove the 3d style from the frame, since the view is


// providing it.


// make sure to recalc the non-client area


ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);


}


return pView;


}

进入 if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,CRect(0,0,0,0), this, nID, pContext)),代码如下:


BOOL CWnd::Create(LPCTSTR lpszClassName,


LPCTSTR lpszWindowName, DWORD dwStyle,


const RECT& rect,


CWnd* pParentWnd, UINT nID,


CCreateContext* pContext)


{


// can't use for desktop or pop-up windows (use CreateEx instead)


ASSERT(pParentWnd != NULL);


ASSERT((dwStyle & WS_POPUP) == 0);




return CreateEx(0, lpszClassName, lpszWindowName,


dwStyle | WS_CHILD,


rect.left, rect.top,


rect.right - rect.left, rect.bottom - rect.top,


pParentWnd->GetSafeHwnd(), (HMENU)(UINT_PTR)nID, (LPVOID)pContext);


}

进入CreateEx,代码如下:


BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,


LPCTSTR lpszWindowName, DWORD dwStyle,


int x, int y, int nWidth, int nHeight,


HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)


{


ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||


AfxIsValidAtom(lpszClassName));


ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));




// allow modification of several common create parameters


CREATESTRUCT cs;


cs.dwExStyle = dwExStyle;


cs.lpszClass = lpszClassName;


cs.lpszName = lpszWindowName;


cs.style = dwStyle;


cs.x = x;


cs.y = y;


cs.cx = nWidth;


cs.cy = nHeight;


cs.hwndParent = hWndParent;


cs.hMenu = nIDorHMenu;


cs.hInstance = AfxGetInstanceHandle();


cs.lpCreateParams = lpParam;




if (!PreCreateWindow(cs))


{


PostNcDestroy();


return FALSE;


}




AfxHookWindowCreate(this);


HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,


cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,


cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);




#ifdef _DEBUG


if (hWnd == NULL)


{


TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X
",


GetLastError());


}


#endif




if (!AfxUnhookWindowCreate())


PostNcDestroy(); // cleanup if CreateWindowEx fails too soon




if (hWnd == NULL)


return FALSE;


ASSERT(hWnd == m_hWnd); // should have been set in send msg hook


return TRUE;


}

注意 cs.hMenu = nIDorHMenu; 这段代码明显是与菜单有关的代码,那么找到了框架是如何生成菜单的,将菜单加入到框架中的,我们又如何在框架中将菜单删除呢?菜单的赋值是赋给CREATESTRUCT结构,所以猜测CREATESTRUCT结构可以控制菜单。返回到CMainFrame中,我们可以看到CMainFrame::OnCreate()的参数是LPCREATESTRUCT,所以先修改CMainFrame::OnCreate()如下:


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)


{


lpCreateStruct->hMenu = NULL;


if (CFrameWnd::OnCreate(lpCreateStruct) == -1)


return -1;


return 0;l


}

编译,运行。还有菜单。 再回到CMainFrame中,发现PreCreateWindow也有CREATESTRUCT结构,修改代码如下:


BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)


{


cs.hMenu = NULL;


if( !CFrameWnd::PreCreateWindow(cs) )


return FALSE;


// TODO: 在此处通过修改


// CREATESTRUCT cs 来修改窗口类或样式




return TRUE;


}

编译运行,成功了。菜单没了。

第二步,既然菜单没用了,那么我们可不可以把wizard自动生成的菜单删除调呢?说干就干,删除菜单IDR_MAINFRAME,编译运行,什么“建立空文档失败”,程序直接退出。继续跟踪,调试吧。

首先估计是在CMainFrame中出的问题,在CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中下断点,编译运行,结果没进入断点,程序就出错了,那在App类的InitInstance()中下断点吧。跟踪,发现是在


if (!ProcessShellCommand(cmdInfo))


return FALSE;

中出错的。再次调试,进入ProcessShellCommand()函数内部,这次出错地方在


case CCommandLineInfo::FileNew:


if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))


OnFileNew();

同理,一步一步的,我们就可以找到最后出错的确切地点,中间过程省略。最后发现出错代码如下:


BOOL CFrameWnd::Create(LPCTSTR lpszClassName,


LPCTSTR lpszWindowName,


DWORD dwStyle,


const RECT& rect,


CWnd* pParentWnd,


LPCTSTR lpszMenuName,


DWORD dwExStyle,


CCreateContext* pContext)


{


HMENU hMenu = NULL;


if (lpszMenuName != NULL)


{


// load in a menu that will get destroyed when window gets destroyed


HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);


if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)


{


TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.
");


PostNcDestroy(); // perhaps delete the C++ object


return FALSE;


}


}




m_strTitle = lpszWindowName; // save title for later




if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,


rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,


pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))


{


TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.
");


if (hMenu != NULL)


DestroyMenu(hMenu);


return FALSE;


}




return TRUE;


}

在LoadMenu的时候出错了,所以执行PostNcDestroy()函数了。找到了错误所在,怎么修改代码呢?

仔细阅读,发现Create()函数是在CFrameWnd类中,而CMainFrame类的父类就是CFrameWnd类,再一查,发现了CFrameWnd::Create()是虚函数,所以只要我们在CMainFrame中实现Create(),并将其中加载菜单的相关代码去掉应该就可以了,修改代码如下:


BOOL CMainFrame::Create(LPCTSTR lpszClassName,


LPCTSTR lpszWindowName,


DWORD dwStyle /*= WS_OVERLAPPEDWINDOW*/,


const RECT& rect/* = rectDefault*/,


CWnd* pParentWnd /*= NULL*/, // != NULL for popups


LPCTSTR lpszMenuName/* = NULL*/,


DWORD dwExStyle/* = 0*/,


CCreateContext* pContext/* = NULL*/)


{


HMENU hMenu = NULL;


if (lpszMenuName != NULL)


{


}


m_strTitle = lpszWindowName; // save title for later


if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,


rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,


pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))


{


TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.
");


if (hMenu != NULL)


DestroyMenu(hMenu);


return FALSE;


}


}

编译,运行,成功了。

以前写代码总是查上网,翻书,看帮助查资料,现在才发现源代码也是很好的资料。上网,翻阅书籍查资料固然不错,但是那是学习别人已有的知识,自己解决问题的能力没什么大的提高。通过研究源代码,我们可以在没有任何书籍,文档的情况下解决问题。这是我第一次深入跟踪到MFC源代码内部进行调试,希望对像我一样的初学者有帮助。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: