|
MMC相关的编程接口 MMC 实现了下面的接口: IColumnData (new in MMC 1.2) IConsole2 IConsoleNameSpace2 IConsoleVerb IContextMenuCallback IContextMenuProvider IControlbar IDisplayHelp IHeaderCtrl2 (new in MMC 1.2) IImageList IMenuButton IPropertySheetCallback IPropertySheetProvider IRequiredExtensions IResultData IToolbar Snap-ins 实现了下面的接口: IComponent IComponentData IEnumTASK IExtendContextMenu IExtendControlbar IExtendPropertySheet2 IExtendTaskPad IRequiredExtensions IResultDataCompare IResultDataCompareEx (new in MMC 1.2) IResultOwnerData ISnapinAbout ISnapinHelp2
IComponent接口表示了右边的结果栏(Result pane)里的对象,MMC需要和结果栏里的对象通信时使用这个接口; IComponentData接口表示了左边的目录栏(scopet pane)里的对象(这些对象可以称之为节点),MMC需要和目录栏里的对象通信时使用这个接口; IExtendContextMenu接口,无论用户用鼠标右键点击了左边的节点(scope pane item)还是右边的项(result pane item),MMC都会弹出一个默认的关联菜单,这时MMC会使用这个接口询问该对象(Item),是否需要添加自己的菜单项。 IExtendControlbar和IExtendPropertySheet2也是类似的道理。 ISnapinAbout实现了一个关于对话框的接口,MMC通过这个接口了解Snapin的版本、描述信息、提供者信息、对话框图标,并且特别重要的是,MMC通过这个接口得到了Snapin的所有结点、项的默认图标,要修改根结点的图标也可以通过这个函数GetStaticFolderImage来向MMC提供。
详尽的描述请参考Platform SDK。
Snapin不需要实现全部接口,Sample仅实现了其中的: IComponent IComponentData IExtendContextMenu IExtendControlbar IExtendPropertySheet2 ISnapinAbout MMC的ATL实现支持类简介 Sample的模型图
 ATL实现了几乎全部的接口,提供了默认的处理代码。ATL Wizzard生成的代码提供了基本的编程框架,Snapin的特性实现依赖于对CmySnapinData的某些函数的重载来修改默认行为。 上图中,CMySnapinComponent的基类IExtendContextMenuImpl,IExtendContextPropertySheet,也同CMySnapin的一样,依赖于对CMySnapinData的几个同名函数的调用,只是因为线条太乱才没有画出。 Snapin DLL Sample的代码生成Step by Step 1, 新建ATL工程,如果需要使用MFC,可以使用Wizzard选项支持MFC工程命名test6; 2, “New ATL Object”,选择“MMC Snapin”; 3, 命名“MySnapin”,并在第二个属性页“MMC Snapin”上,选择IextendContextMenu,IextendControlbar,IextendPropertySheet,IsnapinAbout;并且去掉对持久性的支持; 4, 确定后,ATL Wizzard为我们生成了CMySnapin, CCMySnapinComponent, CMySnapinData,CMySnapinAbout, CMySnapinPage; 5, 编译;然后运行mmc.exe,添加一个管理单元“MySnapin”,另存为MySnapin.msc; 代码简介 CMySnapin实现了IComponentData,IExtendContextMenu,,IExtendPropertySheet三个接口。CMySnapinComponent实现了IComponent,IExtendContextMenu,IExtendControlbar,IExtendPropertySheet 四个接口,CMySnapinAbout则实现了ISnapinAbout接口。 DLL向外部暴露了CMySnapin和CMySnapinAbout两个对象。这样MMC就可以找到ISnapinAbout和IComponentData,IExtendContextMenu,,IExtendPropertySheet三个接口,然后MMC通过IComponentData的CreateComponent方法创建了CMySnapinComponent对象,从而又提供了CMySnapinComponent的IComponent,IExtendContextMenu,IExtendControlbar,IExtendPropertySheet 四个接口。 从图中可以看到,ATL的接口实现类实现了部分方法的默认行为,然后把几个重要方法如Notify等传递到CMySnapinData对象的同名函数,让其继续处理。 CMySnapinData从CSnapinItemImpl继承,CSnapinItemImpl实现了CSnapinItem接口,该接口定义了Item对象(左边的节点(scope pane item)还有右边的项(result pane item))的所有行为。 CMySnapin在构造函数里创建了CMySnapinData作为根结点。一个Item的所有行为都可以通过重载某些方法来修改默认行为。比如在根结点下面添加几个子节点,可以在重载的Notify方法中处理MMCN_EXPAND消息等等。 MSDN中的MMC Snap-In Wizard MMC Snap-In Wizard讲了MMC Snap-In FAQ: 1. How Do I Add a Toolbar Resource to the Snap-In Object? 2. How Do I Add Menu Items, Control Bar Buttons, and a Property Sheet to the Snap-In Object? 3. How Do I Enumerate the Child Items of the Snap-In Object? 4. How Do I Add Custom Item Types to the Snap-In Object? 以上内容请自行参考msdn,按照以上的方法,可以做出一个完整功能的Snapin了。但是,非常遗憾的是,ATL Wizzard提供的无论是菜单、工具栏还是属性页都是所有的Item公用的,无论是Scope Item还是Result Item。特别是在有多个不同种类的节点的时候,这样几乎是不合适宜的。 按需定制的Item实现QA 如何生成子节点(Scope Item)? 在按照以上步骤生成代码框架以后,运行MySnapin.msc可以看到只有一个根结点,名字是MySnapin。修改这个根结点的名字,只需要修改MySnapin.cpp文件中 const OLECHAR* CMySnapinData::m_SZDISPLAY_NAME = OLESTR("MySnapin"); 这一行即可。 前面讲过ATL把Item抽象成CSnapinItem接口,CSnapinItemImpl类实现了这个接口的大部分默认行为,因此,所有的Item都应该从这个类继承。考虑到CMySnapinData作为我们的根结点,ATL已经为我们生成了一些代码,从CMySnapinData继承是更好的选择。(能够重用的代码其实只有CMySnapinData构造函数中的初始化代码,不过从CMySnapinData继承的最大好处是可以将公共的实现放在CMySnapinData类中,而不需要每个Item类都实现一份相同的代码。),本文将从CMySnapinData类继承,也由于CMySnapinData将作为基类实现,因此,我们需要另外做一个根结点。 接下来我们将生成三个一级子节点,分别为类似“服务”、“事件查看器”、“帐户管理”,然后在“事件查看器”和“帐户管理”中再生成二级子节点,基本上包含了定制菜单、属性页、工具栏等。 分别从CMySnapinData继承,生成CItemRoot, CServicesContainer, CEventsContainer, CAccountsContainer。注意,在MySnapin.h文件中加入一行 #include "test6.h"。 在CMySnapin的构造函数里,我们把根结点改为CItemRoot。 在CItemRoot类中,重载Notify方法,增加代码处理MMCN_EXPAND,这个消息给了我们添加子节点(Scope Item)的机会。 case MMCN_EXPAND: { CComQIPtr<IConsoleNameSpace, &IID_IConsoleNameSpace> spConsoleNameSpace(spConsole); m_pServicesContainer->m_scopeDataItem.mask |= SDI_PARENT; m_pServicesContainer->m_scopeDataItem.relativeID = param; spConsoleNameSpace->InsertItem(&m_pServicesContainer->m_scopeDataItem);
m_pEventsContainer->m_scopeDataItem.mask |= SDI_PARENT; m_pEventsContainer->m_scopeDataItem.relativeID = param; spConsoleNameSpace->InsertItem(&m_pEventsContainer->m_scopeDataItem);
m_pAccountsContainer->m_scopeDataItem.mask |= SDI_PARENT; m_pAccountsContainer->m_scopeDataItem.relativeID = param; spConsoleNameSpace->InsertItem(&m_pAccountsContainer->m_scopeDataItem); hr = S_OK; } break; 如何生成结果栏的项(Result Item) 从CMySnapinData继承,生成CEventEntry类,表示一个事件内容。在CEvent类中修改Notify方法的MMCN_SHOW消息,这个消息给了我们添加Result Item的机会。 如何修改子节点的名字? 每一个结点都从CMySnapinData继承,其父类CSnapinItemImpl有一个成员变量CComBSTR m_bstrDisplayName。 MMC调用IComponentData或者IComponent 的GetDisplayInfo来获得显示信息,包括结点名字,GetDisplayInfo把调用传递到GetScopePaneInfo或者GetResultPaneInfo函数,GetScopePaneInfo和GetResultPaneInfo函数默认返回m_bstrDisplayName。 因此只要在构造函数里修改m_bstrDisplayName的值即可达到目的。 如何修改子节点的图标? 在CMySnapinData中实现Notify方法的MMCN_ADD_IMAGES消息,这这里我们把所有的图标全部加载。这样其他的Item类就不需要再实现这部分代码了,只要把Notify中没有处理的消息让CMySnapinData的Notify方法处理一下即可。 对于每个Snapin都有一个IImageList接口可供使用,MMC利用这个图标列表来保存所有需要用到的图标,以后每个Item只需要指出它的图标在图标列表的索引即可: m_scopeDataItem.nImage = 2; m_scopeDataItem.nOpenImage = 2; 但是Scope栏中的图标却不是通过MMCN_ADD_IMAGES消息来加载的,而必须在CMySnapin类的Initialize方法中首先加载所有必要的图标。 还有,以上的方法对根结点的图标都没有作用。根结点的图标的修改必须通过修改CMySnapinAbout类GetStaticFolderImage方法才可以。也可以顺便为About对话框添加一个ICON。 如何修改子节点在结果栏中的默认显示? Scope Item在Result pane中默认仅仅显示一个名称。比如,在选中“MySnapin”时,默认行为是在Result pane中显示一级子节点。下面我们让“事件查看器”显示更详尽的信息。 在CEventsContainer的Notify方法中修改MMCN_SHOW消息: case MMCN_SHOW: { CComQIPtr<IResultData, &IID_IResultData> spResultData(spConsole); spHeader->InsertColumn(0, L"日志", LVCFMT_LEFT, 100); spHeader->InsertColumn(1, L"描述", LVCFMT_LEFT, 200);
hr = S_OK; } break; 然后在CEvent中重载GetResultPaneColInfo方法,提供每一列的显示字符串。 由于GetResultPaneColInfo是非虚函数,因此我们还得在CEvent重载GetResultPaneInfo函数。或者修改CMySnapinData的GetResultPaneColInfo函数为虚函数,那么就可以不用再重载GetResultPaneInfo了。 如何给某一个子节点添加自己独特的菜单? 由于从CSnapinItemImpl以模版方式实现,因此从CMySnapinData继承的Item类,将需要重载相应的处理函数,而不能最大限度地重用CSnapinItemImpl的实现。 所以,我们让CService等几个需要更多功能的类从CSnapinItemImpl<CService>继承。
给“服务”项目添加“启动”、“停止”菜单。 1, 在菜单资源中添加菜单IDR_MYSNAPIN_MENU1,添加菜单项“启动”、“停止”到第一个下拉菜单中,如果需要在其他菜单项上,也可以添加到其他的下拉菜单中; 2, 使用宏SNAPINMENUID(IDR_MYSNAPIN_MENU1)来告诉ATL菜单资源的ID; 3, 使用宏 BEGIN_SNAPINCOMMAND_MAP(CService, FALSE) SNAPINCOMMAND_ENTRY(IDM_START, OnStart) SNAPINCOMMAND_ENTRY(IDM_STOP, OnStop) END_SNAPINCOMMAND_MAP() 来接收命令消息。 4, 并使用 HRESULT OnStart(bool& bHandled, CSnapInObjectRootBase* pObj); HRESULT OnStop(bool& bHandled, CSnapInObjectRootBase* pObj); 来处理消息。 5 [1] [2] 下一页 |