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