/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenSL ES private and global functions not associated with an interface or class */ #include "sles_allinclusive.h" /** \brief Return true if the specified interface exists and has been initialized for this object. * Returns false if the class does not support this kind of interface, or the class supports the * interface but this particular object has not had the interface exposed at object creation time * or by DynamicInterface::AddInterface. Note that the return value is not affected by whether * the application has requested access to the interface with Object::GetInterface. Assumes on * entry that the object is locked for either shared or exclusive access. */ bool IsInterfaceInitialized(IObject *thiz, unsigned MPH) { assert(NULL != thiz); assert( /* (MPH_MIN <= MPH) && */ (MPH < (unsigned) MPH_MAX)); const ClassTable *clazz = thiz->mClass; assert(NULL != clazz); int index; if (0 > (index = clazz->mMPH_to_index[MPH])) { return false; } assert(MAX_INDEX >= clazz->mInterfaceCount); assert(clazz->mInterfaceCount > (unsigned) index); switch (thiz->mInterfaceStates[index]) { case INTERFACE_EXPOSED: case INTERFACE_ADDED: return true; default: return false; } } /** \brief Map an IObject to it's "object ID" (which is really a class ID) */ SLuint32 IObjectToObjectID(IObject *thiz) { assert(NULL != thiz); // Note this returns the OpenSL ES object ID in preference to the OpenMAX AL if both available const ClassTable *clazz = thiz->mClass; assert(NULL != clazz); SLuint32 id = clazz->mSLObjectID; if (!id) { id = clazz->mXAObjectID; } return id; } /** \brief Acquire a strong reference to an object. * Check that object has the specified "object ID" (which is really a class ID) and is in the * realized state. If so, then acquire a strong reference to it and return true. * Otherwise return false. */ SLresult AcquireStrongRef(IObject *object, SLuint32 expectedObjectID) { if (NULL == object) { return SL_RESULT_PARAMETER_INVALID; } // NTH additional validity checks on address here SLresult result; object_lock_exclusive(object); SLuint32 actualObjectID = IObjectToObjectID(object); if (expectedObjectID != actualObjectID) { SL_LOGE("object %p has object ID %u but expected %u", object, actualObjectID, expectedObjectID); result = SL_RESULT_PARAMETER_INVALID; } else if (SL_OBJECT_STATE_REALIZED != object->mState) { SL_LOGE("object %p with object ID %u is not realized", object, actualObjectID); result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { ++object->mStrongRefCount; result = SL_RESULT_SUCCESS; } object_unlock_exclusive(object); return result; } /** \brief Release a strong reference to an object. * Entry condition: the object is locked. * Exit condition: the object is unlocked. * Finishes the destroy if needed. */ void ReleaseStrongRefAndUnlockExclusive(IObject *object) { #ifdef USE_DEBUG assert(pthread_equal(pthread_self(), object->mOwner)); #endif assert(0 < object->mStrongRefCount); if ((0 == --object->mStrongRefCount) && (SL_OBJECT_STATE_DESTROYING == object->mState)) { // FIXME do the destroy here - merge with IDestroy // but can't do this until we move Destroy to the sync thread // as Destroy is now a blocking operation, and to avoid a race } else { object_unlock_exclusive(object); } } /** \brief Release a strong reference to an object. * Entry condition: the object is unlocked. * Exit condition: the object is unlocked. * Finishes the destroy if needed. */ void ReleaseStrongRef(IObject *object) { assert(NULL != object); object_lock_exclusive(object); ReleaseStrongRefAndUnlockExclusive(object); } /** \brief Convert POSIX pthread error code to OpenSL ES result code */ SLresult err_to_result(int err) { if (EAGAIN == err || ENOMEM == err) { return SL_RESULT_RESOURCE_ERROR; } if (0 != err) { return SL_RESULT_INTERNAL_ERROR; } return SL_RESULT_SUCCESS; } /** \brief Check the interface IDs passed into a Create operation */ SLresult checkInterfaces(const ClassTable *clazz, SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired, unsigned *pExposedMask, unsigned *pRequiredMask) { assert(NULL != clazz && NULL != pExposedMask); // Initially no interfaces are exposed unsigned exposedMask = 0; unsigned requiredMask = 0; const struct iid_vtable *interfaces = clazz->mInterfaces; SLuint32 interfaceCount = clazz->mInterfaceCount; SLuint32 i; // Expose all implicit interfaces for (i = 0; i < interfaceCount; ++i) { switch (interfaces[i].mInterface) { case INTERFACE_IMPLICIT: case INTERFACE_IMPLICIT_PREREALIZE: // there must be an initialization hook present if (NULL != MPH_init_table[interfaces[i].mMPH].mInit) { exposedMask |= 1 << i; } break; case INTERFACE_EXPLICIT: case INTERFACE_DYNAMIC: case INTERFACE_UNAVAILABLE: case INTERFACE_EXPLICIT_PREREALIZE: break; default: assert(false); break; } } if (0 < numInterfaces) { if (NULL == pInterfaceIds || NULL == pInterfaceRequired) { return SL_RESULT_PARAMETER_INVALID; } bool anyRequiredButUnsupported = false; // Loop for each requested interface for (i = 0; i < numInterfaces; ++i) { SLInterfaceID iid = pInterfaceIds[i]; if (NULL == iid) { return SL_RESULT_PARAMETER_INVALID; } SLboolean isRequired = pInterfaceRequired[i]; int MPH, index; if ((0 > (MPH = IID_to_MPH(iid))) || // there must be an initialization hook present (NULL == MPH_init_table[MPH].mInit) || (0 > (index = clazz->mMPH_to_index[MPH])) || (INTERFACE_UNAVAILABLE == interfaces[index].mInterface)) { // Here if interface was not found, or is not available for this object type if (isRequired) { // Application said it required the interface, so give up SL_LOGE("class %s interface %u required but unavailable MPH=%d", clazz->mName, i, MPH); anyRequiredButUnsupported = true; } // Application said it didn't really need the interface, so ignore with warning SL_LOGW("class %s interface %u requested but unavailable MPH=%d", clazz->mName, i, MPH); continue; } if (isRequired) { requiredMask |= (1 << index); } // The requested interface was both found and available, so expose it exposedMask |= (1 << index); // Note that we ignore duplicate requests, including equal and aliased IDs } if (anyRequiredButUnsupported) { return SL_RESULT_FEATURE_UNSUPPORTED; } } *pExposedMask = exposedMask; if (NULL != pRequiredMask) { *pRequiredMask = requiredMask; } return SL_RESULT_SUCCESS; } /* Interface initialization hooks */ extern void I3DCommit_init(void *), I3DDoppler_init(void *), I3DGrouping_init(void *), I3DLocation_init(void *), I3DMacroscopic_init(void *), I3DSource_init(void *), IAndroidConfiguration_init(void *), IAndroidEffect_init(void *), IAndroidEffectCapabilities_init(void *), IAndroidEffectSend_init(void *), IAndroidBufferQueue_init(void *), IAudioDecoderCapabilities_init(void *), IAudioEncoder_init(void *), IAudioEncoderCapabilities_init(void *), IAudioIODeviceCapabilities_init(void *), IBassBoost_init(void *), IBufferQueue_init(void *), IDeviceVolume_init(void *), IDynamicInterfaceManagement_init(void *), IDynamicSource_init(void *), IEffectSend_init(void *), IEngine_init(void *), IEngineCapabilities_init(void *), IEnvironmentalReverb_init(void *), IEqualizer_init(void *), ILEDArray_init(void *), IMIDIMessage_init(void *), IMIDIMuteSolo_init(void *), IMIDITempo_init(void *), IMIDITime_init(void *), IMetadataExtraction_init(void *), IMetadataTraversal_init(void *), IMuteSolo_init(void *), IObject_init(void *), IOutputMix_init(void *), IOutputMixExt_init(void *), IPitch_init(void *), IPlay_init(void *), IPlaybackRate_init(void *), IPrefetchStatus_init(void *), IPresetReverb_init(void *), IRatePitch_init(void *), IRecord_init(void *), ISeek_init(void *), IThreadSync_init(void *), IVibra_init(void *), IVirtualizer_init(void *), IVisualization_init(void *), IVolume_init(void *); extern void I3DGrouping_deinit(void *), IAndroidEffect_deinit(void *), IAndroidEffectCapabilities_deinit(void *), IAndroidBufferQueue_deinit(void *), IBassBoost_deinit(void *), IBufferQueue_deinit(void *), IEngine_deinit(void *), IEnvironmentalReverb_deinit(void *), IEqualizer_deinit(void *), IObject_deinit(void *), IPresetReverb_deinit(void *), IThreadSync_deinit(void *), IVirtualizer_deinit(void *); extern bool IAndroidEffectCapabilities_Expose(void *), IBassBoost_Expose(void *), IEnvironmentalReverb_Expose(void *), IEqualizer_Expose(void *), IPresetReverb_Expose(void *), IVirtualizer_Expose(void *); extern void IXAEngine_init(void *), IStreamInformation_init(void*), IVideoDecoderCapabilities_init(void *); extern void IXAEngine_deinit(void *), IStreamInformation_deinit(void *), IVideoDecoderCapabilities_deinit(void *); extern bool IVideoDecoderCapabilities_expose(void *); #if !(USE_PROFILES & USE_PROFILES_MUSIC) #define IDynamicSource_init NULL #define IMetadataTraversal_init NULL #define IVisualization_init NULL #endif #if !(USE_PROFILES & USE_PROFILES_GAME) #define I3DCommit_init NULL #define I3DDoppler_init NULL #define I3DGrouping_init NULL #define I3DLocation_init NULL #define I3DMacroscopic_init NULL #define I3DSource_init NULL #define IMIDIMessage_init NULL #define IMIDIMuteSolo_init NULL #define IMIDITempo_init NULL #define IMIDITime_init NULL #define IPitch_init NULL #define IRatePitch_init NULL #define I3DGrouping_deinit NULL #endif #if !(USE_PROFILES & USE_PROFILES_BASE) #define IAudioDecoderCapabilities_init NULL #define IAudioEncoderCapabilities_init NULL #define IAudioEncoder_init NULL #define IAudioIODeviceCapabilities_init NULL #define IDeviceVolume_init NULL #define IEngineCapabilities_init NULL #define IThreadSync_init NULL #define IThreadSync_deinit NULL #endif #if !(USE_PROFILES & USE_PROFILES_OPTIONAL) #define ILEDArray_init NULL #define IVibra_init NULL #endif #ifndef ANDROID #define IAndroidConfiguration_init NULL #define IAndroidEffect_init NULL #define IAndroidEffectCapabilities_init NULL #define IAndroidEffectSend_init NULL #define IAndroidEffect_deinit NULL #define IAndroidEffectCapabilities_deinit NULL #define IAndroidEffectCapabilities_Expose NULL #define IAndroidBufferQueue_init NULL #define IStreamInformation_init NULL #define IAndroidBufferQueue_deinit NULL #define IStreamInformation_deinit NULL #endif #ifndef USE_OUTPUTMIXEXT #define IOutputMixExt_init NULL #endif /*static*/ const struct MPH_init MPH_init_table[MPH_MAX] = { { /* MPH_3DCOMMIT, */ I3DCommit_init, NULL, NULL, NULL, NULL }, { /* MPH_3DDOPPLER, */ I3DDoppler_init, NULL, NULL, NULL, NULL }, { /* MPH_3DGROUPING, */ I3DGrouping_init, NULL, I3DGrouping_deinit, NULL, NULL }, { /* MPH_3DLOCATION, */ I3DLocation_init, NULL, NULL, NULL, NULL }, { /* MPH_3DMACROSCOPIC, */ I3DMacroscopic_init, NULL, NULL, NULL, NULL }, { /* MPH_3DSOURCE, */ I3DSource_init, NULL, NULL, NULL, NULL }, { /* MPH_AUDIODECODERCAPABILITIES, */ IAudioDecoderCapabilities_init, NULL, NULL, NULL, NULL }, { /* MPH_AUDIOENCODER, */ IAudioEncoder_init, NULL, NULL, NULL, NULL }, { /* MPH_AUDIOENCODERCAPABILITIES, */ IAudioEncoderCapabilities_init, NULL, NULL, NULL, NULL }, { /* MPH_AUDIOIODEVICECAPABILITIES, */ IAudioIODeviceCapabilities_init, NULL, NULL, NULL, NULL }, { /* MPH_BASSBOOST, */ IBassBoost_init, NULL, IBassBoost_deinit, IBassBoost_Expose, NULL }, { /* MPH_BUFFERQUEUE, */ IBufferQueue_init, NULL, IBufferQueue_deinit, NULL, NULL }, { /* MPH_DEVICEVOLUME, */ IDeviceVolume_init, NULL, NULL, NULL, NULL }, { /* MPH_DYNAMICINTERFACEMANAGEMENT, */ IDynamicInterfaceManagement_init, NULL, NULL, NULL, NULL }, { /* MPH_DYNAMICSOURCE, */ IDynamicSource_init, NULL, NULL, NULL, NULL }, { /* MPH_EFFECTSEND, */ IEffectSend_init, NULL, NULL, NULL, NULL }, { /* MPH_ENGINE, */ IEngine_init, NULL, IEngine_deinit, NULL, NULL }, { /* MPH_ENGINECAPABILITIES, */ IEngineCapabilities_init, NULL, NULL, NULL, NULL }, { /* MPH_ENVIRONMENTALREVERB, */ IEnvironmentalReverb_init, NULL, IEnvironmentalReverb_deinit, IEnvironmentalReverb_Expose, NULL }, { /* MPH_EQUALIZER, */ IEqualizer_init, NULL, IEqualizer_deinit, IEqualizer_Expose, NULL }, { /* MPH_LED, */ ILEDArray_init, NULL, NULL, NULL, NULL }, { /* MPH_METADATAEXTRACTION, */ IMetadataExtraction_init, NULL, NULL, NULL, NULL }, { /* MPH_METADATATRAVERSAL, */ IMetadataTraversal_init, NULL, NULL, NULL, NULL }, { /* MPH_MIDIMESSAGE, */ IMIDIMessage_init, NULL, NULL, NULL, NULL }, { /* MPH_MIDITIME, */ IMIDITime_init, NULL, NULL, NULL, NULL }, { /* MPH_MIDITEMPO, */ IMIDITempo_init, NULL, NULL, NULL, NULL }, { /* MPH_MIDIMUTESOLO, */ IMIDIMuteSolo_init, NULL, NULL, NULL, NULL }, { /* MPH_MUTESOLO, */ IMuteSolo_init, NULL, NULL, NULL, NULL }, { /* MPH_NULL, */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_OBJECT, */ IObject_init, NULL, IObject_deinit, NULL, NULL }, { /* MPH_OUTPUTMIX, */ IOutputMix_init, NULL, NULL, NULL, NULL }, { /* MPH_PITCH, */ IPitch_init, NULL, NULL, NULL, NULL }, { /* MPH_PLAY, */ IPlay_init, NULL, NULL, NULL, NULL }, { /* MPH_PLAYBACKRATE, */ IPlaybackRate_init, NULL, NULL, NULL, NULL }, { /* MPH_PREFETCHSTATUS, */ IPrefetchStatus_init, NULL, NULL, NULL, NULL }, { /* MPH_PRESETREVERB, */ IPresetReverb_init, NULL, IPresetReverb_deinit, IPresetReverb_Expose, NULL }, { /* MPH_RATEPITCH, */ IRatePitch_init, NULL, NULL, NULL, NULL }, { /* MPH_RECORD, */ IRecord_init, NULL, NULL, NULL, NULL }, { /* MPH_SEEK, */ ISeek_init, NULL, NULL, NULL, NULL }, { /* MPH_THREADSYNC, */ IThreadSync_init, NULL, IThreadSync_deinit, NULL, NULL }, { /* MPH_VIBRA, */ IVibra_init, NULL, NULL, NULL, NULL }, { /* MPH_VIRTUALIZER, */ IVirtualizer_init, NULL, IVirtualizer_deinit, IVirtualizer_Expose, NULL }, { /* MPH_VISUALIZATION, */ IVisualization_init, NULL, NULL, NULL, NULL }, { /* MPH_VOLUME, */ IVolume_init, NULL, NULL, NULL, NULL }, // Wilhelm desktop extended interfaces { /* MPH_OUTPUTMIXEXT, */ IOutputMixExt_init, NULL, NULL, NULL, NULL }, // Android API level 9 extended interfaces { /* MPH_ANDROIDEFFECT */ IAndroidEffect_init, NULL, IAndroidEffect_deinit, NULL, NULL }, { /* MPH_ANDROIDEFFECTCAPABILITIES */ IAndroidEffectCapabilities_init, NULL, IAndroidEffectCapabilities_deinit, IAndroidEffectCapabilities_Expose, NULL }, { /* MPH_ANDROIDEFFECTSEND */ IAndroidEffectSend_init, NULL, NULL, NULL, NULL }, { /* MPH_ANDROIDCONFIGURATION */ IAndroidConfiguration_init, NULL, NULL, NULL, NULL }, { /* MPH_ANDROIDSIMPLEBUFFERQUEUE */ IBufferQueue_init /* alias */, NULL, NULL, NULL, NULL }, // Android API level 10 extended interfaces { /* MPH_ANDROIDBUFFERQUEUESOURCE */ IAndroidBufferQueue_init, NULL, IAndroidBufferQueue_deinit, NULL, NULL }, // OpenMAX AL 1.0.1 interfaces { /* MPH_XAAUDIODECODERCAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAAUDIOENCODER */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAAUDIOENCODERCAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAAUDIOIODEVICECAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XACAMERA */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XACAMERACAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XACONFIGEXTENSION */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XADEVICEVOLUME */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XADYNAMICINTERFACEMANAGEMENT 59 */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XADYNAMICSOURCE */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAENGINE */ IXAEngine_init, NULL, IXAEngine_deinit, NULL, NULL }, { /* MPH_XAEQUALIZER */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAIMAGECONTROLS */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAIMAGEDECODERCAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAIMAGEEFFECTS */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAIMAGEENCODER */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAIMAGEENCODERCAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XALED */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAMETADATAEXTRACTION */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAMETADATAINSERTION */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAMETADATATRAVERSAL */ NULL, NULL, NULL, NULL, NULL }, // { /* MPH_XANULL */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAOBJECT */ IObject_init, NULL, IObject_deinit, NULL, NULL }, { /* MPH_XAOUTPUTMIX */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAPLAY */ IPlay_init, NULL, NULL, NULL, NULL }, { /* MPH_XAPLAYBACKRATE */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAPREFETCHSTATUS, */ IPrefetchStatus_init, NULL, NULL, NULL, NULL }, { /* MPH_XARADIO */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XARDS */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XARECORD */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XASEEK */ ISeek_init, NULL, NULL, NULL, NULL }, { /* MPH_XASNAPSHOT */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XASTREAMINFORMATION */ IStreamInformation_init, NULL, IStreamInformation_deinit, NULL, NULL }, { /* MPH_XATHREADSYNC */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAVIBRA */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAVIDEODECODERCAPABILITIES */ IVideoDecoderCapabilities_init, NULL, IVideoDecoderCapabilities_deinit, IVideoDecoderCapabilities_expose, NULL }, { /* MPH_XAVIDEOENCODER */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAVIDEOENCODERCAPABILITIES */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAVIDEOPOSTPROCESSING */ NULL, NULL, NULL, NULL, NULL }, { /* MPH_XAVOLUME, */ IVolume_init, NULL, NULL, NULL, NULL }, }; /** \brief Construct a new instance of the specified class, exposing selected interfaces */ IObject *construct(const ClassTable *clazz, unsigned exposedMask, SLEngineItf engine) { IObject *thiz; // Do not change this to malloc; we depend on the object being memset to zero thiz = (IObject *) calloc(1, clazz->mSize); if (NULL != thiz) { SL_LOGV("construct %s at %p", clazz->mName, thiz); unsigned lossOfControlMask = 0; // a NULL engine means we are constructing the engine IEngine *thisEngine = (IEngine *) engine; if (NULL == thisEngine) { // thisEngine = &((CEngine *) thiz)->mEngine; thiz->mEngine = (CEngine *) thiz; } else { thiz->mEngine = (CEngine *) thisEngine->mThis; interface_lock_exclusive(thisEngine); if (MAX_INSTANCE <= thisEngine->mInstanceCount) { SL_LOGE("Too many objects"); interface_unlock_exclusive(thisEngine); free(thiz); return NULL; } // pre-allocate a pending slot, but don't assign bit from mInstanceMask yet ++thisEngine->mInstanceCount; assert(((unsigned) ~0) != thisEngine->mInstanceMask); interface_unlock_exclusive(thisEngine); // const, no lock needed if (thisEngine->mLossOfControlGlobal) { lossOfControlMask = ~0; } } thiz->mLossOfControlMask = lossOfControlMask; thiz->mClass = clazz; const struct iid_vtable *x = clazz->mInterfaces; SLuint8 *interfaceStateP = thiz->mInterfaceStates; SLuint32 index; for (index = 0; index < clazz->mInterfaceCount; ++index, ++x, exposedMask >>= 1) { SLuint8 state; // initialize all interfaces with init hooks, even if not exposed const struct MPH_init *mi = &MPH_init_table[x->mMPH]; VoidHook init = mi->mInit; if (NULL != init) { void *self = (char *) thiz + x->mOffset; // IObject does not have an mThis, so [1] is not always defined if (index) { ((IObject **) self)[1] = thiz; } // call the initialization hook (*init)(self); // IObject does not require a call to GetInterface if (index) { // This trickery invalidates the v-table until GetInterface ((size_t *) self)[0] ^= ~0; } // if interface is exposed, also call the optional expose hook BoolHook expose; state = (exposedMask & 1) && ((NULL == (expose = mi->mExpose)) || (*expose)(self)) ? INTERFACE_EXPOSED : INTERFACE_INITIALIZED; // FIXME log or report to application if an expose hook on a // required explicit interface fails at creation time } else { state = INTERFACE_UNINITIALIZED; } *interfaceStateP++ = state; } // note that the new object is not yet published; creator must call IObject_Publish } return thiz; }