PRB: EKOALA5 Custom Marshaling Sample Error

ID: Q143314

2.00    | 2.00
WINDOWS | WINDOWS NT kbole kbprg kbprb

The information in this article applies to:

SYMPTOMS

It is only possible to instantiate the Koala object implemented by the EKOALA5 sample if the client initially asks for the IUnknown interface. In this case, subsequent calls to QueryInterface to retrieve pointers to the custom interfaces IKoala or IAnimal will also succeed. This can be observed by using the OBJUSER3 client sample included with Chapter 6.

However, if the client initially asks for IKoala or IAnimal, then IClassFactory::CreateInstance and CoCreateInstance will fail with E_NOINTERFACE. This can be observed by modifying OBJUSER3 to ask for one of these interfaces when calling CoCreateInstance.

CAUSE

EKOALA5 does not actually implement IKoala or IAnimal as interfaces, and returns E_NOINTERFACE when queried for those interfaces. This causes EKOALA5's IClassFactory::CreateInstance and CoCreateInstance to fail.

Note that when an object is custom marshaled, its interfaces need only exist in the client's process. Thus, the proxy must implement the object's interfaces, but it is optional for the server (implementation dependent).

The error in this case is that the server claims the object does not support IKoala and IAnimal when in fact it does (through the proxy).

RESOLUTION

One solution would be to add interface implementations for IKoala and IAnimal to EKOALA5 and modify CKoala::HandleCall to simply call the appropriate interface method for each request.

However, an easier solution is to modify CKoala::QueryInterface to return IUnknown when queried for IKoala or IAnimal, even though the CKoala does not implement these interfaces.

CAUTION: This solution is specific to the peculiar architecture of the EKOALA5 sample. Returning IUnknown in place of unimplemented interfaces can easily cause a crash in other situations.

In KOALA.CPP:

STDMETHODIMP CKoala::QueryInterface(REFIID riid, PPVOID ppv)

    {
    *ppv=NULL;

    if (IID_IUnknown==riid || IID_IMarshal==riid)
        *ppv=this;

    // Add these 2 lines
    else if (IID_IKoala==riid ||  IID_IAnimal==riid)
        *ppv=this;

    if (NULL!=*ppv)
        {
        ((LPUNKNOWN)*ppv)->AddRef();
        return NOERROR;
        }

    return ResultFromScode(E_NOINTERFACE);
    }

Note that the OLE stub in the server process will never directly call interface methods on an object that is custom marshaled, except for IUnknown and IMarshal methods. Other interface method calls are handled in the client process by the proxy (perhaps using private communication with the server). Thus even if the server does not implement certain interfaces supported by the object, as is the case with EKOALA5, it is safe to return IUnknown when queried for those interfaces.

In general, it is incorrect for a server to disallow a request for an interface from CoCreateInstance but allow it from the proxy.

STATUS

This behavior is by design.

MORE INFORMATION

The EKOALA5 server and corresponding proxy KOALAPRX implement custom marshaling for the Koala object, which provides the IKoala and IAnimal custom interfaces. These components are designed so that the proxy implements the custom interfaces in the client's process, but delegates to the server to do the actual work involved for some, but not all, of the interface methods. The server does not implement the interfaces themselves, but merely responds to specific requests from the proxy.

When creating the object, the requested interface is passed as a parameter to the server's IClassFactory::CreateInstance method. In EKOALA5, this method creates the object and then calls QueryInterface. If the requested interface is IKoala or IAnimal, QueryInterface fails causing IClassFactory::CreateInstance and CoCreateInstance to fail.

If the initially requested interface is IUnknown, QueryInterface and CreateInstance succeed. The IUnknown interface is returned first to the OLE stub, which then queries for IMarshal and proceeds to custom marshal the object back to the client by calling the IMarshal methods. On the client side, the proxy (KOALAPRX) is loaded and the object is unmarshaled. Any subsequent calls to QueryInterface are handled by the proxy, which implements IKoala and IAnimal so everything works normally from that point.

With the modifications noted in the "Resolution" section of this article, the creation process works the same when the client asks for IKoala or IAnimal using CoCreateInstance as it does when the client asks for IUnknown.

Additional reference words: 2.00 2.0 KBCategory: kbole kbprg kbprb KBSubcategory: LeTwoCom

Keywords          : LeTwoCom 
Version           : 2.00    | 2.00
Platform          : NT WINDOWS

Last Reviewed: February 1, 1996