/* * Monikers * * Copyright 1998 Marcus Meissner * Copyright 1999 Noomen Hamza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * TODO: * - IRunningObjectTable should work interprocess, but currently doesn't. * Native (on Win2k at least) uses an undocumented RPC interface, IROT, to * communicate with RPCSS which contains the table of marshalled data. * - IRunningObjectTable should use marshalling instead of simple ref * counting as there is the possibility of using the running object table * to access objects in other apartments. */ #include #include #include #define COBJMACROS #include "winerror.h" #include "windef.h" #include "winbase.h" #include "winuser.h" #include "wtypes.h" #include "wine/debug.h" #include "ole2.h" #include "compobj_private.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); #define BLOCK_TAB_SIZE 20 /* represent the first size table and it's increment block size */ /* define the structure of the running object table elements */ typedef struct RunObject{ IUnknown* pObj; /* points on a running object*/ IMoniker* pmkObj; /* points on a moniker who identifies this object */ FILETIME lastModifObj; DWORD identRegObj; /* registration key relative to this object */ DWORD regTypeObj; /* registration type : strong or weak */ }RunObject; /* define the RunningObjectTableImpl structure */ typedef struct RunningObjectTableImpl{ IRunningObjectTableVtbl *lpVtbl; ULONG ref; RunObject* runObjTab; /* pointer to the first object in the table */ DWORD runObjTabSize; /* current table size */ DWORD runObjTabLastIndx; /* first free index element in the table. */ DWORD runObjTabRegister; /* registration key of the next registered object */ } RunningObjectTableImpl; static RunningObjectTableImpl* runningObjectTableInstance = NULL; static HRESULT WINAPI RunningObjectTableImpl_GetObjectIndex(RunningObjectTableImpl*,DWORD,IMoniker*,DWORD *); /* define the EnumMonikerImpl structure */ typedef struct EnumMonikerImpl{ IEnumMonikerVtbl *lpVtbl; ULONG ref; RunObject* TabMoniker; /* pointer to the first object in the table */ DWORD TabSize; /* current table size */ DWORD TabLastIndx; /* last used index element in the table. */ DWORD TabCurrentPos; /* enum position in the list */ } EnumMonikerImpl; /* IEnumMoniker Local functions*/ static HRESULT WINAPI EnumMonikerImpl_CreateEnumROTMoniker(RunObject* runObjTab, ULONG TabSize, ULONG TabLastIndx, ULONG TabCurrentPos, IEnumMoniker ** ppenumMoniker); /*********************************************************************** * RunningObjectTable_QueryInterface */ static HRESULT WINAPI RunningObjectTableImpl_QueryInterface(IRunningObjectTable* iface, REFIID riid,void** ppvObject) { RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%p,%p)\n",This,riid,ppvObject); /* validate arguments */ if (This==0) return CO_E_NOTINITIALIZED; if (ppvObject==0) return E_INVALIDARG; *ppvObject = 0; if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IRunningObjectTable, riid)) *ppvObject = (IRunningObjectTable*)This; if ((*ppvObject)==0) return E_NOINTERFACE; IRunningObjectTable_AddRef(iface); return S_OK; } /*********************************************************************** * RunningObjectTable_AddRef */ static ULONG WINAPI RunningObjectTableImpl_AddRef(IRunningObjectTable* iface) { RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p)\n",This); return InterlockedIncrement(&This->ref); } /*********************************************************************** * RunningObjectTable_Initialize */ static HRESULT WINAPI RunningObjectTableImpl_Destroy(void) { TRACE("()\n"); if (runningObjectTableInstance==NULL) return E_INVALIDARG; /* free the ROT table memory */ HeapFree(GetProcessHeap(),0,runningObjectTableInstance->runObjTab); /* free the ROT structure memory */ HeapFree(GetProcessHeap(),0,runningObjectTableInstance); runningObjectTableInstance = NULL; return S_OK; } /*********************************************************************** * RunningObjectTable_Release */ static ULONG WINAPI RunningObjectTableImpl_Release(IRunningObjectTable* iface) { DWORD i; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; ULONG ref; TRACE("(%p)\n",This); ref = InterlockedDecrement(&This->ref); /* unitialize ROT structure if there's no more reference to it*/ if (ref == 0) { /* release all registered objects */ for(i=0;irunObjTabLastIndx;i++) { if (( This->runObjTab[i].regTypeObj & ROTFLAGS_REGISTRATIONKEEPSALIVE) != 0) IUnknown_Release(This->runObjTab[i].pObj); IMoniker_Release(This->runObjTab[i].pmkObj); } /* RunningObjectTable data structure will be not destroyed here ! the destruction will be done only * when RunningObjectTableImpl_UnInitialize function is called */ /* there's no more elements in the table */ This->runObjTabRegister=0; This->runObjTabLastIndx=0; } return ref; } /*********************************************************************** * RunningObjectTable_Register * * PARAMS * grfFlags [in] Registration options * punkObject [in] the object being registered * pmkObjectName [in] the moniker of the object being registered * pdwRegister [in] the value identifying the registration */ static HRESULT WINAPI RunningObjectTableImpl_Register(IRunningObjectTable* iface, DWORD grfFlags, IUnknown *punkObject, IMoniker *pmkObjectName, DWORD *pdwRegister) { HRESULT res=S_OK; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%ld,%p,%p,%p)\n",This,grfFlags,punkObject,pmkObjectName,pdwRegister); /* * there's only two types of register : strong and or weak registration * (only one must be passed on parameter) */ if ( ( (grfFlags & ROTFLAGS_REGISTRATIONKEEPSALIVE) || !(grfFlags & ROTFLAGS_ALLOWANYCLIENT)) && (!(grfFlags & ROTFLAGS_REGISTRATIONKEEPSALIVE) || (grfFlags & ROTFLAGS_ALLOWANYCLIENT)) && (grfFlags) ) return E_INVALIDARG; if (punkObject==NULL || pmkObjectName==NULL || pdwRegister==NULL) return E_INVALIDARG; /* verify if the object to be registered was registered before */ if (RunningObjectTableImpl_GetObjectIndex(This,-1,pmkObjectName,NULL)==S_OK) res = MK_S_MONIKERALREADYREGISTERED; /* put the new registered object in the first free element in the table */ This->runObjTab[This->runObjTabLastIndx].pObj = punkObject; This->runObjTab[This->runObjTabLastIndx].pmkObj = pmkObjectName; This->runObjTab[This->runObjTabLastIndx].regTypeObj = grfFlags; This->runObjTab[This->runObjTabLastIndx].identRegObj = This->runObjTabRegister; CoFileTimeNow(&(This->runObjTab[This->runObjTabLastIndx].lastModifObj)); /* gives a registration identifier to the registered object*/ (*pdwRegister)= This->runObjTabRegister; if (This->runObjTabRegister == 0xFFFFFFFF){ FIXME("runObjTabRegister: %ld is out of data limite \n",This->runObjTabRegister); return E_FAIL; } This->runObjTabRegister++; This->runObjTabLastIndx++; if (This->runObjTabLastIndx == This->runObjTabSize){ /* table is full ! so it must be resized */ This->runObjTabSize+=BLOCK_TAB_SIZE; /* newsize table */ This->runObjTab=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->runObjTab, This->runObjTabSize * sizeof(RunObject)); if (!This->runObjTab) return E_OUTOFMEMORY; } /* add a reference to the object in the strong registration case */ if ((grfFlags & ROTFLAGS_REGISTRATIONKEEPSALIVE) !=0 ) { TRACE("strong registration, reffing %p\n", punkObject); /* this is wrong; we should always add a reference to the object */ IUnknown_AddRef(punkObject); } IMoniker_AddRef(pmkObjectName); return res; } /*********************************************************************** * RunningObjectTable_Revoke * * PARAMS * dwRegister [in] Value identifying registration to be revoked */ static HRESULT WINAPI RunningObjectTableImpl_Revoke( IRunningObjectTable* iface, DWORD dwRegister) { DWORD index,j; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%ld)\n",This,dwRegister); /* verify if the object to be revoked was registered before or not */ if (RunningObjectTableImpl_GetObjectIndex(This,dwRegister,NULL,&index)==S_FALSE) return E_INVALIDARG; /* release the object if it was registered with a strong registrantion option */ if ((This->runObjTab[index].regTypeObj & ROTFLAGS_REGISTRATIONKEEPSALIVE)!=0) { TRACE("releasing %p\n", This->runObjTab[index].pObj); /* this is also wrong; we should always release the object (see above) */ IUnknown_Release(This->runObjTab[index].pObj); } IMoniker_Release(This->runObjTab[index].pmkObj); /* remove the object from the table */ for(j=index; jrunObjTabLastIndx-1; j++) This->runObjTab[j]= This->runObjTab[j+1]; This->runObjTabLastIndx--; return S_OK; } /*********************************************************************** * RunningObjectTable_IsRunning * * PARAMS * pmkObjectName [in] moniker of the object whose status is desired */ static HRESULT WINAPI RunningObjectTableImpl_IsRunning( IRunningObjectTable* iface, IMoniker *pmkObjectName) { RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%p)\n",This,pmkObjectName); return RunningObjectTableImpl_GetObjectIndex(This,-1,pmkObjectName,NULL); } /*********************************************************************** * RunningObjectTable_GetObject * * PARAMS * pmkObjectName [in] Pointer to the moniker on the object * ppunkObject [out] variable that receives the IUnknown interface pointer */ static HRESULT WINAPI RunningObjectTableImpl_GetObject( IRunningObjectTable* iface, IMoniker *pmkObjectName, IUnknown **ppunkObject) { DWORD index; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%p,%p)\n",This,pmkObjectName,ppunkObject); if (ppunkObject==NULL) return E_POINTER; *ppunkObject=0; /* verify if the object was registered before or not */ if (RunningObjectTableImpl_GetObjectIndex(This,-1,pmkObjectName,&index)==S_FALSE) { WARN("Moniker unavailable - needs to work interprocess?\n"); return MK_E_UNAVAILABLE; } /* add a reference to the object then set output object argument */ IUnknown_AddRef(This->runObjTab[index].pObj); *ppunkObject=This->runObjTab[index].pObj; return S_OK; } /*********************************************************************** * RunningObjectTable_NoteChangeTime * * PARAMS * dwRegister [in] Value identifying registration being updated * pfiletime [in] Pointer to structure containing object's last change time */ static HRESULT WINAPI RunningObjectTableImpl_NoteChangeTime(IRunningObjectTable* iface, DWORD dwRegister, FILETIME *pfiletime) { DWORD index=-1; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%ld,%p)\n",This,dwRegister,pfiletime); /* verify if the object to be changed was registered before or not */ if (RunningObjectTableImpl_GetObjectIndex(This,dwRegister,NULL,&index)==S_FALSE) return E_INVALIDARG; /* set the new value of the last time change */ This->runObjTab[index].lastModifObj= (*pfiletime); return S_OK; } /*********************************************************************** * RunningObjectTable_GetTimeOfLastChange * * PARAMS * pmkObjectName [in] moniker of the object whose status is desired * pfiletime [out] structure that receives object's last change time */ static HRESULT WINAPI RunningObjectTableImpl_GetTimeOfLastChange(IRunningObjectTable* iface, IMoniker *pmkObjectName, FILETIME *pfiletime) { DWORD index=-1; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; TRACE("(%p,%p,%p)\n",This,pmkObjectName,pfiletime); if (pmkObjectName==NULL || pfiletime==NULL) return E_INVALIDARG; /* verify if the object was registered before or not */ if (RunningObjectTableImpl_GetObjectIndex(This,-1,pmkObjectName,&index)==S_FALSE) return MK_E_UNAVAILABLE; (*pfiletime)= This->runObjTab[index].lastModifObj; return S_OK; } /*********************************************************************** * RunningObjectTable_EnumRunning * * PARAMS * ppenumMoniker [out] receives the IEnumMoniker interface pointer */ static HRESULT WINAPI RunningObjectTableImpl_EnumRunning(IRunningObjectTable* iface, IEnumMoniker **ppenumMoniker) { /* create the unique instance of the EnumMonkikerImpl structure * and copy the Monikers referenced in the ROT so that they can be * enumerated by the Enum interface */ HRESULT rc = 0; RunningObjectTableImpl *This = (RunningObjectTableImpl *)iface; rc=EnumMonikerImpl_CreateEnumROTMoniker(This->runObjTab, This->runObjTabSize, This->runObjTabLastIndx, 0, ppenumMoniker); return rc; } /*********************************************************************** * GetObjectIndex */ static HRESULT WINAPI RunningObjectTableImpl_GetObjectIndex(RunningObjectTableImpl* This, DWORD identReg, IMoniker* pmk, DWORD *indx) { DWORD i; TRACE("(%p,%ld,%p,%p)\n",This,identReg,pmk,indx); if (pmk!=NULL) /* search object identified by a moniker */ for(i=0 ; (i < This->runObjTabLastIndx) &&(!IMoniker_IsEqual(This->runObjTab[i].pmkObj,pmk)==S_OK);i++); else /* search object identified by a register identifier */ for(i=0;((irunObjTabLastIndx)&&(This->runObjTab[i].identRegObj!=identReg));i++); if (i==This->runObjTabLastIndx) return S_FALSE; if (indx != NULL) *indx=i; return S_OK; } /****************************************************************************** * GetRunningObjectTable (OLE2.30) */ HRESULT WINAPI GetRunningObjectTable16(DWORD reserved, LPRUNNINGOBJECTTABLE *pprot) { FIXME("(%ld,%p),stub!\n",reserved,pprot); return E_NOTIMPL; } /*********************************************************************** * GetRunningObjectTable (OLE32.@) */ HRESULT WINAPI GetRunningObjectTable(DWORD reserved, LPRUNNINGOBJECTTABLE *pprot) { IID riid=IID_IRunningObjectTable; HRESULT res; TRACE("()\n"); if (reserved!=0) return E_UNEXPECTED; if(runningObjectTableInstance==NULL) return CO_E_NOTINITIALIZED; res = IRunningObjectTable_QueryInterface((IRunningObjectTable*)runningObjectTableInstance,&riid,(void**)pprot); return res; } /****************************************************************************** * OleRun [OLE32.@] */ HRESULT WINAPI OleRun(LPUNKNOWN pUnknown) { IRunnableObject *runable; IRunnableObject *This = (IRunnableObject *)pUnknown; LRESULT ret; ret = IRunnableObject_QueryInterface(This,&IID_IRunnableObject,(LPVOID*)&runable); if (ret) return 0; /* Appears to return no error. */ ret = IRunnableObject_Run(runable,NULL); IRunnableObject_Release(runable); return ret; } /****************************************************************************** * MkParseDisplayName [OLE32.@] */ HRESULT WINAPI MkParseDisplayName(LPBC pbc, LPCOLESTR szUserName, LPDWORD pchEaten, LPMONIKER *ppmk) { FIXME("(%p, %s, %p, %p): stub.\n", pbc, debugstr_w(szUserName), pchEaten, *ppmk); if (!(IsValidInterface((LPUNKNOWN) pbc))) return E_INVALIDARG; return MK_E_SYNTAX; } /****************************************************************************** * CreateClassMoniker [OLE32.@] */ HRESULT WINAPI CreateClassMoniker(REFCLSID rclsid, IMoniker ** ppmk) { FIXME("%s\n", debugstr_guid( rclsid )); if( ppmk ) *ppmk = NULL; return E_NOTIMPL; } /* Virtual function table for the IRunningObjectTable class. */ static IRunningObjectTableVtbl VT_RunningObjectTableImpl = { RunningObjectTableImpl_QueryInterface, RunningObjectTableImpl_AddRef, RunningObjectTableImpl_Release, RunningObjectTableImpl_Register, RunningObjectTableImpl_Revoke, RunningObjectTableImpl_IsRunning, RunningObjectTableImpl_GetObject, RunningObjectTableImpl_NoteChangeTime, RunningObjectTableImpl_GetTimeOfLastChange, RunningObjectTableImpl_EnumRunning }; /*********************************************************************** * RunningObjectTable_Initialize */ HRESULT WINAPI RunningObjectTableImpl_Initialize(void) { TRACE("\n"); /* create the unique instance of the RunningObjectTableImpl structure */ runningObjectTableInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(RunningObjectTableImpl)); if (runningObjectTableInstance == 0) return E_OUTOFMEMORY; /* initialize the virtual table function */ runningObjectTableInstance->lpVtbl = &VT_RunningObjectTableImpl; /* the initial reference is set to "1" ! because if set to "0" it will be not practis when */ /* the ROT referred many times not in the same time (all the objects in the ROT will */ /* be removed every time the ROT is removed ) */ runningObjectTableInstance->ref = 1; /* allocate space memory for the table which contains all the running objects */ runningObjectTableInstance->runObjTab = HeapAlloc(GetProcessHeap(), 0, sizeof(RunObject[BLOCK_TAB_SIZE])); if (runningObjectTableInstance->runObjTab == NULL) return E_OUTOFMEMORY; runningObjectTableInstance->runObjTabSize=BLOCK_TAB_SIZE; runningObjectTableInstance->runObjTabRegister=1; runningObjectTableInstance->runObjTabLastIndx=0; return S_OK; } /*********************************************************************** * RunningObjectTable_UnInitialize */ HRESULT WINAPI RunningObjectTableImpl_UnInitialize() { TRACE("\n"); if (runningObjectTableInstance==NULL) return E_POINTER; RunningObjectTableImpl_Release((IRunningObjectTable*)runningObjectTableInstance); RunningObjectTableImpl_Destroy(); return S_OK; } /*********************************************************************** * EnumMoniker_QueryInterface */ static HRESULT WINAPI EnumMonikerImpl_QueryInterface(IEnumMoniker* iface,REFIID riid,void** ppvObject) { EnumMonikerImpl *This = (EnumMonikerImpl *)iface; TRACE("(%p,%p,%p)\n",This,riid,ppvObject); /* validate arguments */ if (ppvObject == NULL) return E_INVALIDARG; *ppvObject = NULL; if (IsEqualIID(&IID_IUnknown, riid)) *ppvObject = (IEnumMoniker*)This; else if (IsEqualIID(&IID_IEnumMoniker, riid)) *ppvObject = (IEnumMoniker*)This; if ((*ppvObject)==NULL) return E_NOINTERFACE; IEnumMoniker_AddRef(iface); return S_OK; } /*********************************************************************** * EnumMoniker_AddRef */ static ULONG WINAPI EnumMonikerImpl_AddRef(IEnumMoniker* iface) { EnumMonikerImpl *This = (EnumMonikerImpl *)iface; TRACE("(%p)\n",This); return InterlockedIncrement(&This->ref); } /*********************************************************************** * EnumMoniker_release */ static ULONG WINAPI EnumMonikerImpl_Release(IEnumMoniker* iface) { DWORD i; EnumMonikerImpl *This = (EnumMonikerImpl *)iface; ULONG ref; TRACE("(%p)\n",This); ref = InterlockedDecrement(&This->ref); /* unitialize rot structure if there's no more reference to it*/ if (ref == 0) { /* release all registered objects in Moniker list */ for(i=0; i < This->TabLastIndx ;i++) { IMoniker_Release(This->TabMoniker[i].pmkObj); } /* there're no more elements in the table */ TRACE("(%p) Deleting\n",This); HeapFree (GetProcessHeap(), 0, This->TabMoniker); /* free Moniker list */ HeapFree (GetProcessHeap(), 0, This); /* free Enum Instance */ } return ref; } /*********************************************************************** * EnmumMoniker_Next */ static HRESULT WINAPI EnumMonikerImpl_Next(IEnumMoniker* iface, ULONG celt, IMoniker** rgelt, ULONG * pceltFetched) { ULONG i; EnumMonikerImpl *This = (EnumMonikerImpl *)iface; TRACE("(%p) TabCurrentPos %ld Tablastindx %ld\n",This, This->TabCurrentPos, This->TabLastIndx); /* retrieve the requested number of moniker from the current position */ for(i=0; (This->TabCurrentPos < This->TabLastIndx) && (i < celt); i++) rgelt[i]=(IMoniker*)This->TabMoniker[This->TabCurrentPos++].pmkObj; if (pceltFetched!=NULL) *pceltFetched= i; if (i==celt) return S_OK; else return S_FALSE; } /*********************************************************************** * EnmumMoniker_Skip */ static HRESULT WINAPI EnumMonikerImpl_Skip(IEnumMoniker* iface, ULONG celt) { EnumMonikerImpl *This = (EnumMonikerImpl *)iface; TRACE("(%p)\n",This); if (This->TabCurrentPos+celt >= This->TabLastIndx) return S_FALSE; This->TabCurrentPos+=celt; return S_OK; } /*********************************************************************** * EnmumMoniker_Reset */ static HRESULT WINAPI EnumMonikerImpl_Reset(IEnumMoniker* iface) { EnumMonikerImpl *This = (EnumMonikerImpl *)iface; This->TabCurrentPos = 0; /* set back to start of list */ TRACE("(%p)\n",This); return S_OK; } /*********************************************************************** * EnmumMoniker_Clone */ static HRESULT WINAPI EnumMonikerImpl_Clone(IEnumMoniker* iface, IEnumMoniker ** ppenum) { EnumMonikerImpl *This = (EnumMonikerImpl *)iface; TRACE("(%p)\n",This); /* copy the enum structure */ return EnumMonikerImpl_CreateEnumROTMoniker(This->TabMoniker, This->TabSize, This->TabLastIndx, This->TabCurrentPos, ppenum); } /* Virtual function table for the IEnumMoniker class. */ static IEnumMonikerVtbl VT_EnumMonikerImpl = { EnumMonikerImpl_QueryInterface, EnumMonikerImpl_AddRef, EnumMonikerImpl_Release, EnumMonikerImpl_Next, EnumMonikerImpl_Skip, EnumMonikerImpl_Reset, EnumMonikerImpl_Clone }; /*********************************************************************** * EnumMonikerImpl_CreateEnumROTMoniker * Used by EnumRunning to create the structure and EnumClone * to copy the structure */ HRESULT WINAPI EnumMonikerImpl_CreateEnumROTMoniker(RunObject* TabMoniker, ULONG TabSize, ULONG TabLastIndx, ULONG TabCurrentPos, IEnumMoniker ** ppenumMoniker) { int i; EnumMonikerImpl* This = NULL; if (TabCurrentPos > TabSize) return E_INVALIDARG; if (ppenumMoniker == NULL) return E_INVALIDARG; This = HeapAlloc(GetProcessHeap(), 0, sizeof(EnumMonikerImpl)); if (!ppenumMoniker) return E_OUTOFMEMORY; TRACE("(%p)\n", This); /* initialize the virtual table function */ This->lpVtbl = &VT_EnumMonikerImpl; /* the initial reference is set to "1" */ This->ref = 1; /* set the ref count to one */ This->TabCurrentPos=0; /* Set the list start posn to start */ This->TabSize=TabSize; /* Need the same size table as ROT */ This->TabLastIndx=TabLastIndx; /* end element */ This->TabMoniker=HeapAlloc(GetProcessHeap(),0,This->TabSize*sizeof(RunObject)); if (This->TabMoniker==NULL) { HeapFree(GetProcessHeap(), 0, This); return E_OUTOFMEMORY; } for (i=0; i < This->TabLastIndx; i++){ This->TabMoniker[i]=TabMoniker[i]; IMoniker_AddRef(TabMoniker[i].pmkObj); } *ppenumMoniker = (IEnumMoniker*)This; return S_OK; }