/* * FileMonikers implementation * * Copyright 1999 Noomen Hamza * Copyright 2007 Robert Shearman * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #define COBJMACROS #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winnls.h" #include "wine/debug.h" #include "objbase.h" #include "moniker.h" #include "compobj_private.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); /* filemoniker data structure */ typedef struct FileMonikerImpl{ IMoniker IMoniker_iface; IROTData IROTData_iface; LONG ref; LPOLESTR filePathName; /* path string identified by this filemoniker */ IUnknown *pMarshal; /* custom marshaler */ } FileMonikerImpl; static inline FileMonikerImpl *impl_from_IMoniker(IMoniker *iface) { return CONTAINING_RECORD(iface, FileMonikerImpl, IMoniker_iface); } static inline FileMonikerImpl *impl_from_IROTData(IROTData *iface) { return CONTAINING_RECORD(iface, FileMonikerImpl, IROTData_iface); } static const IMonikerVtbl VT_FileMonikerImpl; static FileMonikerImpl *unsafe_impl_from_IMoniker(IMoniker *iface) { if (iface->lpVtbl != &VT_FileMonikerImpl) return NULL; return CONTAINING_RECORD(iface, FileMonikerImpl, IMoniker_iface); } /* Local function used by filemoniker implementation */ static HRESULT FileMonikerImpl_Construct(FileMonikerImpl* iface, LPCOLESTR lpszPathName); /******************************************************************************* * FileMoniker_QueryInterface */ static HRESULT WINAPI FileMonikerImpl_QueryInterface(IMoniker *iface, REFIID riid, void **ppvObject) { FileMonikerImpl *This = impl_from_IMoniker(iface); TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), ppvObject); if (!ppvObject) return E_INVALIDARG; *ppvObject = 0; if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPersist, riid) || IsEqualIID(&IID_IPersistStream,riid) || IsEqualIID(&IID_IMoniker, riid) || IsEqualGUID(&CLSID_FileMoniker, riid)) { *ppvObject = iface; } else if (IsEqualIID(&IID_IROTData, riid)) *ppvObject = &This->IROTData_iface; else if (IsEqualIID(&IID_IMarshal, riid)) { HRESULT hr = S_OK; if (!This->pMarshal) hr = MonikerMarshal_Create(iface, &This->pMarshal); if (hr != S_OK) return hr; return IUnknown_QueryInterface(This->pMarshal, riid, ppvObject); } if (!*ppvObject) return E_NOINTERFACE; IMoniker_AddRef(iface); return S_OK; } /****************************************************************************** * FileMoniker_AddRef */ static ULONG WINAPI FileMonikerImpl_AddRef(IMoniker* iface) { FileMonikerImpl *This = impl_from_IMoniker(iface); TRACE("(%p)\n",iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI FileMonikerImpl_Release(IMoniker* iface) { FileMonikerImpl *moniker = impl_from_IMoniker(iface); ULONG ref = InterlockedDecrement(&moniker->ref); TRACE("(%p, refcount %d)\n", iface, ref); if (!ref) { if (moniker->pMarshal) IUnknown_Release(moniker->pMarshal); HeapFree(GetProcessHeap(), 0, moniker->filePathName); HeapFree(GetProcessHeap(), 0, moniker); } return ref; } /****************************************************************************** * FileMoniker_GetClassID */ static HRESULT WINAPI FileMonikerImpl_GetClassID(IMoniker* iface, CLSID *pClassID) { TRACE("(%p,%p)\n",iface,pClassID); if (pClassID==NULL) return E_POINTER; *pClassID = CLSID_FileMoniker; return S_OK; } /****************************************************************************** * FileMoniker_IsDirty * * Note that the OLE-provided implementations of the IPersistStream::IsDirty * method in the OLE-provided moniker interfaces always return S_FALSE because * their internal state never changes. */ static HRESULT WINAPI FileMonikerImpl_IsDirty(IMoniker* iface) { TRACE("(%p)\n",iface); return S_FALSE; } /****************************************************************************** * FileMoniker_Load * * this function locates and reads from the stream the filePath string * written by FileMonikerImpl_Save */ static HRESULT WINAPI FileMonikerImpl_Load(IMoniker* iface, IStream* pStm) { FileMonikerImpl *This = impl_from_IMoniker(iface); HRESULT res; CHAR* filePathA = NULL; WCHAR* filePathW = NULL; ULONG bread; WORD wbuffer; DWORD dwbuffer, bytesA, bytesW, len; int i; TRACE("(%p,%p)\n",iface,pStm); /* first WORD */ res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread!=sizeof(WORD)) { WARN("Couldn't read 0 word\n"); goto fail; } /* read filePath string length (plus one) */ res=IStream_Read(pStm,&bytesA,sizeof(DWORD),&bread); if (bread != sizeof(DWORD)) { WARN("Couldn't read file string length\n"); goto fail; } /* read filePath string */ filePathA=HeapAlloc(GetProcessHeap(),0,bytesA); if (!filePathA) { res = E_OUTOFMEMORY; goto fail; } res=IStream_Read(pStm,filePathA,bytesA,&bread); if (bread != bytesA) { WARN("Couldn't read file path string\n"); goto fail; } /* read the unknown value */ IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread != sizeof(WORD)) { WARN("Couldn't read unknown value\n"); goto fail; } /* read the DEAD constant */ IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread != sizeof(WORD)) { WARN("Couldn't read DEAD constant\n"); goto fail; } for(i=0;i<5;i++) { res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); if (bread!=sizeof(DWORD)) { WARN("Couldn't read 0 padding\n"); goto fail; } } res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); if (bread!=sizeof(DWORD)) goto fail; if (!dwbuffer) /* No W-string */ { bytesA--; len=MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, bytesA, NULL, 0); if (!len) goto fail; filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR)); if (!filePathW) { res = E_OUTOFMEMORY; goto fail; } MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, -1, filePathW, len+1); goto succeed; } if (dwbuffer < 6) goto fail; bytesW=dwbuffer - 6; res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); if (bread!=sizeof(DWORD) || dwbuffer!=bytesW) goto fail; res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread!=sizeof(WORD) || wbuffer!=0x3) goto fail; len=bytesW/sizeof(WCHAR); filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR)); if(!filePathW) { res = E_OUTOFMEMORY; goto fail; } res=IStream_Read(pStm,filePathW,bytesW,&bread); if (bread!=bytesW) goto fail; filePathW[len]=0; succeed: HeapFree(GetProcessHeap(),0,filePathA); HeapFree(GetProcessHeap(),0,This->filePathName); This->filePathName=filePathW; return S_OK; fail: HeapFree(GetProcessHeap(), 0, filePathA); HeapFree(GetProcessHeap(), 0, filePathW); if (SUCCEEDED(res)) res = E_FAIL; return res; } /****************************************************************************** * FileMoniker_Save * * This function saves data of this object. In the beginning I thought * that I have just to write the filePath string on Stream. But, when I * tested this function with windows program samples, I noticed that it * was not the case. This implementation is based on XP SP2. Other versions * of Windows have minor variations. * * Data which must be written on stream is: * 1) WORD constant: zero (not validated by Windows) * 2) length of the path string ("\0" included) * 3) path string type A * 4) Unknown WORD value: Frequently 0xFFFF, but not always. If set very large, * Windows returns E_OUTOFMEMORY * 5) WORD Constant: 0xDEAD (not validated by Windows) * 6) five DWORD constant: zero (not validated by Windows) * 7) If we're only writing the multibyte version, * write a zero DWORD and finish. * * 8) DWORD: double-length of the path string type W ("\0" not * included) * 9) WORD constant: 0x3 * 10) filePath unicode string. * */ static HRESULT WINAPI FileMonikerImpl_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty) { FileMonikerImpl *This = impl_from_IMoniker(iface); HRESULT res; LPOLESTR filePathW=This->filePathName; CHAR* filePathA; DWORD bytesA, bytesW, len; static const WORD FFFF = 0xFFFF; /* Constants */ static const WORD DEAD = 0xDEAD; static const DWORD ZERO = 0; static const WORD THREE = 0x3; int i; BOOL bUsedDefault, bWriteWide; TRACE("(%p,%p,%d)\n",iface,pStm,fClearDirty); if (pStm==NULL) return E_POINTER; /* write a 0 WORD */ res=IStream_Write(pStm,&ZERO,sizeof(WORD),NULL); if (FAILED(res)) return res; /* write length of filePath string ( 0 included )*/ bytesA = WideCharToMultiByte( CP_ACP, 0, filePathW, -1, NULL, 0, NULL, NULL ); res=IStream_Write(pStm,&bytesA,sizeof(DWORD),NULL); if (FAILED(res)) return res; /* write A string (with '\0') */ filePathA=HeapAlloc(GetProcessHeap(),0,bytesA); if (!filePathA) return E_OUTOFMEMORY; WideCharToMultiByte( CP_ACP, 0, filePathW, -1, filePathA, bytesA, NULL, &bUsedDefault); res=IStream_Write(pStm,filePathA,bytesA,NULL); HeapFree(GetProcessHeap(),0,filePathA); if (FAILED(res)) return res; /* write a WORD 0xFFFF */ res=IStream_Write(pStm,&FFFF,sizeof(WORD),NULL); if (FAILED(res)) return res; /* write a WORD 0xDEAD */ res=IStream_Write(pStm,&DEAD,sizeof(WORD),NULL); if (FAILED(res)) return res; /* write 5 zero DWORDs */ for(i=0;i<5;i++) { res=IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL); if (FAILED(res)) return res; } /* Write the wide version if: * + couldn't convert to CP_ACP, * or + it's a directory, * or + there's a character > 0xFF */ len = lstrlenW(filePathW); bWriteWide = (bUsedDefault || (len > 0 && filePathW[len-1]=='\\' )); if (!bWriteWide) { WCHAR* pch; for(pch=filePathW;*pch;++pch) { if (*pch > 0xFF) { bWriteWide = TRUE; break; } } } if (!bWriteWide) return IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL); /* write bytes needed for the filepathW (without 0) + 6 */ bytesW = len*sizeof(WCHAR) + 6; res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL); if (FAILED(res)) return res; /* try again, without the extra 6 */ bytesW -= 6; res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL); if (FAILED(res)) return res; /* write a WORD 3 */ res=IStream_Write(pStm,&THREE,sizeof(WORD),NULL); if (FAILED(res)) return res; /* write W string (no 0) */ return IStream_Write(pStm,filePathW,bytesW,NULL); } /****************************************************************************** * FileMoniker_GetSizeMax */ static HRESULT WINAPI FileMonikerImpl_GetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize) { FileMonikerImpl *This = impl_from_IMoniker(iface); TRACE("(%p,%p)\n",iface,pcbSize); if (!pcbSize) return E_POINTER; /* We could calculate exactly (see ...::Save()) but instead * we'll make a quick over-estimate, like Windows (NT4, XP) does. */ pcbSize->u.LowPart = 0x38 + 4 * lstrlenW(This->filePathName); pcbSize->u.HighPart = 0; return S_OK; } /****************************************************************************** * FileMoniker_BindToObject */ static HRESULT WINAPI FileMonikerImpl_BindToObject(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riid, VOID** ppvResult) { FileMonikerImpl *This = impl_from_IMoniker(iface); HRESULT res=E_FAIL; CLSID clsID; IUnknown* pObj=0; IRunningObjectTable *prot=0; IPersistFile *ppf=0; IClassFactory *pcf=0; IClassActivator *pca=0; *ppvResult=0; TRACE("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvResult); if(pmkToLeft==NULL){ res=IBindCtx_GetRunningObjectTable(pbc,&prot); if (SUCCEEDED(res)){ /* if the requested class was loaded before ! we don't need to reload it */ res = IRunningObjectTable_GetObject(prot,iface,&pObj); if (res != S_OK){ /* first activation of this class */ res=GetClassFile(This->filePathName,&clsID); if (SUCCEEDED(res)){ res=CoCreateInstance(&clsID,NULL,CLSCTX_SERVER,&IID_IPersistFile,(void**)&ppf); if (SUCCEEDED(res)){ res=IPersistFile_Load(ppf,This->filePathName,STGM_READ); if (SUCCEEDED(res)){ pObj=(IUnknown*)ppf; IUnknown_AddRef(pObj); } } } } } } else{ res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassFactory,(void**)&pcf); if (res==E_NOINTERFACE){ res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassActivator,(void**)&pca); if (res==E_NOINTERFACE) return MK_E_INTERMEDIATEINTERFACENOTSUPPORTED; } if (pcf!=NULL){ IClassFactory_CreateInstance(pcf,NULL,&IID_IPersistFile,(void**)&ppf); res=IPersistFile_Load(ppf,This->filePathName,STGM_READ); if (SUCCEEDED(res)){ pObj=(IUnknown*)ppf; IUnknown_AddRef(pObj); } } if (pca!=NULL){ FIXME("()\n"); /*res=GetClassFile(This->filePathName,&clsID); if (SUCCEEDED(res)){ res=IClassActivator_GetClassObject(pca,&clsID,CLSCTX_ALL,0,&IID_IPersistFile,(void**)&ppf); if (SUCCEEDED(res)){ pObj=(IUnknown*)ppf; IUnknown_AddRef(pObj); } }*/ } } if (pObj!=NULL){ /* get the requested interface from the loaded class */ res= IUnknown_QueryInterface(pObj,riid,ppvResult); IBindCtx_RegisterObjectBound(pbc,*ppvResult); IUnknown_Release(pObj); } if (prot!=NULL) IRunningObjectTable_Release(prot); if (ppf!=NULL) IPersistFile_Release(ppf); if (pca!=NULL) IClassActivator_Release(pca); if (pcf!=NULL) IClassFactory_Release(pcf); return res; } /****************************************************************************** * FileMoniker_BindToStorage */ static HRESULT WINAPI FileMonikerImpl_BindToStorage(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riid, void **object) { FileMonikerImpl *moniker = impl_from_IMoniker(iface); BIND_OPTS bind_opts; HRESULT hr; TRACE("(%p,%p,%p,%s,%p)\n", iface, pbc, pmkToLeft, debugstr_guid(riid), object); if (!pbc) return E_INVALIDARG; bind_opts.cbStruct = sizeof(bind_opts); hr = IBindCtx_GetBindOptions(pbc, &bind_opts); if (FAILED(hr)) return hr; if (!pmkToLeft) { if (IsEqualIID(&IID_IStorage, riid)) { return StgOpenStorage(moniker->filePathName, NULL, bind_opts.grfMode, NULL, 0, (IStorage **)object); } else if ((IsEqualIID(&IID_IStream, riid)) || (IsEqualIID(&IID_ILockBytes, riid))) return E_FAIL; else return E_NOINTERFACE; } FIXME("(%p,%p,%p,%s,%p)\n", iface, pbc, pmkToLeft, debugstr_guid(riid), object); return E_NOTIMPL; } /****************************************************************************** * FileMoniker_Reduce ******************************************************************************/ static HRESULT WINAPI FileMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar, IMoniker** ppmkToLeft, IMoniker** ppmkReduced) { TRACE("(%p,%p,%d,%p,%p)\n",iface,pbc,dwReduceHowFar,ppmkToLeft,ppmkReduced); if (ppmkReduced==NULL) return E_POINTER; IMoniker_AddRef(iface); *ppmkReduced=iface; return MK_S_REDUCED_TO_SELF; } static void free_stringtable(LPOLESTR *stringTable) { int i; for (i=0; stringTable[i]!=NULL; i++) CoTaskMemFree(stringTable[i]); CoTaskMemFree(stringTable); } /****************************************************************************** * FileMoniker_ComposeWith */ static HRESULT WINAPI FileMonikerImpl_ComposeWith(IMoniker* iface, IMoniker* pmkRight, BOOL fOnlyIfNotGeneric, IMoniker** ppmkComposite) { HRESULT res; LPOLESTR str1=0,str2=0,*strDec1=0,*strDec2=0,newStr=0; IBindCtx *bind=0; int i=0,j=0,lastIdx1=0,lastIdx2=0; DWORD mkSys, order; TRACE("(%p,%p,%d,%p)\n",iface,pmkRight,fOnlyIfNotGeneric,ppmkComposite); if (ppmkComposite==NULL) return E_POINTER; if (pmkRight==NULL) return E_INVALIDARG; *ppmkComposite=0; IMoniker_IsSystemMoniker(pmkRight,&mkSys); /* check if we have two FileMonikers to compose or not */ if(mkSys==MKSYS_FILEMONIKER){ CreateBindCtx(0,&bind); IMoniker_GetDisplayName(iface,bind,NULL,&str1); IMoniker_GetDisplayName(pmkRight,bind,NULL,&str2); /* decompose pathnames of the two monikers : (to prepare the path merge operation ) */ lastIdx1=FileMonikerImpl_DecomposePath(str1,&strDec1)-1; lastIdx2=FileMonikerImpl_DecomposePath(str2,&strDec2)-1; if ((lastIdx1 == -1 && lastIdx2 > -1) || (lastIdx1 == 1 && !wcscmp(strDec1[0], L".."))) res = MK_E_SYNTAX; else{ if (!wcscmp(strDec1[lastIdx1], L"\\")) lastIdx1--; /* for each "..\" in the left of str2 remove the right element from str1 */ for (i = 0; lastIdx1 >= 0 && strDec2[i] && !wcscmp(strDec2[i], L".."); i += 2) lastIdx1-=2; /* the length of the composed path string is increased by the sum of the two paths' lengths */ newStr=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(lstrlenW(str1)+lstrlenW(str2)+1)); if (newStr){ /* new path is the concatenation of the rest of str1 and str2 */ for(*newStr=0,j=0;j<=lastIdx1;j++) lstrcatW(newStr,strDec1[j]); if ((!strDec2[i] && lastIdx1 > -1 && lastIdx2 > -1) || wcscmp(strDec2[i], L"\\")) lstrcatW(newStr, L"\\"); for(j=i;j<=lastIdx2;j++) lstrcatW(newStr,strDec2[j]); /* create a new moniker with the new string */ res=CreateFileMoniker(newStr,ppmkComposite); /* free string memory used by this function */ HeapFree(GetProcessHeap(),0,newStr); } else res = E_OUTOFMEMORY; } free_stringtable(strDec1); free_stringtable(strDec2); CoTaskMemFree(str1); CoTaskMemFree(str2); return res; } else if (is_anti_moniker(pmkRight, &order)) { return order > 1 ? create_anti_moniker(order - 1, ppmkComposite) : S_OK; } else if (fOnlyIfNotGeneric){ *ppmkComposite=NULL; return MK_E_NEEDGENERIC; } else return CreateGenericComposite(iface,pmkRight,ppmkComposite); } /****************************************************************************** * FileMoniker_Enum */ static HRESULT WINAPI FileMonikerImpl_Enum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker) { TRACE("(%p,%d,%p)\n",iface,fForward,ppenumMoniker); if (ppenumMoniker == NULL) return E_POINTER; *ppenumMoniker = NULL; return S_OK; } static HRESULT WINAPI FileMonikerImpl_IsEqual(IMoniker *iface, IMoniker *other) { FileMonikerImpl *moniker = impl_from_IMoniker(iface), *other_moniker; TRACE("%p, %p.\n", iface, other); if (!other) return E_INVALIDARG; other_moniker = unsafe_impl_from_IMoniker(other); if (!other_moniker) return S_FALSE; return !wcsicmp(moniker->filePathName, other_moniker->filePathName) ? S_OK : S_FALSE; } /****************************************************************************** * FileMoniker_Hash */ static HRESULT WINAPI FileMonikerImpl_Hash(IMoniker* iface,DWORD* pdwHash) { FileMonikerImpl *This = impl_from_IMoniker(iface); int h = 0,i,skip,len; int off = 0; LPOLESTR val; if (pdwHash==NULL) return E_POINTER; val = This->filePathName; len = lstrlenW(val); if (len < 16) { for (i = len ; i > 0; i--) { h = (h * 37) + val[off++]; } } else { /* only sample some characters */ skip = len / 8; for (i = len ; i > 0; i -= skip, off += skip) { h = (h * 39) + val[off]; } } *pdwHash=h; return S_OK; } /****************************************************************************** * FileMoniker_IsRunning */ static HRESULT WINAPI FileMonikerImpl_IsRunning(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, IMoniker* pmkNewlyRunning) { IRunningObjectTable* rot; HRESULT res; TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pmkNewlyRunning); if ( (pmkNewlyRunning!=NULL) && (IMoniker_IsEqual(pmkNewlyRunning,iface)==S_OK) ) return S_OK; if (pbc==NULL) return E_POINTER; res=IBindCtx_GetRunningObjectTable(pbc,&rot); if (FAILED(res)) return res; res = IRunningObjectTable_IsRunning(rot,iface); IRunningObjectTable_Release(rot); return res; } /****************************************************************************** * FileMoniker_GetTimeOfLastChange ******************************************************************************/ static HRESULT WINAPI FileMonikerImpl_GetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, FILETIME* pFileTime) { FileMonikerImpl *This = impl_from_IMoniker(iface); IRunningObjectTable* rot; HRESULT res; WIN32_FILE_ATTRIBUTE_DATA info; TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pFileTime); if (pFileTime==NULL) return E_POINTER; if (pmkToLeft!=NULL) return E_INVALIDARG; res=IBindCtx_GetRunningObjectTable(pbc,&rot); if (FAILED(res)) return res; res= IRunningObjectTable_GetTimeOfLastChange(rot,iface,pFileTime); if (FAILED(res)){ /* the moniker is not registered */ if (!GetFileAttributesExW(This->filePathName,GetFileExInfoStandard,&info)) return MK_E_NOOBJECT; *pFileTime=info.ftLastWriteTime; } return S_OK; } /****************************************************************************** * FileMoniker_Inverse */ static HRESULT WINAPI FileMonikerImpl_Inverse(IMoniker* iface,IMoniker** ppmk) { TRACE("(%p,%p)\n",iface,ppmk); return CreateAntiMoniker(ppmk); } /****************************************************************************** * FileMoniker_CommonPrefixWith */ static HRESULT WINAPI FileMonikerImpl_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix) { LPOLESTR pathThis = NULL, pathOther = NULL, *stringTable1 = NULL; LPOLESTR *stringTable2 = NULL, commonPath = NULL; IBindCtx *bindctx; DWORD mkSys; ULONG nb1,nb2,i,sameIdx; BOOL machineNameCase = FALSE; HRESULT ret; if (ppmkPrefix==NULL) return E_POINTER; if (pmkOther==NULL) return E_INVALIDARG; *ppmkPrefix=0; /* check if we have the same type of moniker */ IMoniker_IsSystemMoniker(pmkOther,&mkSys); if (mkSys != MKSYS_FILEMONIKER) return MonikerCommonPrefixWith(iface, pmkOther, ppmkPrefix); ret = CreateBindCtx(0, &bindctx); if (FAILED(ret)) return ret; /* create a string based on common part of the two paths */ ret = IMoniker_GetDisplayName(iface, bindctx, NULL, &pathThis); if (FAILED(ret)) goto failed; ret = IMoniker_GetDisplayName(pmkOther, bindctx, NULL, &pathOther); if (FAILED(ret)) goto failed; nb1 = FileMonikerImpl_DecomposePath(pathThis, &stringTable1); if (FAILED(nb1)) { ret = nb1; goto failed; } nb2 = FileMonikerImpl_DecomposePath(pathOther, &stringTable2); if (FAILED(nb2)) { ret = nb2; goto failed; } if (nb1 == 0 || nb2 == 0) { ret = MK_E_NOPREFIX; goto failed; } commonPath = CoTaskMemAlloc(sizeof(WCHAR)*(min(lstrlenW(pathThis),lstrlenW(pathOther))+1)); if (!commonPath) { ret = E_OUTOFMEMORY; goto failed; } *commonPath = 0; for(sameIdx=0; ( (stringTable1[sameIdx]!=NULL) && (stringTable2[sameIdx]!=NULL) && (lstrcmpiW(stringTable1[sameIdx],stringTable2[sameIdx])==0)); sameIdx++); if (sameIdx > 1 && *stringTable1[0]=='\\' && *stringTable2[1]=='\\'){ machineNameCase = TRUE; for(i=2;i 3 || nb2 > 3) ) ret = MK_E_NOPREFIX; else { for (i = 0; i < sameIdx; i++) lstrcatW(commonPath,stringTable1[i]); ret = CreateFileMoniker(commonPath, ppmkPrefix); } failed: IBindCtx_Release(bindctx); CoTaskMemFree(pathThis); CoTaskMemFree(pathOther); CoTaskMemFree(commonPath); if (stringTable1) free_stringtable(stringTable1); if (stringTable2) free_stringtable(stringTable2); return ret; } /****************************************************************************** * DecomposePath (local function) */ int FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable) { LPOLESTR word; int i=0,j,tabIndex=0, ret=0; LPOLESTR *strgtable ; int len=lstrlenW(str); TRACE("%s, %p\n", debugstr_w(str), *stringTable); strgtable = CoTaskMemAlloc((len + 1)*sizeof(*strgtable)); if (strgtable==NULL) return E_OUTOFMEMORY; word = CoTaskMemAlloc((len + 1)*sizeof(WCHAR)); if (word==NULL) { ret = E_OUTOFMEMORY; goto lend; } while(str[i]!=0){ if (str[i] == L'\\') { strgtable[tabIndex]=CoTaskMemAlloc(2*sizeof(WCHAR)); if (strgtable[tabIndex]==NULL) { ret = E_OUTOFMEMORY; goto lend; } lstrcpyW(strgtable[tabIndex++], L"\\"); i++; } else { for (j = 0; str[i] && str[i] != L'\\'; i++, j++) word[j]=str[i]; word[j]=0; strgtable[tabIndex]=CoTaskMemAlloc(sizeof(WCHAR)*(j+1)); if (strgtable[tabIndex]==NULL) { ret = E_OUTOFMEMORY; goto lend; } lstrcpyW(strgtable[tabIndex++],word); } } strgtable[tabIndex]=NULL; *stringTable=strgtable; ret = tabIndex; lend: if (ret < 0) { for (i = 0; i < tabIndex; i++) CoTaskMemFree(strgtable[i]); CoTaskMemFree(strgtable); } CoTaskMemFree(word); return ret; } /****************************************************************************** * FileMoniker_RelativePathTo */ static HRESULT WINAPI FileMonikerImpl_RelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath) { IBindCtx *bind; HRESULT res; LPOLESTR str1=0,str2=0,*tabStr1=0,*tabStr2=0,relPath=0; DWORD len1=0,len2=0,sameIdx=0,j=0; TRACE("(%p,%p,%p)\n",iface,pmOther,ppmkRelPath); if (ppmkRelPath==NULL) return E_POINTER; if (pmOther==NULL) return E_INVALIDARG; res=CreateBindCtx(0,&bind); if (FAILED(res)) return res; res=IMoniker_GetDisplayName(iface,bind,NULL,&str1); if (FAILED(res)) return res; res=IMoniker_GetDisplayName(pmOther,bind,NULL,&str2); if (FAILED(res)) return res; len1=FileMonikerImpl_DecomposePath(str1,&tabStr1); if (FAILED(len1)) return E_OUTOFMEMORY; len2=FileMonikerImpl_DecomposePath(str2,&tabStr2); if (FAILED(len2)) { free_stringtable(tabStr1); return E_OUTOFMEMORY; } /* count the number of similar items from the begin of the two paths */ for(sameIdx=0; ( (tabStr1[sameIdx]!=NULL) && (tabStr2[sameIdx]!=NULL) && (lstrcmpiW(tabStr1[sameIdx],tabStr2[sameIdx])==0)); sameIdx++); /* begin the construction of relativePath */ /* if the two paths have a consecutive similar item from the begin ! the relativePath will be composed */ /* by "..\\" in the begin */ relPath=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(1+lstrlenW(str1)+lstrlenW(str2))); *relPath=0; if (len2>0 && !(len1==1 && len2==1 && sameIdx==0)) for(j=sameIdx;(tabStr1[j] != NULL); j++) if (*tabStr1[j]!='\\') lstrcatW(relPath, L"..\\"); /* add items of the second path (similar items with the first path are not included) to the relativePath */ for(j=sameIdx;tabStr2[j]!=NULL;j++) lstrcatW(relPath,tabStr2[j]); res=CreateFileMoniker(relPath,ppmkRelPath); free_stringtable(tabStr1); free_stringtable(tabStr2); CoTaskMemFree(str1); CoTaskMemFree(str2); HeapFree(GetProcessHeap(),0,relPath); if (len1==0 || len2==0 || (len1==1 && len2==1 && sameIdx==0)) return MK_S_HIM; return res; } /****************************************************************************** * FileMoniker_GetDisplayName */ static HRESULT WINAPI FileMonikerImpl_GetDisplayName(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, LPOLESTR *ppszDisplayName) { FileMonikerImpl *This = impl_from_IMoniker(iface); int len=lstrlenW(This->filePathName); TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,ppszDisplayName); if (ppszDisplayName==NULL) return E_POINTER; if (pmkToLeft!=NULL) return E_INVALIDARG; *ppszDisplayName=CoTaskMemAlloc(sizeof(WCHAR)*(len+1)); if (*ppszDisplayName==NULL) return E_OUTOFMEMORY; lstrcpyW(*ppszDisplayName,This->filePathName); TRACE("-- %s\n", debugstr_w(*ppszDisplayName)); return S_OK; } /****************************************************************************** * FileMoniker_ParseDisplayName */ static HRESULT WINAPI FileMonikerImpl_ParseDisplayName(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut) { FIXME("(%p,%p,%p,%p,%p,%p),stub!\n",iface,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut); return E_NOTIMPL; } /****************************************************************************** * FileMoniker_IsSystemMoniker */ static HRESULT WINAPI FileMonikerImpl_IsSystemMoniker(IMoniker* iface,DWORD* pwdMksys) { TRACE("(%p,%p)\n",iface,pwdMksys); if (!pwdMksys) return E_POINTER; (*pwdMksys)=MKSYS_FILEMONIKER; return S_OK; } /******************************************************************************* * FileMonikerIROTData_QueryInterface */ static HRESULT WINAPI FileMonikerROTDataImpl_QueryInterface(IROTData *iface,REFIID riid,VOID** ppvObject) { FileMonikerImpl *This = impl_from_IROTData(iface); TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppvObject); return FileMonikerImpl_QueryInterface(&This->IMoniker_iface, riid, ppvObject); } /*********************************************************************** * FileMonikerIROTData_AddRef */ static ULONG WINAPI FileMonikerROTDataImpl_AddRef(IROTData *iface) { FileMonikerImpl *This = impl_from_IROTData(iface); TRACE("(%p)\n",This); return IMoniker_AddRef(&This->IMoniker_iface); } /*********************************************************************** * FileMonikerIROTData_Release */ static ULONG WINAPI FileMonikerROTDataImpl_Release(IROTData* iface) { FileMonikerImpl *This = impl_from_IROTData(iface); TRACE("(%p)\n",This); return FileMonikerImpl_Release(&This->IMoniker_iface); } /****************************************************************************** * FileMonikerIROTData_GetComparisonData */ static HRESULT WINAPI FileMonikerROTDataImpl_GetComparisonData(IROTData* iface, BYTE* pbData, ULONG cbMax, ULONG* pcbData) { FileMonikerImpl *This = impl_from_IROTData(iface); int len = lstrlenW(This->filePathName)+1; int i; LPWSTR pszFileName; TRACE("(%p, %u, %p)\n", pbData, cbMax, pcbData); *pcbData = sizeof(CLSID) + len * sizeof(WCHAR); if (cbMax < *pcbData) return E_OUTOFMEMORY; memcpy(pbData, &CLSID_FileMoniker, sizeof(CLSID)); pszFileName = (LPWSTR)(pbData+sizeof(CLSID)); for (i = 0; i < len; i++) pszFileName[i] = towupper(This->filePathName[i]); return S_OK; } /* * Virtual function table for the FileMonikerImpl class which include IPersist, * IPersistStream and IMoniker functions. */ static const IMonikerVtbl VT_FileMonikerImpl = { FileMonikerImpl_QueryInterface, FileMonikerImpl_AddRef, FileMonikerImpl_Release, FileMonikerImpl_GetClassID, FileMonikerImpl_IsDirty, FileMonikerImpl_Load, FileMonikerImpl_Save, FileMonikerImpl_GetSizeMax, FileMonikerImpl_BindToObject, FileMonikerImpl_BindToStorage, FileMonikerImpl_Reduce, FileMonikerImpl_ComposeWith, FileMonikerImpl_Enum, FileMonikerImpl_IsEqual, FileMonikerImpl_Hash, FileMonikerImpl_IsRunning, FileMonikerImpl_GetTimeOfLastChange, FileMonikerImpl_Inverse, FileMonikerImpl_CommonPrefixWith, FileMonikerImpl_RelativePathTo, FileMonikerImpl_GetDisplayName, FileMonikerImpl_ParseDisplayName, FileMonikerImpl_IsSystemMoniker }; /* Virtual function table for the IROTData class. */ static const IROTDataVtbl VT_ROTDataImpl = { FileMonikerROTDataImpl_QueryInterface, FileMonikerROTDataImpl_AddRef, FileMonikerROTDataImpl_Release, FileMonikerROTDataImpl_GetComparisonData }; /****************************************************************************** * FileMoniker_Construct (local function) */ static HRESULT FileMonikerImpl_Construct(FileMonikerImpl* This, LPCOLESTR lpszPathName) { int nb=0,i; int sizeStr=lstrlenW(lpszPathName); LPOLESTR *tabStr=0; BOOL addBkSlash; TRACE("(%p,%s)\n",This,debugstr_w(lpszPathName)); /* Initialize the virtual function table. */ This->IMoniker_iface.lpVtbl = &VT_FileMonikerImpl; This->IROTData_iface.lpVtbl = &VT_ROTDataImpl; This->ref = 0; This->pMarshal = NULL; This->filePathName=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(sizeStr+1)); if (This->filePathName==NULL) return E_OUTOFMEMORY; lstrcpyW(This->filePathName,lpszPathName); nb=FileMonikerImpl_DecomposePath(This->filePathName,&tabStr); if (nb > 0 ){ addBkSlash = TRUE; if (wcscmp(tabStr[0], L"..")) addBkSlash = FALSE; else for(i=0;ifilePathName=HeapReAlloc(GetProcessHeap(),0,This->filePathName,(sizeStr+1)*sizeof(WCHAR)); *This->filePathName=0; for(i=0;tabStr[i]!=NULL;i++) lstrcatW(This->filePathName,tabStr[i]); if (addBkSlash) lstrcatW(This->filePathName, L"\\"); } free_stringtable(tabStr); return S_OK; } /****************************************************************************** * CreateFileMoniker (OLE32.@) ******************************************************************************/ HRESULT WINAPI CreateFileMoniker(LPCOLESTR lpszPathName, IMoniker **ppmk) { FileMonikerImpl* newFileMoniker; HRESULT hr; TRACE("(%s,%p)\n",debugstr_w(lpszPathName),ppmk); if (!ppmk) return E_POINTER; if(!lpszPathName) return MK_E_SYNTAX; *ppmk=NULL; newFileMoniker = HeapAlloc(GetProcessHeap(), 0, sizeof(FileMonikerImpl)); if (!newFileMoniker) return E_OUTOFMEMORY; hr = FileMonikerImpl_Construct(newFileMoniker,lpszPathName); if (SUCCEEDED(hr)) hr = IMoniker_QueryInterface(&newFileMoniker->IMoniker_iface,&IID_IMoniker,(void**)ppmk); else HeapFree(GetProcessHeap(),0,newFileMoniker); return hr; } /* find a character from a set in reverse without the string having to be null-terminated */ static inline WCHAR *memrpbrkW(const WCHAR *ptr, size_t n, const WCHAR *accept) { const WCHAR *end, *ret = NULL; for (end = ptr + n; ptr < end; ptr++) if (wcschr(accept, *ptr)) ret = ptr; return (WCHAR *)ret; } HRESULT FileMoniker_CreateFromDisplayName(LPBC pbc, LPCOLESTR szDisplayName, LPDWORD pchEaten, IMoniker **ppmk) { LPCWSTR end; for (end = szDisplayName + lstrlenW(szDisplayName); end && (end != szDisplayName); end = memrpbrkW(szDisplayName, end - szDisplayName, L":\\/!")) { HRESULT hr; IRunningObjectTable *rot; IMoniker *file_moniker; LPWSTR file_display_name; LPWSTR full_path_name; DWORD full_path_name_len; int len = end - szDisplayName; file_display_name = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); if (!file_display_name) return E_OUTOFMEMORY; memcpy(file_display_name, szDisplayName, len * sizeof(WCHAR)); file_display_name[len] = '\0'; hr = CreateFileMoniker(file_display_name, &file_moniker); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, file_display_name); return hr; } hr = IBindCtx_GetRunningObjectTable(pbc, &rot); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, file_display_name); IMoniker_Release(file_moniker); return hr; } hr = IRunningObjectTable_IsRunning(rot, file_moniker); IRunningObjectTable_Release(rot); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, file_display_name); IMoniker_Release(file_moniker); return hr; } if (hr == S_OK) { TRACE("found running file moniker for %s\n", debugstr_w(file_display_name)); *pchEaten = len; *ppmk = file_moniker; HeapFree(GetProcessHeap(), 0, file_display_name); return S_OK; } full_path_name_len = GetFullPathNameW(file_display_name, 0, NULL, NULL); if (!full_path_name_len) { HeapFree(GetProcessHeap(), 0, file_display_name); IMoniker_Release(file_moniker); return MK_E_SYNTAX; } full_path_name = HeapAlloc(GetProcessHeap(), 0, full_path_name_len * sizeof(WCHAR)); if (!full_path_name) { HeapFree(GetProcessHeap(), 0, file_display_name); IMoniker_Release(file_moniker); return E_OUTOFMEMORY; } GetFullPathNameW(file_display_name, full_path_name_len, full_path_name, NULL); if (GetFileAttributesW(full_path_name) == INVALID_FILE_ATTRIBUTES) TRACE("couldn't open file %s\n", debugstr_w(full_path_name)); else { TRACE("got file moniker for %s\n", debugstr_w(szDisplayName)); *pchEaten = len; *ppmk = file_moniker; HeapFree(GetProcessHeap(), 0, file_display_name); HeapFree(GetProcessHeap(), 0, full_path_name); return S_OK; } HeapFree(GetProcessHeap(), 0, file_display_name); HeapFree(GetProcessHeap(), 0, full_path_name); IMoniker_Release(file_moniker); } return MK_E_CANTOPENFILE; } HRESULT WINAPI FileMoniker_CreateInstance(IClassFactory *iface, IUnknown *pUnk, REFIID riid, void **ppv) { FileMonikerImpl* newFileMoniker; HRESULT hr; TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv); *ppv = NULL; if (pUnk) return CLASS_E_NOAGGREGATION; newFileMoniker = HeapAlloc(GetProcessHeap(), 0, sizeof(FileMonikerImpl)); if (!newFileMoniker) return E_OUTOFMEMORY; hr = FileMonikerImpl_Construct(newFileMoniker, L""); if (SUCCEEDED(hr)) hr = IMoniker_QueryInterface(&newFileMoniker->IMoniker_iface, riid, ppv); if (FAILED(hr)) HeapFree(GetProcessHeap(),0,newFileMoniker); return hr; }