VS2010中CMainFrame::OnCreate()函数里用GetMenu()无法获取菜单指针

文章详细阐述了在使用VS2010 MFC单文档应用程序中动态添加菜单时遇到的GetMenu()无法获取菜单指针的问题,并提供了解决方案。主要涉及CMainFrame类中CMFCMenuBar_wndMenuBar成员变量的相关代码注释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  今天学习VC++ 2010 MFC单文档应用程序中动态添加菜单,下面是代码部分:

  但是GetMenu()返回始终未NULL,无法获取菜单指针,网上查了下,说VS2010建的SDI/MDI与之前的不一样,是类似BCG的一些东西,所以用之前的方法GetMenu()得到CMenu是NULL或者无效。

【解决方案】

  在CMainFrame类中找到CMFCMenuBar m_wndMenuBar;这个成员变量,将跟它相关的代码注释掉的话,然后运行,OK。

<think>我们之前已经定义过`CMainFrame::OnCreate`,现在用户说这个函数已经有主体了,所以我们需要考虑在已有的`OnCreate`函数中添加设置标题的代码。 由于用户可能已经在该函数中有其他初始化代码,我们只需在适当位置添加设置标题的语句即可。 通常,在`OnCreate`函数的最后部分设置标题,因为此时窗口已经创建完成,并且其他初始化也已完成。 假设用户已有的`OnCreate`函数如下(这是一个典型的MFC框架生成的`OnCreate`函数): ```cpp 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) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // 创建失败 } if (!m_wndStatusBar.Create(this)) { TRACE0("Failed to create status bar\n"); return -1; // 创建失败 } m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)); // 设置工具栏停靠 m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); // 在这添加设置窗口标题的代码 SetWindowText(_T("我的应用程序")); return 0; } ``` 因此,我们只需在`OnCreate`函数的最后(但在`return 0;`之前)添加一行: ```cpp SetWindowText(_T("我的应用程序")); ``` 如果用户已经有一个`OnCreate`函数,那么只需添加这一行即可。 但是,如果用户不想修改`OnCreate`函数,或者想在创建窗口后的其他地方设置标题,我们可以考虑其他时机: ### 备选方案:在`PreCreateWindow`中设置 ```cpp BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // 设置窗口标题 cs.lpszName = _T("我的应用程序"); // 设置窗口标题 return TRUE; } ``` ### 备选方案:在`OnCreate`之后通过`PostMessage`设置 ```cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // ... 原有代码 // 发送一个自定义消息,在消息处理函数中设置标题 PostMessage(WM_SET_TITLE, 0, 0); return 0; } // 在消息映射中添加 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_MESSAGE(WM_SET_TITLE, OnSetTitle) END_MESSAGE_MAP() // 消息处理函数 LRESULT CMainFrame::OnSetTitle(WPARAM wParam, LPARAM lParam) { SetWindowText(_T("我的应用程序")); return 0; } ``` ### 备选方案:重写`ActivateFrame`函数 ```cpp void CMainFrame::ActivateFrame(int nCmdShow) { // 先调用基类 CFrameWnd::ActivateFrame(nCmdShow); // 设置标题 SetWindowText(_T("我的应用程序")); } ``` ### 推荐方案 如果`OnCreate`函数已经存在,且用户可以在其中添加代码,那么最简单的方式就是在`OnCreate`函数的末尾添加设置标题的代码。 如果由于某些原因不能修改`OnCreate`函数,那么使用`PreCreateWindow`也是一个好方法,但注意`PreCreateWindow`中设置的标题可能会被文档模板的标题覆盖(如果是文档视图结构)。因此,在文档视图应用程序中,通常我们会在文档模板中设置标题(如之前所述),或者动态设置。 ### 最终建议 根据用户的具体情况,如果窗口标题是固定的,那么在`PreCreateWindow`中设置是最合适的。如果标题需要根据程序状态变化,那么可以在`OnCreate`中设置初始标题,然后在其他地方动态更新。 如果用户已经有一个`OnCreate`函数,并且可以修改,那么添加一行代码是最简单的。 ### 代码添加位置示例(在已有OnCreate中添加) ```cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // ... 用户原有的代码(如工具栏、状态栏的创建) // 在函数返回前添加设置标题的代码 SetWindowText(_T("我的应用程序")); return 0; // 确保返回0 } ``` ### 注意事项 1. 确保在窗口创建成功后再设置标题(即`CFrameWnd::OnCreate`调用成功,且其他初始化也成功)。 2. 如果设置标题的字符串需要动态生成,注意字符串的生命周期和格式。 3. 在多文档界面(MDI)中,主框架标题通常由活动文档的标题决定,因此可能需要不同的方法。 ### §§相关问题§§: 1. 在MFC中,`OnCreate`和`PreCreateWindow`函数的主要区别是什么? 2. 如何动态更新MFC应用程序窗口的标题? 3. 在MDI应用程序中,如何设置主框架窗口和子窗口的标题? 4. 为什么有时候在`PreCreateWindow`中设置的标题会被覆盖?如何避免?</think>您提到 `CMainFrame::OnCreate` 函数已有主体,这很常见,因为 MFC 应用程序向导通常会生成这个函数的基本实现。以下是几种安全的修改方法: ### ✅ 方法一:在现有 `OnCreate` 中添加标题设置(推荐) ```cpp // MainFrm.cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 您原有的工具栏/状态栏代码... // if (!m_wndToolBar.CreateEx(...)) ... // if (!m_wndStatusBar.Create(...)) ... // ===== 添加这行代码 ===== SetWindowText(_T("我的应用程序 - 专业版")); return 0; // 确保返回0表示成功 } ``` ### ✅ 方法二:使用 `PreCreateWindow` 设置初始标题 ```cpp // MainFrm.h class CMainFrame : public CFrameWnd { protected: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); }; // MainFrm.cpp BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if (!CFrameWnd::PreCreateWindow(cs)) return FALSE; // 设置初始窗口标题 cs.lpszName = _T("我的应用程序 - 启动中"); return TRUE; } ``` ### ✅ 方法三:通过文档模板设置(SDI/MDI通用) ```cpp // ZZZApp.cpp (应用程序类) BOOL CZZZApp::InitInstance() { // ...其他初始化代码... CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CZZZDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CZZZView)); // 设置文档标题(影响窗口标题) pDocTemplate->SetDocStrings(_T("\n我的文档\n我的应用程序标题\n")); AddDocTemplate(pDocTemplate); // ...后续代码... } ``` ### ✅ 方法四:响应 `WM_WINDOWPOSCHANGED` 消息 ```cpp // MainFrm.h class CMainFrame : public CFrameWnd { protected: afx_msg void OnWindowPosChanged(WINDOWPOS* lpwndpos); DECLARE_MESSAGE_MAP() }; // MainFrm.cpp BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_WINDOWPOSCHANGED() END_MESSAGE_MAP() void CMainFrame::OnWindowPosChanged(WINDOWPOS* lpwndpos) { CFrameWnd::OnWindowPosChanged(lpwndpos); // 窗口位置变化时设置标题(确保窗口已创建) if ((lpwndpos->flags & SWP_SHOWWINDOW) && !m_bTitleSet) { SetWindowText(_T("我的应用程序")); m_bTitleSet = TRUE; } } // 在类定义中添加成员变量 class CMainFrame : public CFrameWnd { private: BOOL m_bTitleSet; // 初始化在构造函数中 }; ``` ### ✅ 方法五:使用定时器延迟设置 ```cpp // MainFrm.cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 设置定时器(100ms后触发) SetTimer(1, 100, NULL); return 0; } void CMainFrame::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { KillTimer(1); // 只执行一次 SetWindowText(_T("延迟设置的标题")); } CFrameWnd::OnTimer(nIDEvent); } ``` ### ⚠️ 关键注意事项 1. **调用时机**: - `OnCreate` 中:窗口已创建但可能未完全初始化 - `PreCreateWindow`:创建前设置,但可能被后续流程覆盖 - 首次绘制后:最安全但需要额外机制 2. **避免覆盖**: - MFC 文档/视图架构会自动设置标题 - 使用 `GetWindowText` 检查当前标题: ```cpp CString strCurrent; GetWindowText(strCurrent); if (strCurrent.IsEmpty()) { SetWindowText(_T("我的标题")); } ``` 3. **多语言支持**: ```cpp // 使用资源ID更佳 CString strTitle; strTitle.LoadString(IDS_APP_TITLE); SetWindowText(strTitle); ``` ### ✅ 最终推荐方案 ```cpp // MainFrm.cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 原有代码保持不变... // ===== 添加在函数末尾 ===== PostMessage(WM_SETTITLE, 0, 0); // 安全的消息队列方式 return 0; } // MainFrm.h class CMainFrame : public CFrameWnd { protected: afx_msg LRESULT OnSetTitle(WPARAM, LPARAM); DECLARE_MESSAGE_MAP() }; // 消息映射 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_MESSAGE(WM_SETTITLE, OnSetTitle) END_MESSAGE_MAP() LRESULT CMainFrame::OnSetTitle(WPARAM, LPARAM) { SetWindowText(_T("安全设置的应用程序标题")); return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值