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