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