Overlay icons for .lnk files with a small arrow in the lower left
[wine] / dlls / ole32 / filemoniker.c
1 /*
2  * FileMonikers implementation
3  *
4  * Copyright 1999  Noomen Hamza
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <string.h>
24
25 #define COBJMACROS
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winerror.h"
32 #include "winnls.h"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
35 #include "objbase.h"
36 #include "moniker.h"
37
38 #include "compobj_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(ole);
41
42 const CLSID CLSID_FileMoniker = {
43   0x303, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46}
44 };
45
46 /* filemoniker data structure */
47 typedef struct FileMonikerImpl{
48
49     IMonikerVtbl*  lpvtbl1;  /* VTable relative to the IMoniker interface.*/
50
51     /* The ROT (RunningObjectTable implementation) uses the IROTData interface to test whether
52      * two monikers are equal. That's whay IROTData interface is implemented by monikers.
53      */
54     IROTDataVtbl*  lpvtbl2;  /* VTable relative to the IROTData interface.*/
55
56     ULONG ref; /* reference counter for this object */
57
58     LPOLESTR filePathName; /* path string identified by this filemoniker */
59
60 } FileMonikerImpl;
61
62 /* IROTData prototype function */
63 static HRESULT WINAPI FileMonikerROTDataImpl_GetComparaisonData(IROTData* iface,BYTE* pbData,ULONG cbMax,ULONG* pcbData);
64
65 /* Local function used by filemoniker implementation */
66 static HRESULT WINAPI FileMonikerImpl_Construct(FileMonikerImpl* iface, LPCOLESTR lpszPathName);
67 static HRESULT WINAPI 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 = (IROTData*)&(This->lpvtbl2);
96
97     /* Check that we obtained an interface.*/
98     if ((*ppvObject)==0)
99         return E_NOINTERFACE;
100
101     /* Query Interface always increases the reference count by one when it is successful */
102     IMoniker_AddRef(iface);
103
104     return S_OK;
105 }
106
107 /******************************************************************************
108  *        FileMoniker_AddRef
109  */
110 static ULONG WINAPI
111 FileMonikerImpl_AddRef(IMoniker* iface)
112 {
113     FileMonikerImpl *This = (FileMonikerImpl *)iface;
114
115     TRACE("(%p)\n",iface);
116
117     return InterlockedIncrement(&This->ref);
118 }
119
120 /******************************************************************************
121  *        FileMoniker_Release
122  */
123 static ULONG WINAPI
124 FileMonikerImpl_Release(IMoniker* iface)
125 {
126     FileMonikerImpl *This = (FileMonikerImpl *)iface;
127     ULONG ref;
128
129     TRACE("(%p)\n",iface);
130
131     ref = InterlockedDecrement(&This->ref);
132
133     /* destroy the object if there's no more reference on it */
134     if (ref == 0) FileMonikerImpl_Destroy(This);
135
136     return ref;
137 }
138
139 /******************************************************************************
140  *        FileMoniker_GetClassID
141  */
142 static HRESULT WINAPI
143 FileMonikerImpl_GetClassID(IMoniker* iface, CLSID *pClassID)
144 {
145     TRACE("(%p,%p)\n",iface,pClassID);
146
147     if (pClassID==NULL)
148         return E_POINTER;
149
150     *pClassID = CLSID_FileMoniker;
151
152     return S_OK;
153 }
154
155 /******************************************************************************
156  *        FileMoniker_IsDirty
157  *
158  * Note that the OLE-provided implementations of the IPersistStream::IsDirty
159  * method in the OLE-provided moniker interfaces always return S_FALSE because
160  * their internal state never changes.
161  */
162 static HRESULT WINAPI
163 FileMonikerImpl_IsDirty(IMoniker* iface)
164 {
165
166     TRACE("(%p)\n",iface);
167
168     return S_FALSE;
169 }
170
171 /******************************************************************************
172  *        FileMoniker_Load
173  *
174  * this function locates and reads from the stream the filePath string
175  * written by FileMonikerImpl_Save
176  */
177 static HRESULT WINAPI
178 FileMonikerImpl_Load(IMoniker* iface,IStream* pStm)
179 {
180     HRESULT res;
181     CHAR* filePathA;
182     WCHAR* filePathW;
183     ULONG bread;
184     WORD  wbuffer;
185     DWORD dwbuffer,length,i,doubleLenHex,doubleLenDec;
186
187     FileMonikerImpl *This = (FileMonikerImpl *)iface;
188
189     TRACE("(%p,%p)\n",iface,pStm);
190
191     /* first WORD is non significative */
192     res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
193     if (bread!=sizeof(WORD) || wbuffer!=0)
194         return E_FAIL;
195
196     /* read filePath string length (plus one) */
197     res=IStream_Read(pStm,&length,sizeof(DWORD),&bread);
198     if (bread != sizeof(DWORD))
199         return E_FAIL;
200
201     /* read filePath string */
202     filePathA=HeapAlloc(GetProcessHeap(),0,length);
203     res=IStream_Read(pStm,filePathA,length,&bread);
204     HeapFree(GetProcessHeap(),0,filePathA);
205     if (bread != length)
206         return E_FAIL;
207
208     /* read the first constant */
209     IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
210     if (bread != sizeof(DWORD) || dwbuffer != 0xDEADFFFF)
211         return E_FAIL;
212
213     length--;
214
215     for(i=0;i<10;i++){
216         res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
217         if (bread!=sizeof(WORD) || wbuffer!=0)
218             return E_FAIL;
219     }
220
221     if (length>8)
222         length=0;
223
224     doubleLenHex=doubleLenDec=2*length;
225     if (length > 5)
226         doubleLenDec+=6;
227
228     res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
229     if (bread!=sizeof(DWORD) || dwbuffer!=doubleLenDec)
230         return E_FAIL;
231
232     if (length==0)
233         return res;
234
235     res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread);
236     if (bread!=sizeof(DWORD) || dwbuffer!=doubleLenHex)
237         return E_FAIL;
238
239     res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread);
240     if (bread!=sizeof(WORD) || wbuffer!=0x3)
241         return E_FAIL;
242
243     filePathW=HeapAlloc(GetProcessHeap(),0,(length+1)*sizeof(WCHAR));
244     filePathW[length]=0;
245     res=IStream_Read(pStm,filePathW,doubleLenHex,&bread);
246     if (bread!=doubleLenHex) {
247         HeapFree(GetProcessHeap(), 0, filePathW);
248         return E_FAIL;
249     }
250
251     HeapFree(GetProcessHeap(),0,This->filePathName);
252
253     This->filePathName=filePathW;
254
255     return res;
256 }
257
258 /******************************************************************************
259  *        FileMoniker_Save
260  *
261  * This function saves data of this object. In the beginning I thougth
262  * that I have just to write the filePath string on Stream. But, when I
263  * tested this function whith windows programs samples, I noticed that it
264  * was not the case. So I analysed data written by this function on
265  * Windows and what this did function exactly ! But I have no idea about
266  * its logic !
267  * I guessed data which must be written on stream is:
268  * 1) WORD constant:zero
269  * 2) length of the path string ("\0" included)
270  * 3) path string type A
271  * 4) DWORD constant : 0xDEADFFFF
272  * 5) ten WORD constant: zero
273  * 6) DWORD: double-length of the the path string type W ("\0" not
274  *    included)
275  * 7) WORD constant: 0x3
276  * 8) filePath unicode string.
277  *    if the length(filePath) > 8 or length(filePath) == 8 stop at step 5)
278  */
279 static HRESULT WINAPI
280 FileMonikerImpl_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty)
281 {
282     FileMonikerImpl *This = (FileMonikerImpl *)iface;
283
284     HRESULT res;
285     LPOLESTR filePathW=This->filePathName;
286     CHAR*     filePathA;
287     DWORD  len;
288
289     DWORD  constant1 = 0xDEADFFFF; /* these constants are detected after analysing the data structure written by */
290     WORD   constant2 = 0x3;        /* FileMoniker_Save function in a windows program system */
291
292     WORD   zero=0;
293     DWORD doubleLenHex;
294     DWORD doubleLenDec;
295     int i=0;
296
297     TRACE("(%p,%p,%d)\n",iface,pStm,fClearDirty);
298
299     if (pStm==NULL)
300         return E_POINTER;
301
302     /* write a DWORD set to 0 : constant */
303     res=IStream_Write(pStm,&zero,sizeof(WORD),NULL);
304
305     /* write length of filePath string ( "\0" included )*/
306     len = WideCharToMultiByte( CP_ACP, 0, filePathW, -1, NULL, 0, NULL, NULL );
307     res=IStream_Write(pStm,&len,sizeof(DWORD),NULL);
308
309     /* write filePath string type A */
310     filePathA=HeapAlloc(GetProcessHeap(),0,len);
311     WideCharToMultiByte( CP_ACP, 0, filePathW, -1, filePathA, len, NULL, NULL );
312     res=IStream_Write(pStm,filePathA,len,NULL);
313     HeapFree(GetProcessHeap(),0,filePathA);
314
315     /* write a DWORD set to 0xDEADFFFF: constant */
316     res=IStream_Write(pStm,&constant1,sizeof(DWORD),NULL);
317
318     len--;
319     /* write 10 times a DWORD set to 0 : constants */
320     for(i=0;i<10;i++)
321         res=IStream_Write(pStm,&zero,sizeof(WORD),NULL);
322
323     if (len>8)
324         len=0;
325
326     doubleLenHex=doubleLenDec=2*len;
327     if (len > 5)
328         doubleLenDec+=6;
329
330     /* write double-length of the path string ( "\0" included )*/
331     res=IStream_Write(pStm,&doubleLenDec,sizeof(DWORD),NULL);
332
333     if (len==0)
334         return res;
335
336     /* write double-length (hexa representation) of the path string ( "\0" included ) */
337     res=IStream_Write(pStm,&doubleLenHex,sizeof(DWORD),NULL);
338
339     /* write a WORD set to 0x3: constant */
340     res=IStream_Write(pStm,&constant2,sizeof(WORD),NULL);
341
342     /* write path unicode string */
343     res=IStream_Write(pStm,filePathW,doubleLenHex,NULL);
344
345     return res;
346 }
347
348 /******************************************************************************
349  *        FileMoniker_GetSizeMax
350  */
351 static HRESULT WINAPI
352 FileMonikerImpl_GetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize)
353 {
354     FileMonikerImpl *This = (FileMonikerImpl *)iface;
355     DWORD len=lstrlenW(This->filePathName);
356     DWORD sizeMAx;
357
358     TRACE("(%p,%p)\n",iface,pcbSize);
359
360     if (pcbSize!=NULL)
361         return E_POINTER;
362
363     /* for more details see FileMonikerImpl_Save coments */
364
365     sizeMAx =  sizeof(WORD) +           /* first WORD is 0 */
366                sizeof(DWORD)+           /* length of filePath including "\0" in the end of the string */
367                (len+1)+                 /* filePath string */
368                sizeof(DWORD)+           /* constant : 0xDEADFFFF */
369                10*sizeof(WORD)+         /* 10 zero WORD */
370                sizeof(DWORD);           /* size of the unicode filePath: "\0" not included */
371
372     if (len==0 || len > 8)
373         return S_OK;
374
375     sizeMAx += sizeof(DWORD)+           /* size of the unicode filePath: "\0" not included */
376                sizeof(WORD)+            /* constant : 0x3 */
377                len*sizeof(WCHAR);       /* unicde filePath string */
378
379     pcbSize->u.LowPart=sizeMAx;
380     pcbSize->u.HighPart=0;
381
382     return S_OK;
383 }
384
385 /******************************************************************************
386  *        FileMoniker_Destroy (local function)
387  *******************************************************************************/
388 HRESULT WINAPI FileMonikerImpl_Destroy(FileMonikerImpl* This)
389 {
390     TRACE("(%p)\n",This);
391
392     HeapFree(GetProcessHeap(),0,This->filePathName);
393     HeapFree(GetProcessHeap(),0,This);
394
395     return S_OK;
396 }
397
398 /******************************************************************************
399  *                  FileMoniker_BindToObject
400  */
401 static HRESULT WINAPI
402 FileMonikerImpl_BindToObject(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
403                              REFIID riid, VOID** ppvResult)
404 {
405     HRESULT   res=E_FAIL;
406     CLSID     clsID;
407     IUnknown* pObj=0;
408     IRunningObjectTable *prot=0;
409     IPersistFile  *ppf=0;
410     IClassFactory *pcf=0;
411     IClassActivator *pca=0;
412
413     FileMonikerImpl *This = (FileMonikerImpl *)iface;
414
415     *ppvResult=0;
416
417     TRACE("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvResult);
418
419     if(pmkToLeft==NULL){
420
421         res=IBindCtx_GetRunningObjectTable(pbc,&prot);
422
423         if (SUCCEEDED(res)){
424             /* if the requested class was loaded before ! we don't need to reload it */
425             res = IRunningObjectTable_GetObject(prot,iface,&pObj);
426
427             if (res==S_FALSE){
428                 /* first activation of this class */
429                 res=GetClassFile(This->filePathName,&clsID);
430                 if (SUCCEEDED(res)){
431
432                     res=CoCreateInstance(&clsID,NULL,CLSCTX_ALL,&IID_IPersistFile,(void**)&ppf);
433                     if (SUCCEEDED(res)){
434
435                         res=IPersistFile_Load(ppf,This->filePathName,STGM_READ);
436                         if (SUCCEEDED(res)){
437
438                             pObj=(IUnknown*)ppf;
439                             IUnknown_AddRef(pObj);
440                         }
441                     }
442                 }
443             }
444         }
445     }
446     else{
447         res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassFactory,(void**)&pcf);
448
449         if (res==E_NOINTERFACE){
450
451             res=IMoniker_BindToObject(pmkToLeft,pbc,NULL,&IID_IClassActivator,(void**)&pca);
452
453             if (res==E_NOINTERFACE)
454                 return MK_E_INTERMEDIATEINTERFACENOTSUPPORTED;
455         }
456         if (pcf!=NULL){
457
458             IClassFactory_CreateInstance(pcf,NULL,&IID_IPersistFile,(void**)ppf);
459
460             res=IPersistFile_Load(ppf,This->filePathName,STGM_READ);
461
462             if (SUCCEEDED(res)){
463
464                 pObj=(IUnknown*)ppf;
465                 IUnknown_AddRef(pObj);
466             }
467         }
468         if (pca!=NULL){
469
470             FIXME("()\n");
471
472             /*res=GetClassFile(This->filePathName,&clsID);
473
474             if (SUCCEEDED(res)){
475
476                 res=IClassActivator_GetClassObject(pca,&clsID,CLSCTX_ALL,0,&IID_IPersistFile,(void**)&ppf);
477
478                 if (SUCCEEDED(res)){
479
480                     pObj=(IUnknown*)ppf;
481                     IUnknown_AddRef(pObj);
482                 }
483             }*/
484         }
485     }
486
487     if (pObj!=NULL){
488         /* get the requested interface from the loaded class */
489         res= IUnknown_QueryInterface(pObj,riid,ppvResult);
490
491         IBindCtx_RegisterObjectBound(pbc,(IUnknown*)*ppvResult);
492
493         IUnknown_Release(pObj);
494     }
495
496     if (prot!=NULL)
497         IRunningObjectTable_Release(prot);
498
499     if (ppf!=NULL)
500         IPersistFile_Release(ppf);
501
502     if (pca!=NULL)
503         IClassActivator_Release(pca);
504
505     if (pcf!=NULL)
506         IClassFactory_Release(pcf);
507
508     return res;
509 }
510
511 /******************************************************************************
512  *        FileMoniker_BindToStorage
513  */
514 static HRESULT WINAPI
515 FileMonikerImpl_BindToStorage(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
516                               REFIID riid, VOID** ppvObject)
517 {
518     LPOLESTR filePath=0;
519     IStorage *pstg=0;
520     HRESULT res;
521
522     TRACE("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvObject);
523
524     if (pmkToLeft==NULL){
525
526         if (IsEqualIID(&IID_IStorage, riid)){
527
528             /* get the file name */
529             IMoniker_GetDisplayName(iface,pbc,pmkToLeft,&filePath);
530
531             /* verifie if the file contains a storage object */
532             res=StgIsStorageFile(filePath);
533
534             if(res==S_OK){
535
536                 res=StgOpenStorage(filePath,NULL,STGM_READWRITE|STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
537
538                 if (SUCCEEDED(res)){
539
540                     *ppvObject=pstg;
541
542                     IStorage_AddRef(pstg);
543
544                     return res;
545                 }
546             }
547             CoTaskMemFree(filePath);
548         }
549         else
550             if ( (IsEqualIID(&IID_IStream, riid)) || (IsEqualIID(&IID_ILockBytes, riid)) )
551                 return E_FAIL;
552             else
553                 return E_NOINTERFACE;
554     }
555     else {
556
557         FIXME("(%p,%p,%p,%s,%p)\n",iface,pbc,pmkToLeft,debugstr_guid(riid),ppvObject);
558
559         return E_NOTIMPL;
560     }
561     return res;
562 }
563
564 /******************************************************************************
565  *        FileMoniker_Reduce
566  ******************************************************************************/
567 static HRESULT WINAPI
568 FileMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar,
569                        IMoniker** ppmkToLeft, IMoniker** ppmkReduced)
570 {
571     TRACE("(%p,%p,%ld,%p,%p)\n",iface,pbc,dwReduceHowFar,ppmkToLeft,ppmkReduced);
572
573     if (ppmkReduced==NULL)
574         return E_POINTER;
575
576     IMoniker_AddRef(iface);
577
578     *ppmkReduced=iface;
579
580     return MK_S_REDUCED_TO_SELF;
581 }
582
583 /******************************************************************************
584  *        FileMoniker_ComposeWith
585  */
586 static HRESULT WINAPI
587 FileMonikerImpl_ComposeWith(IMoniker* iface, IMoniker* pmkRight,
588                             BOOL fOnlyIfNotGeneric, IMoniker** ppmkComposite)
589 {
590     HRESULT res;
591     LPOLESTR str1=0,str2=0,*strDec1=0,*strDec2=0,newStr=0;
592     static const WCHAR twoPoint[]={'.','.',0};
593     static const WCHAR bkSlash[]={'\\',0};
594     IBindCtx *bind=0;
595     int i=0,j=0,lastIdx1=0,lastIdx2=0;
596     DWORD mkSys;
597
598     TRACE("(%p,%p,%d,%p)\n",iface,pmkRight,fOnlyIfNotGeneric,ppmkComposite);
599
600     if (ppmkComposite==NULL)
601         return E_POINTER;
602
603     if (pmkRight==NULL)
604         return E_INVALIDARG;
605
606     *ppmkComposite=0;
607
608     IMoniker_IsSystemMoniker(pmkRight,&mkSys);
609
610     /* check if we have two filemonikers to compose or not */
611     if(mkSys==MKSYS_FILEMONIKER){
612
613         CreateBindCtx(0,&bind);
614
615         IMoniker_GetDisplayName(iface,bind,NULL,&str1);
616         IMoniker_GetDisplayName(pmkRight,bind,NULL,&str2);
617
618         /* decompose pathnames of the two monikers : (to prepare the path merge operation ) */
619         lastIdx1=FileMonikerImpl_DecomposePath(str1,&strDec1)-1;
620         lastIdx2=FileMonikerImpl_DecomposePath(str2,&strDec2)-1;
621
622         if ((lastIdx1==-1 && lastIdx2>-1)||(lastIdx1==1 && lstrcmpW(strDec1[0],twoPoint)==0))
623             return MK_E_SYNTAX;
624
625         if(lstrcmpW(strDec1[lastIdx1],bkSlash)==0)
626             lastIdx1--;
627
628         /* for etch "..\" in the left of str2 remove the right element from str1 */
629         for(i=0; ( (lastIdx1>=0) && (strDec2[i]!=NULL) && (lstrcmpW(strDec2[i],twoPoint)==0) ) ;i+=2){
630
631             lastIdx1-=2;
632         }
633
634         /* the length of the composed path string  is raised by the sum of the two paths lengths  */
635         newStr=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(lstrlenW(str1)+lstrlenW(str2)+1));
636
637           if (newStr==NULL)
638                 return E_OUTOFMEMORY;
639
640         /* new path is the concatenation of the rest of str1 and str2 */
641         for(*newStr=0,j=0;j<=lastIdx1;j++)
642             strcatW(newStr,strDec1[j]);
643
644         if ((strDec2[i]==NULL && lastIdx1>-1 && lastIdx2>-1) || lstrcmpW(strDec2[i],bkSlash)!=0)
645             strcatW(newStr,bkSlash);
646
647         for(j=i;j<=lastIdx2;j++)
648             strcatW(newStr,strDec2[j]);
649
650         /* create a new moniker with the new string */
651         res=CreateFileMoniker(newStr,ppmkComposite);
652
653         /* free all strings space memory used by this function */
654         HeapFree(GetProcessHeap(),0,newStr);
655
656         for(i=0; strDec1[i]!=NULL;i++)
657             CoTaskMemFree(strDec1[i]);
658         for(i=0; strDec2[i]!=NULL;i++)
659             CoTaskMemFree(strDec2[i]);
660         CoTaskMemFree(strDec1);
661         CoTaskMemFree(strDec2);
662
663         CoTaskMemFree(str1);
664         CoTaskMemFree(str2);
665
666         return res;
667     }
668     else if(mkSys==MKSYS_ANTIMONIKER){
669
670         *ppmkComposite=NULL;
671         return S_OK;
672     }
673     else if (fOnlyIfNotGeneric){
674
675         *ppmkComposite=NULL;
676         return MK_E_NEEDGENERIC;
677     }
678     else
679
680         return CreateGenericComposite(iface,pmkRight,ppmkComposite);
681 }
682
683 /******************************************************************************
684  *        FileMoniker_Enum
685  */
686 static HRESULT WINAPI
687 FileMonikerImpl_Enum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker)
688 {
689     TRACE("(%p,%d,%p)\n",iface,fForward,ppenumMoniker);
690
691     if (ppenumMoniker == NULL)
692         return E_POINTER;
693
694     *ppenumMoniker = NULL;
695
696     return S_OK;
697 }
698
699 /******************************************************************************
700  *        FileMoniker_IsEqual
701  */
702 static HRESULT WINAPI
703 FileMonikerImpl_IsEqual(IMoniker* iface,IMoniker* pmkOtherMoniker)
704 {
705     FileMonikerImpl *This = (FileMonikerImpl *)iface;
706     CLSID clsid;
707     LPOLESTR filePath;
708     IBindCtx* bind;
709     HRESULT res;
710
711     TRACE("(%p,%p)\n",iface,pmkOtherMoniker);
712
713     if (pmkOtherMoniker==NULL)
714         return S_FALSE;
715
716     IMoniker_GetClassID(pmkOtherMoniker,&clsid);
717
718     if (!IsEqualCLSID(&clsid,&CLSID_FileMoniker))
719         return S_FALSE;
720
721     res = CreateBindCtx(0,&bind);
722     if (FAILED(res)) return res;
723
724     if (SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker,bind,NULL,&filePath))) {
725         int result = lstrcmpiW(filePath, This->filePathName);
726         CoTaskMemFree(filePath);
727         if ( result == 0 ) return S_OK;
728     }
729     return S_FALSE;
730
731 }
732
733 /******************************************************************************
734  *        FileMoniker_Hash
735  */
736 static HRESULT WINAPI
737 FileMonikerImpl_Hash(IMoniker* iface,DWORD* pdwHash)
738 {
739     FileMonikerImpl *This = (FileMonikerImpl *)iface;
740
741     int  h = 0,i,skip,len;
742     int  off = 0;
743     LPOLESTR val;
744
745     if (pdwHash==NULL)
746         return E_POINTER;
747
748     val =  This->filePathName;
749     len = lstrlenW(val);
750
751     if (len < 16) {
752         for (i = len ; i > 0; i--) {
753             h = (h * 37) + val[off++];
754         }
755     } else {
756         /* only sample some characters */
757         skip = len / 8;
758         for (i = len ; i > 0; i -= skip, off += skip) {
759             h = (h * 39) + val[off];
760         }
761     }
762
763     *pdwHash=h;
764
765     return S_OK;
766 }
767
768 /******************************************************************************
769  *        FileMoniker_IsRunning
770  */
771 static HRESULT WINAPI
772 FileMonikerImpl_IsRunning(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
773                           IMoniker* pmkNewlyRunning)
774 {
775     IRunningObjectTable* rot;
776     HRESULT res;
777
778     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pmkNewlyRunning);
779
780     if ( (pmkNewlyRunning!=NULL) && (IMoniker_IsEqual(pmkNewlyRunning,iface)==S_OK) )
781         return S_OK;
782
783     if (pbc==NULL)
784         return E_POINTER;
785
786     res=IBindCtx_GetRunningObjectTable(pbc,&rot);
787
788     if (FAILED(res))
789         return res;
790
791     res = IRunningObjectTable_IsRunning(rot,iface);
792
793     IRunningObjectTable_Release(rot);
794
795     return res;
796 }
797
798 /******************************************************************************
799  *        FileMoniker_GetTimeOfLastChange
800  ******************************************************************************/
801 static HRESULT WINAPI
802 FileMonikerImpl_GetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc,
803                                     IMoniker* pmkToLeft, FILETIME* pFileTime)
804 {
805     FileMonikerImpl *This = (FileMonikerImpl *)iface;
806     IRunningObjectTable* rot;
807     HRESULT res;
808     WIN32_FILE_ATTRIBUTE_DATA info;
809
810     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,pFileTime);
811
812     if (pFileTime==NULL)
813         return E_POINTER;
814
815     if (pmkToLeft!=NULL)
816         return E_INVALIDARG;
817
818     res=IBindCtx_GetRunningObjectTable(pbc,&rot);
819
820     if (FAILED(res))
821         return res;
822
823     res= IRunningObjectTable_GetTimeOfLastChange(rot,iface,pFileTime);
824
825     if (FAILED(res)){ /* the moniker is not registered */
826
827         if (!GetFileAttributesExW(This->filePathName,GetFileExInfoStandard,&info))
828             return MK_E_NOOBJECT;
829
830         *pFileTime=info.ftLastWriteTime;
831     }
832
833     return S_OK;
834 }
835
836 /******************************************************************************
837  *        FileMoniker_Inverse
838  */
839 static HRESULT WINAPI
840 FileMonikerImpl_Inverse(IMoniker* iface,IMoniker** ppmk)
841 {
842     TRACE("(%p,%p)\n",iface,ppmk);
843
844     return CreateAntiMoniker(ppmk);
845 }
846
847 /******************************************************************************
848  *        FileMoniker_CommonPrefixWith
849  */
850 static HRESULT WINAPI
851 FileMonikerImpl_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix)
852 {
853
854     LPOLESTR pathThis,pathOther,*stringTable1,*stringTable2,commonPath;
855     IBindCtx *pbind;
856     DWORD mkSys;
857     ULONG nb1,nb2,i,sameIdx;
858     BOOL machimeNameCase=FALSE;
859
860     if (ppmkPrefix==NULL)
861         return E_POINTER;
862
863     if (pmkOther==NULL)
864         return E_INVALIDARG;
865
866     *ppmkPrefix=0;
867
868     /* check if we have the same type of moniker */
869     IMoniker_IsSystemMoniker(pmkOther,&mkSys);
870
871     if(mkSys==MKSYS_FILEMONIKER){
872         HRESULT ret;
873
874         CreateBindCtx(0,&pbind);
875
876         /* create a string based on common part of the two paths */
877
878         IMoniker_GetDisplayName(iface,pbind,NULL,&pathThis);
879         IMoniker_GetDisplayName(pmkOther,pbind,NULL,&pathOther);
880
881         nb1=FileMonikerImpl_DecomposePath(pathThis,&stringTable1);
882         nb2=FileMonikerImpl_DecomposePath(pathOther,&stringTable2);
883
884         if (nb1==0 || nb2==0)
885             return MK_E_NOPREFIX;
886
887         commonPath=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(min(lstrlenW(pathThis),lstrlenW(pathOther))+1));
888
889         *commonPath=0;
890
891         for(sameIdx=0; ( (stringTable1[sameIdx]!=NULL) &&
892                          (stringTable2[sameIdx]!=NULL) &&
893                          (lstrcmpiW(stringTable1[sameIdx],stringTable2[sameIdx])==0)); sameIdx++);
894
895         if (sameIdx > 1 && *stringTable1[0]=='\\' && *stringTable2[1]=='\\'){
896
897             machimeNameCase=TRUE;
898
899             for(i=2;i<sameIdx;i++)
900
901                 if( (*stringTable1[i]=='\\') && (i+1 < sameIdx) && (*stringTable1[i+1]=='\\') ){
902                     machimeNameCase=FALSE;
903                     break;
904             }
905         }
906
907         if (machimeNameCase && *stringTable1[sameIdx-1]=='\\')
908             sameIdx--;
909
910         if (machimeNameCase && (sameIdx<=3) && (nb1 > 3 || nb2 > 3) )
911             ret = MK_E_NOPREFIX;
912         else
913         {
914             for(i=0;i<sameIdx;i++)
915                 strcatW(commonPath,stringTable1[i]);
916     
917             for(i=0;i<nb1;i++)
918                 CoTaskMemFree(stringTable1[i]);
919     
920             CoTaskMemFree(stringTable1);
921     
922             for(i=0;i<nb2;i++)
923                 CoTaskMemFree(stringTable2[i]);
924     
925             CoTaskMemFree(stringTable2);
926     
927             ret = CreateFileMoniker(commonPath,ppmkPrefix);
928         }
929         HeapFree(GetProcessHeap(),0,commonPath);
930         return ret;
931     }
932     else
933         return MonikerCommonPrefixWith(iface,pmkOther,ppmkPrefix);
934 }
935
936 /******************************************************************************
937  *        DecomposePath (local function)
938  */
939 int FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable)
940 {
941     static const WCHAR bSlash[] = {'\\',0};
942     WCHAR word[MAX_PATH];
943     int i=0,j,tabIndex=0;
944     LPOLESTR *strgtable ;
945
946     int len=lstrlenW(str);
947
948     TRACE("%s, %p\n", debugstr_w(str), *stringTable);
949
950     strgtable =CoTaskMemAlloc(len*sizeof(LPOLESTR));
951
952     if (strgtable==NULL)
953         return E_OUTOFMEMORY;
954
955     while(str[i]!=0){
956
957         if(str[i]==bSlash[0]){
958
959             strgtable[tabIndex]=CoTaskMemAlloc(2*sizeof(WCHAR));
960
961             if (strgtable[tabIndex]==NULL)
962                 return E_OUTOFMEMORY;
963
964             strcpyW(strgtable[tabIndex++],bSlash);
965
966             i++;
967
968         }
969         else {
970
971             for(j=0; str[i]!=0 && str[i]!=bSlash[0] ; i++,j++)
972                 word[j]=str[i];
973
974             word[j]=0;
975
976             strgtable[tabIndex]=CoTaskMemAlloc(sizeof(WCHAR)*(j+1));
977
978             if (strgtable[tabIndex]==NULL)
979                 return E_OUTOFMEMORY;
980
981             strcpyW(strgtable[tabIndex++],word);
982         }
983     }
984     strgtable[tabIndex]=NULL;
985
986     *stringTable=strgtable;
987
988     return tabIndex;
989 }
990
991 /******************************************************************************
992  *        FileMoniker_RelativePathTo
993  */
994 static HRESULT WINAPI
995 FileMonikerImpl_RelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath)
996 {
997     IBindCtx *bind;
998     HRESULT res;
999     LPOLESTR str1=0,str2=0,*tabStr1=0,*tabStr2=0,relPath=0;
1000     DWORD len1=0,len2=0,sameIdx=0,j=0;
1001     static const WCHAR back[] ={'.','.','\\',0};
1002
1003     TRACE("(%p,%p,%p)\n",iface,pmOther,ppmkRelPath);
1004
1005     if (ppmkRelPath==NULL)
1006         return E_POINTER;
1007
1008     if (pmOther==NULL)
1009         return E_INVALIDARG;
1010
1011     res=CreateBindCtx(0,&bind);
1012     if (FAILED(res))
1013         return res;
1014
1015     res=IMoniker_GetDisplayName(iface,bind,NULL,&str1);
1016     if (FAILED(res))
1017         return res;
1018     res=IMoniker_GetDisplayName(pmOther,bind,NULL,&str2);
1019     if (FAILED(res))
1020         return res;
1021
1022     len1=FileMonikerImpl_DecomposePath(str1,&tabStr1);
1023     len2=FileMonikerImpl_DecomposePath(str2,&tabStr2);
1024
1025     if (FAILED(len1) || FAILED(len2))
1026         return E_OUTOFMEMORY;
1027
1028     /* count the number of similar items from the begin of the two paths */
1029     for(sameIdx=0; ( (tabStr1[sameIdx]!=NULL) &&
1030                    (tabStr2[sameIdx]!=NULL) &&
1031                (lstrcmpiW(tabStr1[sameIdx],tabStr2[sameIdx])==0)); sameIdx++);
1032
1033     /* begin the construction of relativePath */
1034     /* if the two paths have a consecutive similar item from the begin ! the relativePath will be composed */
1035     /* by "..\\" in the begin */
1036     relPath=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(1+lstrlenW(str1)+lstrlenW(str2)));
1037
1038     *relPath=0;
1039
1040     if (len2>0 && !(len1==1 && len2==1 && sameIdx==0))
1041         for(j=sameIdx;(tabStr1[j] != NULL); j++)
1042             if (*tabStr1[j]!='\\')
1043                 strcatW(relPath,back);
1044
1045     /* add items of the second path (similar items with the first path are not included) to the relativePath */
1046     for(j=sameIdx;tabStr2[j]!=NULL;j++)
1047         strcatW(relPath,tabStr2[j]);
1048
1049     res=CreateFileMoniker(relPath,ppmkRelPath);
1050
1051     for(j=0; tabStr1[j]!=NULL;j++)
1052         CoTaskMemFree(tabStr1[j]);
1053     for(j=0; tabStr2[j]!=NULL;j++)
1054         CoTaskMemFree(tabStr2[j]);
1055     CoTaskMemFree(tabStr1);
1056     CoTaskMemFree(tabStr2);
1057     CoTaskMemFree(str1);
1058     CoTaskMemFree(str2);
1059     HeapFree(GetProcessHeap(),0,relPath);
1060
1061     if (len1==0 || len2==0 || (len1==1 && len2==1 && sameIdx==0))
1062         return MK_S_HIM;
1063
1064     return res;
1065 }
1066
1067 /******************************************************************************
1068  *        FileMoniker_GetDisplayName
1069  */
1070 static HRESULT WINAPI
1071 FileMonikerImpl_GetDisplayName(IMoniker* iface, IBindCtx* pbc,
1072                                IMoniker* pmkToLeft, LPOLESTR *ppszDisplayName)
1073 {
1074     FileMonikerImpl *This = (FileMonikerImpl *)iface;
1075
1076     int len=lstrlenW(This->filePathName);
1077
1078     TRACE("(%p,%p,%p,%p)\n",iface,pbc,pmkToLeft,ppszDisplayName);
1079
1080     if (ppszDisplayName==NULL)
1081         return E_POINTER;
1082
1083     if (pmkToLeft!=NULL)
1084         return E_INVALIDARG;
1085
1086     *ppszDisplayName=CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
1087     if (*ppszDisplayName==NULL)
1088         return E_OUTOFMEMORY;
1089
1090     strcpyW(*ppszDisplayName,This->filePathName);
1091
1092     TRACE("-- %s\n", debugstr_w(*ppszDisplayName));
1093     
1094     return S_OK;
1095 }
1096
1097 /******************************************************************************
1098  *        FileMoniker_ParseDisplayName
1099  */
1100 static HRESULT WINAPI
1101 FileMonikerImpl_ParseDisplayName(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft,
1102                      LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut)
1103 {
1104     FIXME("(%p,%p,%p,%p,%p,%p),stub!\n",iface,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut);
1105     return E_NOTIMPL;
1106 }
1107
1108 /******************************************************************************
1109  *        FileMoniker_IsSystemMoniker
1110  */
1111 static HRESULT WINAPI
1112 FileMonikerImpl_IsSystemMoniker(IMoniker* iface,DWORD* pwdMksys)
1113 {
1114     TRACE("(%p,%p)\n",iface,pwdMksys);
1115
1116     if (!pwdMksys)
1117         return E_POINTER;
1118
1119     (*pwdMksys)=MKSYS_FILEMONIKER;
1120
1121     return S_OK;
1122 }
1123
1124 /*******************************************************************************
1125  *        FileMonikerIROTData_QueryInterface
1126  */
1127 static HRESULT WINAPI
1128 FileMonikerROTDataImpl_QueryInterface(IROTData *iface,REFIID riid,VOID** ppvObject)
1129 {
1130
1131     ICOM_THIS_From_IROTData(IMoniker, iface);
1132
1133     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppvObject);
1134
1135     return FileMonikerImpl_QueryInterface(This, riid, ppvObject);
1136 }
1137
1138 /***********************************************************************
1139  *        FileMonikerIROTData_AddRef
1140  */
1141 static ULONG WINAPI
1142 FileMonikerROTDataImpl_AddRef(IROTData *iface)
1143 {
1144     ICOM_THIS_From_IROTData(IMoniker, iface);
1145
1146     TRACE("(%p)\n",This);
1147
1148     return IMoniker_AddRef(This);
1149 }
1150
1151 /***********************************************************************
1152  *        FileMonikerIROTData_Release
1153  */
1154 static ULONG WINAPI
1155 FileMonikerROTDataImpl_Release(IROTData* iface)
1156 {
1157     ICOM_THIS_From_IROTData(IMoniker, iface);
1158
1159     TRACE("(%p)\n",This);
1160
1161     return FileMonikerImpl_Release(This);
1162 }
1163
1164 /******************************************************************************
1165  *        FileMonikerIROTData_GetComparaisonData
1166  */
1167 static HRESULT WINAPI
1168 FileMonikerROTDataImpl_GetComparaisonData(IROTData* iface, BYTE* pbData,
1169                                           ULONG cbMax, ULONG* pcbData)
1170 {
1171     FIXME("(),stub!\n");
1172     return E_NOTIMPL;
1173 }
1174
1175 /*
1176  * Virtual function table for the FileMonikerImpl class which include IPersist,
1177  * IPersistStream and IMoniker functions.
1178  */
1179 static IMonikerVtbl VT_FileMonikerImpl =
1180 {
1181     FileMonikerImpl_QueryInterface,
1182     FileMonikerImpl_AddRef,
1183     FileMonikerImpl_Release,
1184     FileMonikerImpl_GetClassID,
1185     FileMonikerImpl_IsDirty,
1186     FileMonikerImpl_Load,
1187     FileMonikerImpl_Save,
1188     FileMonikerImpl_GetSizeMax,
1189     FileMonikerImpl_BindToObject,
1190     FileMonikerImpl_BindToStorage,
1191     FileMonikerImpl_Reduce,
1192     FileMonikerImpl_ComposeWith,
1193     FileMonikerImpl_Enum,
1194     FileMonikerImpl_IsEqual,
1195     FileMonikerImpl_Hash,
1196     FileMonikerImpl_IsRunning,
1197     FileMonikerImpl_GetTimeOfLastChange,
1198     FileMonikerImpl_Inverse,
1199     FileMonikerImpl_CommonPrefixWith,
1200     FileMonikerImpl_RelativePathTo,
1201     FileMonikerImpl_GetDisplayName,
1202     FileMonikerImpl_ParseDisplayName,
1203     FileMonikerImpl_IsSystemMoniker
1204 };
1205
1206 /* Virtual function table for the IROTData class. */
1207 static IROTDataVtbl VT_ROTDataImpl =
1208 {
1209     FileMonikerROTDataImpl_QueryInterface,
1210     FileMonikerROTDataImpl_AddRef,
1211     FileMonikerROTDataImpl_Release,
1212     FileMonikerROTDataImpl_GetComparaisonData
1213 };
1214
1215 /******************************************************************************
1216  *         FileMoniker_Construct (local function)
1217  */
1218 static HRESULT WINAPI
1219 FileMonikerImpl_Construct(FileMonikerImpl* This, LPCOLESTR lpszPathName)
1220 {
1221     int nb=0,i;
1222     int sizeStr=lstrlenW(lpszPathName);
1223     LPOLESTR *tabStr=0;
1224     static const WCHAR twoPoint[]={'.','.',0};
1225     static const WCHAR bkSlash[]={'\\',0};
1226     BYTE addBkSlash;
1227
1228     TRACE("(%p,%s)\n",This,debugstr_w(lpszPathName));
1229
1230     /* Initialize the virtual fgunction table. */
1231     This->lpvtbl1      = &VT_FileMonikerImpl;
1232     This->lpvtbl2      = &VT_ROTDataImpl;
1233     This->ref          = 0;
1234
1235     This->filePathName=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(sizeStr+1));
1236
1237     if (This->filePathName==NULL)
1238         return E_OUTOFMEMORY;
1239
1240     strcpyW(This->filePathName,lpszPathName);
1241
1242     nb=FileMonikerImpl_DecomposePath(This->filePathName,&tabStr);
1243
1244     if (nb > 0 ){
1245
1246         addBkSlash=1;
1247         if (lstrcmpW(tabStr[0],twoPoint)!=0)
1248             addBkSlash=0;
1249         else
1250             for(i=0;i<nb;i++){
1251
1252                 if ( (lstrcmpW(tabStr[i],twoPoint)!=0) && (lstrcmpW(tabStr[i],bkSlash)!=0) ){
1253                     addBkSlash=0;
1254                     break;
1255                 }
1256                 else
1257
1258                     if (lstrcmpW(tabStr[i],bkSlash)==0 && i<nb-1 && lstrcmpW(tabStr[i+1],bkSlash)==0){
1259                         *tabStr[i]=0;
1260                         sizeStr--;
1261                         addBkSlash=0;
1262                         break;
1263                     }
1264             }
1265
1266         if (lstrcmpW(tabStr[nb-1],bkSlash)==0)
1267             addBkSlash=0;
1268
1269         This->filePathName=HeapReAlloc(GetProcessHeap(),0,This->filePathName,(sizeStr+1)*sizeof(WCHAR));
1270
1271         *This->filePathName=0;
1272
1273         for(i=0;tabStr[i]!=NULL;i++)
1274             strcatW(This->filePathName,tabStr[i]);
1275
1276         if (addBkSlash)
1277             strcatW(This->filePathName,bkSlash);
1278     }
1279
1280     for(i=0; tabStr[i]!=NULL;i++)
1281         CoTaskMemFree(tabStr[i]);
1282     CoTaskMemFree(tabStr);
1283
1284     return S_OK;
1285 }
1286
1287 /******************************************************************************
1288  *        CreateFileMoniker (OLE32.@)
1289  ******************************************************************************/
1290 HRESULT WINAPI CreateFileMoniker(LPCOLESTR lpszPathName, LPMONIKER * ppmk)
1291 {
1292     FileMonikerImpl* newFileMoniker;
1293     HRESULT  hr;
1294
1295     TRACE("(%s,%p)\n",debugstr_w(lpszPathName),ppmk);
1296
1297     if (!ppmk)
1298         return E_POINTER;
1299
1300     if(!lpszPathName)
1301         return MK_E_SYNTAX;
1302
1303     *ppmk=NULL;
1304
1305     newFileMoniker = HeapAlloc(GetProcessHeap(), 0, sizeof(FileMonikerImpl));
1306
1307     if (!newFileMoniker)
1308         return E_OUTOFMEMORY;
1309
1310     hr = FileMonikerImpl_Construct(newFileMoniker,lpszPathName);
1311
1312     if (SUCCEEDED(hr))
1313         hr = FileMonikerImpl_QueryInterface((IMoniker*)newFileMoniker,&IID_IMoniker,(void**)ppmk);
1314     else
1315         HeapFree(GetProcessHeap(),0,newFileMoniker);
1316
1317     return hr;
1318 }