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