最近想在VC中单文档框架中删除菜单,状态栏,工具栏 。状态栏,工具栏在CMainFrame::OnCreate中就可以注释调,但是怎么样将菜单删掉呢?百度了一下,不太好找到。只好依靠现有知识,自己找到办法了。通过跟踪调试MFC的源代码居然让我找到了办法。
首先,我们先想办法达到在单文档界面中不显示菜单的效果。
通过现有知识,我们知道MFC是在CMainFrame::OnCreate中生成状态栏,工具栏的。代码如下:
{
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) 处下断点,进入函数内部,代码如下:
{
ENSURE_ARG(lpcs != NULL );
CCreateContext * pContext = (CCreateContext * )lpcs -> lpCreateParams;
return OnCreateHelper(lpcs , pContext);
}
继续进入OnCreateHelper函数,代码如下:
{
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,代码如下:
{
// 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 ;
}
{
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;
}
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,代码如下:
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()如下:
{
lpCreateStruct -> hMenu = NULL ;
if (CFrameWnd :: OnCreate(lpCreateStruct) == - 1 )
return - 1 ;
return 0 ;l
}
编译,运行。还有菜单。 再回到CMainFrame中,发现PreCreateWindow也有CREATESTRUCT结构,修改代码如下:
{
cs . hMenu = NULL ;
if ( ! CFrameWnd :: PreCreateWindow(cs) )
return FALSE ;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return TRUE ;
}
编译运行,成功了。菜单没了。
第二步,既然菜单没用了,那么我们可不可以把wizard自动生成的菜单删除调呢?说干就干,删除菜单IDR_MAINFRAME,编译运行,什么“建立空文档失败”,程序直接退出。继续跟踪,调试吧。
首先估计是在CMainFrame中出的问题,在CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中下断点,编译运行,结果没进入断点,程序就出错了,那在App类的InitInstance()中下断点吧。跟踪,发现是在
return FALSE;
if ( ! AfxGetApp() -> OnCmdMsg(ID_FILE_NEW, 0 , NULL, NULL))
OnFileNew();
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(),并将其中加载菜单的相关代码去掉应该就可以了,修改代码如下:
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 ;
}
}
编译,运行,成功了。