xref: /reactos/dll/win32/ole32/filemoniker.c (revision 234f89c0)
1 /*
2  * FileMonikers implementation
3  *
4  * Copyright 1999  Noomen Hamza
5  * Copyright 2007  Robert Shearman
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <string.h>
25 
26 #define COBJMACROS
27 #define NONAMELESSUNION
28 
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winerror.h"
32 #include "winnls.h"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
35 #include "objbase.h"
36 #include "moniker.h"
37 
38 #include "compobj_private.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(ole);
41 
42 /* filemoniker data structure */
43 typedef struct FileMonikerImpl{
44     IMoniker IMoniker_iface;
45     IROTData IROTData_iface;
46     LONG ref;
47     LPOLESTR filePathName; /* path string identified by this filemoniker */
48     IUnknown *pMarshal; /* custom marshaler */
49 } FileMonikerImpl;
50 
51 static inline FileMonikerImpl *impl_from_IMoniker(IMoniker *iface)
52 {
53     return CONTAINING_RECORD(iface, FileMonikerImpl, IMoniker_iface);
54 }
55 
56 static inline FileMonikerImpl *impl_from_IROTData(IROTData *iface)
57 {
58     return CONTAINING_RECORD(iface, FileMonikerImpl, IROTData_iface);
59 }
60 
61 /* Local function used by filemoniker implementation */
62 static HRESULT FileMonikerImpl_Construct(FileMonikerImpl* iface, LPCOLESTR lpszPathName);
63 static HRESULT FileMonikerImpl_Destroy(FileMonikerImpl* iface);
64 
65 /*******************************************************************************
66  *        FileMoniker_QueryInterface
67  */
68 static HRESULT WINAPI
69 FileMonikerImpl_QueryInterface(IMoniker* iface,REFIID riid,void** ppvObject)
70 {
71     FileMonikerImpl *This = impl_from_IMoniker(iface);
72 
73     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppvObject);
74 
75     /* Perform a sanity check on the parameters.*/
76     if ( ppvObject==0 )
77 	return E_INVALIDARG;
78 
79     /* Initialize the return parameter */
80     *ppvObject = 0;
81 
82     /* Compare the riid with the interface IDs implemented by this object.*/
83     if (IsEqualIID(&IID_IUnknown, riid)      ||
84         IsEqualIID(&IID_IPersist, riid)      ||
85         IsEqualIID(&IID_IPersistStream,riid) ||
86         IsEqualIID(&IID_IMoniker, riid)
87        )
88         *ppvObject = iface;
89 
90     else if (IsEqualIID(&IID_IROTData, riid))
91         *ppvObject = &This->IROTData_iface;
92     else if (IsEqualIID(&IID_IMarshal, riid))
93     {
94         HRESULT hr = S_OK;
95         if (!This->pMarshal)
96             hr = MonikerMarshal_Create(iface, &This->pMarshal);
97         if (hr != S_OK)
98             return hr;
99         return IUnknown_QueryInterface(This->pMarshal, riid, ppvObject);
100     }
101 
102     /* Check that we obtained an interface.*/
103     if ((*ppvObject)==0)
104         return E_NOINTERFACE;
105 
106     /* Query Interface always increases the reference count by one when it is successful */
107     IMoniker_AddRef(iface);
108 
109     return S_OK;
110 }
111 
112 /******************************************************************************
113  *        FileMoniker_AddRef
114  */
115 static ULONG WINAPI
116 FileMonikerImpl_AddRef(IMoniker* iface)
117 {
118     FileMonikerImpl *This = impl_from_IMoniker(iface);
119 
120     TRACE("(%p)\n",iface);
121 
122     return InterlockedIncrement(&This->ref);
123 }
124 
125 /******************************************************************************
126  *        FileMoniker_Release
127  */
128 static ULONG WINAPI
129 FileMonikerImpl_Release(IMoniker* iface)
130 {
131     FileMonikerImpl *This = impl_from_IMoniker(iface);
132     ULONG ref;
133 
134     TRACE("(%p)\n",iface);
135 
136     ref = InterlockedDecrement(&This->ref);
137 
138     /* destroy the object if there are no more references to it */
139     if (ref == 0) FileMonikerImpl_Destroy(This);
140 
141     return ref;
142 }
143 
144 /******************************************************************************
145  *        FileMoniker_GetClassID
146  */
147 static HRESULT WINAPI
148 FileMonikerImpl_GetClassID(IMoniker* iface, CLSID *pClassID)
149 {
150     TRACE("(%p,%p)\n",iface,pClassID);
151 
152     if (pClassID==NULL)
153         return E_POINTER;
154 
155     *pClassID = CLSID_FileMoniker;
156 
157     return S_OK;
158 }
159 
160 /******************************************************************************
161  *        FileMoniker_IsDirty
162  *
163  * Note that the OLE-provided implementations of the IPersistStream::IsDirty
164  * method in the OLE-provided moniker interfaces always return S_FALSE because
165  * their internal state never changes.
166  */
167 static HRESULT WINAPI
168 FileMonikerImpl_IsDirty(IMoniker* iface)
169 {
170 
171     TRACE("(%p)\n",iface);
172 
173     return S_FALSE;
174 }
175 
176 /******************************************************************************
177  *        FileMoniker_Load
178  *
179  * this function locates and reads from the stream the filePath string
180  * written by FileMonikerImpl_Save
181  */
182 static HRESULT WINAPI
183 FileMonikerImpl_Load(IMoniker* iface, IStream* pStm)
184 {
185     FileMonikerImpl *This = impl_from_IMoniker(iface);
186     HRESULT res;
187     CHAR* filePathA = NULL;
188     WCHAR* filePathW = NULL;
189     ULONG bread;
190     WORD  wbuffer;
191     DWORD dwbuffer, bytesA, bytesW, len;
192     int i;
193 
194 
195     TRACE("(%p,%p)\n",iface,pStm);
196 
197     /* first WORD */
198     res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
199     if (bread!=sizeof(WORD))
200     {
201         WARN("Couldn't read 0 word\n");
202         goto fail;
203     }
204 
205     /* read filePath string length (plus one) */
206     res=IStream_Read(pStm,&bytesA,sizeof(DWORD),&bread);
207     if (bread != sizeof(DWORD))
208     {
209         WARN("Couldn't read file string length\n");
210         goto fail;
211     }
212 
213     /* read filePath string */
214     filePathA=HeapAlloc(GetProcessHeap(),0,bytesA);
215     if (!filePathA)
216     {
217         res = E_OUTOFMEMORY;
218         goto fail;
219     }
220 
221     res=IStream_Read(pStm,filePathA,bytesA,&bread);
222     if (bread != bytesA)
223     {
224         WARN("Couldn't read file path string\n");
225         goto fail;
226     }
227 
228     /* read the unknown value */
229     IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
230     if (bread != sizeof(WORD))
231     {
232         WARN("Couldn't read unknown value\n");
233         goto fail;
234     }
235 
236     /* read the DEAD constant */
237     IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
238     if (bread != sizeof(WORD))
239     {
240         WARN("Couldn't read DEAD constant\n");
241         goto fail;
242     }
243 
244     for(i=0;i<5;i++)
245     {
246         res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
247         if (bread!=sizeof(DWORD))
248         {
249             WARN("Couldn't read 0 padding\n");
250             goto fail;
251         }
252     }
253 
254     res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
255     if (bread!=sizeof(DWORD))
256         goto fail;
257 
258     if (!dwbuffer) /* No W-string */
259     {
260         bytesA--;
261         len=MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, bytesA, NULL, 0);
262         if (!len)
263             goto fail;
264 
265         filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
266         if (!filePathW)
267         {
268             res = E_OUTOFMEMORY;
269             goto fail;
270         }
271         MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, -1, filePathW, len+1);
272         goto succeed;
273     }
274 
275     if (dwbuffer < 6)
276         goto fail;
277 
278     bytesW=dwbuffer - 6;
279 
280     res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
281     if (bread!=sizeof(DWORD) || dwbuffer!=bytesW)
282         goto fail;
283 
284     res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
285     if (bread!=sizeof(WORD) || wbuffer!=0x3)
286         goto fail;
287 
288     len=bytesW/sizeof(WCHAR);
289     filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
290     if(!filePathW)
291     {
292          res = E_OUTOFMEMORY;
293          goto fail;
294     }
295     res=IStream_Read(pStm,filePathW,bytesW,&bread);
296     if (bread!=bytesW)
297          goto fail;
298 
299     filePathW[len]=0;
300 
301  succeed:
302     HeapFree(GetProcessHeap(),0,filePathA);
303     HeapFree(GetProcessHeap(),0,This->filePathName);
304     This->filePathName=filePathW;
305 
306     return S_OK;
307 
308  fail:
309     HeapFree(GetProcessHeap(), 0, filePathA);
310     HeapFree(GetProcessHeap(), 0, filePathW);
311 
312     if (SUCCEEDED(res))
313          res = E_FAIL;
314     return res;
315 }
316 
317 /******************************************************************************
318  *        FileMoniker_Save
319  *
320  * This function saves data of this object. In the beginning I thought
321  * that I have just to write the filePath string on Stream. But, when I
322  * tested this function with windows program samples, I noticed that it
323  * was not the case. This implementation is based on XP SP2. Other versions
324  * of Windows have minor variations.
325  *
326  * Data which must be written on stream is:
327  * 1) WORD constant: zero (not validated by Windows)
328  * 2) length of the path string ("\0" included)
329  * 3) path string type A
330  * 4) Unknown WORD value: Frequently 0xFFFF, but not always. If set very large,
331  *     Windows returns E_OUTOFMEMORY
332  * 5) WORD Constant: 0xDEAD (not validated by Windows)
333  * 6) five DWORD constant: zero (not validated by Windows)
334  * 7) If we're only writing the multibyte version,
335  *     write a zero DWORD and finish.
336  *
337  * 8) DWORD: double-length of the path string type W ("\0" not
338  *    included)
339  * 9) WORD constant: 0x3
340  * 10) filePath unicode string.
341  *
342  */
343 static HRESULT WINAPI
344 FileMonikerImpl_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty)
345 {
346     FileMonikerImpl *This = impl_from_IMoniker(iface);
347     HRESULT res;
348     LPOLESTR filePathW=This->filePathName;
349     CHAR*    filePathA;
350     DWORD bytesA, bytesW, len;
351 
352     static const WORD FFFF = 0xFFFF; /* Constants */
353     static const WORD DEAD = 0xDEAD;
354     static const DWORD ZERO     = 0;
355     static const WORD  THREE    = 0x3;
356 
357     int i;
358     BOOL bUsedDefault, bWriteWide;
359 
360     TRACE("(%p,%p,%d)\n",iface,pStm,fClearDirty);
361 
362     if (pStm==NULL)
363         return E_POINTER;
364 
365     /* write a 0 WORD */
366     res=IStream_Write(pStm,&ZERO,sizeof(WORD),NULL);
367     if (FAILED(res)) return res;
368 
369     /* write length of filePath string ( 0 included )*/
370     bytesA = WideCharToMultiByte( CP_ACP, 0, filePathW, -1, NULL, 0, NULL, NULL );
371     res=IStream_Write(pStm,&bytesA,sizeof(DWORD),NULL);
372     if (FAILED(res)) return res;
373 
374     /* write A string (with '\0') */
375     filePathA=HeapAlloc(GetProcessHeap(),0,bytesA);
376     if (!filePathA)
377         return E_OUTOFMEMORY;
378     WideCharToMultiByte( CP_ACP, 0, filePathW, -1, filePathA, bytesA, NULL, &bUsedDefault);
379     res=IStream_Write(pStm,filePathA,bytesA,NULL);
380     HeapFree(GetProcessHeap(),0,filePathA);
381     if (FAILED(res)) return res;
382 
383     /* write a WORD 0xFFFF */
384     res=IStream_Write(pStm,&FFFF,sizeof(WORD),NULL);
385     if (FAILED(res)) return res;
386 
387     /* write a WORD 0xDEAD */
388     res=IStream_Write(pStm,&DEAD,sizeof(WORD),NULL);
389     if (FAILED(res)) return res;
390 
391     /* write 5 zero DWORDs */
392     for(i=0;i<5;i++)
393     {
394         res=IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL);
395         if (FAILED(res)) return res;
396     }
397 
398     /* Write the wide version if:
399      *    + couldn't convert to CP_ACP,
400      * or + it's a directory,
401      * or + there's a character > 0xFF
402      */
403     len = lstrlenW(filePathW);
404     bWriteWide = (bUsedDefault || (len > 0 && filePathW[len-1]=='\\' ));
405     if (!bWriteWide)
406     {
407         WCHAR* pch;
408         for(pch=filePathW;*pch;++pch)
409         {
410             if (*pch > 0xFF)
411             {
412                 bWriteWide = TRUE;
413                 break;
414             }
415         }
416     }
417 
418     if (!bWriteWide)
419         return IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL);
420 
421     /* write bytes needed for the filepathW (without 0) + 6 */
422     bytesW = len*sizeof(WCHAR) + 6;
423     res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL);
424     if (FAILED(res)) return res;
425 
426     /* try again, without the extra 6 */
427     bytesW -= 6;
428     res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL);
429     if (FAILED(res)) return res;
430 
431     /* write a WORD 3 */
432     res=IStream_Write(pStm,&THREE,sizeof(WORD),NULL);
433     if (FAILED(res)) return res;
434 
435     /* write W string (no 0) */
436     return IStream_Write(pStm,filePathW,bytesW,NULL);
437 }
438 
439 /******************************************************************************
440  *        FileMoniker_GetSizeMax
441  */
442 static HRESULT WINAPI
443 FileMonikerImpl_GetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize)
444 {
445     FileMonikerImpl *This = impl_from_IMoniker(iface);
446 
447     TRACE("(%p,%p)\n",iface,pcbSize);
448 
449     if (!pcbSize)
450         return E_POINTER;
451 
452     /* We could calculate exactly (see ...::Save()) but instead
453      * we'll make a quick over-estimate, like Windows (NT4, XP) does.
454      */
455     pcbSize->u.LowPart  = 0x38 + 4 * lstrlenW(This->filePathName);
456     pcbSize->u.HighPart = 0;
457 
458     return S_OK;
459 }
460 
461 /******************************************************************************
462  *        FileMoniker_Destroy (local function)
463  *******************************************************************************/
464 HRESULT FileMonikerImpl_Destroy(FileMonikerImpl* This)
465 {
466     TRACE("(%p)\n",This);
467 
468     if (This->pMarshal) IUnknown_Release(This->pMarshal);
469     HeapFree(GetProcessHeap(),0,This->filePathName);
470     HeapFree(GetProcessHeap(),0,This);
471 
472     return S_OK;
473 }
474 
475 /******************************************************************************
476  *                  FileMoniker_BindToObject
477  */
478 static HRESULT WINAPI
479 FileMonikerImpl_BindToObject(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
480                              REFIID riid, VOID** ppvResult)
481 {
482     FileMonikerImpl *This = impl_from_IMoniker(iface);
483     HRESULT   res=E_FAIL;
484     CLSID     clsID;
485     IUnknown* pObj=0;
486     IRunningObjectTable *prot=0;
487     IPersistFile  *ppf=0;
488     IClassFactory *pcf=0;
489     IClassActivator *pca=0;
490 
491     *ppvResult=0;
492 
493     TRACE("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvResult);
494 
495     if(pmkToLeft==NULL){
496 
497         res=IBindCtx_GetRunningObjectTable(pbc,&prot);
498 
499         if (SUCCEEDED(res)){
500             /* if the requested class was loaded before ! we don't need to reload it */
501             res = IRunningObjectTable_GetObject(prot,iface,&pObj);
502 
503             if (res != S_OK){
504                 /* first activation of this class */
505                 res=GetClassFile(This->filePathName,&clsID);
506                 if (SUCCEEDED(res)){
507 
508                     res=CoCreateInstance(&clsID,NULL,CLSCTX_SERVER,&IID_IPersistFile,(void**)&ppf);
509                     if (SUCCEEDED(res)){
510 
511                         res=IPersistFile_Load(ppf,This->filePathName,STGM_READ);
512                         if (SUCCEEDED(res)){
513 
514                             pObj=(IUnknown*)ppf;
515                             IUnknown_AddRef(pObj);
516                         }
517                     }
518                 }
519             }
520         }
521     }
522     else{
523         res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassFactory,(void**)&pcf);
524 
525         if (res==E_NOINTERFACE){
526 
527             res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassActivator,(void**)&pca);
528 
529             if (res==E_NOINTERFACE)
530                 return MK_E_INTERMEDIATEINTERFACENOTSUPPORTED;
531         }
532         if (pcf!=NULL){
533 
534             IClassFactory_CreateInstance(pcf,NULL,&IID_IPersistFile,(void**)&ppf);
535 
536             res=IPersistFile_Load(ppf,This->filePathName,STGM_READ);
537 
538             if (SUCCEEDED(res)){
539 
540                 pObj=(IUnknown*)ppf;
541                 IUnknown_AddRef(pObj);
542             }
543         }
544         if (pca!=NULL){
545 
546             FIXME("()\n");
547 
548             /*res=GetClassFile(This->filePathName,&clsID);
549 
550             if (SUCCEEDED(res)){
551 
552                 res=IClassActivator_GetClassObject(pca,&clsID,CLSCTX_ALL,0,&IID_IPersistFile,(void**)&ppf);
553 
554                 if (SUCCEEDED(res)){
555 
556                     pObj=(IUnknown*)ppf;
557                     IUnknown_AddRef(pObj);
558                 }
559             }*/
560         }
561     }
562 
563     if (pObj!=NULL){
564         /* get the requested interface from the loaded class */
565         res= IUnknown_QueryInterface(pObj,riid,ppvResult);
566 
567         IBindCtx_RegisterObjectBound(pbc,*ppvResult);
568 
569         IUnknown_Release(pObj);
570     }
571 
572     if (prot!=NULL)
573         IRunningObjectTable_Release(prot);
574 
575     if (ppf!=NULL)
576         IPersistFile_Release(ppf);
577 
578     if (pca!=NULL)
579         IClassActivator_Release(pca);
580 
581     if (pcf!=NULL)
582         IClassFactory_Release(pcf);
583 
584     return res;
585 }
586 
587 /******************************************************************************
588  *        FileMoniker_BindToStorage
589  */
590 static HRESULT WINAPI
591 FileMonikerImpl_BindToStorage(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
592                               REFIID riid, VOID** ppvObject)
593 {
594     LPOLESTR filePath=0;
595     IStorage *pstg=0;
596     HRESULT res;
597 
598     TRACE("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvObject);
599 
600     if (pmkToLeft==NULL){
601 
602         if (IsEqualIID(&IID_IStorage, riid)){
603 
604             /* get the file name */
605             IMoniker_GetDisplayName(iface,pbc,pmkToLeft,&filePath);
606 
607             res=StgOpenStorage(filePath,NULL,STGM_READWRITE|STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
608 
609             if (SUCCEEDED(res))
610                 *ppvObject=pstg;
611 
612             CoTaskMemFree(filePath);
613         }
614         else
615             if ( (IsEqualIID(&IID_IStream, riid)) || (IsEqualIID(&IID_ILockBytes, riid)) )
616                 return E_FAIL;
617             else
618                 return E_NOINTERFACE;
619     }
620     else {
621 
622         FIXME("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvObject);
623 
624         return E_NOTIMPL;
625     }
626     return res;
627 }
628 
629 /******************************************************************************
630  *        FileMoniker_Reduce
631  ******************************************************************************/
632 static HRESULT WINAPI
633 FileMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar,
634                        IMoniker** ppmkToLeft, IMoniker** ppmkReduced)
635 {
636     TRACE("(%p,%p,%d,%p,%p)\n",iface,pbc,dwReduceHowFar,ppmkToLeft,ppmkReduced);
637 
638     if (ppmkReduced==NULL)
639         return E_POINTER;
640 
641     IMoniker_AddRef(iface);
642 
643     *ppmkReduced=iface;
644 
645     return MK_S_REDUCED_TO_SELF;
646 }
647 
648 static void free_stringtable(LPOLESTR *stringTable)
649 {
650     int i;
651 
652     for (i=0; stringTable[i]!=NULL; i++)
653         CoTaskMemFree(stringTable[i]);
654     CoTaskMemFree(stringTable);
655 }
656 
657 /******************************************************************************
658  *        FileMoniker_ComposeWith
659  */
660 static HRESULT WINAPI
661 FileMonikerImpl_ComposeWith(IMoniker* iface, IMoniker* pmkRight,
662                             BOOL fOnlyIfNotGeneric, IMoniker** ppmkComposite)
663 {
664     HRESULT res;
665     LPOLESTR str1=0,str2=0,*strDec1=0,*strDec2=0,newStr=0;
666     static const WCHAR twoPoint[]={'.','.',0};
667     static const WCHAR bkSlash[]={'\\',0};
668     IBindCtx *bind=0;
669     int i=0,j=0,lastIdx1=0,lastIdx2=0;
670     DWORD mkSys;
671 
672     TRACE("(%p,%p,%d,%p)\n",iface,pmkRight,fOnlyIfNotGeneric,ppmkComposite);
673 
674     if (ppmkComposite==NULL)
675         return E_POINTER;
676 
677     if (pmkRight==NULL)
678 	return E_INVALIDARG;
679 
680     *ppmkComposite=0;
681 
682     IMoniker_IsSystemMoniker(pmkRight,&mkSys);
683 
684     /* check if we have two FileMonikers to compose or not */
685     if(mkSys==MKSYS_FILEMONIKER){
686 
687         CreateBindCtx(0,&bind);
688 
689         IMoniker_GetDisplayName(iface,bind,NULL,&str1);
690         IMoniker_GetDisplayName(pmkRight,bind,NULL,&str2);
691 
692         /* decompose pathnames of the two monikers : (to prepare the path merge operation ) */
693         lastIdx1=FileMonikerImpl_DecomposePath(str1,&strDec1)-1;
694         lastIdx2=FileMonikerImpl_DecomposePath(str2,&strDec2)-1;
695 
696         if ((lastIdx1==-1 && lastIdx2>-1)||(lastIdx1==1 && lstrcmpW(strDec1[0],twoPoint)==0))
697             res = MK_E_SYNTAX;
698         else{
699             if(lstrcmpW(strDec1[lastIdx1],bkSlash)==0)
700                 lastIdx1--;
701 
702             /* for each "..\" in the left of str2 remove the right element from str1 */
703             for(i=0; ( (lastIdx1>=0) && (strDec2[i]!=NULL) && (lstrcmpW(strDec2[i],twoPoint)==0) ); i+=2){
704 
705                 lastIdx1-=2;
706             }
707 
708             /* the length of the composed path string is increased by the sum of the two paths' lengths */
709             newStr=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(lstrlenW(str1)+lstrlenW(str2)+1));
710 
711             if (newStr){
712                 /* new path is the concatenation of the rest of str1 and str2 */
713                 for(*newStr=0,j=0;j<=lastIdx1;j++)
714                     strcatW(newStr,strDec1[j]);
715 
716                 if ((strDec2[i]==NULL && lastIdx1>-1 && lastIdx2>-1) || lstrcmpW(strDec2[i],bkSlash)!=0)
717                     strcatW(newStr,bkSlash);
718 
719                 for(j=i;j<=lastIdx2;j++)
720                     strcatW(newStr,strDec2[j]);
721 
722                 /* create a new moniker with the new string */
723                 res=CreateFileMoniker(newStr,ppmkComposite);
724 
725                 /* free string memory used by this function */
726                 HeapFree(GetProcessHeap(),0,newStr);
727             }
728             else res = E_OUTOFMEMORY;
729         }
730 
731         free_stringtable(strDec1);
732         free_stringtable(strDec2);
733 
734         CoTaskMemFree(str1);
735         CoTaskMemFree(str2);
736 
737         return res;
738     }
739     else if(mkSys==MKSYS_ANTIMONIKER){
740 
741         *ppmkComposite=NULL;
742         return S_OK;
743     }
744     else if (fOnlyIfNotGeneric){
745 
746         *ppmkComposite=NULL;
747         return MK_E_NEEDGENERIC;
748     }
749     else
750 
751         return CreateGenericComposite(iface,pmkRight,ppmkComposite);
752 }
753 
754 /******************************************************************************
755  *        FileMoniker_Enum
756  */
757 static HRESULT WINAPI
758 FileMonikerImpl_Enum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker)
759 {
760     TRACE("(%p,%d,%p)\n",iface,fForward,ppenumMoniker);
761 
762     if (ppenumMoniker == NULL)
763         return E_POINTER;
764 
765     *ppenumMoniker = NULL;
766 
767     return S_OK;
768 }
769 
770 /******************************************************************************
771  *        FileMoniker_IsEqual
772  */
773 static HRESULT WINAPI
774 FileMonikerImpl_IsEqual(IMoniker* iface,IMoniker* pmkOtherMoniker)
775 {
776     FileMonikerImpl *This = impl_from_IMoniker(iface);
777     CLSID clsid;
778     LPOLESTR filePath;
779     IBindCtx* bind;
780     HRESULT res;
781 
782     TRACE("(%p,%p)\n",iface,pmkOtherMoniker);
783 
784     if (pmkOtherMoniker==NULL)
785         return S_FALSE;
786 
787     IMoniker_GetClassID(pmkOtherMoniker,&clsid);
788 
789     if (!IsEqualCLSID(&clsid,&CLSID_FileMoniker))
790         return S_FALSE;
791 
792     res = CreateBindCtx(0,&bind);
793     if (FAILED(res)) return res;
794 
795     res = S_FALSE;
796     if (SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker,bind,NULL,&filePath))) {
797 	if (!lstrcmpiW(filePath, This->filePathName))
798             res = S_OK;
799 	CoTaskMemFree(filePath);
800     }
801 
802     IBindCtx_Release(bind);
803     return res;
804 }
805 
806 /******************************************************************************
807  *        FileMoniker_Hash
808  */
809 static HRESULT WINAPI
810 FileMonikerImpl_Hash(IMoniker* iface,DWORD* pdwHash)
811 {
812     FileMonikerImpl *This = impl_from_IMoniker(iface);
813     int  h = 0,i,skip,len;
814     int  off = 0;
815     LPOLESTR val;
816 
817     if (pdwHash==NULL)
818         return E_POINTER;
819 
820     val =  This->filePathName;
821     len = lstrlenW(val);
822 
823     if (len < 16) {
824         for (i = len ; i > 0; i--) {
825             h = (h * 37) + val[off++];
826  	}
827     } else {
828         /* only sample some characters */
829  	skip = len / 8;
830  	for (i = len ; i > 0; i -= skip, off += skip) {
831             h = (h * 39) + val[off];
832  	}
833     }
834 
835     *pdwHash=h;
836 
837     return S_OK;
838 }
839 
840 /******************************************************************************
841  *        FileMoniker_IsRunning
842  */
843 static HRESULT WINAPI
844 FileMonikerImpl_IsRunning(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
845                           IMoniker* pmkNewlyRunning)
846 {
847     IRunningObjectTable* rot;
848     HRESULT res;
849 
850     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pmkNewlyRunning);
851 
852     if ( (pmkNewlyRunning!=NULL) && (IMoniker_IsEqual(pmkNewlyRunning,iface)==S_OK) )
853         return S_OK;
854 
855     if (pbc==NULL)
856         return E_POINTER;
857 
858     res=IBindCtx_GetRunningObjectTable(pbc,&rot);
859 
860     if (FAILED(res))
861         return res;
862 
863     res = IRunningObjectTable_IsRunning(rot,iface);
864 
865     IRunningObjectTable_Release(rot);
866 
867     return res;
868 }
869 
870 /******************************************************************************
871  *        FileMoniker_GetTimeOfLastChange
872  ******************************************************************************/
873 static HRESULT WINAPI
874 FileMonikerImpl_GetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc,
875                                     IMoniker* pmkToLeft, FILETIME* pFileTime)
876 {
877     FileMonikerImpl *This = impl_from_IMoniker(iface);
878     IRunningObjectTable* rot;
879     HRESULT res;
880     WIN32_FILE_ATTRIBUTE_DATA info;
881 
882     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pFileTime);
883 
884     if (pFileTime==NULL)
885         return E_POINTER;
886 
887     if (pmkToLeft!=NULL)
888         return E_INVALIDARG;
889 
890     res=IBindCtx_GetRunningObjectTable(pbc,&rot);
891 
892     if (FAILED(res))
893         return res;
894 
895     res= IRunningObjectTable_GetTimeOfLastChange(rot,iface,pFileTime);
896 
897     if (FAILED(res)){ /* the moniker is not registered */
898 
899         if (!GetFileAttributesExW(This->filePathName,GetFileExInfoStandard,&info))
900             return MK_E_NOOBJECT;
901 
902         *pFileTime=info.ftLastWriteTime;
903     }
904 
905     return S_OK;
906 }
907 
908 /******************************************************************************
909  *        FileMoniker_Inverse
910  */
911 static HRESULT WINAPI
912 FileMonikerImpl_Inverse(IMoniker* iface,IMoniker** ppmk)
913 {
914     TRACE("(%p,%p)\n",iface,ppmk);
915 
916     return CreateAntiMoniker(ppmk);
917 }
918 
919 /******************************************************************************
920  *        FileMoniker_CommonPrefixWith
921  */
922 static HRESULT WINAPI
923 FileMonikerImpl_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix)
924 {
925 
926     LPOLESTR pathThis = NULL, pathOther = NULL, *stringTable1 = NULL;
927     LPOLESTR *stringTable2 = NULL, commonPath = NULL;
928     IBindCtx *bindctx;
929     DWORD mkSys;
930     ULONG nb1,nb2,i,sameIdx;
931     BOOL machineNameCase = FALSE;
932     HRESULT ret;
933 
934     if (ppmkPrefix==NULL)
935         return E_POINTER;
936 
937     if (pmkOther==NULL)
938         return E_INVALIDARG;
939 
940     *ppmkPrefix=0;
941 
942     /* check if we have the same type of moniker */
943     IMoniker_IsSystemMoniker(pmkOther,&mkSys);
944     if (mkSys != MKSYS_FILEMONIKER)
945         return MonikerCommonPrefixWith(iface, pmkOther, ppmkPrefix);
946 
947     ret = CreateBindCtx(0, &bindctx);
948     if (FAILED(ret))
949         return ret;
950 
951     /* create a string based on common part of the two paths */
952     ret = IMoniker_GetDisplayName(iface, bindctx, NULL, &pathThis);
953     if (FAILED(ret))
954         goto failed;
955 
956     ret = IMoniker_GetDisplayName(pmkOther, bindctx, NULL, &pathOther);
957     if (FAILED(ret))
958         goto failed;
959 
960     nb1 = FileMonikerImpl_DecomposePath(pathThis, &stringTable1);
961     if (FAILED(nb1)) {
962         ret = nb1;
963         goto failed;
964     }
965 
966     nb2 = FileMonikerImpl_DecomposePath(pathOther, &stringTable2);
967     if (FAILED(nb2)) {
968         ret = nb2;
969         goto failed;
970     }
971 
972     if (nb1 == 0 || nb2 == 0) {
973         ret = MK_E_NOPREFIX;
974         goto failed;
975     }
976 
977     commonPath = CoTaskMemAlloc(sizeof(WCHAR)*(min(lstrlenW(pathThis),lstrlenW(pathOther))+1));
978     if (!commonPath) {
979         ret = E_OUTOFMEMORY;
980         goto failed;
981     }
982 
983     *commonPath = 0;
984     for(sameIdx=0; ( (stringTable1[sameIdx]!=NULL) &&
985                      (stringTable2[sameIdx]!=NULL) &&
986                      (lstrcmpiW(stringTable1[sameIdx],stringTable2[sameIdx])==0)); sameIdx++);
987 
988     if (sameIdx > 1 && *stringTable1[0]=='\\' && *stringTable2[1]=='\\'){
989         machineNameCase = TRUE;
990 
991     for(i=2;i<sameIdx;i++)
992         if( (*stringTable1[i]=='\\') && (i+1 < sameIdx) && (*stringTable1[i+1]=='\\') ){
993             machineNameCase = FALSE;
994             break;
995         }
996     }
997 
998     if (machineNameCase && *stringTable1[sameIdx-1]=='\\')
999         sameIdx--;
1000 
1001     if (machineNameCase && (sameIdx<=3) && (nb1 > 3 || nb2 > 3) )
1002         ret = MK_E_NOPREFIX;
1003     else
1004     {
1005         for (i = 0; i < sameIdx; i++)
1006             strcatW(commonPath,stringTable1[i]);
1007         ret = CreateFileMoniker(commonPath, ppmkPrefix);
1008     }
1009 
1010 failed:
1011     IBindCtx_Release(bindctx);
1012     CoTaskMemFree(pathThis);
1013     CoTaskMemFree(pathOther);
1014     CoTaskMemFree(commonPath);
1015     if (stringTable1) free_stringtable(stringTable1);
1016     if (stringTable2) free_stringtable(stringTable2);
1017 
1018     return ret;
1019 }
1020 
1021 /******************************************************************************
1022  *        DecomposePath (local function)
1023  */
1024 int FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable)
1025 {
1026     static const WCHAR bSlash[] = {'\\',0};
1027     LPOLESTR word;
1028     int i=0,j,tabIndex=0, ret=0;
1029     LPOLESTR *strgtable ;
1030 
1031     int len=lstrlenW(str);
1032 
1033     TRACE("%s, %p\n", debugstr_w(str), *stringTable);
1034 
1035     strgtable = CoTaskMemAlloc((len + 1)*sizeof(*strgtable));
1036 
1037     if (strgtable==NULL)
1038 	return E_OUTOFMEMORY;
1039 
1040     word = CoTaskMemAlloc((len + 1)*sizeof(WCHAR));
1041 
1042     if (word==NULL)
1043     {
1044         ret = E_OUTOFMEMORY;
1045         goto lend;
1046     }
1047 
1048     while(str[i]!=0){
1049 
1050         if(str[i]==bSlash[0]){
1051 
1052             strgtable[tabIndex]=CoTaskMemAlloc(2*sizeof(WCHAR));
1053 
1054             if (strgtable[tabIndex]==NULL)
1055             {
1056                 ret = E_OUTOFMEMORY;
1057                 goto lend;
1058             }
1059 
1060             strcpyW(strgtable[tabIndex++],bSlash);
1061 
1062             i++;
1063 
1064         }
1065         else {
1066 
1067             for(j=0; str[i]!=0 && str[i]!=bSlash[0] ; i++,j++)
1068                 word[j]=str[i];
1069 
1070             word[j]=0;
1071 
1072             strgtable[tabIndex]=CoTaskMemAlloc(sizeof(WCHAR)*(j+1));
1073 
1074             if (strgtable[tabIndex]==NULL)
1075             {
1076                 ret = E_OUTOFMEMORY;
1077                 goto lend;
1078             }
1079 
1080             strcpyW(strgtable[tabIndex++],word);
1081         }
1082     }
1083     strgtable[tabIndex]=NULL;
1084 
1085     *stringTable=strgtable;
1086 
1087     ret = tabIndex;
1088 
1089 lend:
1090     if (ret < 0)
1091     {
1092         for (i = 0; i < tabIndex; i++)
1093             CoTaskMemFree(strgtable[i]);
1094 
1095         CoTaskMemFree(strgtable);
1096     }
1097 
1098     CoTaskMemFree(word);
1099 
1100     return ret;
1101 }
1102 
1103 /******************************************************************************
1104  *        FileMoniker_RelativePathTo
1105  */
1106 static HRESULT WINAPI
1107 FileMonikerImpl_RelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath)
1108 {
1109     IBindCtx *bind;
1110     HRESULT res;
1111     LPOLESTR str1=0,str2=0,*tabStr1=0,*tabStr2=0,relPath=0;
1112     DWORD len1=0,len2=0,sameIdx=0,j=0;
1113     static const WCHAR back[] ={'.','.','\\',0};
1114 
1115     TRACE("(%p,%p,%p)\n",iface,pmOther,ppmkRelPath);
1116 
1117     if (ppmkRelPath==NULL)
1118         return E_POINTER;
1119 
1120     if (pmOther==NULL)
1121         return E_INVALIDARG;
1122 
1123     res=CreateBindCtx(0,&bind);
1124     if (FAILED(res))
1125 	return res;
1126 
1127     res=IMoniker_GetDisplayName(iface,bind,NULL,&str1);
1128     if (FAILED(res))
1129 	return res;
1130     res=IMoniker_GetDisplayName(pmOther,bind,NULL,&str2);
1131     if (FAILED(res))
1132 	return res;
1133 
1134     len1=FileMonikerImpl_DecomposePath(str1,&tabStr1);
1135     if (FAILED(len1))
1136         return E_OUTOFMEMORY;
1137     len2=FileMonikerImpl_DecomposePath(str2,&tabStr2);
1138 
1139     if (FAILED(len2))
1140     {
1141         free_stringtable(tabStr1);
1142         return E_OUTOFMEMORY;
1143     }
1144 
1145     /* count the number of similar items from the begin of the two paths */
1146     for(sameIdx=0; ( (tabStr1[sameIdx]!=NULL) &&
1147 		   (tabStr2[sameIdx]!=NULL) &&
1148                (lstrcmpiW(tabStr1[sameIdx],tabStr2[sameIdx])==0)); sameIdx++);
1149 
1150     /* begin the construction of relativePath */
1151     /* if the two paths have a consecutive similar item from the begin ! the relativePath will be composed */
1152     /* by "..\\" in the begin */
1153     relPath=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(1+lstrlenW(str1)+lstrlenW(str2)));
1154 
1155     *relPath=0;
1156 
1157     if (len2>0 && !(len1==1 && len2==1 && sameIdx==0))
1158         for(j=sameIdx;(tabStr1[j] != NULL); j++)
1159             if (*tabStr1[j]!='\\')
1160                 strcatW(relPath,back);
1161 
1162     /* add items of the second path (similar items with the first path are not included) to the relativePath */
1163     for(j=sameIdx;tabStr2[j]!=NULL;j++)
1164         strcatW(relPath,tabStr2[j]);
1165 
1166     res=CreateFileMoniker(relPath,ppmkRelPath);
1167 
1168     free_stringtable(tabStr1);
1169     free_stringtable(tabStr2);
1170     CoTaskMemFree(str1);
1171     CoTaskMemFree(str2);
1172     HeapFree(GetProcessHeap(),0,relPath);
1173 
1174     if (len1==0 || len2==0 || (len1==1 && len2==1 && sameIdx==0))
1175         return MK_S_HIM;
1176 
1177     return res;
1178 }
1179 
1180 /******************************************************************************
1181  *        FileMoniker_GetDisplayName
1182  */
1183 static HRESULT WINAPI
1184 FileMonikerImpl_GetDisplayName(IMoniker* iface, IBindCtx* pbc,
1185                                IMoniker* pmkToLeft, LPOLESTR *ppszDisplayName)
1186 {
1187     FileMonikerImpl *This = impl_from_IMoniker(iface);
1188     int len=lstrlenW(This->filePathName);
1189 
1190     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,ppszDisplayName);
1191 
1192     if (ppszDisplayName==NULL)
1193         return E_POINTER;
1194 
1195     if (pmkToLeft!=NULL)
1196         return E_INVALIDARG;
1197 
1198     *ppszDisplayName=CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
1199     if (*ppszDisplayName==NULL)
1200         return E_OUTOFMEMORY;
1201 
1202     strcpyW(*ppszDisplayName,This->filePathName);
1203 
1204     TRACE("-- %s\n", debugstr_w(*ppszDisplayName));
1205 
1206     return S_OK;
1207 }
1208 
1209 /******************************************************************************
1210  *        FileMoniker_ParseDisplayName
1211  */
1212 static HRESULT WINAPI
1213 FileMonikerImpl_ParseDisplayName(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
1214                      LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut)
1215 {
1216     FIXME("(%p,%p,%p,%p,%p,%p),stub!\n",iface,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut);
1217     return E_NOTIMPL;
1218 }
1219 
1220 /******************************************************************************
1221  *        FileMoniker_IsSystemMoniker
1222  */
1223 static HRESULT WINAPI
1224 FileMonikerImpl_IsSystemMoniker(IMoniker* iface,DWORD* pwdMksys)
1225 {
1226     TRACE("(%p,%p)\n",iface,pwdMksys);
1227 
1228     if (!pwdMksys)
1229         return E_POINTER;
1230 
1231     (*pwdMksys)=MKSYS_FILEMONIKER;
1232 
1233     return S_OK;
1234 }
1235 
1236 /*******************************************************************************
1237  *        FileMonikerIROTData_QueryInterface
1238  */
1239 static HRESULT WINAPI
1240 FileMonikerROTDataImpl_QueryInterface(IROTData *iface,REFIID riid,VOID** ppvObject)
1241 {
1242 
1243     FileMonikerImpl *This = impl_from_IROTData(iface);
1244 
1245     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppvObject);
1246 
1247     return FileMonikerImpl_QueryInterface(&This->IMoniker_iface, riid, ppvObject);
1248 }
1249 
1250 /***********************************************************************
1251  *        FileMonikerIROTData_AddRef
1252  */
1253 static ULONG WINAPI
1254 FileMonikerROTDataImpl_AddRef(IROTData *iface)
1255 {
1256     FileMonikerImpl *This = impl_from_IROTData(iface);
1257 
1258     TRACE("(%p)\n",This);
1259 
1260     return IMoniker_AddRef(&This->IMoniker_iface);
1261 }
1262 
1263 /***********************************************************************
1264  *        FileMonikerIROTData_Release
1265  */
1266 static ULONG WINAPI
1267 FileMonikerROTDataImpl_Release(IROTData* iface)
1268 {
1269     FileMonikerImpl *This = impl_from_IROTData(iface);
1270 
1271     TRACE("(%p)\n",This);
1272 
1273     return FileMonikerImpl_Release(&This->IMoniker_iface);
1274 }
1275 
1276 /******************************************************************************
1277  *        FileMonikerIROTData_GetComparisonData
1278  */
1279 static HRESULT WINAPI
1280 FileMonikerROTDataImpl_GetComparisonData(IROTData* iface, BYTE* pbData,
1281                                           ULONG cbMax, ULONG* pcbData)
1282 {
1283     FileMonikerImpl *This = impl_from_IROTData(iface);
1284     int len = strlenW(This->filePathName)+1;
1285     int i;
1286     LPWSTR pszFileName;
1287 
1288     TRACE("(%p, %u, %p)\n", pbData, cbMax, pcbData);
1289 
1290     *pcbData = sizeof(CLSID) + len * sizeof(WCHAR);
1291     if (cbMax < *pcbData)
1292         return E_OUTOFMEMORY;
1293 
1294     memcpy(pbData, &CLSID_FileMoniker, sizeof(CLSID));
1295     pszFileName = (LPWSTR)(pbData+sizeof(CLSID));
1296     for (i = 0; i < len; i++)
1297         pszFileName[i] = toupperW(This->filePathName[i]);
1298 
1299     return S_OK;
1300 }
1301 
1302 /*
1303  * Virtual function table for the FileMonikerImpl class which include IPersist,
1304  * IPersistStream and IMoniker functions.
1305  */
1306 static const IMonikerVtbl VT_FileMonikerImpl =
1307 {
1308     FileMonikerImpl_QueryInterface,
1309     FileMonikerImpl_AddRef,
1310     FileMonikerImpl_Release,
1311     FileMonikerImpl_GetClassID,
1312     FileMonikerImpl_IsDirty,
1313     FileMonikerImpl_Load,
1314     FileMonikerImpl_Save,
1315     FileMonikerImpl_GetSizeMax,
1316     FileMonikerImpl_BindToObject,
1317     FileMonikerImpl_BindToStorage,
1318     FileMonikerImpl_Reduce,
1319     FileMonikerImpl_ComposeWith,
1320     FileMonikerImpl_Enum,
1321     FileMonikerImpl_IsEqual,
1322     FileMonikerImpl_Hash,
1323     FileMonikerImpl_IsRunning,
1324     FileMonikerImpl_GetTimeOfLastChange,
1325     FileMonikerImpl_Inverse,
1326     FileMonikerImpl_CommonPrefixWith,
1327     FileMonikerImpl_RelativePathTo,
1328     FileMonikerImpl_GetDisplayName,
1329     FileMonikerImpl_ParseDisplayName,
1330     FileMonikerImpl_IsSystemMoniker
1331 };
1332 
1333 /* Virtual function table for the IROTData class. */
1334 static const IROTDataVtbl VT_ROTDataImpl =
1335 {
1336     FileMonikerROTDataImpl_QueryInterface,
1337     FileMonikerROTDataImpl_AddRef,
1338     FileMonikerROTDataImpl_Release,
1339     FileMonikerROTDataImpl_GetComparisonData
1340 };
1341 
1342 /******************************************************************************
1343  *         FileMoniker_Construct (local function)
1344  */
1345 static HRESULT FileMonikerImpl_Construct(FileMonikerImpl* This, LPCOLESTR lpszPathName)
1346 {
1347     int nb=0,i;
1348     int sizeStr=lstrlenW(lpszPathName);
1349     LPOLESTR *tabStr=0;
1350     static const WCHAR twoPoint[]={'.','.',0};
1351     static const WCHAR bkSlash[]={'\\',0};
1352     BOOL addBkSlash;
1353 
1354     TRACE("(%p,%s)\n",This,debugstr_w(lpszPathName));
1355 
1356     /* Initialize the virtual function table. */
1357     This->IMoniker_iface.lpVtbl = &VT_FileMonikerImpl;
1358     This->IROTData_iface.lpVtbl = &VT_ROTDataImpl;
1359     This->ref          = 0;
1360     This->pMarshal     = NULL;
1361 
1362     This->filePathName=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(sizeStr+1));
1363 
1364     if (This->filePathName==NULL)
1365         return E_OUTOFMEMORY;
1366 
1367     strcpyW(This->filePathName,lpszPathName);
1368 
1369     nb=FileMonikerImpl_DecomposePath(This->filePathName,&tabStr);
1370 
1371     if (nb > 0 ){
1372 
1373         addBkSlash = TRUE;
1374         if (lstrcmpW(tabStr[0],twoPoint)!=0)
1375             addBkSlash = FALSE;
1376         else
1377             for(i=0;i<nb;i++){
1378 
1379                 if ( (lstrcmpW(tabStr[i],twoPoint)!=0) && (lstrcmpW(tabStr[i],bkSlash)!=0) ){
1380                     addBkSlash = FALSE;
1381                     break;
1382                 }
1383                 else
1384 
1385                     if (lstrcmpW(tabStr[i],bkSlash)==0 && i<nb-1 && lstrcmpW(tabStr[i+1],bkSlash)==0){
1386                         *tabStr[i]=0;
1387                         sizeStr--;
1388                         addBkSlash = FALSE;
1389                         break;
1390                     }
1391             }
1392 
1393         if (lstrcmpW(tabStr[nb-1],bkSlash)==0)
1394             addBkSlash = FALSE;
1395 
1396         This->filePathName=HeapReAlloc(GetProcessHeap(),0,This->filePathName,(sizeStr+1)*sizeof(WCHAR));
1397 
1398         *This->filePathName=0;
1399 
1400         for(i=0;tabStr[i]!=NULL;i++)
1401             strcatW(This->filePathName,tabStr[i]);
1402 
1403         if (addBkSlash)
1404             strcatW(This->filePathName,bkSlash);
1405     }
1406 
1407     free_stringtable(tabStr);
1408 
1409     return S_OK;
1410 }
1411 
1412 /******************************************************************************
1413  *        CreateFileMoniker (OLE32.@)
1414  ******************************************************************************/
1415 HRESULT WINAPI CreateFileMoniker(LPCOLESTR lpszPathName, IMoniker **ppmk)
1416 {
1417     FileMonikerImpl* newFileMoniker;
1418     HRESULT  hr;
1419 
1420     TRACE("(%s,%p)\n",debugstr_w(lpszPathName),ppmk);
1421 
1422     if (!ppmk)
1423         return E_POINTER;
1424 
1425     if(!lpszPathName)
1426         return MK_E_SYNTAX;
1427 
1428     *ppmk=NULL;
1429 
1430     newFileMoniker = HeapAlloc(GetProcessHeap(), 0, sizeof(FileMonikerImpl));
1431 
1432     if (!newFileMoniker)
1433         return E_OUTOFMEMORY;
1434 
1435     hr = FileMonikerImpl_Construct(newFileMoniker,lpszPathName);
1436 
1437     if (SUCCEEDED(hr))
1438         hr = IMoniker_QueryInterface(&newFileMoniker->IMoniker_iface,&IID_IMoniker,(void**)ppmk);
1439     else
1440         HeapFree(GetProcessHeap(),0,newFileMoniker);
1441 
1442     return hr;
1443 }
1444 
1445 /* find a character from a set in reverse without the string having to be null-terminated */
1446 static inline WCHAR *memrpbrkW(const WCHAR *ptr, size_t n, const WCHAR *accept)
1447 {
1448     const WCHAR *end, *ret = NULL;
1449     for (end = ptr + n; ptr < end; ptr++) if (strchrW(accept, *ptr)) ret = ptr;
1450     return (WCHAR *)ret;
1451 }
1452 
1453 HRESULT FileMoniker_CreateFromDisplayName(LPBC pbc, LPCOLESTR szDisplayName,
1454                                           LPDWORD pchEaten, IMoniker **ppmk)
1455 {
1456     LPCWSTR end;
1457     static const WCHAR wszSeparators[] = {':','\\','/','!',0};
1458 
1459     for (end = szDisplayName + strlenW(szDisplayName);
1460          end && (end != szDisplayName);
1461          end = memrpbrkW(szDisplayName, end - szDisplayName, wszSeparators))
1462     {
1463         HRESULT hr;
1464         IRunningObjectTable *rot;
1465         IMoniker *file_moniker;
1466         LPWSTR file_display_name;
1467         LPWSTR full_path_name;
1468         DWORD full_path_name_len;
1469         int len = end - szDisplayName;
1470 
1471         file_display_name = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1472         if (!file_display_name) return E_OUTOFMEMORY;
1473         memcpy(file_display_name, szDisplayName, len * sizeof(WCHAR));
1474         file_display_name[len] = '\0';
1475 
1476         hr = CreateFileMoniker(file_display_name, &file_moniker);
1477         if (FAILED(hr))
1478         {
1479             HeapFree(GetProcessHeap(), 0, file_display_name);
1480             return hr;
1481         }
1482 
1483         hr = IBindCtx_GetRunningObjectTable(pbc, &rot);
1484         if (FAILED(hr))
1485         {
1486             HeapFree(GetProcessHeap(), 0, file_display_name);
1487             IMoniker_Release(file_moniker);
1488             return hr;
1489         }
1490 
1491         hr = IRunningObjectTable_IsRunning(rot, file_moniker);
1492         IRunningObjectTable_Release(rot);
1493         if (FAILED(hr))
1494         {
1495             HeapFree(GetProcessHeap(), 0, file_display_name);
1496             IMoniker_Release(file_moniker);
1497             return hr;
1498         }
1499         if (hr == S_OK)
1500         {
1501             TRACE("found running file moniker for %s\n", debugstr_w(file_display_name));
1502             *pchEaten = len;
1503             *ppmk = file_moniker;
1504             HeapFree(GetProcessHeap(), 0, file_display_name);
1505             return S_OK;
1506         }
1507 
1508         full_path_name_len = GetFullPathNameW(file_display_name, 0, NULL, NULL);
1509         if (!full_path_name_len)
1510         {
1511             HeapFree(GetProcessHeap(), 0, file_display_name);
1512             IMoniker_Release(file_moniker);
1513             return MK_E_SYNTAX;
1514         }
1515         full_path_name = HeapAlloc(GetProcessHeap(), 0, full_path_name_len * sizeof(WCHAR));
1516         if (!full_path_name)
1517         {
1518             HeapFree(GetProcessHeap(), 0, file_display_name);
1519             IMoniker_Release(file_moniker);
1520             return E_OUTOFMEMORY;
1521         }
1522         GetFullPathNameW(file_display_name, full_path_name_len, full_path_name, NULL);
1523 
1524         if (GetFileAttributesW(full_path_name) == INVALID_FILE_ATTRIBUTES)
1525             TRACE("couldn't open file %s\n", debugstr_w(full_path_name));
1526         else
1527         {
1528             TRACE("got file moniker for %s\n", debugstr_w(szDisplayName));
1529             *pchEaten = len;
1530             *ppmk = file_moniker;
1531             HeapFree(GetProcessHeap(), 0, file_display_name);
1532             HeapFree(GetProcessHeap(), 0, full_path_name);
1533             return S_OK;
1534         }
1535         HeapFree(GetProcessHeap(), 0, file_display_name);
1536         HeapFree(GetProcessHeap(), 0, full_path_name);
1537         IMoniker_Release(file_moniker);
1538     }
1539 
1540     return MK_E_CANTOPENFILE;
1541 }
1542 
1543 
1544 HRESULT WINAPI FileMoniker_CreateInstance(IClassFactory *iface, IUnknown *pUnk, REFIID riid, void **ppv)
1545 {
1546     FileMonikerImpl* newFileMoniker;
1547     HRESULT  hr;
1548     static const WCHAR wszEmpty[] = { 0 };
1549 
1550     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
1551 
1552     *ppv = NULL;
1553 
1554     if (pUnk)
1555         return CLASS_E_NOAGGREGATION;
1556 
1557     newFileMoniker = HeapAlloc(GetProcessHeap(), 0, sizeof(FileMonikerImpl));
1558     if (!newFileMoniker)
1559         return E_OUTOFMEMORY;
1560 
1561     hr = FileMonikerImpl_Construct(newFileMoniker, wszEmpty);
1562 
1563     if (SUCCEEDED(hr))
1564         hr = IMoniker_QueryInterface(&newFileMoniker->IMoniker_iface, riid, ppv);
1565     if (FAILED(hr))
1566         HeapFree(GetProcessHeap(),0,newFileMoniker);
1567 
1568     return hr;
1569 }
1570