gdi32: Add an AlphaBlend entry point in the DIB driver.
[wine] / dlls / shell32 / shelllink.c
1 /*
2  *
3  *      Copyright 1997  Marcus Meissner
4  *      Copyright 1998  Juergen Schmied
5  *      Copyright 2005  Mike McCormack
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  * NOTES
22  *   Nearly complete information about the binary formats
23  *   of .lnk files available at http://www.wotsit.org
24  *
25  *  You can use winedump to examine the contents of a link file:
26  *   winedump lnk sc.lnk
27  *
28  *  MSI advertised shortcuts are totally undocumented.  They provide an
29  *   icon for a program that is not yet installed, and invoke MSI to
30  *   install the program when the shortcut is clicked on.  They are
31  *   created by passing a special string to SetPath, and the information
32  *   in that string is parsed an stored.
33  */
34
35 #define COBJMACROS
36 #define NONAMELESSUNION
37
38 #include "wine/debug.h"
39 #include "winerror.h"
40 #include "windef.h"
41 #include "winbase.h"
42 #include "winnls.h"
43 #include "winreg.h"
44
45 #include "winuser.h"
46 #include "wingdi.h"
47 #include "shlobj.h"
48 #include "undocshell.h"
49
50 #include "pidl.h"
51 #include "shell32_main.h"
52 #include "shlguid.h"
53 #include "shlwapi.h"
54 #include "msi.h"
55 #include "appmgmt.h"
56
57 #include "initguid.h"
58
59 WINE_DEFAULT_DEBUG_CHANNEL(shell);
60
61 DEFINE_GUID( SHELL32_AdvtShortcutProduct,
62        0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
63 DEFINE_GUID( SHELL32_AdvtShortcutComponent,
64        0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
65
66 /* link file formats */
67
68 #include "pshpack1.h"
69
70 typedef struct _LINK_HEADER
71 {
72         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
73         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
74         DWORD    dwFlags;       /* 0x14 describes elements following */
75         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
76         FILETIME Time1;         /* 0x1c */
77         FILETIME Time2;         /* 0x24 */
78         FILETIME Time3;         /* 0x2c */
79         DWORD    dwFileLength;  /* 0x34 File length */
80         DWORD    nIcon;         /* 0x38 icon number */
81         DWORD   fStartup;       /* 0x3c startup type */
82         DWORD   wHotKey;        /* 0x40 hotkey */
83         DWORD   Unknown5;       /* 0x44 */
84         DWORD   Unknown6;       /* 0x48 */
85 } LINK_HEADER, * PLINK_HEADER;
86
87 #define SHLINK_LOCAL  0
88 #define SHLINK_REMOTE 1
89
90 typedef struct _LOCATION_INFO
91 {
92     DWORD  dwTotalSize;
93     DWORD  dwHeaderSize;
94     DWORD  dwFlags;
95     DWORD  dwVolTableOfs;
96     DWORD  dwLocalPathOfs;
97     DWORD  dwNetworkVolTableOfs;
98     DWORD  dwFinalPathOfs;
99 } LOCATION_INFO;
100
101 typedef struct _LOCAL_VOLUME_INFO
102 {
103     DWORD dwSize;
104     DWORD dwType;
105     DWORD dwVolSerial;
106     DWORD dwVolLabelOfs;
107 } LOCAL_VOLUME_INFO;
108
109 typedef struct volume_info_t
110 {
111     DWORD type;
112     DWORD serial;
113     WCHAR label[12];  /* assume 8.3 */
114 } volume_info;
115
116 #include "poppack.h"
117
118 static const IShellLinkAVtbl slvt;
119 static const IShellLinkWVtbl slvtw;
120 static const IPersistFileVtbl pfvt;
121 static const IPersistStreamVtbl psvt;
122 static const IShellLinkDataListVtbl dlvt;
123 static const IShellExtInitVtbl eivt;
124 static const IContextMenuVtbl cmvt;
125 static const IObjectWithSiteVtbl owsvt;
126
127 /* IShellLink Implementation */
128
129 typedef struct
130 {
131         IShellLinkA IShellLinkA_iface;
132         IShellLinkW IShellLinkW_iface;
133         IPersistFile IPersistFile_iface;
134         IPersistStream IPersistStream_iface;
135         IShellLinkDataList IShellLinkDataList_iface;
136         IShellExtInit IShellExtInit_iface;
137         IContextMenu IContextMenu_iface;
138         IObjectWithSite IObjectWithSite_iface;
139
140         LONG            ref;
141
142         /* data structures according to the information in the link */
143         LPITEMIDLIST    pPidl;
144         WORD            wHotKey;
145         SYSTEMTIME      time1;
146         SYSTEMTIME      time2;
147         SYSTEMTIME      time3;
148
149         DWORD         iShowCmd;
150         LPWSTR        sIcoPath;
151         INT           iIcoNdx;
152         LPWSTR        sPath;
153         LPWSTR        sArgs;
154         LPWSTR        sWorkDir;
155         LPWSTR        sDescription;
156         LPWSTR        sPathRel;
157         LPWSTR        sProduct;
158         LPWSTR        sComponent;
159         volume_info   volume;
160
161         BOOL          bDirty;
162         INT           iIdOpen;  /* id of the "Open" entry in the context menu */
163         IUnknown      *site;
164
165         LPOLESTR      filepath; /* file path returned by IPersistFile::GetCurFile */
166 } IShellLinkImpl;
167
168 static inline IShellLinkImpl *impl_from_IShellLinkA(IShellLinkA *iface)
169 {
170     return CONTAINING_RECORD(iface, IShellLinkImpl, IShellLinkA_iface);
171 }
172
173 static inline IShellLinkImpl *impl_from_IShellLinkW(IShellLinkW *iface)
174 {
175     return CONTAINING_RECORD(iface, IShellLinkImpl, IShellLinkW_iface);
176 }
177
178 static inline IShellLinkImpl *impl_from_IPersistFile(IPersistFile *iface)
179 {
180     return CONTAINING_RECORD(iface, IShellLinkImpl, IPersistFile_iface);
181 }
182
183 static inline IShellLinkImpl *impl_from_IPersistStream(IPersistStream *iface)
184 {
185     return CONTAINING_RECORD(iface, IShellLinkImpl, IPersistStream_iface);
186 }
187
188 static inline IShellLinkImpl *impl_from_IShellLinkDataList(IShellLinkDataList *iface)
189 {
190     return CONTAINING_RECORD(iface, IShellLinkImpl, IShellLinkDataList_iface);
191 }
192
193 static inline IShellLinkImpl *impl_from_IShellExtInit(IShellExtInit *iface)
194 {
195     return CONTAINING_RECORD(iface, IShellLinkImpl, IShellExtInit_iface);
196 }
197
198 static inline IShellLinkImpl *impl_from_IContextMenu(IContextMenu *iface)
199 {
200     return CONTAINING_RECORD(iface, IShellLinkImpl, IContextMenu_iface);
201 }
202
203 static inline IShellLinkImpl *impl_from_IObjectWithSite(IObjectWithSite *iface)
204 {
205     return CONTAINING_RECORD(iface, IShellLinkImpl, IObjectWithSite_iface);
206 }
207
208 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
209
210 /* strdup on the process heap */
211 static inline LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
212 {
213     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
214     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
215     if( !p )
216         return p;
217     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
218     return p;
219 }
220
221 static inline LPWSTR strdupW( LPCWSTR src )
222 {
223     LPWSTR dest;
224     if (!src) return NULL;
225     dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
226     if (dest)
227         lstrcpyW(dest, src);
228     return dest;
229 }
230
231 /**************************************************************************
232  *  ShellLink::QueryInterface implementation
233  */
234 static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
235 {
236     TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
237
238     *ppvObj = NULL;
239
240     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
241     {
242         *ppvObj = This;
243     }
244     else if(IsEqualIID(riid, &IID_IShellLinkW))
245     {
246         *ppvObj = &This->IShellLinkW_iface;
247     }
248     else if(IsEqualIID(riid, &IID_IPersistFile))
249     {
250         *ppvObj = &This->IPersistFile_iface;
251     }
252     else if(IsEqualIID(riid, &IID_IPersistStream))
253     {
254         *ppvObj = &This->IPersistStream_iface;
255     }
256     else if(IsEqualIID(riid, &IID_IShellLinkDataList))
257     {
258         *ppvObj = &This->IShellLinkDataList_iface;
259     }
260     else if(IsEqualIID(riid, &IID_IShellExtInit))
261     {
262         *ppvObj = &This->IShellExtInit_iface;
263     }
264     else if(IsEqualIID(riid, &IID_IContextMenu))
265     {
266         *ppvObj = &This->IContextMenu_iface;
267     }
268     else if(IsEqualIID(riid, &IID_IObjectWithSite))
269     {
270         *ppvObj = &This->IObjectWithSite_iface;
271     }
272
273     if(*ppvObj)
274     {
275         IUnknown_AddRef((IUnknown*)(*ppvObj));
276         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
277         return S_OK;
278     }
279     ERR("-- Interface: E_NOINTERFACE\n");
280     return E_NOINTERFACE;
281 }
282
283 /**************************************************************************
284  *  ShellLink::AddRef implementation
285  */
286 static ULONG ShellLink_AddRef( IShellLinkImpl *This )
287 {
288     ULONG refCount = InterlockedIncrement(&This->ref);
289
290     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
291
292     return refCount;
293 }
294
295 /**************************************************************************
296  *  ShellLink::Release implementation
297  */
298 static ULONG ShellLink_Release( IShellLinkImpl *This )
299 {
300     ULONG refCount = InterlockedDecrement(&This->ref);
301
302     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
303
304     if (refCount)
305         return refCount;
306
307     TRACE("-- destroying IShellLink(%p)\n",This);
308
309     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
310     HeapFree(GetProcessHeap(), 0, This->sArgs);
311     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
312     HeapFree(GetProcessHeap(), 0, This->sDescription);
313     HeapFree(GetProcessHeap(), 0, This->sPath);
314     HeapFree(GetProcessHeap(), 0, This->sPathRel);
315     HeapFree(GetProcessHeap(), 0, This->sProduct);
316     HeapFree(GetProcessHeap(), 0, This->sComponent);
317     HeapFree(GetProcessHeap(), 0, This->filepath);
318
319     if (This->site)
320         IUnknown_Release( This->site );
321
322     if (This->pPidl)
323         ILFree(This->pPidl);
324
325     LocalFree(This);
326
327     return 0;
328 }
329
330 static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
331 {
332     TRACE("%p %p\n", This, pclsid);
333
334     *pclsid = CLSID_ShellLink;
335     return S_OK;
336 }
337
338 /**************************************************************************
339  *  IPersistFile_QueryInterface
340  */
341 static HRESULT WINAPI IPersistFile_fnQueryInterface(
342         IPersistFile* iface,
343         REFIID riid,
344         LPVOID *ppvObj)
345 {
346     IShellLinkImpl *This = impl_from_IPersistFile(iface);
347     return ShellLink_QueryInterface( This, riid, ppvObj );
348 }
349
350 /******************************************************************************
351  * IPersistFile_AddRef
352  */
353 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
354 {
355     IShellLinkImpl *This = impl_from_IPersistFile(iface);
356     return ShellLink_AddRef( This );
357 }
358
359 /******************************************************************************
360  * IPersistFile_Release
361  */
362 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
363 {
364     IShellLinkImpl *This = impl_from_IPersistFile(iface);
365     return IShellLinkA_Release(&This->IShellLinkA_iface);
366 }
367
368 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
369 {
370     IShellLinkImpl *This = impl_from_IPersistFile(iface);
371     return ShellLink_GetClassID( This, pClassID );
372 }
373
374 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
375 {
376         IShellLinkImpl *This = impl_from_IPersistFile(iface);
377
378         TRACE("(%p)\n",This);
379
380         if (This->bDirty)
381             return S_OK;
382
383         return S_FALSE;
384 }
385
386 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
387 {
388         IShellLinkImpl *This = impl_from_IPersistFile(iface);
389         IPersistStream *StreamThis = &This->IPersistStream_iface;
390         HRESULT r;
391         IStream *stm;
392
393         TRACE("(%p, %s, %x)\n",This, debugstr_w(pszFileName), dwMode);
394
395         if( dwMode == 0 )
396                 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
397         r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
398         if( SUCCEEDED( r ) )
399         {
400             r = IPersistStream_Load(StreamThis, stm);
401             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
402             IStream_Release( stm );
403
404             /* update file path */
405             HeapFree(GetProcessHeap(), 0, This->filepath);
406             This->filepath = strdupW(pszFileName);
407
408             This->bDirty = FALSE;
409         }
410         TRACE("-- returning hr %08x\n", r);
411         return r;
412 }
413
414 BOOL run_winemenubuilder( const WCHAR *args )
415 {
416     static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
417     LONG len;
418     LPWSTR buffer;
419     STARTUPINFOW si;
420     PROCESS_INFORMATION pi;
421     BOOL ret;
422     WCHAR app[MAX_PATH];
423     void *redir;
424
425     GetSystemDirectoryW( app, MAX_PATH - sizeof(menubuilder)/sizeof(WCHAR) );
426     strcatW( app, menubuilder );
427
428     len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR);
429     buffer = HeapAlloc( GetProcessHeap(), 0, len );
430     if( !buffer )
431         return FALSE;
432
433     strcpyW( buffer, app );
434     strcatW( buffer, args );
435
436     TRACE("starting %s\n",debugstr_w(buffer));
437
438     memset(&si, 0, sizeof(si));
439     si.cb = sizeof(si);
440
441     Wow64DisableWow64FsRedirection( &redir );
442     ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
443     Wow64RevertWow64FsRedirection( redir );
444
445     HeapFree( GetProcessHeap(), 0, buffer );
446
447     if (ret)
448     {
449         CloseHandle( pi.hProcess );
450         CloseHandle( pi.hThread );
451     }
452
453     return ret;
454 }
455
456 static BOOL StartLinkProcessor( LPCOLESTR szLink )
457 {
458     static const WCHAR szFormat[] = {' ','-','w',' ','"','%','s','"',0 };
459     LONG len;
460     LPWSTR buffer;
461     BOOL ret;
462
463     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
464     buffer = HeapAlloc( GetProcessHeap(), 0, len );
465     if( !buffer )
466         return FALSE;
467
468     wsprintfW( buffer, szFormat, szLink );
469     ret = run_winemenubuilder( buffer );
470     HeapFree( GetProcessHeap(), 0, buffer );
471     return ret;
472 }
473
474 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
475 {
476     IShellLinkImpl *This = impl_from_IPersistFile(iface);
477     IPersistStream *StreamThis = &This->IPersistStream_iface;
478     HRESULT r;
479     IStream *stm;
480
481     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
482
483     if (!pszFileName)
484         return E_FAIL;
485
486     r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
487     if( SUCCEEDED( r ) )
488     {
489         r = IPersistStream_Save(StreamThis, stm, FALSE);
490         IStream_Release( stm );
491
492         if( SUCCEEDED( r ) )
493         {
494             StartLinkProcessor( pszFileName );
495
496             /* update file path */
497             HeapFree(GetProcessHeap(), 0, This->filepath);
498             This->filepath = strdupW(pszFileName);
499
500             This->bDirty = FALSE;
501         }
502         else
503         {
504             DeleteFileW( pszFileName );
505             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
506         }
507     }
508
509     return r;
510 }
511
512 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
513 {
514         IShellLinkImpl *This = impl_from_IPersistFile(iface);
515         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
516         return NOERROR;
517 }
518
519 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *filename)
520 {
521     IShellLinkImpl *This = impl_from_IPersistFile(iface);
522     IMalloc *pMalloc;
523
524     TRACE("(%p)->(%p)\n", This, filename);
525
526     if (!This->filepath)
527     {
528         *filename = NULL;
529         return S_FALSE;
530     }
531
532     SHGetMalloc(&pMalloc);
533     *filename = IMalloc_Alloc(pMalloc, (strlenW(This->filepath)+1)*sizeof(WCHAR));
534     if (!*filename) return E_OUTOFMEMORY;
535
536     strcpyW(*filename, This->filepath);
537
538     return S_OK;
539 }
540
541 static const IPersistFileVtbl pfvt =
542 {
543         IPersistFile_fnQueryInterface,
544         IPersistFile_fnAddRef,
545         IPersistFile_fnRelease,
546         IPersistFile_fnGetClassID,
547         IPersistFile_fnIsDirty,
548         IPersistFile_fnLoad,
549         IPersistFile_fnSave,
550         IPersistFile_fnSaveCompleted,
551         IPersistFile_fnGetCurFile
552 };
553
554 /************************************************************************
555  * IPersistStream_QueryInterface
556  */
557 static HRESULT WINAPI IPersistStream_fnQueryInterface(
558         IPersistStream* iface,
559         REFIID     riid,
560         VOID**     ppvObj)
561 {
562     IShellLinkImpl *This = impl_from_IPersistStream(iface);
563     return ShellLink_QueryInterface( This, riid, ppvObj );
564 }
565
566 /************************************************************************
567  * IPersistStream_Release
568  */
569 static ULONG WINAPI IPersistStream_fnRelease(
570         IPersistStream* iface)
571 {
572     IShellLinkImpl *This = impl_from_IPersistStream(iface);
573     return IShellLinkA_Release(&This->IShellLinkA_iface);
574 }
575
576 /************************************************************************
577  * IPersistStream_AddRef
578  */
579 static ULONG WINAPI IPersistStream_fnAddRef(
580         IPersistStream* iface)
581 {
582     IShellLinkImpl *This = impl_from_IPersistStream(iface);
583     return ShellLink_AddRef( This );
584 }
585
586 /************************************************************************
587  * IPersistStream_GetClassID
588  *
589  */
590 static HRESULT WINAPI IPersistStream_fnGetClassID(
591         IPersistStream* iface,
592         CLSID* pClassID)
593 {
594     IShellLinkImpl *This = impl_from_IPersistStream(iface);
595     return ShellLink_GetClassID( This, pClassID );
596 }
597
598 /************************************************************************
599  * IPersistStream_IsDirty (IPersistStream)
600  */
601 static HRESULT WINAPI IPersistStream_fnIsDirty(
602         IPersistStream*  iface)
603 {
604         IShellLinkImpl *This = impl_from_IPersistStream(iface);
605
606         TRACE("(%p)\n", This);
607
608         return S_OK;
609 }
610
611
612 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
613 {
614     DWORD count;
615     USHORT len;
616     LPVOID temp;
617     LPWSTR str;
618     HRESULT r;
619
620     TRACE("%p\n", stm);
621
622     count = 0;
623     r = IStream_Read(stm, &len, sizeof(len), &count);
624     if ( FAILED (r) || ( count != sizeof(len) ) )
625         return E_FAIL;
626
627     if( unicode )
628         len *= sizeof (WCHAR);
629
630     TRACE("reading %d\n", len);
631     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
632     if( !temp )
633         return E_OUTOFMEMORY;
634     count = 0;
635     r = IStream_Read(stm, temp, len, &count);
636     if( FAILED (r) || ( count != len ) )
637     {
638         HeapFree( GetProcessHeap(), 0, temp );
639         return E_FAIL;
640     }
641
642     TRACE("read %s\n", debugstr_an(temp,len));
643
644     /* convert to unicode if necessary */
645     if( !unicode )
646     {
647         count = MultiByteToWideChar( CP_ACP, 0, temp, len, NULL, 0 );
648         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
649         if( !str )
650         {
651             HeapFree( GetProcessHeap(), 0, temp );
652             return E_OUTOFMEMORY;
653         }
654         MultiByteToWideChar( CP_ACP, 0, temp, len, str, count );
655         HeapFree( GetProcessHeap(), 0, temp );
656     }
657     else
658     {
659         count /= 2;
660         str = temp;
661     }
662     str[count] = 0;
663
664     *pstr = str;
665
666     return S_OK;
667 }
668
669 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
670 {
671     DWORD size;
672     ULONG count;
673     HRESULT r;
674     struct sized_chunk {
675         DWORD size;
676         unsigned char data[1];
677     } *chunk;
678
679     TRACE("%p\n",stm);
680
681     r = IStream_Read( stm, &size, sizeof(size), &count );
682     if( FAILED( r )  || count != sizeof(size) )
683         return E_FAIL;
684
685     chunk = HeapAlloc( GetProcessHeap(), 0, size );
686     if( !chunk )
687         return E_OUTOFMEMORY;
688
689     chunk->size = size;
690     r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
691     if( FAILED( r ) || count != (size - sizeof(size)) )
692     {
693         HeapFree( GetProcessHeap(), 0, chunk );
694         return E_FAIL;
695     }
696
697     TRACE("Read %d bytes\n",chunk->size);
698
699     *data = chunk;
700
701     return S_OK;
702 }
703
704 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
705 {
706     const int label_sz = sizeof volume->label/sizeof volume->label[0];
707     LPSTR label;
708     int len;
709
710     volume->serial = vol->dwVolSerial;
711     volume->type = vol->dwType;
712
713     if( !vol->dwVolLabelOfs )
714         return FALSE;
715     if( vol->dwSize <= vol->dwVolLabelOfs )
716         return FALSE;
717     len = vol->dwSize - vol->dwVolLabelOfs;
718
719     label = (LPSTR) vol;
720     label += vol->dwVolLabelOfs;
721     MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
722
723     return TRUE;
724 }
725
726 static LPWSTR Stream_LoadPath( LPCSTR p, DWORD maxlen )
727 {
728     int len = 0, wlen;
729     LPWSTR path;
730
731     while( p[len] && (len < maxlen) )
732         len++;
733
734     wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
735     path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
736     MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
737     path[wlen] = 0;
738
739     return path;
740 }
741
742 static HRESULT Stream_LoadLocation( IStream *stm,
743                 volume_info *volume, LPWSTR *path )
744 {
745     char *p = NULL;
746     LOCATION_INFO *loc;
747     HRESULT r;
748     DWORD n;
749
750     r = Stream_ReadChunk( stm, (LPVOID*) &p );
751     if( FAILED(r) )
752         return r;
753
754     loc = (LOCATION_INFO*) p;
755     if (loc->dwTotalSize < sizeof(LOCATION_INFO))
756     {
757         HeapFree( GetProcessHeap(), 0, p );
758         return E_FAIL;
759     }
760
761     /* if there's valid local volume information, load it */
762     if( loc->dwVolTableOfs && 
763        ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
764     {
765         LOCAL_VOLUME_INFO *volume_info;
766
767         volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
768         Stream_LoadVolume( volume_info, volume );
769     }
770
771     /* if there's a local path, load it */
772     n = loc->dwLocalPathOfs;
773     if( n && (n < loc->dwTotalSize) )
774         *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
775
776     TRACE("type %d serial %08x name %s path %s\n", volume->type,
777           volume->serial, debugstr_w(volume->label), debugstr_w(*path));
778
779     HeapFree( GetProcessHeap(), 0, p );
780     return S_OK;
781 }
782
783 /*
784  *  The format of the advertised shortcut info seems to be:
785  *
786  *  Offset     Description
787  *  ------     -----------
788  *
789  *    0          Length of the block (4 bytes, usually 0x314)
790  *    4          tag (dword)
791  *    8          string data in ASCII
792  *    8+0x104    string data in UNICODE
793  *
794  * In the original Win32 implementation the buffers are not initialized
795  *  to zero, so data trailing the string is random garbage.
796  */
797 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
798 {
799     DWORD size;
800     ULONG count;
801     HRESULT r;
802     EXP_DARWIN_LINK buffer;
803     
804     TRACE("%p\n",stm);
805
806     r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
807     if( FAILED( r ) )
808         return r;
809
810     /* make sure that we read the size of the structure even on error */
811     size = sizeof buffer - sizeof (DWORD);
812     if( buffer.dbh.cbSize != sizeof buffer )
813     {
814         ERR("Ooops.  This structure is not as expected...\n");
815         return E_FAIL;
816     }
817
818     r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
819     if( FAILED( r ) )
820         return r;
821
822     if( count != size )
823         return E_FAIL;
824
825     TRACE("magic %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
826
827     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
828     {
829         ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
830         return E_FAIL;
831     }
832
833     *str = HeapAlloc( GetProcessHeap(), 0, 
834                      (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
835     lstrcpyW( *str, buffer.szwDarwinID );
836
837     return S_OK;
838 }
839
840 /************************************************************************
841  * IPersistStream_Load (IPersistStream)
842  */
843 static HRESULT WINAPI IPersistStream_fnLoad(
844     IPersistStream*  iface,
845     IStream*         stm)
846 {
847     LINK_HEADER hdr;
848     ULONG    dwBytesRead;
849     BOOL     unicode;
850     HRESULT  r;
851     DWORD    zero;
852
853     IShellLinkImpl *This = impl_from_IPersistStream(iface);
854
855     TRACE("%p %p\n", This, stm);
856
857     if( !stm )
858         return STG_E_INVALIDPOINTER;
859
860     dwBytesRead = 0;
861     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
862     if( FAILED( r ) )
863         return r;
864
865     if( dwBytesRead != sizeof(hdr))
866         return E_FAIL;
867     if( hdr.dwSize != sizeof(hdr))
868         return E_FAIL;
869     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
870         return E_FAIL;
871
872     /* free all the old stuff */
873     ILFree(This->pPidl);
874     This->pPidl = NULL;
875     memset( &This->volume, 0, sizeof This->volume );
876     HeapFree(GetProcessHeap(), 0, This->sPath);
877     This->sPath = NULL;
878     HeapFree(GetProcessHeap(), 0, This->sDescription);
879     This->sDescription = NULL;
880     HeapFree(GetProcessHeap(), 0, This->sPathRel);
881     This->sPathRel = NULL;
882     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
883     This->sWorkDir = NULL;
884     HeapFree(GetProcessHeap(), 0, This->sArgs);
885     This->sArgs = NULL;
886     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
887     This->sIcoPath = NULL;
888     HeapFree(GetProcessHeap(), 0, This->sProduct);
889     This->sProduct = NULL;
890     HeapFree(GetProcessHeap(), 0, This->sComponent);
891     This->sComponent = NULL;
892         
893     This->wHotKey = (WORD)hdr.wHotKey;
894     This->iIcoNdx = hdr.nIcon;
895     FileTimeToSystemTime (&hdr.Time1, &This->time1);
896     FileTimeToSystemTime (&hdr.Time2, &This->time2);
897     FileTimeToSystemTime (&hdr.Time3, &This->time3);
898     if (TRACE_ON(shell))
899     {
900         WCHAR sTemp[MAX_PATH];
901         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
902                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
903         TRACE("-- time1: %s\n", debugstr_w(sTemp) );
904         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
905                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
906         TRACE("-- time2: %s\n", debugstr_w(sTemp) );
907         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
908                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
909         TRACE("-- time3: %s\n", debugstr_w(sTemp) );
910     }
911
912     /* load all the new stuff */
913     if( hdr.dwFlags & SLDF_HAS_ID_LIST )
914     {
915         r = ILLoadFromStream( stm, &This->pPidl );
916         if( FAILED( r ) )
917             return r;
918     }
919     pdump(This->pPidl);
920
921     /* load the location information */
922     if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
923         r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
924     if( FAILED( r ) )
925         goto end;
926
927     unicode = hdr.dwFlags & SLDF_UNICODE;
928     if( hdr.dwFlags & SLDF_HAS_NAME )
929     {
930         r = Stream_LoadString( stm, unicode, &This->sDescription );
931         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
932     }
933     if( FAILED( r ) )
934         goto end;
935
936     if( hdr.dwFlags & SLDF_HAS_RELPATH )
937     {
938         r = Stream_LoadString( stm, unicode, &This->sPathRel );
939         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
940     }
941     if( FAILED( r ) )
942         goto end;
943
944     if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
945     {
946         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
947         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
948     }
949     if( FAILED( r ) )
950         goto end;
951
952     if( hdr.dwFlags & SLDF_HAS_ARGS )
953     {
954         r = Stream_LoadString( stm, unicode, &This->sArgs );
955         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
956     }
957     if( FAILED( r ) )
958         goto end;
959
960     if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
961     {
962         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
963         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
964     }
965     if( FAILED( r ) )
966         goto end;
967
968     if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
969     {
970         r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
971         TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
972     }
973     if( FAILED( r ) )
974         goto end;
975
976     if( hdr.dwFlags & SLDF_HAS_DARWINID )
977     {
978         r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
979         TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
980     }
981     if( FAILED( r ) )
982         goto end;
983
984     r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
985     if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
986     {
987         /* Some lnk files have extra data blocks starting with a
988          * DATABLOCK_HEADER. For instance EXP_SPECIAL_FOLDER and an unknown
989          * one with a 0xa0000003 signature. However these don't seem to matter
990          * too much.
991          */
992         WARN("Last word was not zero\n");
993     }
994
995     TRACE("OK\n");
996
997     pdump (This->pPidl);
998
999     return S_OK;
1000 end:
1001     return r;
1002 }
1003
1004 /************************************************************************
1005  * Stream_WriteString
1006  *
1007  * Helper function for IPersistStream_Save. Writes a unicode string 
1008  *  with terminating nul byte to a stream, preceded by the its length.
1009  */
1010 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
1011 {
1012     USHORT len = lstrlenW( str ) + 1;
1013     DWORD count;
1014     HRESULT r;
1015
1016     r = IStream_Write( stm, &len, sizeof(len), &count );
1017     if( FAILED( r ) )
1018         return r;
1019
1020     len *= sizeof(WCHAR);
1021
1022     r = IStream_Write( stm, str, len, &count );
1023     if( FAILED( r ) )
1024         return r;
1025
1026     return S_OK;
1027 }
1028
1029 /************************************************************************
1030  * Stream_WriteLocationInfo
1031  *
1032  * Writes the location info to a stream
1033  *
1034  * FIXME: One day we might want to write the network volume information
1035  *        and the final path.
1036  *        Figure out how Windows deals with unicode paths here.
1037  */
1038 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
1039                                          volume_info *volume )
1040 {
1041     DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
1042     LOCAL_VOLUME_INFO *vol;
1043     LOCATION_INFO *loc;
1044     LPSTR szLabel, szPath, szFinalPath;
1045     ULONG count = 0;
1046     HRESULT hr;
1047
1048     TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
1049
1050     /* figure out the size of everything */
1051     label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
1052                                       NULL, 0, NULL, NULL );
1053     path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
1054                                      NULL, 0, NULL, NULL );
1055     volume_info_size = sizeof *vol + label_size;
1056     final_path_size = 1;
1057     total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
1058
1059     /* create pointers to everything */
1060     loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
1061     vol = (LOCAL_VOLUME_INFO*) &loc[1];
1062     szLabel = (LPSTR) &vol[1];
1063     szPath = &szLabel[label_size];
1064     szFinalPath = &szPath[path_size];
1065
1066     /* fill in the location information header */
1067     loc->dwTotalSize = total_size;
1068     loc->dwHeaderSize = sizeof (*loc);
1069     loc->dwFlags = 1;
1070     loc->dwVolTableOfs = sizeof (*loc);
1071     loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
1072     loc->dwNetworkVolTableOfs = 0;
1073     loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
1074
1075     /* fill in the volume information */
1076     vol->dwSize = volume_info_size;
1077     vol->dwType = volume->type;
1078     vol->dwVolSerial = volume->serial;
1079     vol->dwVolLabelOfs = sizeof (*vol);
1080
1081     /* copy in the strings */
1082     WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
1083                          szLabel, label_size, NULL, NULL );
1084     WideCharToMultiByte( CP_ACP, 0, path, -1,
1085                          szPath, path_size, NULL, NULL );
1086     szFinalPath[0] = 0;
1087
1088     hr = IStream_Write( stm, loc, total_size, &count );
1089     HeapFree(GetProcessHeap(), 0, loc);
1090
1091     return hr;
1092 }
1093
1094 static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
1095 {
1096     EXP_DARWIN_LINK *buffer;
1097     
1098     buffer = LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
1099     buffer->dbh.cbSize = sizeof *buffer;
1100     buffer->dbh.dwSignature = magic;
1101     lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
1102     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );
1103
1104     return buffer;
1105 }
1106
1107 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1108 {
1109     EXP_DARWIN_LINK *buffer;
1110     ULONG count;
1111     
1112     TRACE("%p\n",stm);
1113
1114     buffer = shelllink_build_darwinid( string, magic );
1115
1116     return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
1117 }
1118
1119 /************************************************************************
1120  * IPersistStream_Save (IPersistStream)
1121  *
1122  * FIXME: makes assumptions about byte order
1123  */
1124 static HRESULT WINAPI IPersistStream_fnSave(
1125         IPersistStream*  iface,
1126         IStream*         stm,
1127         BOOL             fClearDirty)
1128 {
1129     LINK_HEADER header;
1130     ULONG   count;
1131     DWORD   zero;
1132     HRESULT r;
1133
1134     IShellLinkImpl *This = impl_from_IPersistStream(iface);
1135
1136     TRACE("%p %p %x\n", This, stm, fClearDirty);
1137
1138     memset(&header, 0, sizeof(header));
1139     header.dwSize = sizeof(header);
1140     header.fStartup = This->iShowCmd;
1141     header.MagicGuid = CLSID_ShellLink;
1142
1143     header.wHotKey = This->wHotKey;
1144     header.nIcon = This->iIcoNdx;
1145     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1146     if( This->pPidl )
1147         header.dwFlags |= SLDF_HAS_ID_LIST;
1148     if( This->sPath )
1149         header.dwFlags |= SLDF_HAS_LINK_INFO;
1150     if( This->sDescription )
1151         header.dwFlags |= SLDF_HAS_NAME;
1152     if( This->sWorkDir )
1153         header.dwFlags |= SLDF_HAS_WORKINGDIR;
1154     if( This->sArgs )
1155         header.dwFlags |= SLDF_HAS_ARGS;
1156     if( This->sIcoPath )
1157         header.dwFlags |= SLDF_HAS_ICONLOCATION;
1158     if( This->sProduct )
1159         header.dwFlags |= SLDF_HAS_LOGO3ID;
1160     if( This->sComponent )
1161         header.dwFlags |= SLDF_HAS_DARWINID;
1162
1163     SystemTimeToFileTime ( &This->time1, &header.Time1 );
1164     SystemTimeToFileTime ( &This->time2, &header.Time2 );
1165     SystemTimeToFileTime ( &This->time3, &header.Time3 );
1166
1167     /* write the Shortcut header */
1168     r = IStream_Write( stm, &header, sizeof(header), &count );
1169     if( FAILED( r ) )
1170     {
1171         ERR("Write failed at %d\n",__LINE__);
1172         return r;
1173     }
1174
1175     TRACE("Writing pidl\n");
1176
1177     /* write the PIDL to the shortcut */
1178     if( This->pPidl )
1179     {
1180         r = ILSaveToStream( stm, This->pPidl );
1181         if( FAILED( r ) )
1182         {
1183             ERR("Failed to write PIDL at %d\n",__LINE__);
1184             return r;
1185         }
1186     }
1187
1188     if( This->sPath )
1189         Stream_WriteLocationInfo( stm, This->sPath, &This->volume );
1190
1191     if( This->sDescription )
1192         r = Stream_WriteString( stm, This->sDescription );
1193
1194     if( This->sPathRel )
1195         r = Stream_WriteString( stm, This->sPathRel );
1196
1197     if( This->sWorkDir )
1198         r = Stream_WriteString( stm, This->sWorkDir );
1199
1200     if( This->sArgs )
1201         r = Stream_WriteString( stm, This->sArgs );
1202
1203     if( This->sIcoPath )
1204         r = Stream_WriteString( stm, This->sIcoPath );
1205
1206     if( This->sProduct )
1207         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1208
1209     if( This->sComponent )
1210         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1211
1212     /* the last field is a single zero dword */
1213     zero = 0;
1214     r = IStream_Write( stm, &zero, sizeof zero, &count );
1215
1216     return S_OK;
1217 }
1218
1219 /************************************************************************
1220  * IPersistStream_GetSizeMax (IPersistStream)
1221  */
1222 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1223         IPersistStream*  iface,
1224         ULARGE_INTEGER*  pcbSize)
1225 {
1226         IShellLinkImpl *This = impl_from_IPersistStream(iface);
1227
1228         TRACE("(%p)\n", This);
1229
1230         return E_NOTIMPL;
1231 }
1232
1233 static const IPersistStreamVtbl psvt =
1234 {
1235         IPersistStream_fnQueryInterface,
1236         IPersistStream_fnAddRef,
1237         IPersistStream_fnRelease,
1238         IPersistStream_fnGetClassID,
1239         IPersistStream_fnIsDirty,
1240         IPersistStream_fnLoad,
1241         IPersistStream_fnSave,
1242         IPersistStream_fnGetSizeMax
1243 };
1244
1245 /**************************************************************************
1246  *        IShellLink_Constructor
1247  */
1248 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1249                REFIID riid, LPVOID *ppv )
1250 {
1251         IShellLinkImpl * sl;
1252         HRESULT r;
1253
1254         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1255
1256         *ppv = NULL;
1257
1258         if (pUnkOuter)
1259             return CLASS_E_NOAGGREGATION;
1260         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1261         if (!sl)
1262             return E_OUTOFMEMORY;
1263
1264         sl->ref = 1;
1265         sl->IShellLinkA_iface.lpVtbl = &slvt;
1266         sl->IShellLinkW_iface.lpVtbl = &slvtw;
1267         sl->IPersistFile_iface.lpVtbl = &pfvt;
1268         sl->IPersistStream_iface.lpVtbl = &psvt;
1269         sl->IShellLinkDataList_iface.lpVtbl = &dlvt;
1270         sl->IShellExtInit_iface.lpVtbl = &eivt;
1271         sl->IContextMenu_iface.lpVtbl = &cmvt;
1272         sl->IObjectWithSite_iface.lpVtbl = &owsvt;
1273         sl->iShowCmd = SW_SHOWNORMAL;
1274         sl->bDirty = FALSE;
1275         sl->iIdOpen = -1;
1276         sl->site = NULL;
1277         sl->filepath = NULL;
1278
1279         TRACE("(%p)->()\n",sl);
1280
1281         r = ShellLink_QueryInterface( sl, riid, ppv );
1282         ShellLink_Release( sl );
1283         return r;
1284 }
1285
1286
1287 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1288 {
1289     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1290         return FALSE;
1291     return TRUE;
1292 }
1293
1294 /**************************************************************************
1295  *  ShellLink_UpdatePath
1296  *      update absolute path in sPath using relative path in sPathRel
1297  */
1298 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1299 {
1300     if (!path || !psPath)
1301         return E_INVALIDARG;
1302
1303     if (!*psPath && sPathRel) {
1304         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1305         LPWSTR final = NULL;
1306
1307         /* first try if [directory of link file] + [relative path] finds an existing file */
1308
1309         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1310         if( !final )
1311             final = buffer;
1312         lstrcpyW(final, sPathRel);
1313
1314         *abs_path = '\0';
1315
1316         if (SHELL_ExistsFileW(buffer)) {
1317             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1318                 lstrcpyW(abs_path, buffer);
1319         } else {
1320             /* try if [working directory] + [relative path] finds an existing file */
1321             if (sWorkDir) {
1322                 lstrcpyW(buffer, sWorkDir);
1323                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1324
1325                 if (SHELL_ExistsFileW(buffer))
1326                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1327                         lstrcpyW(abs_path, buffer);
1328             }
1329         }
1330
1331         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1332         if (!*abs_path)
1333             lstrcpyW(abs_path, sPathRel);
1334
1335         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1336         if (!*psPath)
1337             return E_OUTOFMEMORY;
1338
1339         lstrcpyW(*psPath, abs_path);
1340     }
1341
1342     return S_OK;
1343 }
1344
1345 /**************************************************************************
1346  *        IShellLink_ConstructFromFile
1347  */
1348 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1349                LPCITEMIDLIST pidl, LPVOID* ppv)
1350 {
1351     IShellLinkW* psl;
1352
1353     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1354
1355     if (SUCCEEDED(hr)) {
1356         IPersistFile* ppf;
1357
1358         *ppv = NULL;
1359
1360         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1361
1362         if (SUCCEEDED(hr)) {
1363             WCHAR path[MAX_PATH];
1364
1365             if (SHGetPathFromIDListW(pidl, path)) 
1366                 hr = IPersistFile_Load(ppf, path, 0);
1367             else
1368                 hr = E_FAIL;
1369
1370             if (SUCCEEDED(hr))
1371                 *ppv = psl;
1372
1373             IPersistFile_Release(ppf);
1374         }
1375
1376         if (!*ppv)
1377             IShellLinkW_Release(psl);
1378     }
1379
1380     return hr;
1381 }
1382
1383 /**************************************************************************
1384  *  IShellLinkA_QueryInterface
1385  */
1386 static HRESULT WINAPI IShellLinkA_fnQueryInterface(IShellLinkA *iface, REFIID riid, void **ppvObj)
1387 {
1388     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1389     return ShellLink_QueryInterface( This, riid, ppvObj );
1390 }
1391
1392 /******************************************************************************
1393  * IShellLinkA_AddRef
1394  */
1395 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA *iface)
1396 {
1397     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1398     return ShellLink_AddRef( This );
1399 }
1400
1401 /******************************************************************************
1402  *      IShellLinkA_Release
1403  */
1404 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA *iface)
1405 {
1406     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1407     return ShellLink_Release( This );
1408 }
1409
1410 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA *iface, LPSTR pszFile, INT cchMaxPath,
1411         WIN32_FIND_DATAA *pfd, DWORD fFlags)
1412 {
1413     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1414
1415     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1416           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1417
1418     if (This->sComponent || This->sProduct)
1419         return S_FALSE;
1420
1421     if (cchMaxPath)
1422         pszFile[0] = 0;
1423     if (This->sPath)
1424         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1425                              pszFile, cchMaxPath, NULL, NULL);
1426
1427     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1428
1429     return S_OK;
1430 }
1431
1432 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA *iface, LPITEMIDLIST *ppidl)
1433 {
1434     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1435
1436     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1437
1438     return IShellLinkW_GetIDList(&This->IShellLinkW_iface, ppidl);
1439 }
1440
1441 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA *iface, LPCITEMIDLIST pidl)
1442 {
1443     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1444
1445     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1446
1447     if (This->pPidl)
1448         ILFree(This->pPidl);
1449     This->pPidl = ILClone (pidl);
1450     This->bDirty = TRUE;
1451
1452     return S_OK;
1453 }
1454
1455 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA *iface, LPSTR pszName,
1456         INT cchMaxName)
1457 {
1458     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1459
1460     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1461
1462     if( cchMaxName )
1463         pszName[0] = 0;
1464     if( This->sDescription )
1465         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1466             pszName, cchMaxName, NULL, NULL);
1467
1468     return S_OK;
1469 }
1470
1471 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA *iface, LPCSTR pszName)
1472 {
1473     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1474
1475     TRACE("(%p)->(pName=%s)\n", This, debugstr_a(pszName));
1476
1477     HeapFree(GetProcessHeap(), 0, This->sDescription);
1478     if (pszName)
1479     {
1480         This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1481         if ( !This->sDescription )
1482             return E_OUTOFMEMORY;
1483     }
1484     else
1485         This->sDescription = NULL;
1486
1487     This->bDirty = TRUE;
1488
1489     return S_OK;
1490 }
1491
1492 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA *iface, LPSTR pszDir,
1493         INT cchMaxPath)
1494 {
1495     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1496
1497     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1498
1499     if( cchMaxPath )
1500         pszDir[0] = 0;
1501     if( This->sWorkDir )
1502         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1503                              pszDir, cchMaxPath, NULL, NULL);
1504
1505     return S_OK;
1506 }
1507
1508 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA *iface, LPCSTR pszDir)
1509 {
1510     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1511
1512     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1513
1514     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1515     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1516     if ( !This->sWorkDir )
1517         return E_OUTOFMEMORY;
1518
1519     This->bDirty = TRUE;
1520
1521     return S_OK;
1522 }
1523
1524 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA *iface, LPSTR pszArgs, INT cchMaxPath)
1525 {
1526     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1527
1528     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1529
1530     if( cchMaxPath )
1531         pszArgs[0] = 0;
1532     if( This->sArgs )
1533         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1534                              pszArgs, cchMaxPath, NULL, NULL);
1535
1536     return S_OK;
1537 }
1538
1539 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA *iface, LPCSTR pszArgs)
1540 {
1541     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1542
1543     TRACE("(%p)->(args=%s)\n",This, debugstr_a(pszArgs));
1544
1545     HeapFree(GetProcessHeap(), 0, This->sArgs);
1546     if (pszArgs)
1547     {
1548         This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1549         if( !This->sArgs )
1550             return E_OUTOFMEMORY;
1551     }
1552     else This->sArgs = NULL;
1553
1554     This->bDirty = TRUE;
1555
1556     return S_OK;
1557 }
1558
1559 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA *iface, WORD *pwHotkey)
1560 {
1561     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1562
1563     TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1564
1565     *pwHotkey = This->wHotKey;
1566
1567     return S_OK;
1568 }
1569
1570 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA *iface, WORD wHotkey)
1571 {
1572     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1573
1574     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1575
1576     This->wHotKey = wHotkey;
1577     This->bDirty = TRUE;
1578
1579     return S_OK;
1580 }
1581
1582 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA *iface, INT *piShowCmd)
1583 {
1584     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1585
1586     TRACE("(%p)->(%p)\n",This, piShowCmd);
1587     *piShowCmd = This->iShowCmd;
1588     return S_OK;
1589 }
1590
1591 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA *iface, INT iShowCmd)
1592 {
1593     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1594
1595     TRACE("(%p) %d\n",This, iShowCmd);
1596
1597     This->iShowCmd = iShowCmd;
1598     This->bDirty = TRUE;
1599
1600     return NOERROR;
1601 }
1602
1603 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA *iface, LPSTR pszIconPath,
1604         INT cchIconPath, INT *piIcon)
1605 {
1606     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1607
1608     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1609
1610     *piIcon = This->iIcoNdx;
1611
1612     if (This->sIcoPath)
1613         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1614     else
1615         pszIconPath[0] = 0;
1616
1617     return S_OK;
1618 }
1619
1620 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA *iface, LPCSTR pszIconPath,
1621         INT iIcon)
1622 {
1623     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1624
1625     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1626
1627     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1628     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1629     if ( !This->sIcoPath )
1630         return E_OUTOFMEMORY;
1631
1632     This->iIcoNdx = iIcon;
1633     This->bDirty = TRUE;
1634
1635     return S_OK;
1636 }
1637
1638 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA *iface, LPCSTR pszPathRel,
1639         DWORD dwReserved)
1640 {
1641     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1642
1643     TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
1644
1645     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1646     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1647     This->bDirty = TRUE;
1648
1649     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1650 }
1651
1652 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA *iface, HWND hwnd, DWORD fFlags)
1653 {
1654     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1655
1656     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1657
1658     return IShellLinkW_Resolve(&This->IShellLinkW_iface, hwnd, fFlags);
1659 }
1660
1661 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA *iface, LPCSTR pszFile)
1662 {
1663     IShellLinkImpl *This = impl_from_IShellLinkA(iface);
1664     HRESULT r;
1665     LPWSTR str;
1666
1667     TRACE("(%p)->(path=%s)\n",This, debugstr_a(pszFile));
1668
1669     if (!pszFile) return E_INVALIDARG;
1670
1671     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1672     if( !str ) 
1673         return E_OUTOFMEMORY;
1674
1675     r = IShellLinkW_SetPath(&This->IShellLinkW_iface, str);
1676     HeapFree( GetProcessHeap(), 0, str );
1677
1678     return r;
1679 }
1680
1681 /**************************************************************************
1682 * IShellLink Implementation
1683 */
1684
1685 static const IShellLinkAVtbl slvt =
1686 {
1687     IShellLinkA_fnQueryInterface,
1688     IShellLinkA_fnAddRef,
1689     IShellLinkA_fnRelease,
1690     IShellLinkA_fnGetPath,
1691     IShellLinkA_fnGetIDList,
1692     IShellLinkA_fnSetIDList,
1693     IShellLinkA_fnGetDescription,
1694     IShellLinkA_fnSetDescription,
1695     IShellLinkA_fnGetWorkingDirectory,
1696     IShellLinkA_fnSetWorkingDirectory,
1697     IShellLinkA_fnGetArguments,
1698     IShellLinkA_fnSetArguments,
1699     IShellLinkA_fnGetHotkey,
1700     IShellLinkA_fnSetHotkey,
1701     IShellLinkA_fnGetShowCmd,
1702     IShellLinkA_fnSetShowCmd,
1703     IShellLinkA_fnGetIconLocation,
1704     IShellLinkA_fnSetIconLocation,
1705     IShellLinkA_fnSetRelativePath,
1706     IShellLinkA_fnResolve,
1707     IShellLinkA_fnSetPath
1708 };
1709
1710
1711 /**************************************************************************
1712  *  IShellLinkW_fnQueryInterface
1713  */
1714 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1715   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1716 {
1717     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1718     return ShellLink_QueryInterface( This, riid, ppvObj );
1719 }
1720
1721 /******************************************************************************
1722  * IShellLinkW_fnAddRef
1723  */
1724 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1725 {
1726     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1727     return ShellLink_AddRef( This );
1728 }
1729
1730 /******************************************************************************
1731  * IShellLinkW_fnRelease
1732  */
1733 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1734 {
1735     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1736     return ShellLink_Release( This );
1737 }
1738
1739 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1740 {
1741     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1742
1743     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1744           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1745
1746     if (This->sComponent || This->sProduct)
1747         return S_FALSE;
1748
1749     if (cchMaxPath)
1750         pszFile[0] = 0;
1751     if (This->sPath)
1752         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1753
1754     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1755
1756     return S_OK;
1757 }
1758
1759 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1760 {
1761     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1762
1763     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1764
1765     if (!This->pPidl)
1766     {
1767         *ppidl = NULL;
1768         return S_FALSE;
1769     }
1770     *ppidl = ILClone(This->pPidl);
1771     return S_OK;
1772 }
1773
1774 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1775 {
1776     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1777
1778     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1779
1780     if( This->pPidl )
1781         ILFree( This->pPidl );
1782     This->pPidl = ILClone( pidl );
1783     if( !This->pPidl )
1784         return E_FAIL;
1785
1786     This->bDirty = TRUE;
1787
1788     return S_OK;
1789 }
1790
1791 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1792 {
1793     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1794
1795     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1796
1797     pszName[0] = 0;
1798     if( This->sDescription )
1799         lstrcpynW( pszName, This->sDescription, cchMaxName );
1800
1801     return S_OK;
1802 }
1803
1804 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1805 {
1806     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1807
1808     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1809
1810     HeapFree(GetProcessHeap(), 0, This->sDescription);
1811     if (pszName)
1812     {
1813         This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1814                                         (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1815         if ( !This->sDescription )
1816             return E_OUTOFMEMORY;
1817
1818         lstrcpyW( This->sDescription, pszName );
1819     }
1820     else
1821         This->sDescription = NULL;
1822     This->bDirty = TRUE;
1823
1824     return S_OK;
1825 }
1826
1827 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1828 {
1829     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1830
1831     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1832
1833     if( cchMaxPath )
1834         pszDir[0] = 0;
1835     if( This->sWorkDir )
1836         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1837
1838     return S_OK;
1839 }
1840
1841 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1842 {
1843     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1844
1845     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1846
1847     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1848     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1849                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1850     if ( !This->sWorkDir )
1851         return E_OUTOFMEMORY;
1852     lstrcpyW( This->sWorkDir, pszDir );
1853     This->bDirty = TRUE;
1854
1855     return S_OK;
1856 }
1857
1858 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1859 {
1860     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1861
1862     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1863
1864     if( cchMaxPath )
1865         pszArgs[0] = 0;
1866     if( This->sArgs )
1867         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1868
1869     return NOERROR;
1870 }
1871
1872 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1873 {
1874     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1875
1876     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1877
1878     HeapFree(GetProcessHeap(), 0, This->sArgs);
1879     if (pszArgs)
1880     {
1881         This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1882                                  (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1883         if ( !This->sArgs )
1884             return E_OUTOFMEMORY;
1885         lstrcpyW( This->sArgs, pszArgs );
1886     }
1887     else This->sArgs = NULL;
1888
1889     This->bDirty = TRUE;
1890
1891     return S_OK;
1892 }
1893
1894 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1895 {
1896     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1897
1898     TRACE("(%p)->(%p)\n",This, pwHotkey);
1899
1900     *pwHotkey=This->wHotKey;
1901
1902     return S_OK;
1903 }
1904
1905 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1906 {
1907     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1908
1909     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1910
1911     This->wHotKey = wHotkey;
1912     This->bDirty = TRUE;
1913
1914     return S_OK;
1915 }
1916
1917 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1918 {
1919     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1920
1921     TRACE("(%p)->(%p)\n",This, piShowCmd);
1922
1923     *piShowCmd = This->iShowCmd;
1924
1925     return S_OK;
1926 }
1927
1928 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1929 {
1930     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1931
1932     This->iShowCmd = iShowCmd;
1933     This->bDirty = TRUE;
1934
1935     return S_OK;
1936 }
1937
1938 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1939 {
1940     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1941
1942     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1943
1944     *piIcon = This->iIcoNdx;
1945
1946     if (This->sIcoPath)
1947         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1948     else
1949         pszIconPath[0] = 0;
1950
1951     return S_OK;
1952 }
1953
1954 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1955 {
1956     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1957
1958     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1959
1960     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1961     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1962                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1963     if ( !This->sIcoPath )
1964         return E_OUTOFMEMORY;
1965     lstrcpyW( This->sIcoPath, pszIconPath );
1966
1967     This->iIcoNdx = iIcon;
1968     This->bDirty = TRUE;
1969
1970     return S_OK;
1971 }
1972
1973 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1974 {
1975     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1976
1977     TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
1978
1979     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1980     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1981                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1982     if ( !This->sPathRel )
1983         return E_OUTOFMEMORY;
1984     lstrcpyW( This->sPathRel, pszPathRel );
1985     This->bDirty = TRUE;
1986
1987     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1988 }
1989
1990 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1991 {
1992     HRESULT hr = S_OK;
1993     BOOL bSuccess;
1994
1995     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1996
1997     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1998
1999     /*FIXME: use IResolveShellLink interface */
2000
2001     if (!This->sPath && This->pPidl) {
2002         WCHAR buffer[MAX_PATH];
2003
2004         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2005
2006         if (bSuccess && *buffer) {
2007             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2008             if (!This->sPath)
2009                 return E_OUTOFMEMORY;
2010
2011             lstrcpyW(This->sPath, buffer);
2012
2013             This->bDirty = TRUE;
2014         } else
2015             hr = S_OK;    /* don't report an error occurred while just caching information */
2016     }
2017
2018     if (!This->sIcoPath && This->sPath) {
2019         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2020         if (!This->sIcoPath)
2021             return E_OUTOFMEMORY;
2022
2023         lstrcpyW(This->sIcoPath, This->sPath);
2024         This->iIcoNdx = 0;
2025
2026         This->bDirty = TRUE;
2027     }
2028
2029     return hr;
2030 }
2031
2032 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2033 {
2034     LPWSTR ret;
2035     LPCWSTR p;
2036     DWORD len;
2037
2038     if( !str )
2039         return NULL;
2040
2041     p = strchrW( str, ':' );
2042     if( !p )
2043         return NULL;
2044     len = p - str;
2045     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2046     if( !ret )
2047         return ret;
2048     memcpy( ret, str, sizeof(WCHAR)*len );
2049     ret[len] = 0;
2050     return ret;
2051 }
2052
2053 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2054 {
2055     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2056     WCHAR szGuid[39];
2057     HRESULT r;
2058     GUID guid;
2059     int len;
2060
2061     while( str[0] )
2062     {
2063         /* each segment must start with two colons */
2064         if( str[0] != ':' || str[1] != ':' )
2065             return E_FAIL;
2066
2067         /* the last segment is just two colons */
2068         if( !str[2] )
2069             break;
2070         str += 2;
2071
2072         /* there must be a colon straight after a guid */
2073         p = strchrW( str, ':' );
2074         if( !p )
2075             return E_FAIL;
2076         len = p - str;
2077         if( len != 38 )
2078             return E_FAIL;
2079
2080         /* get the guid, and check it's validly formatted */
2081         memcpy( szGuid, str, sizeof(WCHAR)*len );
2082         szGuid[len] = 0;
2083         r = CLSIDFromString( szGuid, &guid );
2084         if( r != S_OK )
2085             return r;
2086         str = p + 1;
2087
2088         /* match it up to a guid that we care about */
2089         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2090             szComponent = str;
2091         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2092             szProduct = str;
2093         else
2094             return E_FAIL;
2095
2096         /* skip to the next field */
2097         str = strchrW( str, ':' );
2098         if( !str )
2099             return E_FAIL;
2100     }
2101
2102     /* we have to have a component for an advertised shortcut */
2103     if( !szComponent )
2104         return E_FAIL;
2105
2106     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2107     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2108
2109     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2110     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2111
2112     return S_OK;
2113 }
2114
2115 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, volume_info *volume)
2116 {
2117     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2118     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2119     BOOL r;
2120
2121     volume->type = GetDriveTypeW(drive);
2122     r = GetVolumeInformationW(drive, volume->label, label_sz,
2123                               &volume->serial, NULL, NULL, NULL, 0);
2124     TRACE("r = %d type %d serial %08x name %s\n", r,
2125           volume->type, volume->serial, debugstr_w(volume->label));
2126     return r;
2127 }
2128
2129 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2130 {
2131     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2132     WCHAR buffer[MAX_PATH];
2133     LPWSTR fname, unquoted = NULL;
2134     HRESULT hr = S_OK;
2135     UINT len;
2136
2137     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2138
2139     if (!pszFile) return E_INVALIDARG;
2140
2141     /* quotes at the ends of the string are stripped */
2142     len = lstrlenW(pszFile);
2143     if (pszFile[0] == '"' && pszFile[len-1] == '"')
2144     {
2145         unquoted = strdupW(pszFile);
2146         PathUnquoteSpacesW(unquoted);
2147         pszFile = unquoted;
2148     }
2149
2150     /* any other quote marks are invalid */
2151     if (strchrW(pszFile, '"'))
2152     {
2153         HeapFree(GetProcessHeap(), 0, unquoted);
2154         return S_FALSE;
2155     }
2156
2157     HeapFree(GetProcessHeap(), 0, This->sPath);
2158     This->sPath = NULL;
2159
2160     HeapFree(GetProcessHeap(), 0, This->sComponent);
2161     This->sComponent = NULL;
2162
2163     if (This->pPidl)
2164         ILFree(This->pPidl);
2165     This->pPidl = NULL;
2166
2167     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2168     {
2169         if (*pszFile == '\0')
2170             *buffer = '\0';
2171         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2172             return E_FAIL;
2173         else if(!PathFileExistsW(buffer) &&
2174                 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
2175           hr = S_FALSE;
2176
2177         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2178         ShellLink_GetVolumeInfo(buffer, &This->volume);
2179
2180         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2181                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2182         if (!This->sPath)
2183         {
2184             HeapFree(GetProcessHeap(), 0, unquoted);
2185             return E_OUTOFMEMORY;
2186         }
2187
2188         lstrcpyW(This->sPath, buffer);
2189     }
2190     This->bDirty = TRUE;
2191     HeapFree(GetProcessHeap(), 0, unquoted);
2192
2193     return hr;
2194 }
2195
2196 /**************************************************************************
2197 * IShellLinkW Implementation
2198 */
2199
2200 static const IShellLinkWVtbl slvtw =
2201 {
2202     IShellLinkW_fnQueryInterface,
2203     IShellLinkW_fnAddRef,
2204     IShellLinkW_fnRelease,
2205     IShellLinkW_fnGetPath,
2206     IShellLinkW_fnGetIDList,
2207     IShellLinkW_fnSetIDList,
2208     IShellLinkW_fnGetDescription,
2209     IShellLinkW_fnSetDescription,
2210     IShellLinkW_fnGetWorkingDirectory,
2211     IShellLinkW_fnSetWorkingDirectory,
2212     IShellLinkW_fnGetArguments,
2213     IShellLinkW_fnSetArguments,
2214     IShellLinkW_fnGetHotkey,
2215     IShellLinkW_fnSetHotkey,
2216     IShellLinkW_fnGetShowCmd,
2217     IShellLinkW_fnSetShowCmd,
2218     IShellLinkW_fnGetIconLocation,
2219     IShellLinkW_fnSetIconLocation,
2220     IShellLinkW_fnSetRelativePath,
2221     IShellLinkW_fnResolve,
2222     IShellLinkW_fnSetPath
2223 };
2224
2225 static HRESULT WINAPI
2226 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2227 {
2228     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2229     return IShellLinkA_QueryInterface(&This->IShellLinkA_iface, riid, ppvObject);
2230 }
2231
2232 static ULONG WINAPI
2233 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2234 {
2235     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2236     return IShellLinkA_AddRef(&This->IShellLinkA_iface);
2237 }
2238
2239 static ULONG WINAPI
2240 ShellLink_DataList_Release( IShellLinkDataList* iface )
2241 {
2242     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2243     return ShellLink_Release( This );
2244 }
2245
2246 static HRESULT WINAPI
2247 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2248 {
2249     FIXME("(%p)->(%p): stub\n", iface, pDataBlock);
2250     return E_NOTIMPL;
2251 }
2252
2253 static HRESULT WINAPI
2254 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2255 {
2256     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2257     LPVOID block = NULL;
2258     HRESULT r = E_FAIL;
2259
2260     TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
2261
2262     switch (dwSig)
2263     {
2264     case EXP_DARWIN_ID_SIG:
2265         if (!This->sComponent)
2266             break;
2267         block = shelllink_build_darwinid( This->sComponent, dwSig );
2268         r = S_OK;
2269         break;
2270     case EXP_SZ_LINK_SIG:
2271     case NT_CONSOLE_PROPS_SIG:
2272     case NT_FE_CONSOLE_PROPS_SIG:
2273     case EXP_SPECIAL_FOLDER_SIG:
2274     case EXP_SZ_ICON_SIG:
2275         FIXME("valid but unhandled datablock %08x\n", dwSig);
2276         break;
2277     default:
2278         ERR("unknown datablock %08x\n", dwSig);
2279     }
2280     *ppDataBlock = block;
2281     return r;
2282 }
2283
2284 static HRESULT WINAPI
2285 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2286 {
2287     FIXME("(%p)->(%u): stub\n", iface, dwSig);
2288     return E_NOTIMPL;
2289 }
2290
2291 static HRESULT WINAPI
2292 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2293 {
2294     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2295     DWORD flags = 0;
2296
2297     FIXME("(%p)->(%p): partially implemented\n", This, pdwFlags);
2298
2299     /* FIXME: add more */
2300     if (This->sArgs)
2301         flags |= SLDF_HAS_ARGS;
2302     if (This->sComponent)
2303         flags |= SLDF_HAS_DARWINID;
2304     if (This->sIcoPath)
2305         flags |= SLDF_HAS_ICONLOCATION;
2306     if (This->sProduct)
2307         flags |= SLDF_HAS_LOGO3ID;
2308     if (This->pPidl)
2309         flags |= SLDF_HAS_ID_LIST;
2310
2311     *pdwFlags = flags;
2312
2313     return S_OK;
2314 }
2315
2316 static HRESULT WINAPI
2317 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2318 {
2319     FIXME("(%p)->(%u): stub\n", iface, dwFlags);
2320     return E_NOTIMPL;
2321 }
2322
2323 static const IShellLinkDataListVtbl dlvt =
2324 {
2325     ShellLink_DataList_QueryInterface,
2326     ShellLink_DataList_AddRef,
2327     ShellLink_DataList_Release,
2328     ShellLink_AddDataBlock,
2329     ShellLink_CopyDataBlock,
2330     ShellLink_RemoveDataBlock,
2331     ShellLink_GetFlags,
2332     ShellLink_SetFlags
2333 };
2334
2335 static HRESULT WINAPI
2336 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2337 {
2338     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2339     return IShellLinkA_QueryInterface(&This->IShellLinkA_iface, riid, ppvObject);
2340 }
2341
2342 static ULONG WINAPI
2343 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2344 {
2345     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2346     return IShellLinkA_AddRef(&This->IShellLinkA_iface);
2347 }
2348
2349 static ULONG WINAPI
2350 ShellLink_ExtInit_Release( IShellExtInit* iface )
2351 {
2352     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2353     return ShellLink_Release( This );
2354 }
2355
2356 /**************************************************************************
2357  * ShellLink implementation of IShellExtInit::Initialize()
2358  *
2359  * Loads the shelllink from the dataobject the shell is pointing to.
2360  */
2361 static HRESULT WINAPI
2362 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2363                               IDataObject *pdtobj, HKEY hkeyProgID )
2364 {
2365     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2366     FORMATETC format;
2367     STGMEDIUM stgm;
2368     UINT count;
2369     HRESULT r = E_FAIL;
2370
2371     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2372
2373     if( !pdtobj )
2374         return r;
2375
2376     format.cfFormat = CF_HDROP;
2377     format.ptd = NULL;
2378     format.dwAspect = DVASPECT_CONTENT;
2379     format.lindex = -1;
2380     format.tymed = TYMED_HGLOBAL;
2381
2382     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2383         return r;
2384
2385     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2386     if( count == 1 )
2387     {
2388         LPWSTR path;
2389
2390         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2391         count++;
2392         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2393         if( path )
2394         {
2395             IPersistFile *pf = &This->IPersistFile_iface;
2396
2397             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2398             r = IPersistFile_Load( pf, path, 0 );
2399             HeapFree( GetProcessHeap(), 0, path );
2400         }
2401     }
2402     ReleaseStgMedium( &stgm );
2403
2404     return r;
2405 }
2406
2407 static const IShellExtInitVtbl eivt =
2408 {
2409     ShellLink_ExtInit_QueryInterface,
2410     ShellLink_ExtInit_AddRef,
2411     ShellLink_ExtInit_Release,
2412     ShellLink_ExtInit_Initialize
2413 };
2414
2415 static HRESULT WINAPI
2416 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2417 {
2418     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2419     return IShellLinkA_QueryInterface(&This->IShellLinkA_iface, riid, ppvObject);
2420 }
2421
2422 static ULONG WINAPI
2423 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2424 {
2425     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2426     return IShellLinkA_AddRef(&This->IShellLinkA_iface);
2427 }
2428
2429 static ULONG WINAPI
2430 ShellLink_ContextMenu_Release( IContextMenu* iface )
2431 {
2432     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2433     return ShellLink_Release( This );
2434 }
2435
2436 static HRESULT WINAPI
2437 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2438                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2439 {
2440     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2441     static WCHAR szOpen[] = { 'O','p','e','n',0 };
2442     MENUITEMINFOW mii;
2443     int id = 1;
2444
2445     TRACE("%p %p %u %u %u %u\n", This,
2446           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2447
2448     if ( !hmenu )
2449         return E_INVALIDARG;
2450
2451     memset( &mii, 0, sizeof mii );
2452     mii.cbSize = sizeof mii;
2453     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2454     mii.dwTypeData = szOpen;
2455     mii.cch = strlenW( mii.dwTypeData );
2456     mii.wID = idCmdFirst + id++;
2457     mii.fState = MFS_DEFAULT | MFS_ENABLED;
2458     mii.fType = MFT_STRING;
2459     if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
2460         return E_FAIL;
2461     This->iIdOpen = 0;
2462
2463     return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2464 }
2465
2466 static LPWSTR
2467 shelllink_get_msi_component_path( LPWSTR component )
2468 {
2469     LPWSTR path;
2470     DWORD r, sz = 0;
2471
2472     r = CommandLineFromMsiDescriptor( component, NULL, &sz );
2473     if (r != ERROR_SUCCESS)
2474          return NULL;
2475
2476     sz++;
2477     path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
2478     r = CommandLineFromMsiDescriptor( component, path, &sz );
2479     if (r != ERROR_SUCCESS)
2480     {
2481         HeapFree( GetProcessHeap(), 0, path );
2482         path = NULL;
2483     }
2484
2485     TRACE("returning %s\n", debugstr_w( path ) );
2486
2487     return path;
2488 }
2489
2490 static HRESULT WINAPI
2491 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2492 {
2493     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2494     static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2495     SHELLEXECUTEINFOW sei;
2496     HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
2497     LPWSTR args = NULL;
2498     LPWSTR path = NULL;
2499     HRESULT r;
2500
2501     TRACE("%p %p\n", This, lpici );
2502
2503     if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
2504         return E_INVALIDARG;
2505
2506     if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
2507     {
2508         ERR("Unknown id %p != %d\n", lpici->lpVerb, This->iIdOpen );
2509         return E_INVALIDARG;
2510     }
2511
2512     r = IShellLinkW_Resolve(&This->IShellLinkW_iface, hwnd, 0);
2513     if ( FAILED( r ) )
2514         return r;
2515
2516     if ( This->sComponent )
2517     {
2518         path = shelllink_get_msi_component_path( This->sComponent );
2519         if (!path)
2520             return E_FAIL;
2521     }
2522     else
2523         path = strdupW( This->sPath );
2524
2525     if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
2526          ( lpici->fMask & CMIC_MASK_UNICODE ) )
2527     {
2528         LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
2529         DWORD len = 2;
2530
2531         if ( This->sArgs )
2532             len += lstrlenW( This->sArgs );
2533         if ( iciex->lpParametersW )
2534             len += lstrlenW( iciex->lpParametersW );
2535
2536         args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
2537         args[0] = 0;
2538         if ( This->sArgs )
2539             lstrcatW( args, This->sArgs );
2540         if ( iciex->lpParametersW )
2541         {
2542             static const WCHAR space[] = { ' ', 0 };
2543             lstrcatW( args, space );
2544             lstrcatW( args, iciex->lpParametersW );
2545         }
2546     }
2547
2548     memset( &sei, 0, sizeof sei );
2549     sei.cbSize = sizeof sei;
2550     sei.fMask = SEE_MASK_UNICODE | (lpici->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
2551     sei.lpFile = path;
2552     sei.nShow = This->iShowCmd;
2553     sei.lpIDList = This->pPidl;
2554     sei.lpDirectory = This->sWorkDir;
2555     sei.lpParameters = args;
2556     sei.lpVerb = szOpen;
2557
2558     if( ShellExecuteExW( &sei ) )
2559         r = S_OK;
2560     else
2561         r = E_FAIL;
2562
2563     HeapFree( GetProcessHeap(), 0, args );
2564     HeapFree( GetProcessHeap(), 0, path );
2565
2566     return r;
2567 }
2568
2569 static HRESULT WINAPI
2570 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2571                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2572 {
2573     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2574
2575     FIXME("(%p)->(%lu %u %p %p %u): stub\n", This,
2576           idCmd, uType, pwReserved, pszName, cchMax );
2577
2578     return E_NOTIMPL;
2579 }
2580
2581 static const IContextMenuVtbl cmvt =
2582 {
2583     ShellLink_ContextMenu_QueryInterface,
2584     ShellLink_ContextMenu_AddRef,
2585     ShellLink_ContextMenu_Release,
2586     ShellLink_QueryContextMenu,
2587     ShellLink_InvokeCommand,
2588     ShellLink_GetCommandString
2589 };
2590
2591 static HRESULT WINAPI
2592 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2593 {
2594     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2595     return ShellLink_QueryInterface( This, riid, ppvObject );
2596 }
2597
2598 static ULONG WINAPI
2599 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2600 {
2601     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2602     return ShellLink_AddRef( This );
2603 }
2604
2605 static ULONG WINAPI
2606 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2607 {
2608     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2609     return ShellLink_Release( This );
2610 }
2611
2612 static HRESULT WINAPI
2613 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2614 {
2615     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2616
2617     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2618
2619     if ( !This->site )
2620         return E_FAIL;
2621     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2622 }
2623
2624 static HRESULT WINAPI
2625 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2626 {
2627     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2628
2629     TRACE("%p %p\n", iface, punk);
2630
2631     if ( punk )
2632         IUnknown_AddRef( punk );
2633     This->site = punk;
2634
2635     return S_OK;
2636 }
2637
2638 static const IObjectWithSiteVtbl owsvt =
2639 {
2640     ShellLink_ObjectWithSite_QueryInterface,
2641     ShellLink_ObjectWithSite_AddRef,
2642     ShellLink_ObjectWithSite_Release,
2643     ShellLink_SetSite,
2644     ShellLink_GetSite,
2645 };