Implementing the Types, Factories, and Interfaces

When implementing a plug-in, you must provide

Listing 1-5 contains the code for a plug-in that implements the type kTestTypeID and its interface.

Listing 1-5 Example plug-in implementation
 #include <CoreFoundation/CoreFoundation.h>
 #include "TestInterface.h"
// The UUID for the factory function.
#define kTestFactoryID (CFUUIDGetConstantUUIDWithBytes(NULL,
 0x68, 0x75, 0x3A, 0x44, 0x4D, 0x6F, 0x12, 0x26, 0x9C, 0x60,
0x00, 0x50, 0xE4, 0xC0, 0x00, 0x67))


// The layout for an instance of MyType.
typedef struct _MyType {
    TestInterfaceStruct *_testInterface;
    CFUUIDRef _factoryID;
    UInt32 _refCount;
 } MyType;


// Forward declaration for the IUnknown implementation.
static void _deallocMyType( MyType *this );


// Implementation of the IUnknown QueryInterface function.
static HRESULT myQueryInterface( void *this, REFIID iid, LPVOID *ppv ) {
    // Create a CoreFoundation UUIDRef for the requested interface.
    CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes( NULL, iid );


    // Test the requested ID against the valid interfaces.
    if (CFEqual(interfaceID, kTestInterfaceID)) {
        // If the TestInterface was requested, bump the ref count,
        // set the ppv parameter equal to the instance, and
        // return good status.
        ((MyType *)this)->_testInterface->AddRef( this );
        *ppv = this;
        CFRelease(interfaceID);
        return S_OK;
    } else if (CFEqual(interfaceID, IUnknownUUID)) {
        // If the IUnknown interface was requested, same as above.
        ((MyType *)this)->_testInterface->AddRef( this );
        *ppv = this;
        CFRelease( interfaceID );
        return S_OK;
    } else {
        // Requested interface unknown, bail with error.
        *ppv = NULL;
        CFRelease( interfaceID );
        return E_NOINTERFACE;
    }
}


// Implementation of reference counting for this type.
// Whenever an interface is requested, bump the refCount for
// the instance. NOTE: returning the refcount is a convention
// but is not required so don't rely on it.
static ULONG myAddRef( void *this ) {
    ((MyType *)this)->_refCount += 1;
    return ((MyType *)this)->_refCount;
}


// When an interface is released, decrement the refCount.
// If the refCount goes to zero, deallocate the instance.
static ULONG myRelease( void *this ) {
    ((MyType *)this)->_refCount -= 1;
        if (((MyType *)this)->_refCount == 0) {
            _deallocMyType( (MyType *)this );
            return 0;
        } else
            return ((MyType *)this)->_refCount;
    }
// The implementation of the TestInterface function.
static void myFooMe( void *this, Boolean flag ) {
    printf("myFooMe: instance 0x%x: I've been fooed.  %s\n",
            (unsigned)this, (flag ? "YES" : "NOPE"));
}
// The TestInterface function table.
static TestInterfaceStruct testInterfaceFtbl = { 
        NULL,               // Required padding for COM
        myQueryInterface,   // These three are the required COM functions
        myAddRef, 
        myRelease, 
        myFooMe };          // Interface implementation
// Utility function that allocates a new instance.
 static MyType *_allocMyType( CFUUIDRef factoryID ) {
    // Allocate memory for the new instance.
    MyType *newOne = (MyType *)malloc( sizeof(MyType) );
    // Point to the function table
    newOne->_testInterface = &testInterfaceFtbl;
    // Retain and keep an open instance refcount<
    // for each factory.
    newOne->_factoryID = CFRetain( factoryID );
    CFPlugInAddInstanceForFactory( factoryID );
    // This function returns the IUnknown interface
    // so set the refCount to one.
    newOne->_refCount = 1;
    return newOne;
}
    // Utility function that deallocates the instance when
    // the refCount goes to zero.
    static void _deallocMyType( MyType *this ) {
        CFUUIDRef factoryID = this->_factoryID;
        free(this);
        if (factoryID) {
            CFPlugInRemoveInstanceForFactory( factoryID );
            CFRelease( factoryID );
        }
    }
// Implementation of the factory function for this type.
void *MyFactory(CFAllocatorRef allocator, CFUUIDRef typeID) {
    // If correct type is being requested, allocate an
    // instance of TestType and return the IUnknown interface.
    if (CFEqual(typeID, kTestTypeID)) {
        MyType *result = _allocMyType( kTestFactoryID );
        return result;
    } else {
        // If the requested type is incorrect, return NULL.
        return NULL;
    }
}

As illustrated in Listing 1-5, the first step in implementing a plug-in to is define the UUID for the factory you are going to supply. This is the same UUID that was used in the CFPlugInFactories key in the information property list. Next, the data structure for instances of the TestType implementation is defined.

After defining the instance structure, you implement the IUnknown interface functions required for every plug-in. In this example, QueryInterface , relies on the fact that the first pointer in the instance structure is an interface, so returning a pointer to the MyType structure is the same as returning a pointer to a pointer to TestInterface . Types that implement more than one interface would be more complicated. In C++, this can be accomplished using multiple inheritance and static casting. In C, you would have to keep track of the interface pointers by hand.

After the IUnknown functions, there is the implementation for the fooMe function from TestInterface . In this example it just prints a message. Next comes the static definition of the actual TestInterface function table. This table is filled in with the IUnknown and TestInterface functions.

Following the function table are two utility functions that allow easy creation and freeing of MyType structures. The allocator fills in the pointer to the interface function table and sets the initial reference count to 1. It also takes care of registering the instance with the factory so PlugIn Services knows not to unload the plug-in's code while there are still instances active. The deallocator function frees the memory for MyType and unregisters the instance from the factory.

Finally, there is the actual factory function that creates a new instance and returns a pointer to it. This pointer is also a pointer to the IUnknown interface. The MyFactory function must conform to the CFPlugInFactoryFunction prototype. Factory functions take allocators and type UUIDs as parameters.

Listing 1-5 contains a lot of glue code that would be unnecessary for C++ developers using a compiler with built-in support for generating COM interface layouts. If you wish to implement CFPlugIns in C++, refer to the wealth of COM documentation by Microsoft and others.


© 1999 Apple Computer, Inc. (Last Updated 10 December 99)