Solved

Urgent: Object Pooling and Monikers

Posted on 2003-10-22
7
411 Views
Last Modified: 2008-02-01
We need to implement a server application in ATL , which needs to house a pool of stateful objects in a table repository.We are not sure if we need to implement the entire infrastructure manually or there is some underlying facility provided , to achieve this with the help of monikers.
Can someone suggest articles and examples to explain
1.What are monikers;where are they typically helpful
2.Would the above situation classify as a typical case of using monikers.
Relevant code samples would be much appreciated.
0
Comment
Question by:ranadhir
  • 5
  • 2
7 Comments
 
LVL 9

Expert Comment

by:_ys_
ID: 9605066
Monikers are locator objects - simply another name for something, typically used to abstract the activation rountine used to obtain an object.

A simple example would be: C:\My Documents\Doc1.doc

For what you are suggesting monikers are perfect. That's what they were designed for. Each instance within the pool would have a unique identifier, which could be used to reinstate that object from the repository at any given time.

Let's put this into context with some code.

A client creates a server object via CoCreateInstance blah blah blah. *BUT* there isn't a proviso for initial parameters to be passed.

However where there's a will there's a way. Ever used CoCreateObject? Compare it with CoCreateInstance.

Given:
// {154EADC9-ADE3-4087-8A44-E8BAFA4B36D0}
static const CLSID CLSID_CoWhatever  =
{ 0x154eadc9, 0xade3, 0x4087, { 0x8a, 0x44, 0xe8, 0xba, 0xfa, 0x4b, 0x36, 0xd0 } };

We could write the following client code:
IWhatever *pWhatever = 0;
CoCreateInstance ( CLSID_CoWhatever, 0, CLSCTX_SERVER,
                             IID_IWhatever, reinterpret_cast<void**>(&pWhatever) );

std::wstring sActivationString ( L"clsid:154EADC9-ADE3-4087-8A44-E8BAFA4B36D0:" );
CoCreateObject    ( sActivationString.c_str( ), 0,
                             IID_IWhatever, reinterpret_cast<void**>(&pWhatever) );

Difference being CTSCTX enumeration does not have to be provided, and the CLSID is now a straightforward string. BTW there is no correlation between the null values passed - just coincidence that they are in the same place.

[Anyone reading this who knows COM will by now be saying that CoCreateObject and CoCreateInstance, in the above forms, do not yield exactly the same result (In fact the CoCreateObject call would probably fail). I know, I just don't need to involve _all_ the nitty gritty details. Just whatever answers the question as posted]

Remember the initial parameter business mentioned previously. Well let's add that:
std::wstring sActivationString ( L"clsid:154EADC9-ADE3-4087-8A44-E8BAFA4B36D0:!eatThis" ); //***
CoCreateObject    ( sActivationString.c_str( ), 0,
                             IID_IWhatever, reinterpret_cast<void**>(&pWhatever) );

Not the extra "!eatThis" appended to sActivationString.

[BTW this call to CoCreateObject would probably work - provided the server is expecting the _extra_ appended information - and handled it properly]


ranadhir,
Is this really what you want to do. Do you _need_ to specify which of the pooled objects are to be reinstated and returned? Is so I'll post the server side for the above server code. It's a bit of work (not the code, but the explaining), so I'd like to know before posting.
0
 

Author Comment

by:ranadhir
ID: 9605538
I am sorry for bothering you about my ignorance about monikers.
I need to create a pool of object instances for a particular class (maybe through the class factory pointer retrieved via CoGetClassObject) in a sort of running table.
Now when a client requests for a instance , I hand it out along with a unique identifier for the instance.The client needs to communicate with this particular stateful object( by its identifier ) throughout its session.
Is monikers relevant to identifying unique object instances of a class? If so,how do we go about imparting unique identifiers to each instance through monikers?
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9606632
I now understand what you're trying to achieve. Thanks for the second post.

I'll knock up some code. Give me a while.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 9

Accepted Solution

by:
_ys_ earned 55 total points
ID: 9607306
Three obvious approaches come to mind.

1) Creating an empty (invalid) instance, and asking it to deserialize using the identifier.
2) Requesting the class object to create an instance for the provided identifier.
3) Giving it the identifer at creation time using monikers.

The first is very Visual Basic like. What if initialisation fails - you're still left with an invalid object.
It's also straight forward to implement. Just add another method to the interface.

The second is viable, but not to Visual Basic clients. If initialisation fails you'll just get nothing back.

The third option is similar to the second, but you don't directly touch the class object. Also, if initialisation fails you'll just get nothing back.

[I'll get to the part about obtaining the identifier later]


Code sample for the third option follows:

//*** Header file ***//

class CWhateverClassFactory : public IClassFactory, public IOleItemContainer
{
    IMPLEMENT_IUNKNOWN_SINGLETON(CWhateverClassFactory)

    BEGIN_INTERFACE_TABLE_MAP(CWhateverClassFactory)
        IMPLEMENTS_INTERFACE_ENTRY(IClassFactory)
        IMPLEMENTS_INTERFACE_ENTRY(IParseDisplayName)
        IMPLEMENTS_INTERFACE_ENTRY(IOleContainer)
        IMPLEMENTS_INTERFACE_ENTRY(IOleItemContainer)
    END_INTERFACE_TABLE_MAP()

public:
    CWhateverClassFactory() {}
    ~CWhateverClassFactory() {}

public:
//  IClassFactory methods
    STDMETHOD(CreateInstance) (IUnknown*, REFIID, void**);
    STDMETHOD(LockServer) (BOOL);

//  IParseDisplayName methods
    STDMETHOD(ParseDisplayName) (IBindCtx*, wchar_t*, ULONG*, IMoniker**);

//  IOleContainer methods
    STDMETHOD(EnumObjects) (DWORD, IEnumUnknown**);
    STDMETHOD(LockContainer) (BOOL);

//  IOleItemContainer methods
    STDMETHOD(GetObject) (wchar_t*, DWORD, IBindCtx*, REFIID, void**);
    STDMETHOD(GetObjectStorage) (wchar_t*, IBindCtx*, REFIID, void**);
    STDMETHOD(IsRunning) (wchar_t*);
};


This looks horrendous. But it's not that bad - honest. There's only _two_ methods that we are really interested in, namely ParseDisplayName and GetObject. The rest, thankfully(?!), can be left unimplemented. The interface semantics allow us to do this.

BTW, the opening seven lines:
    IMPLEMENT_IUNKNOWN_SINGLETON(CWhateverClassFactory)
    ...
    END_INTERFACE_TABLE_MAP()
is just my own personal implentation of the basic COM boilerplate code. It's not too hard for you to replace this with a standard IUnknown implemtation.


//*** Source module ***//

[The basic IUnknown implementation is not detailed here - as mentioned above (that's what my customised macros do)]

//*** IClassFactory::CreateInstance
HRESULT CWhateverClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
//*** boilerplate stuff ***//

    if ( 0 == ppv ) return E_POINTER;
    if ( 0 != pUnkOuter ) return ( *ppv = NULL ), CLASS_E_NOAGGREGATION; //*** if this is what you want

    CWhatever *pWhatever;
    try
    {
        pWhatever = new CWhatever( );  //*** default constructor
    }
    catch (std::bad_alloc&)
    {
        return ( *ppv = 0 ), E_OUTOFMEMORY;
    }

    pWhatever->AddRef( );
    HRESULT hr = pWhatever->QueryInterface (riid, ppv);
    pWhatever->Release( );

    return hr;
}

//*** IClassFactory::LockServer
HRESULT CWhateverClassFactory::LockServer(BOOL fLock)
{
//*** boilerplate stuff ***//

//  LockModule(fLock);  //*** do whatever you do - I normally have a global value for the entire module somewhere

    return S_OK;
}

//*** IParseDisplayName::ParseDisplayName
HRESULT CWhateverClassFactory::ParseDisplayName(IBindCtx* pBc, wchar_t* pszDisplayName, ULONG* pchEaten, IMoniker** ppMkOut)
{
    if ( 0 == ppMkOut ) return E_POINTER;
    if ( 0 == pchEaten ) return ( *ppMkOut = NULL ), E_POINTER;

    *pchEaten = wcslen (pszDisplayName);

    return CreateItemMoniker ( L"!", ++pszDisplayName, ppMkOut );
}

//*** IOleContainer::EnumObjects
HRESULT CWhateverClassFactory::EnumObjects(DWORD grfFlags, IEnumUnknown** ppEnum)
{
//*** we don't implement this ***//

    if ( 0 == ppEnum ) return E_POINTER;

    return ( *ppEnum = NULL ), E_NOTIMPL;
}

//*** IOleContainer::LockContainer
HRESULT CWhateverClassFactory::LockContainer(BOOL fLock)
{
//*** boilerplate stuff ***//

    return this->LockServer (fLock);
}

//*** IOleItemContainer::GetObject
HRESULT CWhateverClassFactory::GetObject(wchar_t* pszItem, DWORD dwSpeedNeeded, IBindCtx* pBc, REFIID riid, void** ppv)
{
    if ( 0 == ppv ) return E_POINTER;

    HRESULT hr;

    CWhatever *pWhatever;
    try
    {
        pWhatever = new CWhatever (pszItem);    //*** overloaded constructor - deserialise information from storage
    }
    catch ( std::bad_alloc& )
    {
        return ( *ppv = 0 ), E_OUTOFMEMORY;
    }

    pWhatever->AddRef( );
    hr = pWhatever->QueryInterface (riid, ppv);
    pWhatever->Release( );

    if ( FAILED(hr) ) return MK_E_NOOBJECT;

    return S_OK;
}

//*** IOleItemContainer::GetObjectStorage
HRESULT CWhateverClassFactory::GetObjectStorage(wchar_t* pszItem, IBindCtx* pBc, REFIID riid, void** ppv)
{
//*** we don't implement this ***//

    if ( 0 == ppv ) return E_POINTER;

    return ( *ppv = 0 ), MK_E_NOSTORAGE;
}

//*** IOleItemContainer::IsRunning
HRESULT CWhateverClassFactory::IsRunning(wchar_t* pszItem)
{
//*** we don't implement this ***//

    return MK_E_NOOBJECT;
}


It's a lot of code, but it's all pretty mundane stuff. As I said the interesting ones are PerseDisplayName and GetObject.


ParseDisplayName:
-----------------
From the first post:
>> std::wstring sActivationString ( L"clsid:154EADC9-ADE3-4087-8A44-E8BAFA4B36D0:!eatThis" );

The first part of this 'clsid:154EADC9-ADE3-4087-8A44-E8BAFA4B36D0:' identifies the standard class moniker, and when processed by the COM SCM will create your class factory CWhateverClassFactory.

The second part '!eatThis' will be passed to your IParseDisplayName::ParseDisplayName method. It expects you to roll out a moniker for what this represents. Thankfully the character '!' typically represents a standard moniker, the item moniker. So we just create this and return it.
---> return CreateItemMoniker(L"!", ++pszDisplayName, ppMkOut);


GetObject:
----------
Because you created and returned an item moniker from ParseDisplayName, the implementation of the item moniker (IMoniker::BindToObject method) will eventually call your IOleItemContainer::GetObject method. Easy.

GetObject simply instantiates your concrete component CWhatever, passing the presented identifier as an initialisation parameter.
---> CWhatever *pWhatever = new CWhatever (pszItem);



>> Is monikers relevant to identifying unique object instances of a class?
They can be used for said purpose. They allow the client to re-create their personal session object in a single step:
//----------------------------------
std::wstring sActivationString ( L"clsid:154EADC9-ADE3-4087-8A44-E8BAFA4B36D0:" );
CoCreateObject    ( sActivationString.c_str( ), 0,
                             IID_IWhatever, reinterpret_cast<void**>(&pWhatever) );
//----------------------------------
without having to obtain a class object, and invoke methods on that to obtain the correct instance.


Side note ----
Given all of the above code, it is possible to use suggestion 2) a noted above.
//----------------------------------
IOleItemContainer *pOic = 0;
CoGetClassObject(CLSID_CoWhatever, CLSCTX_SERVER, 0,
                 IID_IOleItemContainer, reinterpret_cast<void**>(&pOic));

std::wstring sIdentifier = L"eatThis";
IWhatever *pWhatever = 0;
pOic->GetObect(sIdentifier, BINDSPEED_INDEFINITE, 0,
               IID_IWhatever, reinterpret_cast<void**>(&pWhatever);
//----------------------------------
This really depends on whether always looking and creating the class object hurts your performance.
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9648674
Any of this help you?
0
 

Author Comment

by:ranadhir
ID: 9654590
Thanks for the effort . Coincidentally ,a found a very helpful write-up for starters on this issue like me. I am attaching for your reference
http://www.microsoft.com/msj/1199/wicked/wicked1199.aspx

0
 
LVL 9

Expert Comment

by:_ys_
ID: 9656333
Thanks for the link - good article.

I tend not to use the ROT much - as it's restricted to the local machine. It would however be useful within the IOleItemContainer::GetObject function, especially when the bind-speed is IMMEDIATE. It also allows for boiler-plate implementations of IOleContainer::EnumObjects and IOleItemContainer::IsRunning.

I just realised that I left out the getting of the identifer, but hopefully you realise by now that's it's simply a case of creating the right composite moniker and stringifying it.

Have fun.
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now