![]() |
Desktop Development »
Shell and IE programming »
General
Intermediate
Developing MMC Snap-insBy RK_2000Developing an MMC administrative utility |
VC6Win2K, WinXP, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Microsoft first introduced MMC Version 1.0 with WinNT 4.0 Option Pack for WinNT Server. They followed that with MMC 1.1 that came with SQL7 and MMC 1.2 which came with Win2K. The ATL Object Wizard of Visual C6 provided a control that would allow you to build MMC tools. However, for some reason, this utility got lost along the path and so, when Microsoft introduced its latest version of MMC, users were advised not to use the ATL wizard anymore.
So, what should you do? Well, Microsoft provides a very comprehensive package as part of its November of 2001 SDK update which gives you sufficient details to get started developing an MMC snap-in. Nonetheless, programming an MMC snap-in is quite a handful, as there are no wizards helping you out with any part of it. So, in this article, I will give you a few hints that I learned while dealing with the beast. I should mention that this article is just a starter, as for whatever reason MS has made a science out of MMCs and Snap-ins.
|
MMC |
the client that you start by running mmc.exe |
|
Snap-ins |
the server that you load by using ADD\Remove snap-in. |
|
Scope Pane |
This is the left hand side pane. |
|
Results Pane |
This is the right hand side pane. |
|
Console Tree |
This is the left hand side tree that is used to navigate. |
|
Nodes |
Nodes represent individual items on the left hand side. |
In this demo, I have included the code for a basic MMC that connects to a SQL Server database and shows the result on the right hand side. I have also added context menu functionality on the right hand side. This demo is built on the basis of some of the code that Microsoft has provided in their SDK.
An MMC Snap-in is a COM in-process server. When you run mmc.exe, what you get is the
mmc client. When you add a snap-in, you are working with the server DLL.
By definition, a COM in-process server is a DLL that houses COM-based components. It
executes in the context of the calling process.
To be classified as a snap-in, the COM server DLL must implement the following three MMC specific interfaces:
In addition, the snap-in must implement the following COM specific interfaces and functions:
Once the snap-in is registered, you should see the following entries in the registry:
You should also see the snap-in in the list of snap-ins when you start MMC and select to "Add/Remove Snapin..".
The starting point of the application is in BaseSnap.cpp. The entry-point into the
in-process servers is always the "DllGetClassObject". This entry point is
used so that client programs can obtain their class factory interfaces. In this case,
it takes three parameters, a REFCLSID which is the reference to the CLSID of the
specific component, a REFIID which is a reference to an IID for the spceific interface
to be returned from the created COM Object and a void pointer which will point to the
requested interface.
When you try to load the snap-in, MMC starts out as any COM client would, by calling
the CoCreateInstance to get the IUnknown interface of the snap-in. This action calls
the DLLGetclassObject. So, if you want to get a feel for how everything works together,
stick a breakpoint here and carry on.
If you look through the code, you notice that DLLGetClassObject essentially creates the
Class Factory and returns an IClassInterface to COM. When COM calls IClassInterface::CreateInstance,
two objects are created, one representing the IComponentData interface and the other
representing the IAbout interface.
CClassFactory *pFactory = NULL;
// make the factory passing in the creation function for the type of object they want
if (rclsid == CLSID_CComponentData)
pFactory = new CClassFactory(CClassFactory::COMPONENT);
else if (rclsid == CLSID_CSnapinAbout)
pFactory = new CClassFactory(CClassFactory::ABOUT);
if (NULL == pFactory)
return E_OUTOFMEMORY;
HRESULT hr = pFactory->QueryInterface(riid, ppvObj);
Now let's look at the other components of this project. As you can see, we create a
CComponentData object which represents the left hand side of the snap-in.
Looking at the constructor of the CComponentData class, you notice that it creates a
CStaticNode object. This will represent the "root node" of the snap-in and
is assigned to m_pStaticNode variable.
If you look underneath the CStaticNode class, you notice that this is where the
underlying folders are created. For instance, in the case of this example, we have
one underlying folder which is represented by the CReport class:
CStaticNode::CStaticNode()
{
children[0] = new CReport;
}
When you try to expand the root node, MMC calls the "CreateComponent" method on the
CComponentData
class which in turn instantiates a CComponent object. The CComponent object represents the right
hand side of the snap-in. Once the
CComponent object has been created, four different message are sent:
MMCN_EXPAND
MMCN_ADD_IMAGES
MMCN_SHOW
MMCN_SELECT
Something that should be noted is that every node both on the result pane and scope pane are represented by unique identifiers as shown at the beginning of many of the files:
const GUID CPath::thisGuid = { 0x2974380D, 0x4C4B, 0x11d2,
{ 0x89, 0xD8, 0x0, 0x0, 0x21, 0x47, 0x31, 0x28 } };
The final phase of the process involves populating the result pane.
MMC first calls GetResultsView on the CComponent object and obtains the result view type.
In the case of our example, the type is the default.
MMC, then sends a MMCN_ADD_IMAGES to add images and MMCN_SHOW to set focus to the result pane.
At this point, it's time for the Report object to act. If you notice, the right hand side
of our project is represented by Report and ReportContent. The Report object creates the header columns
and fills up the rows. When a user clicks on the result pane and tries to start a context
menu, the ReportContent event handlers are called upon.
With this example, you have a simple snap-in with ContextMenu and database support and this
should be enough to get you started with your MMC journey.
I have included a set of database classes for the application.
It's a simple ADO class which is later used to access the database. You should pay
special attention to the CreateRecordset and SetDSN functions in there. The
CreateRecordset
takes three parameters. The first one of course is the query statement while the second
is a database number. This database number is used to determine which DSN to use. In my
case, my DBClass was working with multiple databases, so I needed this feature.
The last parameter is a flag which indicates what type of query to make: a direct select or
a table query (i.e. return a whole table). So, modify
these according to your needs.
Also there is this one bit of SQL that you should run to create the sample table. You can then manually add some data to the table.
CREATE TABLE [Sample] (
[SampleID] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
) ON [PRIMARY]
If you have questions regarding MMC, I would say the best place to go to is the google MMC group or get a copy of November of 2001 Platform SDK. Hope this will help you find your way around.
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 16 Jul 2002 Editor: Chris Maunder |
Copyright 2002 by RK_2000 Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |