你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / Linux开发
如何创建和访问MFC嵌入菜单功能的动态链接库
 

张邦华

    MFC动态库分为规则动态库和扩展动态,根据调用方式的不同,对动态库的调用可分为静态调用方式和动态调用方式。在本实例介绍中,采用MFC规则动态库,动态调用的方式。由此,基于VS2003建立两个工程,一个就是被动态嵌入菜单的动态库工程(MenuDll),另外一个就是测试程序(TestDll),测试程序采用单文档MFC。以上不是本文重点论述对象,就不详细介绍。要解决动态库嵌入菜单问题,需要考虑两个方面的问题。第一就是动态库菜单的自动添加,第二就是菜单消息的响应机制。
    按照一般的动态库接口添加办法,在MenuDll.def文件中加入动态添加、删除菜单函数接口InstallExtMenu和RemoveExtMenu,以及得到菜单数量的函数接口GetExtMenuItemCount和通过菜单ID映射Windows消息ID函数接口 GetExtMenuItem,在MenuDll.cpp中添加这些函数接口定义,下面是添加测试菜单及其消息映射CMap表,代码如下:
BOOL WINAPI InstallExtMenu( HWND ParentWindow,////父辈窗口句柄
 DWORD ChildID,///动态菜单ID
 UINT * NextMenuID )////动态添加菜单
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState());
 BOOL bReturn = FALSE ;
 if ( ParentWindow != NULL &&  theApp.m_CommandWnd == NULL )////父辈窗口存在,以及消息响应窗口还没有创建
 {
  HMENU hMenu = ::GetMenu( ParentWindow ) ;////得到父辈窗口的菜单句柄
  if ( hMenu != NULL  )
  {
   CMenu ParentMenu ;
   ParentMenu.Attach( hMenu ) ;
   CMenu * SubMenu = NULL ;
   UINT TestPosition = GetMenuPosition( &ParentMenu, _T("测试") ) ;
// 得到“测试”菜单未知,此函数在msdn中有详细解释。
   if ( TestPosition != MENU_POSITION_ERR ) // 如果“测试”菜单存在就得到其子菜单
   {    
    SubMenu = ParentMenu.GetSubMenu( TestPosition ) ;
}
   if(SubMenu == NULL)////如果没有子菜单,就添加子菜单
   {
    CMenu Menu ;
    Menu.CreateMenu() ;
    HMENU hSubMenu = Menu.Detach();
    ParentMenu.InsertMenu( ParentMenu.GetMenuItemCount(), MF_BYPOSITION| MF_POPUP|MF_STRING, (UINT_PTR)hSubMenu, _T("测试") ) ;
    TestPosition = GetMenuPosition( &ParentMenu, _T("测试") ) ;
    SubMenu = ParentMenu.GetSubMenu( TestPosition ) ;

   }
   if ( SubMenu->GetMenuItemCount() > 0 )/////如果子菜单存在,就先添加一个分割线。
   {
    SubMenu->AppendMenu( MF_SEPARATOR, 0, _T("") ) ;
   }
   theApp.m_CommandToMessage.InitHashTable(ID_COUNT) ;/////添加两个子菜单
   SubMenu->AppendMenu( MF_STRING, (UINT_PTR)(*NextMenuID), _T("测试菜单1") ) ;
   theApp.m_CommandToMessage.SetAt( (*NextMenuID)++, CMenuDllApp::ID_MENU_TEST1 ) ;
   SubMenu->AppendMenu( MF_STRING, (UINT_PTR)(*NextMenuID), _T("测试菜单2") ) ;
   theApp.m_CommandToMessage.SetAt( (*NextMenuID)++, CMenuDllApp::ID_MENU_TEST2 ) ;
   ParentMenu.Detach() ;    
   theApp.m_CommandWnd = new CMenuWnd ; // 创建消息响应窗口
   bReturn = theApp.m_CommandWnd->Create( NULL, _T(""), WS_CHILD, CRect(-1,-1,-1,-1), CWnd::FromHandle(ParentWindow), ChildID ) ;    
   CWnd::FromHandle(ParentWindow)->DrawMenuBar() ; //强制重画菜单.
  }
 }
 return bReturn ;
}
    1. 菜单消息响应机制
Windows消息响应可以分为两种,一种是采用用户自定义的消息WM_USER机制,另外一种采用Windows注册消息机制,Windows注册消息是一个静态UINT变量,在动态库加载时注册,动态库卸载时取消。但是,其中需要解决一个问题,就是菜单ID和Windows消息ID之间的匹配关系。因为所建动态库是MFC规则动态库,故可以采用MFC的模板类Map来映射二者之间的关系,通过函数接口GetExtMenuItem来查询二者之间的关系。在menudll.h中定义:
typedef CMap<UINT,UINT,UINT,UINT> CommandToMessage m_CommandToMessage;
    在CTestPluginApp类中定义保存所注册的消息ID的变量。
static const UINT ID_MENU_TEST1 ;static const UINT ID_MENU_TEST2 ;以及在动态库加载时注册这两个消息。并和菜单项ID建立对应映射表。
    为了简化消息响应,在动态库中添加一个继承与CWnd类的子类,主要目的是采用MFC的消息动态映象机制DECLARE_DYNAMIC(CMenuWnd)来处理消息响应。在其中添加消息响应函数接口:
BEGIN_MESSAGE_MAP(CMenuWnd, CWnd)
ON_REGISTERED_MESSAGE( CMenuDllApp::ID_MENU_TEST1, OnMenuTest1 ) ///第一个菜单消息响应接口
ON_REGISTERED_MESSAGE( CMenuDllApp::ID_MENU_TEST2, OnMenuTest2 ) ///第二个菜单消息响应接口
END_MESSAGE_MAP()。
    2. 主框架消息传递
    依据MFC消息响应机制,可以在视类或者文档类中来响应所涉及的消息,为了方便处理,这里主要在主框架类中来处理该消息。按照一般的动态库调用机制,首先将动态库调入,保存其句柄。
    在主框架中添加动态库句柄:HMODULE m_TestModule ;在主菜单项中添加菜单“测试”,和弹出菜单“添加动态库”,定义其ID为ID_LOADMENUDLL;并在主框架类中添加其响应事件。在响应事件中添加加载动态库功能,并加载菜单。为了使菜单能够响应操作,需要在主框架中继承两个虚函数OnCmdMsg和OnCommand,并在其中添加其消息响应机制:
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
 // TODO: 在此添加专用代码和/或调用基类
 if ( nCode == CN_COMMAND )////如果是菜单命令
 { // if nID translates to our internal message then enable the menu item
  // otherwise, let OnCmdMsg() handle nID.
  UINT nSendMsg = 0 ;
  if(m_TestModule )////菜单动态库已经加入
  {
   GETMENUITEM  GetMenuItem =(GETMENUITEM)GetProcAddress( m_TestModule, "GetExtMenuItem");
   nSendMsg = GetMenuItem( nID);////判断是不是所添加的菜单消息
   if(nSendMsg)/////是菜单消息就返回所所得到的消息,否则返回0
return TRUE ;////
}
 }
 return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);////不是时就默认处理
}
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)/////分发命令
{
 // TODO: 在此添加专用代码和/或调用基类
 UINT nSendMsg = 0 ;
 if(m_TestModule )/////动态库已经加载,首先就考虑是不是动态库的菜单命令
 {
  GETMENUITEM  GetMenuItem =(GETMENUITEM)GetProcAddress( m_TestModule, "GetExtMenuItem");
  nSendMsg = GetMenuItem( (UINT)wParam);////同上述
  if(nSendMsg)////是动态库命令就获取动态库所创建的窗口类,然后分发命令,不是就采用默认处理
  {
   CWnd * pWnd = GetDlgItem( CHILD_WINDOW_ID ) ;
   if ( pWnd != NULL && pWnd->GetSafeHwnd() != NULL )
   {
    return (BOOL)pWnd->SendMessage( nSendMsg, 0, 0 ) ;
   }
  }
 }
 return CFrameWnd::OnCommand(wParam, lParam);
}

 

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089