Monikers and the ROT

Comment on this article

There's a situation where you want to run workstation singletons, like if you want an NT service on the machine to be used from a number of places in client code. That's exactly what I wanted, of course. Already knowing that I needed the Running Object Table (ROT) somehow, at least I had an idea of where to start looking.

The ROT keeps references to running objects, and does that by means of "Monikers". For some funny reason, the most obvious type of Moniker isn't in the system and you have to write it yourself as a COM DLL. This moniker then gets run by the system, if its name appears in a "display name prefix". Well, you'll understand all this lingo after a while (a day, or two days, or so).

To create your own Moniker (in this case a Moniker called "QcMk"), just write the following code. To make things slightly easier on yourself, begin by creating an ATL COM project as a DLL, without any MFC or merge proxy/stub or anything. Then add/modify according to what follows here.

Note that I modified the original code in some ways. In particular, I replaced the dumb interface pointers with smart pointers. I even found one place where the original didn't release where it should; reason enough to switch to smarter stuff.

You'll find the original files at: http://www.microsoft.com/MSJ/1199/MSJNov99.zip and Jeff Prosise's article at: http://www.microsoft.com/msj/backissues99.asp, and then look for November 1999, Wicked code column.

Quickjump to files:

QcMk.h

/////////////////////////////////////////////////////////////////////////////
// QcMk.h
//
// Moniker factory and object
//
// JMW 2000
//
// see Prosise, MSJ nov 1999, wicked code + samples
/////////////////////////////////////////////////////////////////////////////

#pragma once
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
// Factory class
/////////////////////////////////////////////////////////////////////////////

template 
class CMonikerFactory :
  public CComObjectRootEx,
  public IParseDisplayName
{
public:
  CMonikerFactory() {}

  BEGIN_COM_MAP(CMonikerFactory)
    COM_INTERFACE_ENTRY(IParseDisplayName)
  END_COM_MAP()

// IParseDisplayName methods

  STDMETHODIMP ParseDisplayName(IBindCtx* pbc, LPOLESTR pszDisplayName,
    ULONG* pchEaten, IMoniker** ppmkOut)
  {
    *pchEaten = 0;
    *ppmkOut = NULL;

    //
    // instantiate the moniker class
    //
    CComObject* pobj;
    CComObject::CreateInstance(&pobj);

    if (pobj == NULL)
      return E_OUTOFMEMORY;

    //
    // pass the moniker its assigned display name
    // by first skipping up past the ':'
    //
    LPOLESTR pszName = pszDisplayName;
    while (*pszName++ != OLESTR(':'));

    if (!pobj->SetName(pszName)) {
      delete pobj;
      return E_OUTOFMEMORY;
    }

    //
    // query the moniker for an IMoniker interface
    //
    HRESULT hr = pobj->QueryInterface(IID_IMoniker, (void**)ppmkOut);

    if (FAILED(hr))  {
      delete pobj;
      return hr;
    }

    //
    // tell the caller how many characters were consumed
    //
    *pchEaten = wcslen(pszDisplayName);
    return hr;
  }
};

/////////////////////////////////////////////////////////////////////////////
// Moniker class
/////////////////////////////////////////////////////////////////////////////

class ATL_NO_VTABLE CQcMk :
  public CComObjectRootEx,
  public CComCoClass,
  public IMoniker,
  public IROTData
{
public:
// -----------------------------------
// construction/destruction

  CQcMk() 
  {
    m_pwszName = NULL;
  }

  // -----------------------------------

  BOOL SetName(LPOLESTR pwszName)
  {
    // 
    // allocate memory to hold the display name
    //
    int nLen = (wcslen(pwszName) + 1) * sizeof(OLECHAR);
    m_pwszName = (OLECHAR*)CoTaskMemAlloc(nLen);

    if (m_pwszName == NULL)
      return FALSE;

    // 
    // copy in the name
    //
    wcscpy(m_pwszName, pwszName);
    return TRUE;
  }

  // -----------------------------------

  DECLARE_CLASSFACTORY_EX(CMonikerFactory)
  DECLARE_REGISTRY_RESOURCEID(IDR_QCMK)
  DECLARE_NOT_AGGREGATABLE(CQcMk)
  DECLARE_PROTECT_FINAL_CONSTRUCT()

  BEGIN_COM_MAP(CQcMk)
    COM_INTERFACE_ENTRY(IMoniker)
    COM_INTERFACE_ENTRY(IROTData)
  END_COM_MAP()

  // -----------------------------------

  void FinalRelease()
  {
    //
    // free buffer that holds the moniker's display name
    //
    if (m_pwszName != NULL)
      CoTaskMemFree(m_pwszName);
  }

// -----------------------------------
// IPersist

  STDMETHOD(GetClassID)(GUID* pClassID)
  {
    *pClassID = CLSID_QcMk;
    return S_OK;
  }

// -----------------------------------
// IPersistStream

  STDMETHOD(IsDirty)() { return E_NOTIMPL; }
  STDMETHOD(Load)(IStream*) { return E_NOTIMPL; }
  STDMETHOD(Save)(IStream*, BOOL) { return E_NOTIMPL; }
  STDMETHOD(GetSizeMax)(_ULARGE_INTEGER*)  { return E_NOTIMPL; }

// -----------------------------------
// IMoniker

  STDMETHOD(GetTimeOfLastChange)(IBindCtx*, IMoniker*, _FILETIME*){ return E_NOTIMPL; }
  STDMETHOD(Inverse)(IMoniker**){ return E_NOTIMPL; }
  STDMETHOD(CommonPrefixWith)(IMoniker*, IMoniker**){ return E_NOTIMPL; }
  STDMETHOD(RelativePathTo)(IMoniker*, IMoniker**){ return E_NOTIMPL; }
  STDMETHOD(BindToStorage)(IBindCtx*, IMoniker*, REFIID, void**){ return E_NOTIMPL; }
  STDMETHOD(ComposeWith)(IMoniker*, BOOL, IMoniker**) { return E_NOTIMPL; }
  STDMETHOD(Enum)(BOOL, IEnumMoniker**){ return E_NOTIMPL; }
  STDMETHOD(Hash)(ULONG*) { return E_NOTIMPL; }
  STDMETHOD(ParseDisplayName)(IBindCtx*, IMoniker*, LPWSTR, ULONG*, IMoniker**)  
    { return E_NOTIMPL; }

  // -----------------------------------

  STDMETHOD(BindToObject)(IBindCtx* pbc, IMoniker* pmkToLeft, 
    REFIID riid, void** ppvResult)
  {
    HRESULT hr = S_OK;
    *ppvResult = NULL;

    CComPtr  spROT;
    hr = pbc->GetRunningObjectTable(&spROT);

    if (SUCCEEDED(hr)) {
      // 
      // search the ROT for a moniker that's equivalent to this one
      //
      CComPtr  spEM;
      hr = spROT->EnumRunning(&spEM);

      if (SUCCEEDED(hr)) {
        hr = MK_E_NOOBJECT;
        CComPtr  spMoniker;

        while (spEM->Next(1, &spMoniker, NULL) == S_OK) {
          if (IsEqual(spMoniker) == S_OK) {
            //
            // if found, convert to requested type
            //
            CComPtr  spUnk;
            hr = spROT->GetObject(spMoniker, &spUnk);

            if (SUCCEEDED(hr) && spUnk.p != NULL) {
              hr = spUnk->QueryInterface(riid, ppvResult);
            }
            else {
              hr = MK_E_NOOBJECT;
            }
            break;
          }
          // have to release, it's being overwritten in the 'while'
          spMoniker.Release();
        }
      }
    }
    return hr;
  }

  // -----------------------------------

  STDMETHOD(Reduce)(IBindCtx* pbc, ULONG dwReduceHowFar, 
    IMoniker** ppmkToLeft, IMoniker** ppmkReduced)
  {
    HRESULT hr = QueryInterface(IID_IMoniker, (void**)&ppmkReduced);
    ATLASSERT(SUCCEEDED(hr));
    return MK_S_REDUCED_TO_SELF;
  }

  // -----------------------------------

  STDMETHOD(IsEqual)(IMoniker* pmkOtherMoniker)
  {
    //
    // compare this moniker to another by comparing display names
    //
    CComPtr spBindCtx;
    HRESULT hr = CreateBindCtx(0, &spBindCtx);

    if (SUCCEEDED(hr)) {
      LPOLESTR pwszName;
      hr = pmkOtherMoniker->GetDisplayName(spBindCtx, NULL, &pwszName);

      if (SUCCEEDED(hr)) {
        hr = _wcsicmp(pwszName, m_pwszName) ? S_FALSE : S_OK;
        CoTaskMemFree(pwszName);
      }
    }
    return hr;
  }

  // -----------------------------------

  STDMETHOD(IsRunning)(IBindCtx* pbc, IMoniker* pmkToLeft, IMoniker* pmkNewlyRunning)
  {
    CComPtr  spROT;
    HRESULT hr = pbc->GetRunningObjectTable(&spROT);

    if (SUCCEEDED(hr)) {
      CComPtr  spMoniker;
      hr = QueryInterface(IID_IMoniker, (void**)&spMoniker);
      ATLASSERT(SUCCEEDED(hr));
      hr = spROT->IsRunning(spMoniker);
    }
    return hr;
  }

  // -----------------------------------

  STDMETHOD(GetDisplayName)(IBindCtx* pbc, IMoniker* pmkToLeft, 
    LPOLESTR* ppszDisplayName)
  {
    int nLen = (wcslen(m_pwszName) + 1) * sizeof(OLECHAR);
    OLECHAR* pwszName = (OLECHAR*) CoTaskMemAlloc(nLen);

    if (pwszName == NULL)
      return E_OUTOFMEMORY;

    wcscpy(pwszName, m_pwszName);
    *ppszDisplayName = pwszName;
    return S_OK;
  }

  // -----------------------------------

  STDMETHOD(IsSystemMoniker)(ULONG* pdwMksys)
  {
    *pdwMksys = MKSYS_NONE;
    return S_FALSE;
  }

// -----------------------------------
// IROTData

  STDMETHOD(GetComparisonData)(BYTE* pbData, ULONG cbMax, ULONG* pcbData)
  {
    ULONG nLen = (wcslen(m_pwszName) + 1) * sizeof(OLECHAR);
    if (nLen > cbMax)
      // ? shouldn't this be E_MOREDATA or something similar?
      return E_OUTOFMEMORY;    
    wcscpy((LPWSTR)pbData, m_pwszName);
    *pcbData = nLen;
    return S_OK;
  }

// -----------------------------------
// instance data

protected:
  LPOLESTR m_pwszName;
};

QcMk.cpp

/////////////////////////////////////////////////////////////////////////////
// QcMk.cpp
//
// JMW 2000
//
// Jeff Prosise MSJ nov 1999
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "QcMoniker.h"
#include "QcMk.h"

QcMk.rgs

// you'll have to write this one by hand as well
// JMW 2000
HKCR
{
  QcMk.1 = s 'QcMk Moniker Class'
  {
    CLSID = s '{A7351CE2-C9CC-47c1-99D0-AA9E99AC9F86}'
  }
  QcMk = s 'QcMk Moniker Class'
  {
    CLSID = s '{A7351CE2-C9CC-47c1-99D0-AA9E99AC9F86}'
    CurVer = s 'QcMk.1'
  }
  NoRemove CLSID
  {
    ForceRemove {A7351CE2-C9CC-47c1-99D0-AA9E99AC9F86} = s 'QcMk Moniker Class'
    {
      ProgID = s 'QcMk.1'
      VersionIndependentProgID = s 'QcMk'
      InprocServer32 = s '%MODULE%'
      {
        val ThreadingModel = s 'Both'
      }
      'TypeLib' = s '{F2D49734-ACCA-4457-8C68-0960D0D121DC}'
    }
  }
}

QcMoniker.idl

// QcMoniker.idl : IDL source for QcMoniker.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (QcMoniker.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
  uuid(F2D49734-ACCA-4457-8C68-0960D0D121DC),
  version(1.0),
  helpstring("QcMoniker 1.0 Type Library")
]
library QCMONIKERLib
{
  importlib("stdole32.tlb");
  importlib("stdole2.tlb");

  // -------------------------------------------------------------

  [
    uuid(A7351CE2-C9CC-47c1-99D0-AA9E99AC9F86),
    helpstring("QcMk instance moniker class")

  ]
  coclass QcMk
  {
    [default] interface IMoniker;
    interface IROTData;
  };
};

QcMoniker.cpp

This file is largely standard. Just add in the red lines.

// QcMoniker.cpp : Implementation of DLL Exports.


// Note: Proxy/Stub Information
//      To build a separate proxy/stub DLL, 
//      run nmake -f QcMonikerps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include <initguid.h>
#include "QcMoniker.h"

#include "QcMoniker_i.c"
#include "QcMk.h"


CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
  OBJECT_ENTRY(CLSID_QcMk, CQcMk)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        _Module.Init(ObjectMap, hInstance, &LIBID_QCMONIKERLib);
        DisableThreadLibraryCalls(hInstance);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
        _Module.Term();
    return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
    return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
    return _Module.UnregisterServer(TRUE);
}


QcMoniker.rc

All this stuff in here is automagically produced by the ATL wizard. Just add the red stuff.

//Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE 9, 1
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "1 TYPELIB ""QcMoniker.tlb""\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,0,0,1
 PRODUCTVERSION 1,0,0,1
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904B0"
        BEGIN
            VALUE "CompanyName", "\0"
            VALUE "FileDescription", "QcMoniker Module\0"
            VALUE "FileVersion", "1, 0, 0, 1\0"
            VALUE "InternalName", "QcMoniker\0"
            VALUE "LegalCopyright", "Copyright 2000\0"
            VALUE "OriginalFilename", "QcMoniker.DLL\0"
            VALUE "ProductName", "QcMoniker Module\0"
            VALUE "ProductVersion", "1, 0, 0, 1\0"
            VALUE "OLESelfRegister", "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x0409, 0x04B0
    END
END

#endif    // !_MAC


/////////////////////////////////////////////////////////////////////////////
//
// Registry
//

IDR_QCMK    REGISTRY DISCARDABLE  "QcMk.rgs"


/////////////////////////////////////////////////////////////////////////////
//
// String Table
//

STRINGTABLE DISCARDABLE 
BEGIN
  IDS_PROJNAME          "QcMoniker"
END

/////////////////////////////////////////////////////////////////////////////


#endif

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
1 TYPELIB "QcMoniker.tlb"

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Resource.h

Just add one line (in red):

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by QcMoniker.rc
//
#define IDS_PROJNAME  100
#define IDR_QCMK    101

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Comment on this article

TOP