Introduction
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.
Basic Terminology
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.
Setup:
- Make sure you install Microsoft November 2001 SDK on your machine. Once you have
done that, make sure you add the SDK's "include" folder and the "lib" folder under "Tools" -->
"Options" --> "Directories".
- In order to build this project or any MMC related projects, you have to link to "mmc.lib" and also "comctl32.lib".
Also, inlcude "mmc.h" in your project.
- To debug, make sure the executable for debug sessions is set to MMC.exe. Also, remember
that we are not using MFC.
The MS files:
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.
General Overview of Snapins
To be classified as a snap-in, the COM server DLL must implement the following three MMC specific
interfaces:
- IComponentData
- IComponent
- ISnapinAbout
In addition, the snap-in must implement the following COM specific interfaces
and functions:
- IClassFactory
- IDataObject
- DLLRegisterServer
- DLLUnregisterServer
- DLLGetClassObject
- DLLCanUnloadNow
Once the snap-in is registered, you should see the following entries in the registry:
- One entry in HKEY_CLASSES_ROOT_CLSID
- One entry under HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\Snapins
You should also see the snap-in in the list of snap-ins when you start MMC and select
to "Add/Remove Snapin..".
General Decription
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;
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.
Additional supplements
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.