//************************************************************************************** // Filename: CFAbstractCMPlugin.cp // Part of Contextual Menu Workshop by Abracode Inc. // // Description: Base code for CM plugin taking care of COM interface and communication with a host. // Not needed to be modified in most cases. // // Usage: see Documentation.txt // // License: This code is based on "CFPluginMenu.cp" and "SampleCMPlugin.c" // of "SampleCMPlugin" from Apple. // Great thanks to George Warner for providing these sources. // //************************************************************************************** #include "CFAbstractCMPlugin.h" #include "CFObjDel.h" #include "DebugSettings.h" #if TARGET_RT_MAC_CFM //_tk_: close your eyes, this is ugly :-) // ----------------------------------------------------------------------------- // MachO<->CFM Kludge // ----------------------------------------------------------------------------- // This function allocates a block of CFM glue code which contains the instructions to call CFM routines // UInt32 _template[6] = { 0x3D800000, // lis r12,0x0000 // load ms-word TVector -> r12 0x618C0000, // ori r12,r12,0x0000 // OR in ls-word TVector -> r12 0x800C0000, // lwz r0,0x0000(r12) // get new PC from TVector 0x804C0004, // lwz RTOC,0x004(r12) // get new RTOC from TVector 0x7C0903A6, // mtctr r0 // new PC -> control register 0x4E800420 // bctr // branch to control register }; static void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp ) { UInt32 *mfp = (UInt32*) NewPtr( sizeof(_template) ); // Must later dispose of allocated memory mfp[0] = _template[0] | ((UInt32)cfmfp >> 16); mfp[1] = _template[1] | ((UInt32)cfmfp & 0xFFFF); mfp[2] = _template[2]; mfp[3] = _template[3]; mfp[4] = _template[4]; mfp[5] = _template[5]; MakeDataExecutable( mfp, sizeof(_template) ); return( mfp ); } #endif //TARGET_RT_MAC_CFM #pragma mark - // ----------------------------------------------------------------------------- // AbstractCMPluginFactory // ----------------------------------------------------------------------------- // Implementation of the factory function for this type. // void *AbstractCMPluginFactory( CFAllocatorRef allocator, CFUUIDRef typeID ) { #pragma unused( allocator ) TRACE_STR( "\pAbstractCMPluginFactory" ); // If correct type is being requested, allocate an // instance and return the IUnknown interface. if ( CFEqual( typeID, kContextualMenuTypeID ) ) return AllocAbstractCMPluginType( kCMImplPluginFactoryID ); // If the requested type is incorrect, return NULL. return NULL; } #pragma mark - // ----------------------------------------------------------------------------- // AbstractCMPluginQueryInterface // ----------------------------------------------------------------------------- // Implementation of the IUnknown QueryInterface function. // HRESULT AbstractCMPluginQueryInterface( void *thisInstance, REFIID iid, LPVOID *ppv ) { TRACE_STR( "\pAbstractCMPluginQueryInterface" ); // Create a CoreFoundation UUIDRef for the requested interface. CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes( NULL, iid ); if(interfaceID == NULL) return E_NOINTERFACE;//unable to create UUID CFObjDel theDel(interfaceID); // Test the requested ID against the valid interfaces. if ( CFEqual( interfaceID, kContextualMenuInterfaceID ) ) { // If the kContextualMenuInterfaceID was requested, bump the ref count, // set the ppv parameter equal to the instance, and return good status. AbstractCMPluginAddRef( thisInstance ); *ppv = thisInstance; return S_OK; } else if ( CFEqual( interfaceID, IUnknownUUID ) ) { // If the IUnknown interface was requested, same as above. AbstractCMPluginAddRef( thisInstance ); *ppv = thisInstance; return S_OK; } else { // Requested interface unknown, bail with error. *ppv = NULL; return E_NOINTERFACE; } } // ----------------------------------------------------------------------------- // AbstractCMPluginAddRef // ----------------------------------------------------------------------------- // 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. // ULONG AbstractCMPluginAddRef( void *thisInstance ) { TRACE_STR( "\pAbstractCMPluginAddRef" ); AbstractCMPluginType *theThis = (AbstractCMPluginType *)thisInstance; theThis->refCount += 1; return theThis->refCount; } // ----------------------------------------------------------------------------- // AbstractCMPluginRelease // ----------------------------------------------------------------------------- // When an interface is released, decrement the refCount. // If the refCount goes to zero, deallocate the instance. // ULONG AbstractCMPluginRelease( void *thisInstance ) { TRACE_STR( "\pAbstractCMPluginRelease" ); AbstractCMPluginType *theThis = (AbstractCMPluginType *)thisInstance; theThis->refCount -= 1; if ( theThis->refCount == 0 ) { DeallocAbstractCMPluginType( theThis ); return 0; } return theThis->refCount; } #pragma mark - //for Mach-O it is simpler static ContextualMenuInterfaceStruct sInterfaceStruct = { // Required padding for COM NULL, // These three are the required COM functions AbstractCMPluginQueryInterface, AbstractCMPluginAddRef, AbstractCMPluginRelease, // Interface implementation CMPluginExamineContext, CMPluginHandleSelection, CMPluginPostMenuCleanup }; // ----------------------------------------------------------------------------- // AllocAbstractCMPluginType // ----------------------------------------------------------------------------- // Utility function that allocates a new instance. // AbstractCMPluginType* AllocAbstractCMPluginType( CFUUIDRef inFactoryID ) { TRACE_STR( "\pAllocAbstractCMPluginType" ); // Allocate memory for the new instance. _tk_: zero all memory, including implementation data AbstractCMPluginType *theNewInstance = (AbstractCMPluginType *) NewPtrClear( sizeof(AbstractCMPluginType) ); if ( theNewInstance ) { #if TARGET_RT_MAC_MACHO // Point to the function table theNewInstance->interface = &sInterfaceStruct; #elif TARGET_RT_MAC_CFM theNewInstance->interface = (ContextualMenuInterfaceStruct *) NewPtr( sizeof(ContextualMenuInterfaceStruct) ); if ( theNewInstance->interface ) { theNewInstance->interface->_reserved = NULL; theNewInstance->interface->QueryInterface = (HRESULT (STDMETHODCALLTYPE *)(void *thisPointer, REFIID iid, LPVOID *ppv)) MachOFunctionPointerForCFMFunctionPointer( AbstractCMPluginQueryInterface ); theNewInstance->interface->AddRef = (ULONG (STDMETHODCALLTYPE *)(void *thisPointer)) MachOFunctionPointerForCFMFunctionPointer( AbstractCMPluginAddRef ); theNewInstance->interface->Release = (ULONG (STDMETHODCALLTYPE *)(void *thisPointer)) MachOFunctionPointerForCFMFunctionPointer( AbstractCMPluginRelease ); theNewInstance->interface->ExamineContext = (OSStatus (*)( void *thisInstance, const AEDesc *inContext, AEDescList *outCommandPairs )) MachOFunctionPointerForCFMFunctionPointer( CMPluginExamineContext ); theNewInstance->interface->HandleSelection = (OSStatus (*)( void *thisInstance, AEDesc *inContext, SInt32 inCommandID )) MachOFunctionPointerForCFMFunctionPointer( CMPluginHandleSelection ); theNewInstance->interface->PostMenuCleanup = (void (*)( void *thisInstance )) MachOFunctionPointerForCFMFunctionPointer( CMPluginPostMenuCleanup ); } #else #error unknown runtime architecture #endif //TARGET_RT_MAC_MACHO // Retain and keep an open instance refcount // for each factory. if ( inFactoryID ) { theNewInstance->factoryID = (CFUUIDRef) CFRetain( inFactoryID ); CFPlugInAddInstanceForFactory( inFactoryID ); } // This function returns the IUnknown interface // so set the refCount to one. theNewInstance->refCount = 1; theNewInstance->bundleRef = CFBundleGetBundleWithIdentifier( CFSTR(CM_IMPL_PLUGIN_BUNDLE_INDENTIFIER) ); } return theNewInstance; } // ----------------------------------------------------------------------------- // DeallocAbstractCMPluginType // ----------------------------------------------------------------------------- // Utility function that deallocates the instance when // the refCount goes to zero. // void DeallocAbstractCMPluginType( AbstractCMPluginType *thisInstance ) { TRACE_STR( "\pDeallocAbstractCMPluginType" ); if ( thisInstance->interface != NULL) { #if TARGET_RT_MAC_MACHO //nothing to do here #elif TARGET_RT_MAC_CFM if ( thisInstance->interface->QueryInterface != NULL) DisposePtr( (Ptr) thisInstance->interface->QueryInterface ); if ( thisInstance->interface->AddRef != NULL) DisposePtr( (Ptr) thisInstance->interface->AddRef ); if ( thisInstance->interface->Release != NULL) DisposePtr( (Ptr) thisInstance->interface->Release ); if ( thisInstance->interface->ExamineContext != NULL) DisposePtr( (Ptr) thisInstance->interface->ExamineContext ); if ( thisInstance->interface->HandleSelection != NULL) DisposePtr( (Ptr) thisInstance->interface->HandleSelection ); if ( thisInstance->interface->PostMenuCleanup != NULL) DisposePtr( (Ptr) thisInstance->interface->PostMenuCleanup ); DisposePtr( (Ptr) thisInstance->interface ); #else #error unknown runtime architecture #endif } CFUUIDRef theFactoryID = thisInstance->factoryID; DisposePtr( (Ptr) thisInstance ); if ( theFactoryID != NULL) { CFPlugInRemoveInstanceForFactory( theFactoryID ); CFRelease( theFactoryID ); } }