分享
 
 
 

用ATL编写MMC Snapin的Sample

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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, 如有必要,重载UpdateMenuState函数修改菜单状态;

如何给某一个子节点添加自己独特的工具栏?

给“服务”项目添加“启动”、“停止”工具栏按钮。

1, 资源中添加工具栏,添加按钮“启动”、“停止”;

2, 使用宏

BEGIN_SNAPINTOOLBARID_MAP(CService)

SNAPINTOOLBARID_ENTRY(IDR_TOOLBAR1)

END_SNAPINTOOLBARID_MAP()

来接收命令消息。

3, 如有必要,重载UpdateToolbarButton函数修改按钮状态;

4, 如有必要,在处理消息后,可以通过IToolbar接口修改工具栏按钮状态;

如何给某一个子节点添加自己独特的属性页?

给“帐户”添加属性页。

1, 在CAccount类中重载Notify方法。

2, 在Notify方法中处理MMCN_SELECT消息:

CComPtr<IConsoleVerb> spConsoleVerb;

if(spConsole->QueryConsoleVerb(&spConsoleVerb) != S_OK)

{

ATLTRACE("==>Query ConsoleVerb faild.\n");

return S_FALSE;

}

else

{

spConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED,

TRUE);

hr = S_OK;

}

3, 重载CreatePropertyPages、QueryPagesFor方法,创建属性页,可以使用ATL Wizzard为我们生成的CMySnapinPage,也可以自行定制一个类似的类。

4, 如有必要,可以在属性页的OnApply函数中发出属性改变通知CAccout;

5, CAccout相应地更新自己的显示。

注意点:

SCOPEDATAITEM/RESULTDATAITEM 结构的lParam,ATL特殊用法

lParam在ATL中有特殊意义,IComponentImpl和IComponentDataImpl的GetDisplayInfo函数都假设SCOPEDATAITEM/RESULTDATAITEM 结构的lParam就是CSnapInItem对象,因此,CMySnapinData的构造函数中有m_scopeDataItem.lParam = (LPARAM) this;和m_resultDataItem.lParam = (LPARAM) this;

因此,如果我们的Item类不从CMySnapinData继承的话,那么也要同样做这些工作。

QueryPagesFor方法

如果Snapin支持属性页,那么必须在Root结点上提供一个QureyPagesFor的方法来支持,否则无法通过MMC控制台“增加”这个管理结点。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有