Introduction

Most COM programmers know that to activate COM objects you call CoCreateInstance(), but that's not the only way. This article looks at activating objects using monikers. A moniker is an object that activates another object associated with a string. These strings contain all the information necessary to resolve it into an object. They are known as display names. The act of resolving the display name in to an object is called "Binding". 

Benefits of Monikers

The benefit is using monikers is in the level of indirection afforded by decoupling an object from the algorithm used to obtain a pointer to it. This means that you can write standard client code to obtain a pointer to any object in any state simply by changing the display name used. 

You can activate an object using CoCreateInstance(). If you wish to initialize the object, you can then add a Create() method to its interface. This, however, breaks the true spirit of the Factory pattern that is the basis of COM design; factories create objects, objects do not create themselves. But, it does work. 

So why use monikers? Here's a simple analogy. When writing a C++ object it is normal practice to use more than one constructor. For example, take a hypothetical class 'a':

class a
{   
    // CoCreateInstance
    a();			
    // File Moniker	
    a(char * MyFileName);
    // Item Moniker
    a(char * AString);
    // Composite Moniker		
    a(char * MyFileName,
      char * Astring);
:
:
};

The above class has four constructors. Using the default constructor is the equivalent of calling CoCreateInstance(). Using the other three constructors would be equivalent to using different kinds of moniker to construct an object that is in a given state. 

CoCreateInstance() is limited because it assumes that you are creating new objects. The moniker architecture has a system of caching objects known as the Running Object Table (ROT). Therefore, if an object already exists when you need a pointer to it then you will get a pointer to this object rather than creating a new one. To use the ROT an object should register itself during its creation.
Using Monikers
To use monikers, what do you actually need to know and do?
You need to know:
1. What types and combinations on moniker you object will support.
2. How the moniker display name convention works.
3. What interfaces and methods to implement for each kind of moniker.
4. How to write client code using the COM APIs available.

You do not particularly need to know how the IMoniker interface works and the construction details of a Bind Context or details of how it all works under the hood. This is because you don't have to implement your own moniker objects. COM comes complete with its own built in set of system monikers ready for you to use.
Types of Moniker
This article is about leveraging the power of the system monikers not about writing your own custom monikers. You can find out what types of system moniker are available by looking in either OBJIDL.H or OBJIDL.IDL. Here you will find an MKSYS enum listing the different types.

typedef 
enum tagMKSYS
{
  MKSYS_NONE	= 0,
  MKSYS_GENERICCOMPOSITE = 1,
  MKSYS_FILEMONIKER = 2,
  MKSYS_ANTIMONIKER = 3,
  MKSYS_ITEMMONIKER = 4,
  MKSYS_POINTERMONIKER = 5,
  MKSYS_CLASSMONIKER = 7
} MKSYS;

Notice that the number 6 is missing from this list. This is a more recent edition to the moniker family and it represents a URL moniker. If you're interested in URL monikers then its in the file URLMON.h as:

#define MKSYS_URLMONIKER 6            

This enhancement shows the extensibility of the architecture "in action". The documentation for URL monikers available with Developer Studio Version 6 states that you need NT 5 in order to use them. This highlights the importance of monikers because the Internet is an important part of Microsoft's corporate strategy and URL monikers are a new addition to the system.

File monikers allows clients to bind to persistent objects stored in files. The filename is usually used as the display name.

Item monikers are simple strings that name a piece of data or the location of a piece of data. They are useless on their own and always need combining with other monikers. For example, when you combine an item moniker with a file moniker you can reference a section of the file. When you combine an item moniker with a class moniker it acts like command line parameter. In linking an item moniker to another moniker, the other moniker always has to be on the left of the item moniker. 
 
For example, take the following display names:

"MySales.doc!CustomerData".
 "clsid:45A5DAC0-A326-11d2-ADF6-000000000000:!Search"

In the first example, the user wants to obtain an object initialized by the "CustomerData" from the MySales.doc document. In the second example, the user wants to create a certain object initialized with the string "Search"

The joining of these monikers together is achieved using a composite moniker.
Composite monikers allow you to mix monikers together creating a path to the data that initializes your object. The examples show that you produce composite moniker display names by concatenating individual display names and separating them with a delimiter. The usual delimiter is an exclamation mark. You are not limited to two monikers in a composite, you can have any File!Item!file!Item!Item combination!

In the moniker architecture each type of moniker has its own underlying interface. It is these interfaces that you need to know how to implement when using monikers. For example, item monikers are linked to IOLEItemContainer, whilst file monikers are linked to IPersistFile. Fortunately, you do not need to know how to implement all the methods in these interfaces. The important methods are IOleItemContainer::GetObject() and IPersistFile::Load().

The rest of this article concentrates upon implementing a sample object that can handle a file moniker and a file!item composite moniker together with the client code that demonstrates the power of monikers.

The demonstration object is a Tribble, but why choose to implement a tribble? This is a nonsense object because tribbles don't exist. For the purposes of this demonstration, tribbles are an object with two properties: Name and Colour. 

There are several ways to implement a COM object; the tribble construction uses multiple inheritance. This technique was chosen because of its simplicity.

There are two main objects in the Tribble. These are the CTribbleClass, which is the class factory for a tribble, and the Tribble itself.

Diagram 1 below shows a UML class diagram for the tribble.



Diagram 1.

For the purposes of implementing the moniker code the files of importance are PersistFile.cpp, OleItemContainer.cpp and ParseDisplayName.cpp. These files contain the underlying interfaces to our chosen monikers.

Note the words "chosen monikers". It is up to the object's implementor to decide what monikers or combination of monikers the object will support. Having made this decision the appropriate supporting code is written.

The tribble's implementation uses a file moniker and a composite File!Item moniker. This means implementing IPersisitFile and IOleContainer and IParseDisplayName. 
 
The idea is that a Tribble can save itself using it's IPersistFile::Save() method. A client can then re-load that tribble using a file moniker. The File!Item moniker gives the client chance to change a tribble's colour during construction. For example, if a client saves a blue tribble by the name of Peter in the file "Pete.trb", then using a file!item moniker the client can turn Peter red by specifying "Pete.trb!Red". 
Creating a Test File.
One of the problems in demonstrating how COM objects can usefully create themselves from a file using monikers is actually creating a file in the first place.

The Stgmain.exe program in file Stgmain.cpp is a console application designed for simplicity. The user passes in the name of a new file, the name of a tribble and it's colour as command line arguments.

The program creates a tribble using CoCreateInstanceEx() and gets hold of the Tribbles ITribble and IPersistFile interfaces. The program then sets the tribbles name and colour using the data supplied in the command line arguments. 

Finally it tells the tribble to save itself via a call to the tribbles IPersistFile::Save() method.

The tribble then creates a new docfile by calling StgCreateDocfile and passing in the file name. This call gives the tribble a pointer to an IStorage interface. In order to store data in a storage you need to create streams. The tribble creates two streams, one called "Name" and one called "Colour". In to these it writes it's m_Name and m_Colour properties. These are now available for later. Finally it stores the tribbles CLSID in the storage using a 
call to IStorage::SetClass().

You do not have to use structured storage when implementing a file moniker. IPersistFile::Save() merely gives you a filename and its up you what you do with it. For example, the filename could be a simple text file or even represent a database. You do have to ensure that COM can get hold of your object's CLSID based on the filename. In using structured storage the CLSID is stored in the file so if you don't use it then you need to ensure that your file type is in the registry under the key HKEY_CLASSES_ROOT\<.extention>. The default data should be the ProgID for your object.

Diagram 2 shows the client program. This is a simple MFC dialogue application containing three input sections. The first demonstrates code that creates an object using a file moniker. The second section demonstrates creating an object using a file!item composite. The third demonstrates CoGetObject() - the power of monikers. In this edit box type in a moniker in display name format as described above. For example "c:\temp\test.trb" or "c:\temp\test.trb!Red".


Diagram 2.

The client program creates a tribble using a file moniker through it's CWinClientDlg::OnFileBind() method. The code fragment in listing 1 from WinClientDlg.cpp shows what happens.

Listing 1

void CWinClientDlg::OnFileBind() 
{
  ITribble * pITribble = NULL;
  IMoniker * pFileMoniker = NULL;

  // Get the file name
  CString strTmp;
  m_FileName.GetWindowText(
                        strTmp);
  
  // convert into wide character
  // string
  COleCharConv fn(strTmp);

  // Use this Wrapper API to
  // get a file moniker
  HRESULT hr;
  hr = CreateFileMoniker(fn,
                &pFileMoniker);

   :
   :

  // Now get the tribble
  // using another Wrapper
  hr = BindMoniker(pFileMoniker,
             0,IID_ITribble,
             (void **)&pITribble);

   :
   :
  // The moniker is no 
  // longer needed
  pFileMoniker->Release();

  // Now use the tribble
   :
   :
  // The tribble is
  // nolonger needed
  pITribble->Release();
TheExit:
  return;
}

You can use the paradigm employed in listing 1 in any moniker client. This paradigm is simple: create a moniker and then bind to. The two important calls are CreateFileMoniker() and BindMoniker(). As its name suggests, CreateFileMoniker merely creates a file moniker object based upon a filename. The BindMoniker() call creates the tribble and tells it to load the file that the moniker describes.


Diagram 3.

Diagram 3 is an overview of what happens when you call BindMoniker() asking for an ITribble interface. To simplify matters the underlying COM libraries are drawn as one object.

Under the hood, BindMoniker() sets up a bind context and then calls the file moniker's BindToObject() method passing in the details of ITribble. BindToObject() then calls the API function GetClassFile() which finds the clsid associated to the file by from either the file or the registry depending on whether or not you have used  structured storage.

Having found the clsid, the file moniker can then instantiate a tribble by calling CoCreateInstance and querying for IPersistFile. This results in the creation of a tribble by its class factory. COM then passes the IPersistFile interface pointer back to the file moniker.

The file moniker then loads the file into the object by calling IPersistFile::Load and finally queries for an ITribble pointer which it passes back to the client.

The tribble implements IPersistFile::Load() as the shown in listing 2 below.

Listing 2.

HRESULT Tribble::Load(
    LPCOLESTR pszFileName,
    DWORD dwMode)
{
  HRESULT hr = S_OK;
  IStorage * pIStorage = NULL;

  // First off, open the storage
  // file we've been given
  // Get hold of an IStorage
  // Interface
  hr=StgOpenStorage(pszFileName,
                  NULL,
                  STGM_TRANSACTED
                  | STGM_READ,
                  NULL,0, 
                  &pIStorage);

  if(SUCCEEDED(hr))
  {
  // Next we open the stream,
  // read the name and colour

  hr = ReadStream(pIStorage,
                OLESTR("Name"),
                m_Name);

  if(SUCCEEDED(hr))
      hr = ReadStream(pIStorage,
               OLESTR("Colour"),
                       m_Colour);

  // If everything is okay 
  // create a moniker for
  // this file object and 
  // register as it running.
  // Save the ROT key so 
  // that we can tell the
  // ROT when we're closing down
  if(!m_pmkFile) // first time in?
  {
    hr =  CreateFileMoniker(
                      pszFileName,
                      &m_pmkFile);

    if (SUCCEEDED(hr))
    { // The Running Object
      // Table pointer
      IRunningObjectTable *pROT;

      // Get the running 
      // object table
      hr = GetRunningObjectTable(0
                         , &pROT);

      if(SUCCEEDED(hr))
      {
        // Register with the ROT
        pROT->Register(0,
                    (IUnknown *)
                    (ITribble*)
                     this
                    ,m_pmkFile,
                    &m_dwROTFile);
        pROT->Release();
	}
      // At this point, use 
      // IROTView.exe to see this
      // object in the Running
      // Object Table
    }
  }
  // Finnished with the Storage.
  pIStorage->Release();
  }

  return hr;
}

The code in listing 2 reads the tribble's document file by first opening its storage and then reading the "Name" and "Colour" streams. The important step here is the final step where the tribble inserts itself in the ROT. This is achieved by making a call to GetRunningObjectTable() which returns an IRunningObject interface pointer: pROT. This is then used to register the tribble through a call to pROT->Register().

The client program can create a tribble using a file!item moniker through it's CWinClientDlg:: OnCmpBind () method. The paradigm used to create the tribble is as before, the only difference here is the creation of the appropriate composite moniker. The code fragment in listing 3 from WinClientDlg.cpp shows how this is done.

Listing 3.

void CWinClientDlg::OnCmpBind() 
{
  ITribble * pITribble = NULL;
  IMoniker * pCmpMoniker = NULL;
  IMoniker * pFileMoniker = NULL;
  IMoniker * pItemMoniker = NULL;
  HRESULT hr;

  // make wide strings
  :
	
  // Use this Wrapper API to get a file moniker
  hr = CreateFileMoniker(fn,
                   &pFileMoniker);

  :
  :
  // Use this Wrapper to create an Item Moniker
  hr = CreateItemMoniker(
                   OLESTR("!"),
                   item,
                   &pItemMoniker);

  :
  :
  // Combine the two into a composite
  hr = pFileMoniker->ComposeWith(
                    pItemMoniker,
                    FALSE,
                    &pCmpMoniker);

  // Don't need these monikers
  // anymore
  pFileMoniker->Release();
  pItemMoniker->Release();


  // Now get the tribble using
  // another Wrapper
  hr = BindMoniker(pCmpMoniker,0,
                    IID_ITribble,
             (void **)&pITribble);
  :
  :

This code creates two intermediate monikers, a file moniker and an item moniker and joins them together to produce a composite moniker by calling the file moniker's ComposeWith() method.

As a composite moniker now fuels the BindMoniker() method, two extra call are made. The first is a call to IPersistFile::QueryInterface for an IOleItemContainer interface and the second is a call to IOleItemContainer::GetObject. This is shown in diagram 4.

Diagram 4.

The IOleItemContainer::GetObject() call is important here. Its first job is to parse through its item string so that it can return an appropriate object. Its second job is to create a composite moniker for the file!item display name and to register it with the running object table. The code fragment in listing 4 shows how this is done.
Listing 4.

  // check that it's a Tribble 
  if(riid != IID_ITribble)
  {
    // display an error
    hr = E_NOINTERFACE;
    goto TheExit;		
  }

  // In real life, item monikers
  // usually refer to part of
  // a file - but in this case
  // just override the tribble 
  // colour attribute
  m_Colour = pszItem;

  // pass back a tribble object
  *ppvObject = (void *)this;

  // Add a File!Item composite
  // to show that an object is
  // running that supports
  // this particular File item
  // combination
  if(m_pmkFile && !m_pmkCmp)
  {
    IMoniker * pmk;
    // Use this Wrapper to create
    // an Item Moniker
    hr = CreateItemMoniker(
                  OLESTR("!"),
                  pszItem,
                  &pmk);

    if(SUCCEEDED(hr))
    {
      // Combine the two into
      // a composite
      hr = m_pmkFile->ComposeWith(
                       pmk,
                       FALSE,
                       &m_pmkCmp);

      if(SUCCEEDED(hr))
      {
          // Register in the ROT
          }

You can see the real benefits of using monikers by looking at the code associated to the Raw Moniker section of the client. This code, in CWinClientDlg::OnCmpBind (), uses the CoGetObject() API call demonstrated in listing 5.

Listing 5.

  // fn contains the
  // display name
  hr = CoGetObject(fn,
                   0,
                   IID_ITribble,
                   &pITribble);
  // Use the Tribble
  :
  :

The paradigm used to create the tribble is exactly as before except that COM does most of the work. The problem here is that COM doesn't know how to construct the item moniker from the display name. To remedy this it asks the tribble to do it using a call to its IParseDisplayName::ParseDisplayName(). The code fragment in listing 6 shows this code which is in the file ParseDisplayName.cpp.

Listing 6.

HRESULT Tribble::ParseDisplayName( 
             IBindCtx  *pbc,
             LPOLESTR pszDisplayName,
             ULONG  *pchEaten,
             IMoniker  * *ppmkOut)
{
  // Here we parse all the
  // different item strings
  // From the string given to use
  // In this case we only have one
  // So check that the first
  // character is a '!' and
  // that it is the only one
  ULONG chEaten=0;

  // first check for a '!'
  OLECHAR ch=*pszDisplayName++;
  chEaten++;

  if ((OLECHAR)'!'!=ch)
    return MK_E_SYNTAX;

  // check for another '!'
  // if there is one then the 
  // tribble doesn't support this
  // type of display name

  LPOLESTR psz;
  for(psz = pszDisplayName;
           *psz != (OLECHAR)'\0';
           psz++,chEaten++)
    if(*psz == (OLECHAR)'!')
      break;

  // Say how much was okay
  *pchEaten=chEaten;  

  if (*psz == (OLECHAR)'!')
    return MK_E_SYNTAX;

  // Create an item moniker for 
  // the string giving it back
  // to COM
  HRESULT hr;
  hr = CreateItemMoniker(
                   OLESTR("!"),
                   pszDisplayName,
                   ppmkOut);
:
:
}

Monikers are a very large and complex subject but the methods and paradigms used in this article can be applied to any COM object. Follow the basic rules of deciding what monikers you need and what underlying interfaces and methods you to implement and you're half way there.

References
Inside OLE, Kraig BrockSchmidt - Microsoft Press
Essential COM, Don Box - Addison Wesley
Inside COM, Dale Rogerson - Microsoft Press

Side Bars

Bind Context.
=============
Many of the member functions of IMoniker contain an IBindCtx * parameter. This refers to a system object that helps in the binding process. As the binding process proceeds, the BindContext holds various pieces of global state data. It does this using a keyed list that is accessible via interface functions such as RegisterObjectParam(...) and GetObjectParam(...). One of its main features is its ability to cope with composit monikers, meaning that it is not limited to a single simple moniker but can deal with many different - but related - monikers at the same time.

CoCreateInstance(Ex)
====================
This API function creates a single uninitialized object of the class associated with a specified CLSID. You call CoCreateInstance when you want to create only one your system. CoCreateInstanceEx, as the name suggests, expands and optimizes CoCreateInstance giving you the ability to efficiently create objects on remote machines.


IPersistFile
============
IPersistFile is an interface provides an object with a set of methods that enable it to access a disk file. This can be any file type rather than a storage object or stream. 

IOleItemContainer
=================
The IOleItemContainer interface is used by item monikers when they are binding to their object. The main function is IOleItemContainer::GetObject(). It is used to hand back a pointer to an object of the requested interface with the characteristics of the string passed in.

Running Object Table (ROT)
==========================
This is a machine wide service listing of all the objects that were created using monikers. The purpose behind the table is a to aid binding efficiency. In resolving a name, the first thing that a moniker must do is to check the running object table for an object of the specified type that may be already running. If it finds one in the ROT, then it hands back an interface pointer from this object to the caller. If it doesn't find one, it creates a new object. 

DataTypes
=========
The example code uses the ColeCharCon class. This converts a char * in to a wchar_t * and then back again. This is because all characters in COM are wide characters. In representing wide characters, COM uses the OLECHAR datatype. Under Windows NT and 95/98 this is defined as the C datatype a "wchar_t". 

Most normal Win32 APIs deal with char based strings. This gives programmers the extra headache of converting between the two.

The classes are simple consisting of a set of constructors and operator overloads. 
Conversion is achieved using the standard C runtime functions:

size_t mbstowcs(wchar_t * pwsz, const char * psz, size_t count);

size_t wcstombs(char * psz, const wchar_t *pwsz, size_t count);

mbstowcs converts a char * in to a wide string, whilst wcstombs converts from a wide string to char *. 

The code for this class can be found in the file OleCharConv.h.

