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