The Custom Marshaling Proxy: KoalaPrx

With custom marshaling, the local object can get away with not implementing any of the interfaces it supposedly provides. However, this is absolutely required for the proxy because it must expose the correct interfaces to the client. The KOALAPRX DLL that manages such a proxy contains much of the same code that we previously had in EKoala4, which means that its Koala object implementation does explicitly implement IAnimal and IKoala to provide the client with the proper entry points. What differs is how the proxy actually implements these functions.

COM loads the proxy DLL and asks it to create an instance of the CLSID returned from the local object's IMarshal::GetUnmarshalClass, just as anything else creates an instance of an in-process object. In calling our DLL's IClassFactory::CreateInstance, however, COM asks for an IMarshal interface, considered to be the initialization interface for a proxy. That pointer in hand, COM calls IMarshal::UnmarshalInterface, which the proxy implements in IMARSHAL.CPP:


STDMETHODIMP CImpIMarshal::UnmarshalInterface(LPSTREAM pstm
, REFIID riid, LPVOID *ppv)
{
KOALAMARSHAL km;

pstm->Read((void *)&km, sizeof(KOALAMARSHAL), NULL);
m_pObj->m_hWndLocal=km.hWnd;

//Get the pointer to return to the client.
return QueryInterface(riid, ppv);
}

This function has two responsibilities. First, it extracts the necessary connection information from the marshaling packet stored in the stream (the pstm argument). This contains only the local object's window handle, to which we can post messages. We extract that handle and save it. The second responsibility is that we have to return, in *ppv, the pointer that COM eventually returns to the client, the type of which is specified in riid. All we need to do is call our own QueryInterface to get the correct pointer and call our own AddRef as required.

After you return from UnmarshalInterface, COM calls IMarshal::ReleaseMarshalData, giving you the chance to clean up any resources that the local object might have saved in the stream. No such resources are in this example, so this function just returns NOERROR. In addition, this proxy object also returns E_NOTIMPL from all the other IMarshal member functions, which are called only in the server process. We don't need to worry about them in the proxy.

Let's now look at how this proxy implements the interfaces as seen by the client:


DWORD CKoala::CallLocal(UINT iMsg, LPARAM lParam, BOOL fAsync)
{
DWORD dwRet=0;

if (fAsync)
PostMessage(m_hWndLocal, WM_COMMAND, (WPARAM)iMsg, lParam);
else
{
dwRet=SendMessage(m_hWndLocal, WM_COMMAND, (WPARAM)iMsg
, lParam);
}

return dwRet;
}


STDMETHODIMP_(ULONG) CKoala::Release(void)
{
if (0L!=--m_cRef)
return m_cRef;

CallLocal(MSG_RELEASE, 0, TRUE);

if (NULL!=m_pfnDestroy)
(*m_pfnDestroy)();

delete this;
return 0;
}


STDMETHODIMP CImpIAnimal::Eat(LPTSTR pszFoodRecommended
, LPTSTR pszFoodEaten, short cchEaten)
{
_tcsncpy(pszFoodEaten, TEXT("Eucalyptus Leaves"), cchEaten);
m_pObj->CallLocal(MSG_EAT, 0L, FALSE);
return NOERROR;
}

STDMETHODIMP CImpIAnimal::Sleep(short *pcMinutes)
{
DWORD dwRet;

//Pass the client's value.
dwRet=m_pObj->CallLocal(MSG_SLEEP, (LPARAM)*pcMinutes, FALSE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

//Store the return value in the client's variable.
*pcMinutes=LOWORD(dwRet);
return NOERROR;
}

STDMETHODIMP CImpIAnimal::Procreate(short *pcOffspring)
{
DWORD dwRet;

dwRet=m_pObj->CallLocal(MSG_PROCREATE, 0, FALSE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

*pcOffspring=(short)LOWORD(dwRet);
return ResultFromScode(0==dwRet ? S_FALSE : S_OK);
}

STDMETHODIMP CImpIAnimal::WhatKindOfAnimal(IID *pIID)
{
//No need to ask the local object for something we know.
*pIID=IID_IKoala;
return NOERROR;
}

STDMETHODIMP CImpIKoala::ClimbEucalyptusTree(short iTree)
{
//We know that the server doesn't need this.
return NOERROR;
}

STDMETHODIMP CImpIKoala::PouchOpensDown(void)
{
//We know that the server doesn't need this.
return NOERROR;
}

STDMETHODIMP CImpIKoala::SleepAfterEating(short cMinutes)
{
DWORD dwRet;

dwRet=m_pObj->CallLocal(MSG_SLEEPAFTEREATING
, (LPARAM)cMinutes, TRUE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

return NOERROR;
}

You can see in this code how the proxy completely implements some member functions while depending on the local object for others, using the CallLocal function to generate the message to the object's window.

IAnimal::Eat is an interesting function because the implementation is split between the proxy and the local object. The proxy knows what Koalas eat, so it fills the client's out-parameter, pszFoodEaten, with the appropriate text. It doesn't bother trying to marshal text back to and from the server. However, because this proxy knows that the local object modifies one of its state variables, it still generates a call to that object. As we saw earlier, the object does nothing more with the call than set its m_fJustAte variable to TRUE. (See EKoala5's HandleCall code in the previous section.)

All of the other members that generate calls to the local object rely on that object for the full implementation. Two of these, Release and IKoala::SleepAfterEating, are implemented as asynchronous calls, for which we use PostMessage instead of the synchronous SendMessage. SleepAfterEating is asynchronous simply for demonstration, whereas Release is asynchronous by design: we have no need to wait for the server to disappear within our own self-destruction code, so we post the message and forget it. This might not work with other designs, however, particularly ones in which a more complex interaction occurs between the proxy and the local object, especially if any synchronization of resources is going on. In that case, you might want to wait for the server to complete its shutdown before finishing the proxy's own cleanup.