[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / shelllink.cpp
1 /*
2 *
3 * Copyright 1997 Marcus Meissner
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2005 Mike McCormack
6 * Copyright 2009 Andrew Hill
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 *
22 * NOTES
23 * Nearly complete information about the binary formats
24 * of .lnk files available at http://www.wotsit.org
25 *
26 * You can use winedump to examine the contents of a link file:
27 * winedump lnk sc.lnk
28 *
29 * MSI advertised shortcuts are totally undocumented. They provide an
30 * icon for a program that is not yet installed, and invoke MSI to
31 * install the program when the shortcut is clicked on. They are
32 * created by passing a special string to SetPath, and the information
33 * in that string is parsed an stored.
34 */
35
36 #include <precomp.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(shell);
39
40 #define SHLINK_LOCAL 0
41 #define SHLINK_REMOTE 1
42 #define MAX_PROPERTY_SHEET_PAGE 32
43
44 /* link file formats */
45
46 #include "pshpack1.h"
47
48 struct LINK_HEADER
49 {
50 DWORD dwSize; /* 0x00 size of the header - 0x4c */
51 GUID MagicGuid; /* 0x04 is CLSID_ShellLink */
52 DWORD dwFlags; /* 0x14 describes elements following */
53 DWORD dwFileAttr; /* 0x18 attributes of the target file */
54 FILETIME Time1; /* 0x1c */
55 FILETIME Time2; /* 0x24 */
56 FILETIME Time3; /* 0x2c */
57 DWORD dwFileLength; /* 0x34 File length */
58 DWORD nIcon; /* 0x38 icon number */
59 DWORD fStartup; /* 0x3c startup type */
60 DWORD wHotKey; /* 0x40 hotkey */
61 DWORD Unknown5; /* 0x44 */
62 DWORD Unknown6; /* 0x48 */
63 };
64
65 struct LOCATION_INFO
66 {
67 DWORD dwTotalSize;
68 DWORD dwHeaderSize;
69 DWORD dwFlags;
70 DWORD dwVolTableOfs;
71 DWORD dwLocalPathOfs;
72 DWORD dwNetworkVolTableOfs;
73 DWORD dwFinalPathOfs;
74 };
75
76 struct LOCAL_VOLUME_INFO
77 {
78 DWORD dwSize;
79 DWORD dwType;
80 DWORD dwVolSerial;
81 DWORD dwVolLabelOfs;
82 };
83
84 struct volume_info
85 {
86 DWORD type;
87 DWORD serial;
88 WCHAR label[12]; /* assume 8.3 */
89 };
90
91 #include "poppack.h"
92
93 /* IShellLink Implementation */
94
95 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
96
97 /* strdup on the process heap */
98 static LPWSTR __inline HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
99 {
100 INT len;
101 LPWSTR p;
102
103 assert(str);
104
105 len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
106 p = (LPWSTR)HeapAlloc( heap, flags, len*sizeof (WCHAR) );
107 if( !p )
108 return p;
109 MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
110 return p;
111 }
112
113 static LPWSTR __inline strdupW( LPCWSTR src )
114 {
115 LPWSTR dest;
116 if (!src) return NULL;
117 dest = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, (wcslen(src)+1)*sizeof(WCHAR) );
118 if (dest)
119 wcscpy(dest, src);
120 return dest;
121 }
122
123 ShellLink::ShellLink()
124 {
125 pPidl = NULL;
126 wHotKey = 0;
127 memset(&time1, 0, sizeof(time1));
128 memset(&time2, 0, sizeof(time2));
129 memset(&time3, 0, sizeof(time3));
130 iShowCmd = SW_SHOWNORMAL;
131 sIcoPath = NULL;
132 iIcoNdx = 0;
133 sPath = NULL;
134 sArgs = NULL;
135 sWorkDir = NULL;
136 sDescription = NULL;
137 sPathRel = NULL;
138 sProduct = NULL;
139 sComponent = NULL;
140 memset(&volume, 0, sizeof(volume));
141 sLinkPath = NULL;
142 bRunAs = FALSE;
143 bDirty = FALSE;
144 iIdOpen = -1;
145 }
146
147 ShellLink::~ShellLink()
148 {
149 TRACE("-- destroying IShellLink(%p)\n", this);
150
151 HeapFree(GetProcessHeap(), 0, sIcoPath);
152 HeapFree(GetProcessHeap(), 0, sArgs);
153 HeapFree(GetProcessHeap(), 0, sWorkDir);
154 HeapFree(GetProcessHeap(), 0, sDescription);
155 HeapFree(GetProcessHeap(), 0, sPath);
156 HeapFree(GetProcessHeap(), 0, sLinkPath);
157
158 if (pPidl)
159 ILFree(pPidl);
160 }
161
162 HRESULT WINAPI ShellLink::GetClassID(CLSID *pclsid )
163 {
164 TRACE("%p %p\n", this, pclsid);
165
166 if (pclsid == NULL)
167 return E_POINTER;
168 *pclsid = CLSID_ShellLink;
169 return S_OK;
170 }
171
172 HRESULT WINAPI ShellLink::IsDirty()
173 {
174 TRACE("(%p)\n",this);
175
176 if (bDirty)
177 return S_OK;
178
179 return S_FALSE;
180 }
181
182 HRESULT WINAPI ShellLink::Load(LPCOLESTR pszFileName, DWORD dwMode)
183 {
184 HRESULT r;
185 CComPtr<IStream> stm;
186
187 TRACE("(%p, %s, %x)\n",this, debugstr_w(pszFileName), dwMode);
188
189 if (dwMode == 0)
190 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
191 r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
192 if (SUCCEEDED(r))
193 {
194 HeapFree(GetProcessHeap(), 0, sLinkPath);
195 sLinkPath = strdupW(pszFileName);
196 r = Load(stm);
197 ShellLink_UpdatePath(sPathRel, pszFileName, sWorkDir, &sPath);
198 bDirty = FALSE;
199 }
200 TRACE("-- returning hr %08x\n", r);
201 return r;
202 }
203
204 static BOOL StartLinkProcessor( LPCOLESTR szLink )
205 {
206 static const WCHAR szFormat[] = {
207 'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
208 ' ','-','w',' ','"','%','s','"',0 };
209 LONG len;
210 LPWSTR buffer;
211 STARTUPINFOW si;
212 PROCESS_INFORMATION pi;
213 BOOL ret;
214
215 len = sizeof(szFormat) + wcslen( szLink ) * sizeof(WCHAR);
216 buffer = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len );
217 if( !buffer )
218 return FALSE;
219
220 swprintf( buffer, szFormat, szLink );
221
222 TRACE("starting %s\n",debugstr_w(buffer));
223
224 memset(&si, 0, sizeof(si));
225 si.cb = sizeof(si);
226
227 ret = CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
228
229 HeapFree( GetProcessHeap(), 0, buffer );
230
231 if (ret)
232 {
233 CloseHandle( pi.hProcess );
234 CloseHandle( pi.hThread );
235 }
236
237 return ret;
238 }
239
240 HRESULT WINAPI ShellLink::Save(LPCOLESTR pszFileName, BOOL fRemember)
241 {
242 HRESULT r;
243 CComPtr<IStream> stm;
244
245 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName));
246
247 if (!pszFileName)
248 return E_FAIL;
249
250 r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
251 if( SUCCEEDED( r ) )
252 {
253 r = Save(stm, FALSE);
254
255 if( SUCCEEDED( r ) )
256 {
257 if ( sLinkPath )
258 {
259 HeapFree(GetProcessHeap(), 0, sLinkPath);
260 }
261 sLinkPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName)+1) * sizeof(WCHAR));
262 if ( sLinkPath )
263 {
264 wcscpy(sLinkPath, pszFileName);
265 }
266
267 StartLinkProcessor( pszFileName );
268
269 bDirty = FALSE;
270 }
271 else
272 {
273 DeleteFileW( pszFileName );
274 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
275 }
276 }
277
278 return r;
279 }
280
281 HRESULT WINAPI ShellLink::SaveCompleted(LPCOLESTR pszFileName)
282 {
283 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName));
284 return NOERROR;
285 }
286
287 HRESULT WINAPI ShellLink::GetCurFile(LPOLESTR *ppszFileName)
288 {
289 *ppszFileName = NULL;
290
291 if ( !sLinkPath)
292 {
293 /* IPersistFile::GetCurFile called before IPersistFile::Save */
294 return S_FALSE;
295 }
296
297 *ppszFileName = (LPOLESTR)CoTaskMemAlloc((wcslen(sLinkPath)+1) * sizeof(WCHAR));
298 if (!*ppszFileName)
299 {
300 /* out of memory */
301 return E_OUTOFMEMORY;
302 }
303
304 /* copy last saved filename */
305 wcscpy(*ppszFileName, sLinkPath);
306
307 return NOERROR;
308 }
309
310 /************************************************************************
311 * IPersistStream_IsDirty (IPersistStream)
312 */
313
314 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
315 {
316 DWORD count;
317 USHORT len;
318 LPSTR temp;
319 LPWSTR str;
320 HRESULT r;
321
322 TRACE("%p\n", stm);
323
324 count = 0;
325 r = stm->Read(&len, sizeof(len), &count);
326 if ( FAILED (r) || ( count != sizeof(len) ) )
327 return E_FAIL;
328
329 if( unicode )
330 len *= sizeof (WCHAR);
331
332 TRACE("reading %d\n", len);
333 temp = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
334 if( !temp )
335 return E_OUTOFMEMORY;
336 count = 0;
337 r = stm->Read(temp, len, &count);
338 if( FAILED (r) || ( count != len ) )
339 {
340 HeapFree( GetProcessHeap(), 0, temp );
341 return E_FAIL;
342 }
343
344 TRACE("read %s\n", debugstr_an(temp,len));
345
346 /* convert to unicode if necessary */
347 if( !unicode )
348 {
349 count = MultiByteToWideChar( CP_ACP, 0, temp, len, NULL, 0 );
350 str = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
351 if( !str )
352 {
353 HeapFree( GetProcessHeap(), 0, temp );
354 return E_OUTOFMEMORY;
355 }
356 MultiByteToWideChar( CP_ACP, 0, temp, len, str, count );
357 HeapFree( GetProcessHeap(), 0, temp );
358 }
359 else
360 {
361 count /= 2;
362 str = (LPWSTR)temp;
363 }
364 str[count] = 0;
365
366 *pstr = str;
367
368 return S_OK;
369 }
370
371 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
372 {
373 DWORD size;
374 ULONG count;
375 HRESULT r;
376 struct sized_chunk {
377 DWORD size;
378 unsigned char data[1];
379 } *chunk;
380
381 TRACE("%p\n",stm);
382
383 r = stm->Read(&size, sizeof(size), &count );
384 if( FAILED( r ) || count != sizeof(size) )
385 return E_FAIL;
386
387 chunk = (sized_chunk *)HeapAlloc( GetProcessHeap(), 0, size );
388 if( !chunk )
389 return E_OUTOFMEMORY;
390
391 chunk->size = size;
392 r = stm->Read(chunk->data, size - sizeof(size), &count );
393 if( FAILED( r ) || count != (size - sizeof(size)) )
394 {
395 HeapFree( GetProcessHeap(), 0, chunk );
396 return E_FAIL;
397 }
398
399 TRACE("Read %d bytes\n",chunk->size);
400
401 *data = chunk;
402
403 return S_OK;
404 }
405
406 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, ShellLink::volume_info *volume )
407 {
408 const int label_sz = sizeof volume->label/sizeof volume->label[0];
409 LPSTR label;
410 int len;
411
412 volume->serial = vol->dwVolSerial;
413 volume->type = vol->dwType;
414
415 if( !vol->dwVolLabelOfs )
416 return FALSE;
417 if( vol->dwSize <= vol->dwVolLabelOfs )
418 return FALSE;
419 len = vol->dwSize - vol->dwVolLabelOfs;
420
421 label = (LPSTR) vol;
422 label += vol->dwVolLabelOfs;
423 MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
424
425 return TRUE;
426 }
427
428 static LPWSTR Stream_LoadPath( LPCSTR p, DWORD maxlen )
429 {
430 unsigned int len = 0, wlen;
431 LPWSTR path;
432
433 while( p[len] && (len < maxlen) )
434 len++;
435
436 wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
437 path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
438 MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
439 path[wlen] = 0;
440
441 return path;
442 }
443
444 static HRESULT Stream_LoadLocation( IStream *stm,
445 ShellLink::volume_info *volume, LPWSTR *path )
446 {
447 char *p = NULL;
448 LOCATION_INFO *loc;
449 HRESULT r;
450 DWORD n;
451
452 r = Stream_ReadChunk( stm, (LPVOID*) &p );
453 if( FAILED(r) )
454 return r;
455
456 loc = (LOCATION_INFO*) p;
457 if (loc->dwTotalSize < sizeof(LOCATION_INFO))
458 {
459 HeapFree( GetProcessHeap(), 0, p );
460 return E_FAIL;
461 }
462
463 /* if there's valid local volume information, load it */
464 if( loc->dwVolTableOfs &&
465 ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
466 {
467 LOCAL_VOLUME_INFO *volume_info;
468
469 volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
470 Stream_LoadVolume( volume_info, volume );
471 }
472
473 /* if there's a local path, load it */
474 n = loc->dwLocalPathOfs;
475 if( n && (n < loc->dwTotalSize) )
476 *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
477
478 TRACE("type %d serial %08x name %s path %s\n", volume->type,
479 volume->serial, debugstr_w(volume->label), debugstr_w(*path));
480
481 HeapFree( GetProcessHeap(), 0, p );
482 return S_OK;
483 }
484
485 /*
486 * The format of the advertised shortcut info seems to be:
487 *
488 * Offset Description
489 * ------ -----------
490 *
491 * 0 Length of the block (4 bytes, usually 0x314)
492 * 4 tag (dword)
493 * 8 string data in ASCII
494 * 8+0x104 string data in UNICODE
495 *
496 * In the original Win32 implementation the buffers are not initialized
497 * to zero, so data trailing the string is random garbage.
498 */
499 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
500 {
501 DWORD size;
502 ULONG count;
503 HRESULT r;
504 EXP_DARWIN_LINK buffer;
505
506 TRACE("%p\n",stm);
507
508 r = stm->Read(&buffer.dbh.cbSize, sizeof (DWORD), &count );
509 if( FAILED( r ) )
510 return r;
511
512 /* make sure that we read the size of the structure even on error */
513 size = sizeof buffer - sizeof (DWORD);
514 if( buffer.dbh.cbSize != sizeof buffer )
515 {
516 ERR("Ooops. This structure is not as expected...\n");
517 return E_FAIL;
518 }
519
520 r = stm->Read(&buffer.dbh.dwSignature, size, &count );
521 if( FAILED( r ) )
522 return r;
523
524 if( count != size )
525 return E_FAIL;
526
527 TRACE("magic %08x string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
528
529 if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
530 {
531 ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
532 return E_FAIL;
533 }
534
535 *str = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
536 (wcslen(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
537 wcscpy( *str, buffer.szwDarwinID );
538
539 return S_OK;
540 }
541
542 /************************************************************************
543 * IPersistStream_Load (IPersistStream)
544 */
545 HRESULT WINAPI ShellLink::Load(IStream *stm)
546 {
547 LINK_HEADER hdr;
548 ULONG dwBytesRead;
549 BOOL unicode;
550 HRESULT r;
551 DWORD zero;
552
553 TRACE("%p %p\n", this, stm);
554
555 if (!stm)
556 return STG_E_INVALIDPOINTER;
557
558 dwBytesRead = 0;
559 r = stm->Read(&hdr, sizeof(hdr), &dwBytesRead);
560 if (FAILED(r))
561 return r;
562
563 if (dwBytesRead != sizeof(hdr))
564 return E_FAIL;
565 if (hdr.dwSize != sizeof(hdr))
566 return E_FAIL;
567 if (!IsEqualIID(hdr.MagicGuid, CLSID_ShellLink))
568 return E_FAIL;
569
570 /* free all the old stuff */
571 ILFree(pPidl);
572 pPidl = NULL;
573 memset( &volume, 0, sizeof volume );
574 HeapFree(GetProcessHeap(), 0, sPath);
575 sPath = NULL;
576 HeapFree(GetProcessHeap(), 0, sDescription);
577 sDescription = NULL;
578 HeapFree(GetProcessHeap(), 0, sPathRel);
579 sPathRel = NULL;
580 HeapFree(GetProcessHeap(), 0, sWorkDir);
581 sWorkDir = NULL;
582 HeapFree(GetProcessHeap(), 0, sArgs);
583 sArgs = NULL;
584 HeapFree(GetProcessHeap(), 0, sIcoPath);
585 sIcoPath = NULL;
586 HeapFree(GetProcessHeap(), 0, sProduct);
587 sProduct = NULL;
588 HeapFree(GetProcessHeap(), 0, sComponent);
589 sComponent = NULL;
590
591 wHotKey = (WORD)hdr.wHotKey;
592 iIcoNdx = hdr.nIcon;
593 FileTimeToSystemTime (&hdr.Time1, &time1);
594 FileTimeToSystemTime (&hdr.Time2, &time2);
595 FileTimeToSystemTime (&hdr.Time3, &time3);
596 if (TRACE_ON(shell))
597 {
598 WCHAR sTemp[MAX_PATH];
599 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &time1,
600 NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
601 TRACE("-- time1: %s\n", debugstr_w(sTemp) );
602 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &time2,
603 NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
604 TRACE("-- time2: %s\n", debugstr_w(sTemp) );
605 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &time3,
606 NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
607 TRACE("-- time3: %s\n", debugstr_w(sTemp) );
608 }
609
610 /* load all the new stuff */
611 if( hdr.dwFlags & SLDF_HAS_ID_LIST )
612 {
613 r = ILLoadFromStream( stm, &pPidl );
614 if( FAILED( r ) )
615 return r;
616 }
617 pdump(pPidl);
618
619 /* load the location information */
620 if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
621 r = Stream_LoadLocation( stm, &volume, &sPath );
622 if( FAILED( r ) )
623 goto end;
624
625 unicode = hdr.dwFlags & SLDF_UNICODE;
626 if( hdr.dwFlags & SLDF_HAS_NAME )
627 {
628 r = Stream_LoadString( stm, unicode, &sDescription );
629 TRACE("Description -> %s\n",debugstr_w(sDescription));
630 }
631 if( FAILED( r ) )
632 goto end;
633
634 if( hdr.dwFlags & SLDF_HAS_RELPATH )
635 {
636 r = Stream_LoadString( stm, unicode, &sPathRel );
637 TRACE("Relative Path-> %s\n",debugstr_w(sPathRel));
638 }
639 if( FAILED( r ) )
640 goto end;
641
642 if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
643 {
644 r = Stream_LoadString( stm, unicode, &sWorkDir );
645 TRACE("Working Dir -> %s\n",debugstr_w(sWorkDir));
646 }
647 if( FAILED( r ) )
648 goto end;
649
650 if( hdr.dwFlags & SLDF_HAS_ARGS )
651 {
652 r = Stream_LoadString( stm, unicode, &sArgs );
653 TRACE("Working Dir -> %s\n",debugstr_w(sArgs));
654 }
655 if( FAILED( r ) )
656 goto end;
657
658 if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
659 {
660 r = Stream_LoadString( stm, unicode, &sIcoPath );
661 TRACE("Icon file -> %s\n",debugstr_w(sIcoPath));
662 }
663 if( FAILED( r ) )
664 goto end;
665
666 #if (NTDDI_VERSION < NTDDI_LONGHORN)
667 if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
668 {
669 r = Stream_LoadAdvertiseInfo( stm, &sProduct );
670 TRACE("Product -> %s\n",debugstr_w(sProduct));
671 }
672 if( FAILED( r ) )
673 goto end;
674 #endif
675
676 if( hdr.dwFlags & SLDF_HAS_DARWINID )
677 {
678 r = Stream_LoadAdvertiseInfo( stm, &sComponent );
679 TRACE("Component -> %s\n",debugstr_w(sComponent));
680 }
681 if( hdr.dwFlags & SLDF_RUNAS_USER )
682 {
683 bRunAs = TRUE;
684 }
685 else
686 {
687 bRunAs = FALSE;
688 }
689
690 if( FAILED( r ) )
691 goto end;
692
693 r = stm->Read(&zero, sizeof zero, &dwBytesRead);
694 if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
695 ERR("Last word was not zero\n");
696
697 TRACE("OK\n");
698
699 pdump (pPidl);
700
701 return S_OK;
702 end:
703 return r;
704 }
705
706 /************************************************************************
707 * Stream_WriteString
708 *
709 * Helper function for IPersistStream_Save. Writes a unicode string
710 * with terminating nul byte to a stream, preceded by the its length.
711 */
712 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
713 {
714 USHORT len = wcslen( str ) + 1;
715 DWORD count;
716 HRESULT r;
717
718 r = stm->Write(&len, sizeof(len), &count );
719 if( FAILED( r ) )
720 return r;
721
722 len *= sizeof(WCHAR);
723
724 r = stm->Write(str, len, &count );
725 if( FAILED( r ) )
726 return r;
727
728 return S_OK;
729 }
730
731 /************************************************************************
732 * Stream_WriteLocationInfo
733 *
734 * Writes the location info to a stream
735 *
736 * FIXME: One day we might want to write the network volume information
737 * and the final path.
738 * Figure out how Windows deals with unicode paths here.
739 */
740 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
741 ShellLink::volume_info *volume )
742 {
743 DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
744 LOCAL_VOLUME_INFO *vol;
745 LOCATION_INFO *loc;
746 LPSTR szLabel, szPath, szFinalPath;
747 ULONG count = 0;
748 HRESULT hr;
749
750 TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
751
752 /* figure out the size of everything */
753 label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
754 NULL, 0, NULL, NULL );
755 path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
756 NULL, 0, NULL, NULL );
757 volume_info_size = sizeof *vol + label_size;
758 final_path_size = 1;
759 total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
760
761 /* create pointers to everything */
762 loc = (LOCATION_INFO *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
763 vol = (LOCAL_VOLUME_INFO*) &loc[1];
764 szLabel = (LPSTR) &vol[1];
765 szPath = &szLabel[label_size];
766 szFinalPath = &szPath[path_size];
767
768 /* fill in the location information header */
769 loc->dwTotalSize = total_size;
770 loc->dwHeaderSize = sizeof (*loc);
771 loc->dwFlags = 1;
772 loc->dwVolTableOfs = sizeof (*loc);
773 loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
774 loc->dwNetworkVolTableOfs = 0;
775 loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
776
777 /* fill in the volume information */
778 vol->dwSize = volume_info_size;
779 vol->dwType = volume->type;
780 vol->dwVolSerial = volume->serial;
781 vol->dwVolLabelOfs = sizeof (*vol);
782
783 /* copy in the strings */
784 WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
785 szLabel, label_size, NULL, NULL );
786 WideCharToMultiByte( CP_ACP, 0, path, -1,
787 szPath, path_size, NULL, NULL );
788 szFinalPath[0] = 0;
789
790 hr = stm->Write(loc, total_size, &count );
791 HeapFree(GetProcessHeap(), 0, loc);
792
793 return hr;
794 }
795
796 static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
797 {
798 EXP_DARWIN_LINK *buffer;
799
800 buffer = (EXP_DARWIN_LINK *)LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
801 buffer->dbh.cbSize = sizeof *buffer;
802 buffer->dbh.dwSignature = magic;
803 lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
804 WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );
805
806 return buffer;
807 }
808
809 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
810 {
811 EXP_DARWIN_LINK *buffer;
812 ULONG count;
813
814 TRACE("%p\n",stm);
815
816 buffer = shelllink_build_darwinid( string, magic );
817
818 return stm->Write(buffer, buffer->dbh.cbSize, &count );
819 }
820
821 /************************************************************************
822 * IPersistStream_Save (IPersistStream)
823 *
824 * FIXME: makes assumptions about byte order
825 */
826 HRESULT WINAPI ShellLink::Save(IStream *stm, BOOL fClearDirty)
827 {
828 LINK_HEADER header;
829 ULONG count;
830 DWORD zero;
831 HRESULT r;
832
833 TRACE("%p %p %x\n", this, stm, fClearDirty);
834
835 memset(&header, 0, sizeof(header));
836 header.dwSize = sizeof(header);
837 header.fStartup = iShowCmd;
838 header.MagicGuid = CLSID_ShellLink;
839
840 header.wHotKey = wHotKey;
841 header.nIcon = iIcoNdx;
842 header.dwFlags = SLDF_UNICODE; /* strings are in unicode */
843 if( pPidl )
844 header.dwFlags |= SLDF_HAS_ID_LIST;
845 if( sPath )
846 header.dwFlags |= SLDF_HAS_LINK_INFO;
847 if( sDescription )
848 header.dwFlags |= SLDF_HAS_NAME;
849 if( sWorkDir )
850 header.dwFlags |= SLDF_HAS_WORKINGDIR;
851 if( sArgs )
852 header.dwFlags |= SLDF_HAS_ARGS;
853 if( sIcoPath )
854 header.dwFlags |= SLDF_HAS_ICONLOCATION;
855 #if (NTDDI_VERSION < NTDDI_LONGHORN)
856 if( sProduct )
857 header.dwFlags |= SLDF_HAS_LOGO3ID;
858 #endif
859 if( sComponent )
860 header.dwFlags |= SLDF_HAS_DARWINID;
861 if( bRunAs )
862 header.dwFlags |= SLDF_RUNAS_USER;
863
864 SystemTimeToFileTime ( &time1, &header.Time1 );
865 SystemTimeToFileTime ( &time2, &header.Time2 );
866 SystemTimeToFileTime ( &time3, &header.Time3 );
867
868 /* write the Shortcut header */
869 r = stm->Write(&header, sizeof(header), &count );
870 if( FAILED( r ) )
871 {
872 ERR("Write failed at %d\n",__LINE__);
873 return r;
874 }
875
876 TRACE("Writing pidl\n");
877
878 /* write the PIDL to the shortcut */
879 if( pPidl )
880 {
881 r = ILSaveToStream( stm, pPidl );
882 if( FAILED( r ) )
883 {
884 ERR("Failed to write PIDL at %d\n",__LINE__);
885 return r;
886 }
887 }
888
889 if( sPath )
890 Stream_WriteLocationInfo( stm, sPath, &volume );
891
892 if( sDescription )
893 r = Stream_WriteString( stm, sDescription );
894
895 if( sPathRel )
896 r = Stream_WriteString( stm, sPathRel );
897
898 if( sWorkDir )
899 r = Stream_WriteString( stm, sWorkDir );
900
901 if( sArgs )
902 r = Stream_WriteString( stm, sArgs );
903
904 if( sIcoPath )
905 r = Stream_WriteString( stm, sIcoPath );
906
907 if( sProduct )
908 r = Stream_WriteAdvertiseInfo( stm, sProduct, EXP_SZ_ICON_SIG );
909
910 if( sComponent )
911 r = Stream_WriteAdvertiseInfo( stm, sComponent, EXP_DARWIN_ID_SIG );
912
913 /* the last field is a single zero dword */
914 zero = 0;
915 r = stm->Write(&zero, sizeof zero, &count );
916
917 return S_OK;
918 }
919
920 /************************************************************************
921 * IPersistStream_GetSizeMax (IPersistStream)
922 */
923 HRESULT WINAPI ShellLink::GetSizeMax(ULARGE_INTEGER *pcbSize)
924 {
925 TRACE("(%p)\n", this);
926
927 return E_NOTIMPL;
928 }
929
930 static BOOL SHELL_ExistsFileW(LPCWSTR path)
931 {
932 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
933 return FALSE;
934 return TRUE;
935 }
936
937 /**************************************************************************
938 * ShellLink_UpdatePath
939 * update absolute path in sPath using relative path in sPathRel
940 */
941 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
942 {
943 if (!path || !psPath)
944 return E_INVALIDARG;
945
946 if (!*psPath && sPathRel) {
947 WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
948 LPWSTR final = NULL;
949
950 /* first try if [directory of link file] + [relative path] finds an existing file */
951
952 GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
953 if( !final )
954 final = buffer;
955 wcscpy(final, sPathRel);
956
957 *abs_path = '\0';
958
959 if (SHELL_ExistsFileW(buffer)) {
960 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
961 wcscpy(abs_path, buffer);
962 } else {
963 /* try if [working directory] + [relative path] finds an existing file */
964 if (sWorkDir) {
965 wcscpy(buffer, sWorkDir);
966 wcscpy(PathAddBackslashW(buffer), sPathRel);
967
968 if (SHELL_ExistsFileW(buffer))
969 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
970 wcscpy(abs_path, buffer);
971 }
972 }
973
974 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
975 if (!*abs_path)
976 wcscpy(abs_path, sPathRel);
977
978 *psPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path)+1)*sizeof(WCHAR));
979 if (!*psPath)
980 return E_OUTOFMEMORY;
981
982 wcscpy(*psPath, abs_path);
983 }
984
985 return S_OK;
986 }
987
988 HRESULT WINAPI ShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
989 {
990 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
991 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(sPath));
992
993 if (sComponent || sProduct)
994 return S_FALSE;
995
996 if (cchMaxPath)
997 pszFile[0] = 0;
998 if (sPath)
999 WideCharToMultiByte( CP_ACP, 0, sPath, -1,
1000 pszFile, cchMaxPath, NULL, NULL);
1001
1002 if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1003
1004 return S_OK;
1005 }
1006
1007 HRESULT WINAPI ShellLink::GetIDList(LPITEMIDLIST * ppidl)
1008 {
1009 TRACE("(%p)->(ppidl=%p)\n",this, ppidl);
1010
1011 if (!pPidl)
1012 {
1013 *ppidl = NULL;
1014 return S_FALSE;
1015 }
1016 *ppidl = ILClone(pPidl);
1017 return S_OK;
1018 }
1019
1020 HRESULT WINAPI ShellLink::SetIDList(LPCITEMIDLIST pidl)
1021 {
1022 TRACE("(%p)->(pidl=%p)\n",this, pidl);
1023
1024 if( pPidl )
1025 ILFree( pPidl );
1026 pPidl = ILClone( pidl );
1027 if( !pPidl )
1028 return E_FAIL;
1029
1030 bDirty = TRUE;
1031
1032 return S_OK;
1033 }
1034
1035 HRESULT WINAPI ShellLink::GetDescription(LPSTR pszName,INT cchMaxName)
1036 {
1037 TRACE("(%p)->(%p len=%u)\n",this, pszName, cchMaxName);
1038
1039 if( cchMaxName )
1040 pszName[0] = 0;
1041 if( sDescription )
1042 WideCharToMultiByte( CP_ACP, 0, sDescription, -1,
1043 pszName, cchMaxName, NULL, NULL);
1044
1045 return S_OK;
1046 }
1047
1048 HRESULT WINAPI ShellLink::SetDescription(LPCSTR pszName)
1049 {
1050 TRACE("(%p)->(pName=%s)\n", this, pszName);
1051
1052 HeapFree(GetProcessHeap(), 0, sDescription);
1053 sDescription = NULL;
1054
1055 if ( pszName ) {
1056 sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1057 if ( !sDescription )
1058 return E_OUTOFMEMORY;
1059 }
1060 bDirty = TRUE;
1061
1062 return S_OK;
1063 }
1064
1065 HRESULT WINAPI ShellLink::GetWorkingDirectory(LPSTR pszDir,INT cchMaxPath)
1066 {
1067 TRACE("(%p)->(%p len=%u)\n", this, pszDir, cchMaxPath);
1068
1069 if( cchMaxPath )
1070 pszDir[0] = 0;
1071 if( sWorkDir )
1072 WideCharToMultiByte( CP_ACP, 0, sWorkDir, -1,
1073 pszDir, cchMaxPath, NULL, NULL);
1074
1075 return S_OK;
1076 }
1077
1078 HRESULT WINAPI ShellLink::SetWorkingDirectory(LPCSTR pszDir)
1079 {
1080 TRACE("(%p)->(dir=%s)\n",this, pszDir);
1081
1082 HeapFree(GetProcessHeap(), 0, sWorkDir);
1083 sWorkDir = NULL;
1084
1085 if ( pszDir ) {
1086 sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1087 if ( !sWorkDir )
1088 return E_OUTOFMEMORY;
1089 }
1090 bDirty = TRUE;
1091
1092 return S_OK;
1093 }
1094
1095 HRESULT WINAPI ShellLink::GetArguments(LPSTR pszArgs,INT cchMaxPath)
1096 {
1097 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath);
1098
1099 if( cchMaxPath )
1100 pszArgs[0] = 0;
1101 if( sArgs )
1102 WideCharToMultiByte( CP_ACP, 0, sArgs, -1,
1103 pszArgs, cchMaxPath, NULL, NULL);
1104
1105 return S_OK;
1106 }
1107
1108 HRESULT WINAPI ShellLink::SetArguments(LPCSTR pszArgs)
1109 {
1110 TRACE("(%p)->(args=%s)\n",this, pszArgs);
1111
1112 HeapFree(GetProcessHeap(), 0, sArgs);
1113 sArgs = NULL;
1114
1115 if ( pszArgs ) {
1116 sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1117 if( !sArgs )
1118 return E_OUTOFMEMORY;
1119 }
1120
1121 bDirty = TRUE;
1122
1123 return S_OK;
1124 }
1125
1126 HRESULT WINAPI ShellLink::GetHotkey(WORD *pwHotkey)
1127 {
1128 TRACE("(%p)->(%p)(0x%08x)\n",this, pwHotkey, wHotKey);
1129
1130 *pwHotkey = wHotKey;
1131
1132 return S_OK;
1133 }
1134
1135 HRESULT WINAPI ShellLink::SetHotkey(WORD wHotkey)
1136 {
1137 TRACE("(%p)->(hotkey=%x)\n",this, wHotkey);
1138
1139 wHotKey = wHotkey;
1140 bDirty = TRUE;
1141
1142 return S_OK;
1143 }
1144
1145 HRESULT WINAPI ShellLink::GetShowCmd(INT *piShowCmd)
1146 {
1147 TRACE("(%p)->(%p)\n",this, piShowCmd);
1148 *piShowCmd = iShowCmd;
1149 return S_OK;
1150 }
1151
1152 HRESULT WINAPI ShellLink::SetShowCmd(INT iShowCmd)
1153 {
1154 TRACE("(%p) %d\n",this, iShowCmd);
1155
1156 this->iShowCmd = iShowCmd;
1157 bDirty = TRUE;
1158
1159 return NOERROR;
1160 }
1161
1162 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPCITEMIDLIST pidl,
1163 LPSTR pszIconPath, int cchIconPath, int* piIcon)
1164 {
1165 LPCITEMIDLIST pidlLast;
1166
1167 HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1168
1169 if (SUCCEEDED(hr)) {
1170 CComPtr<IExtractIconA> pei;
1171
1172 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_IExtractIconA, NULL, (LPVOID*)&pei);
1173
1174 if (SUCCEEDED(hr)) {
1175 hr = pei->GetIconLocation(0, pszIconPath, MAX_PATH, piIcon, NULL);
1176 }
1177
1178 psf->Release();
1179 }
1180
1181 return hr;
1182 }
1183
1184 HRESULT WINAPI ShellLink::GetIconLocation(LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1185 {
1186 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
1187
1188 pszIconPath[0] = 0;
1189 *piIcon = iIcoNdx;
1190
1191 if (sIcoPath)
1192 {
1193 WideCharToMultiByte(CP_ACP, 0, sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1194 return S_OK;
1195 }
1196
1197 if (pPidl || sPath)
1198 {
1199 CComPtr<IShellFolder> pdsk;
1200
1201 HRESULT hr = SHGetDesktopFolder(&pdsk);
1202
1203 if (SUCCEEDED(hr))
1204 {
1205 /* first look for an icon using the PIDL (if present) */
1206 if (pPidl)
1207 hr = SHELL_PidlGeticonLocationA(pdsk, pPidl, pszIconPath, cchIconPath, piIcon);
1208 else
1209 hr = E_FAIL;
1210
1211 /* if we couldn't find an icon yet, look for it using the file system path */
1212 if (FAILED(hr) && sPath)
1213 {
1214 LPITEMIDLIST pidl;
1215
1216 hr = pdsk->ParseDisplayName(0, NULL, sPath, NULL, &pidl, NULL);
1217
1218 if (SUCCEEDED(hr))
1219 {
1220 hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1221
1222 SHFree(pidl);
1223 }
1224 }
1225 }
1226
1227 return hr;
1228 }
1229 return S_OK;
1230 }
1231
1232 HRESULT WINAPI ShellLink::SetIconLocation(LPCSTR pszIconPath,INT iIcon)
1233 {
1234 TRACE("(%p)->(path=%s iicon=%u)\n",this, pszIconPath, iIcon);
1235
1236 HeapFree(GetProcessHeap(), 0, sIcoPath);
1237 sIcoPath = NULL;
1238
1239 if ( pszIconPath )
1240 {
1241 sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1242 if ( !sIcoPath )
1243 return E_OUTOFMEMORY;
1244 }
1245
1246 iIcoNdx = iIcon;
1247 bDirty = TRUE;
1248
1249 return S_OK;
1250 }
1251
1252 HRESULT WINAPI ShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
1253 {
1254 TRACE("(%p)->(path=%s %x)\n",this, pszPathRel, dwReserved);
1255
1256 HeapFree(GetProcessHeap(), 0, sPathRel);
1257 sPathRel = NULL;
1258
1259 if ( pszPathRel )
1260 {
1261 sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1262 bDirty = TRUE;
1263 }
1264
1265 return ShellLink_UpdatePath(sPathRel, sPath, sWorkDir, &sPath);
1266 }
1267
1268 HRESULT WINAPI ShellLink::Resolve(HWND hwnd, DWORD fFlags)
1269 {
1270 HRESULT hr = S_OK;
1271 BOOL bSuccess;
1272
1273 TRACE("(%p)->(hwnd=%p flags=%x)\n",this, hwnd, fFlags);
1274
1275 /*FIXME: use IResolveShellLink interface */
1276
1277 if (!sPath && pPidl)
1278 {
1279 WCHAR buffer[MAX_PATH];
1280
1281 bSuccess = SHGetPathFromIDListW(pPidl, buffer);
1282
1283 if (bSuccess && *buffer)
1284 {
1285 sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer)+1)*sizeof(WCHAR));
1286
1287 if (!sPath)
1288 return E_OUTOFMEMORY;
1289
1290 wcscpy(sPath, buffer);
1291
1292 bDirty = TRUE;
1293 }
1294 else
1295 hr = S_OK; /* don't report an error occurred while just caching information */
1296 }
1297
1298 if (!sIcoPath && sPath)
1299 {
1300 sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(sPath)+1)*sizeof(WCHAR));
1301
1302 if (!sIcoPath)
1303 return E_OUTOFMEMORY;
1304
1305 wcscpy(sIcoPath, sPath);
1306 iIcoNdx = 0;
1307
1308 bDirty = TRUE;
1309 }
1310
1311 return hr;
1312 }
1313
1314 HRESULT WINAPI ShellLink::SetPath(LPCSTR pszFile)
1315 {
1316 HRESULT r;
1317 LPWSTR str;
1318
1319 TRACE("(%p)->(path=%s)\n",this, pszFile);
1320 if (pszFile == NULL)
1321 return E_INVALIDARG;
1322
1323 str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1324 if (!str)
1325 return E_OUTOFMEMORY;
1326
1327 r = SetPath(str);
1328 HeapFree( GetProcessHeap(), 0, str );
1329
1330 return r;
1331 }
1332
1333 HRESULT WINAPI ShellLink::GetPath(LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1334 {
1335 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1336 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(sPath));
1337
1338 if (sComponent || sProduct)
1339 return S_FALSE;
1340
1341 if (cchMaxPath)
1342 pszFile[0] = 0;
1343
1344 if (sPath)
1345 lstrcpynW( pszFile, sPath, cchMaxPath );
1346
1347 if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1348
1349 return S_OK;
1350 }
1351
1352 HRESULT WINAPI ShellLink::GetDescription(LPWSTR pszName,INT cchMaxName)
1353 {
1354 TRACE("(%p)->(%p len=%u)\n",this, pszName, cchMaxName);
1355
1356 pszName[0] = 0;
1357 if (sDescription)
1358 lstrcpynW( pszName, sDescription, cchMaxName );
1359
1360 return S_OK;
1361 }
1362
1363 HRESULT WINAPI ShellLink::SetDescription(LPCWSTR pszName)
1364 {
1365 TRACE("(%p)->(desc=%s)\n",this, debugstr_w(pszName));
1366
1367 HeapFree(GetProcessHeap(), 0, sDescription);
1368 sDescription = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1369 (wcslen( pszName )+1)*sizeof(WCHAR) );
1370 if ( !sDescription )
1371 return E_OUTOFMEMORY;
1372
1373 wcscpy( sDescription, pszName );
1374 bDirty = TRUE;
1375
1376 return S_OK;
1377 }
1378
1379 HRESULT WINAPI ShellLink::GetWorkingDirectory(LPWSTR pszDir,INT cchMaxPath)
1380 {
1381 TRACE("(%p)->(%p len %u)\n", this, pszDir, cchMaxPath);
1382
1383 if( cchMaxPath )
1384 pszDir[0] = 0;
1385 if( sWorkDir )
1386 lstrcpynW( pszDir, sWorkDir, cchMaxPath );
1387
1388 return S_OK;
1389 }
1390
1391 HRESULT WINAPI ShellLink::SetWorkingDirectory(LPCWSTR pszDir)
1392 {
1393 TRACE("(%p)->(dir=%s)\n",this, debugstr_w(pszDir));
1394
1395 HeapFree(GetProcessHeap(), 0, sWorkDir);
1396 sWorkDir = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1397 (wcslen( pszDir )+1)*sizeof (WCHAR) );
1398 if ( !sWorkDir )
1399 return E_OUTOFMEMORY;
1400 wcscpy( sWorkDir, pszDir );
1401 bDirty = TRUE;
1402
1403 return S_OK;
1404 }
1405
1406 HRESULT WINAPI ShellLink::GetArguments(LPWSTR pszArgs,INT cchMaxPath)
1407 {
1408 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath);
1409
1410 if( cchMaxPath )
1411 pszArgs[0] = 0;
1412 if( sArgs )
1413 lstrcpynW( pszArgs, sArgs, cchMaxPath );
1414
1415 return NOERROR;
1416 }
1417
1418 HRESULT WINAPI ShellLink::SetArguments(LPCWSTR pszArgs)
1419 {
1420 TRACE("(%p)->(args=%s)\n",this, debugstr_w(pszArgs));
1421
1422 HeapFree(GetProcessHeap(), 0, sArgs);
1423 sArgs = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1424 (wcslen( pszArgs )+1)*sizeof (WCHAR) );
1425 if ( !sArgs )
1426 return E_OUTOFMEMORY;
1427 wcscpy( sArgs, pszArgs );
1428 bDirty = TRUE;
1429
1430 return S_OK;
1431 }
1432
1433 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
1434 LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1435 {
1436 LPCITEMIDLIST pidlLast;
1437 UINT wFlags;
1438
1439 HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1440
1441 if (SUCCEEDED(hr)) {
1442 CComPtr<IExtractIconW> pei;
1443
1444 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_IExtractIconW, NULL, (LPVOID*)&pei);
1445
1446 if (SUCCEEDED(hr)) {
1447 hr = pei->GetIconLocation(0, pszIconPath, MAX_PATH, piIcon, &wFlags);
1448 }
1449
1450 psf->Release();
1451 }
1452
1453 return hr;
1454 }
1455
1456 HRESULT WINAPI ShellLink::GetIconLocation(LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1457 {
1458 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
1459
1460 pszIconPath[0] = 0;
1461 *piIcon = iIcoNdx;
1462
1463 if (sIcoPath)
1464 {
1465 lstrcpynW(pszIconPath, sIcoPath, cchIconPath);
1466 return S_OK;
1467 }
1468
1469 if (pPidl || sPath)
1470 {
1471 CComPtr<IShellFolder> pdsk;
1472
1473 HRESULT hr = SHGetDesktopFolder(&pdsk);
1474
1475 if (SUCCEEDED(hr))
1476 {
1477 /* first look for an icon using the PIDL (if present) */
1478 if (pPidl)
1479 hr = SHELL_PidlGeticonLocationW(pdsk, pPidl, pszIconPath, cchIconPath, piIcon);
1480 else
1481 hr = E_FAIL;
1482
1483 /* if we couldn't find an icon yet, look for it using the file system path */
1484 if (FAILED(hr) && sPath)
1485 {
1486 LPITEMIDLIST pidl;
1487
1488 hr = pdsk->ParseDisplayName(0, NULL, sPath, NULL, &pidl, NULL);
1489
1490 if (SUCCEEDED(hr))
1491 {
1492 hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1493
1494 SHFree(pidl);
1495 }
1496 }
1497 }
1498 return hr;
1499 }
1500 return S_OK;
1501 }
1502
1503 HRESULT WINAPI ShellLink::SetIconLocation(LPCWSTR pszIconPath,INT iIcon)
1504 {
1505 TRACE("(%p)->(path=%s iicon=%u)\n",this, debugstr_w(pszIconPath), iIcon);
1506
1507 HeapFree(GetProcessHeap(), 0, sIcoPath);
1508 sIcoPath = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1509 (wcslen( pszIconPath )+1)*sizeof (WCHAR) );
1510 if ( !sIcoPath )
1511 return E_OUTOFMEMORY;
1512 wcscpy( sIcoPath, pszIconPath );
1513
1514 iIcoNdx = iIcon;
1515 bDirty = TRUE;
1516
1517 return S_OK;
1518 }
1519
1520 HRESULT WINAPI ShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved)
1521 {
1522 TRACE("(%p)->(path=%s %x)\n",this, debugstr_w(pszPathRel), dwReserved);
1523
1524 HeapFree(GetProcessHeap(), 0, sPathRel);
1525 sPathRel = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1526 (wcslen( pszPathRel )+1) * sizeof (WCHAR) );
1527 if ( !sPathRel )
1528 return E_OUTOFMEMORY;
1529 wcscpy( sPathRel, pszPathRel );
1530 bDirty = TRUE;
1531
1532 return ShellLink_UpdatePath(sPathRel, sPath, sWorkDir, &sPath);
1533 }
1534
1535 LPWSTR ShellLink::ShellLink_GetAdvertisedArg(LPCWSTR str)
1536 {
1537 LPWSTR ret;
1538 LPCWSTR p;
1539 DWORD len;
1540
1541 if( !str )
1542 return NULL;
1543
1544 p = wcschr( str, ':' );
1545 if( !p )
1546 return NULL;
1547 len = p - str;
1548 ret = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
1549 if( !ret )
1550 return ret;
1551 memcpy( ret, str, sizeof(WCHAR)*len );
1552 ret[len] = 0;
1553 return ret;
1554 }
1555
1556 HRESULT ShellLink::ShellLink_SetAdvertiseInfo(LPCWSTR str)
1557 {
1558 LPCWSTR szComponent = NULL, szProduct = NULL, p;
1559 WCHAR szGuid[39];
1560 HRESULT r;
1561 GUID guid;
1562 int len;
1563
1564 while( str[0] )
1565 {
1566 /* each segment must start with two colons */
1567 if( str[0] != ':' || str[1] != ':' )
1568 return E_FAIL;
1569
1570 /* the last segment is just two colons */
1571 if( !str[2] )
1572 break;
1573 str += 2;
1574
1575 /* there must be a colon straight after a guid */
1576 p = wcschr( str, ':' );
1577 if( !p )
1578 return E_FAIL;
1579 len = p - str;
1580 if( len != 38 )
1581 return E_FAIL;
1582
1583 /* get the guid, and check it's validly formatted */
1584 memcpy( szGuid, str, sizeof(WCHAR)*len );
1585 szGuid[len] = 0;
1586 r = CLSIDFromString( szGuid, &guid );
1587 if( r != S_OK )
1588 return r;
1589 str = p + 1;
1590
1591 /* match it up to a guid that we care about */
1592 if( IsEqualGUID( guid, SHELL32_AdvtShortcutComponent ) && !szComponent )
1593 szComponent = str;
1594 else if( IsEqualGUID(guid, SHELL32_AdvtShortcutProduct ) && !szProduct )
1595 szProduct = str;
1596 else
1597 return E_FAIL;
1598
1599 /* skip to the next field */
1600 str = wcschr( str, ':' );
1601 if( !str )
1602 return E_FAIL;
1603 }
1604
1605 /* we have to have a component for an advertised shortcut */
1606 if( !szComponent )
1607 return E_FAIL;
1608
1609 sComponent = ShellLink_GetAdvertisedArg( szComponent );
1610 sProduct = ShellLink_GetAdvertisedArg( szProduct );
1611
1612 TRACE("Component = %s\n", debugstr_w(sComponent));
1613 TRACE("Product = %s\n", debugstr_w(sProduct));
1614
1615 return S_OK;
1616 }
1617
1618 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, ShellLink::volume_info *volume)
1619 {
1620 const int label_sz = sizeof volume->label/sizeof volume->label[0];
1621 WCHAR drive[4] = { path[0], ':', '\\', 0 };
1622 BOOL r;
1623
1624 volume->type = GetDriveTypeW(drive);
1625 r = GetVolumeInformationW(drive, volume->label, label_sz, &volume->serial, NULL, NULL, NULL, 0);
1626 TRACE("r = %d type %d serial %08x name %s\n", r,
1627 volume->type, volume->serial, debugstr_w(volume->label));
1628 return r;
1629 }
1630
1631 HRESULT WINAPI ShellLink::SetPath(LPCWSTR pszFile)
1632 {
1633 WCHAR buffer[MAX_PATH];
1634 LPWSTR fname, unquoted = NULL;
1635 HRESULT hr = S_OK;
1636 UINT len;
1637
1638 TRACE("(%p)->(path=%s)\n",this, debugstr_w(pszFile));
1639
1640 if (!pszFile) return E_INVALIDARG;
1641
1642 /* quotes at the ends of the string are stripped */
1643 len = wcslen(pszFile);
1644 if (pszFile[0] == '"' && pszFile[len-1] == '"')
1645 {
1646 unquoted = strdupW(pszFile);
1647 PathUnquoteSpacesW(unquoted);
1648 pszFile = unquoted;
1649 }
1650
1651 /* any other quote marks are invalid */
1652 if (wcschr(pszFile, '"'))
1653 {
1654 HeapFree(GetProcessHeap(), 0, unquoted);
1655 return S_FALSE;
1656 }
1657
1658 HeapFree(GetProcessHeap(), 0, sPath);
1659 sPath = NULL;
1660
1661 HeapFree(GetProcessHeap(), 0, sComponent);
1662 sComponent = NULL;
1663
1664 if (pPidl)
1665 ILFree(pPidl);
1666 pPidl = NULL;
1667
1668 if (S_OK != ShellLink_SetAdvertiseInfo(pszFile ))
1669 {
1670 if (*pszFile == '\0')
1671 *buffer = '\0';
1672 else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
1673 return E_FAIL;
1674 else if(!PathFileExistsW(buffer) &&
1675 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
1676 hr = S_FALSE;
1677
1678 pPidl = SHSimpleIDListFromPathW(pszFile);
1679 ShellLink_GetVolumeInfo(buffer, &volume);
1680
1681 sPath = (LPWSTR)HeapAlloc( GetProcessHeap(), 0,
1682 (wcslen( buffer )+1) * sizeof (WCHAR) );
1683 if (!sPath)
1684 return E_OUTOFMEMORY;
1685
1686 wcscpy(sPath, buffer);
1687 }
1688 bDirty = TRUE;
1689 HeapFree(GetProcessHeap(), 0, unquoted);
1690
1691 return hr;
1692 }
1693
1694 HRESULT WINAPI ShellLink::AddDataBlock(void* pDataBlock )
1695 {
1696 FIXME("\n");
1697 return E_NOTIMPL;
1698 }
1699
1700 HRESULT WINAPI ShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock )
1701 {
1702 LPVOID block = NULL;
1703 HRESULT r = E_FAIL;
1704
1705 TRACE("%p %08x %p\n", this, dwSig, ppDataBlock );
1706
1707 switch (dwSig)
1708 {
1709 case EXP_DARWIN_ID_SIG:
1710 if (!sComponent)
1711 break;
1712 block = shelllink_build_darwinid( sComponent, dwSig );
1713 r = S_OK;
1714 break;
1715 case EXP_SZ_LINK_SIG:
1716 case NT_CONSOLE_PROPS_SIG:
1717 case NT_FE_CONSOLE_PROPS_SIG:
1718 case EXP_SPECIAL_FOLDER_SIG:
1719 case EXP_SZ_ICON_SIG:
1720 FIXME("valid but unhandled datablock %08x\n", dwSig);
1721 break;
1722 default:
1723 ERR("unknown datablock %08x\n", dwSig);
1724 }
1725 *ppDataBlock = block;
1726 return r;
1727 }
1728
1729 HRESULT WINAPI ShellLink::RemoveDataBlock(DWORD dwSig )
1730 {
1731 FIXME("\n");
1732 return E_NOTIMPL;
1733 }
1734
1735 HRESULT WINAPI ShellLink::GetFlags(DWORD* pdwFlags )
1736 {
1737 DWORD flags = 0;
1738
1739 FIXME("%p %p\n", this, pdwFlags );
1740
1741 /* FIXME: add more */
1742 if (sArgs)
1743 flags |= SLDF_HAS_ARGS;
1744 if (sComponent)
1745 flags |= SLDF_HAS_DARWINID;
1746 if (sIcoPath)
1747 flags |= SLDF_HAS_ICONLOCATION;
1748 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1749 if (sProduct)
1750 flags |= SLDF_HAS_LOGO3ID;
1751 #endif
1752 if (pPidl)
1753 flags |= SLDF_HAS_ID_LIST;
1754
1755 *pdwFlags = flags;
1756
1757 return S_OK;
1758 }
1759
1760 HRESULT WINAPI ShellLink::SetFlags(DWORD dwFlags )
1761 {
1762 FIXME("\n");
1763 return E_NOTIMPL;
1764 }
1765
1766 /**************************************************************************
1767 * ShellLink implementation of IShellExtInit::Initialize()
1768 *
1769 * Loads the shelllink from the dataobject the shell is pointing to.
1770 */
1771 HRESULT WINAPI ShellLink::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID )
1772 {
1773 FORMATETC format;
1774 STGMEDIUM stgm;
1775 UINT count;
1776 HRESULT r = E_FAIL;
1777
1778 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
1779
1780 if( !pdtobj )
1781 return r;
1782
1783 format.cfFormat = CF_HDROP;
1784 format.ptd = NULL;
1785 format.dwAspect = DVASPECT_CONTENT;
1786 format.lindex = -1;
1787 format.tymed = TYMED_HGLOBAL;
1788
1789 if( FAILED(pdtobj->GetData(&format, &stgm ) ) )
1790 return r;
1791
1792 count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0 );
1793 if( count == 1 )
1794 {
1795 LPWSTR path;
1796
1797 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, NULL, 0 );
1798 count++;
1799 path = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
1800 if( path )
1801 {
1802 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, path, count );
1803 r = Load(path, 0 );
1804 HeapFree(GetProcessHeap(), 0, path );
1805 }
1806 }
1807 ReleaseStgMedium(&stgm );
1808
1809 return r;
1810 }
1811
1812 HRESULT WINAPI ShellLink::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
1813 {
1814 WCHAR szOpen[20];
1815 MENUITEMINFOW mii;
1816 int id = 1;
1817
1818 TRACE("%p %p %u %u %u %u\n", this,
1819 hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
1820
1821 if ( !hmenu )
1822 return E_INVALIDARG;
1823
1824 if (!LoadStringW(shell32_hInstance, IDS_OPEN_VERB, szOpen, sizeof(szOpen)/sizeof(WCHAR)))
1825 szOpen[0] = L'\0';
1826 else
1827 szOpen[(sizeof(szOpen)/sizeof(WCHAR))-1] = L'\0';
1828
1829 memset( &mii, 0, sizeof(mii) );
1830 mii.cbSize = sizeof (mii);
1831 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
1832 mii.dwTypeData = (LPWSTR)szOpen;
1833 mii.cch = wcslen( mii.dwTypeData );
1834 mii.wID = idCmdFirst + id++;
1835 mii.fState = MFS_DEFAULT | MFS_ENABLED;
1836 mii.fType = MFT_STRING;
1837 if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
1838 return E_FAIL;
1839 iIdOpen = 1;
1840
1841 return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
1842 }
1843
1844 static LPWSTR
1845 shelllink_get_msi_component_path( LPWSTR component )
1846 {
1847 LPWSTR path;
1848 DWORD r, sz = 0;
1849
1850 r = CommandLineFromMsiDescriptor( component, NULL, &sz );
1851 if (r != ERROR_SUCCESS)
1852 return NULL;
1853
1854 sz++;
1855 path = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
1856 r = CommandLineFromMsiDescriptor( component, path, &sz );
1857 if (r != ERROR_SUCCESS)
1858 {
1859 HeapFree( GetProcessHeap(), 0, path );
1860 path = NULL;
1861 }
1862
1863 TRACE("returning %s\n", debugstr_w( path ) );
1864
1865 return path;
1866 }
1867
1868 HRESULT WINAPI ShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1869 {
1870 static const WCHAR szOpen[] = { 'o','p','e','n',0 };
1871 static const WCHAR szCplOpen[] = { 'c','p','l','o','p','e','n',0 };
1872 SHELLEXECUTEINFOW sei;
1873 HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
1874 LPWSTR args = NULL;
1875 LPWSTR path = NULL;
1876 HRESULT r;
1877
1878 TRACE("%p %p\n", this, lpici );
1879
1880 if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
1881 return E_INVALIDARG;
1882
1883 r = Resolve(hwnd, 0 );
1884 if ( FAILED( r ) )
1885 {
1886 TRACE("failed to resolve component with error 0x%08x", r);
1887 return r;
1888 }
1889 if ( sComponent )
1890 {
1891 path = shelllink_get_msi_component_path( sComponent );
1892 if (!path)
1893 return E_FAIL;
1894 }
1895 else
1896 path = strdupW( sPath );
1897
1898 if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
1899 ( lpici->fMask & CMIC_MASK_UNICODE ) )
1900 {
1901 LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
1902 DWORD len = 2;
1903
1904 if ( sArgs )
1905 len += wcslen( sArgs );
1906 if ( iciex->lpParametersW )
1907 len += wcslen( iciex->lpParametersW );
1908
1909 args = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1910 args[0] = 0;
1911 if ( sArgs )
1912 wcscat( args, sArgs );
1913 if ( iciex->lpParametersW )
1914 {
1915 static const WCHAR space[] = { ' ', 0 };
1916 wcscat( args, space );
1917 wcscat( args, iciex->lpParametersW );
1918 }
1919 }
1920 else if (sArgs != NULL)
1921 {
1922 args = strdupW(sArgs);
1923 }
1924
1925 memset( &sei, 0, sizeof sei );
1926 sei.cbSize = sizeof sei;
1927 sei.fMask = SEE_MASK_UNICODE | (lpici->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
1928 sei.lpFile = path;
1929 sei.nShow = iShowCmd;
1930 sei.lpDirectory = sWorkDir;
1931 sei.lpParameters = args;
1932 sei.lpVerb = szOpen;
1933
1934 // HACK for ShellExecuteExW
1935 if (!wcsstr(sPath, L".cpl"))
1936 sei.lpVerb = szOpen;
1937 else
1938 sei.lpVerb = szCplOpen;
1939
1940 if( ShellExecuteExW( &sei ) )
1941 r = S_OK;
1942 else
1943 r = E_FAIL;
1944
1945 HeapFree( GetProcessHeap(), 0, args );
1946 HeapFree( GetProcessHeap(), 0, path );
1947
1948 return r;
1949 }
1950
1951 HRESULT WINAPI ShellLink::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax)
1952 {
1953 FIXME("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax );
1954
1955 return E_NOTIMPL;
1956 }
1957
1958 INT_PTR CALLBACK ExtendedShortcutProc(HWND hwndDlg, UINT uMsg,
1959 WPARAM wParam, LPARAM lParam)
1960 {
1961 HWND hDlgCtrl;
1962
1963 switch(uMsg)
1964 {
1965 case WM_INITDIALOG:
1966 if (lParam)
1967 {
1968 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1969 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
1970 }
1971 return TRUE;
1972 case WM_COMMAND:
1973 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1974 if (LOWORD(wParam) == IDOK)
1975 {
1976 if ( SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED )
1977 EndDialog(hwndDlg, 1);
1978 else
1979 EndDialog(hwndDlg, 0);
1980 }
1981 else if (LOWORD(wParam) == IDCANCEL)
1982 {
1983 EndDialog(hwndDlg, -1);
1984 }
1985 else if (LOWORD(wParam) == 14000)
1986 {
1987 if ( SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
1988 SendMessage(hDlgCtrl, BM_SETCHECK, BST_UNCHECKED, 0);
1989 else
1990 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
1991 }
1992 }
1993 return FALSE;
1994 }
1995
1996 /**************************************************************************
1997 * SH_ShellLinkDlgProc
1998 *
1999 * dialog proc of the shortcut property dialog
2000 */
2001
2002 INT_PTR CALLBACK ShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
2003 {
2004 LPPROPSHEETPAGEW ppsp;
2005 LPPSHNOTIFY lppsn;
2006 ShellLink *pThis;
2007 HWND hDlgCtrl;
2008 WCHAR szBuffer[MAX_PATH];
2009 WCHAR * ptr;
2010 int IconIndex;
2011 INT_PTR result;
2012
2013 pThis = (ShellLink *)GetWindowLongPtr(hwndDlg, DWLP_USER);
2014
2015 switch(uMsg)
2016 {
2017 case WM_INITDIALOG:
2018 {
2019 ppsp = (LPPROPSHEETPAGEW)lParam;
2020 if (ppsp == NULL)
2021 break;
2022
2023 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n",hwndDlg, lParam, ppsp->lParam);
2024
2025 pThis = (ShellLink *)ppsp->lParam;
2026 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pThis);
2027
2028 TRACE("sArgs: %S sComponent: %S sDescription: %S sIcoPath: %S sPath: %S sPathRel: %S sProduct: %S sWorkDir: %S\n", pThis->sArgs, pThis->sComponent, pThis->sDescription,
2029 pThis->sIcoPath, pThis->sPath, pThis->sPathRel, pThis->sProduct, pThis->sWorkDir);
2030
2031 /* target location */
2032 wchar_t * wTrgtLocat;
2033 const int ch = '\\';
2034 wTrgtLocat = wcsrchr(pThis->sWorkDir, ch)+1;
2035 hDlgCtrl = GetDlgItem( hwndDlg, 14007 );
2036 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wTrgtLocat);
2037
2038 /* target path */
2039 hDlgCtrl = GetDlgItem( hwndDlg, 14009 );
2040 if ( hDlgCtrl != NULL )
2041 SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)pThis->sPath );
2042
2043 /* working dir */
2044 hDlgCtrl = GetDlgItem( hwndDlg, 14011 );
2045 if ( hDlgCtrl != NULL )
2046 SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)pThis->sWorkDir );
2047
2048 /* description */
2049 hDlgCtrl = GetDlgItem( hwndDlg, 14019 );
2050 if ( hDlgCtrl != NULL )
2051 SendMessageW( hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)pThis->sDescription );
2052 return TRUE;
2053 }
2054
2055 case WM_NOTIFY:
2056 lppsn = (LPPSHNOTIFY) lParam;
2057 if ( lppsn->hdr.code == PSN_APPLY )
2058 {
2059 /* set working directory */
2060 hDlgCtrl = GetDlgItem( hwndDlg, 14011 );
2061 SendMessageW( hDlgCtrl, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)szBuffer );
2062 pThis->SetWorkingDirectory(szBuffer);
2063 /* set link destination */
2064 hDlgCtrl = GetDlgItem( hwndDlg, 14009 );
2065 SendMessageW( hDlgCtrl, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)szBuffer);
2066 if ( !SHELL_ExistsFileW(szBuffer) )
2067 {
2068 //FIXME load localized error msg
2069 MessageBoxW( hwndDlg, L"file not existing", szBuffer, MB_OK );
2070 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE );
2071 return TRUE;
2072 }
2073 ptr = wcsrchr(szBuffer, L'.');
2074 if (ptr && !_wcsnicmp(ptr, L".lnk", 4))
2075 {
2076 // FIXME load localized error msg
2077 MessageBoxW( hwndDlg, L"You cannot create a link to a shortcut", L"Error", MB_ICONERROR );
2078 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE );
2079 return TRUE;
2080 }
2081
2082 pThis->SetPath(szBuffer);
2083
2084 TRACE("This %p sLinkPath %S\n", pThis, pThis->sLinkPath);
2085 pThis->Save(pThis->sLinkPath, TRUE );
2086 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR );
2087 return TRUE;
2088 }
2089 break;
2090 case WM_COMMAND:
2091 switch(LOWORD(wParam))
2092 {
2093 case 14020:
2094 ///
2095 /// FIXME
2096 /// open target directory
2097 ///
2098 return TRUE;
2099 case 14021:
2100 if (pThis->sIcoPath)
2101 wcscpy(szBuffer, pThis->sIcoPath);
2102 IconIndex = pThis->iIcoNdx;
2103 if (PickIconDlg(hwndDlg, szBuffer, MAX_PATH, &IconIndex))
2104 {
2105 pThis->SetIconLocation(szBuffer, IconIndex);
2106 ///
2107 /// FIXME redraw icon
2108 }
2109 return TRUE;
2110 case 14022:
2111 result = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(SHELL_EXTENDED_SHORTCUT_DLG), hwndDlg, ExtendedShortcutProc, (LPARAM)pThis->bRunAs);
2112 if (result == 1 || result == 0)
2113 {
2114 if (pThis->bRunAs != result )
2115 {
2116 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
2117 }
2118
2119 pThis->bRunAs = result;
2120 }
2121 return TRUE;
2122 }
2123 switch(HIWORD(wParam))
2124 {
2125 case EN_CHANGE:
2126 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
2127 break;
2128 }
2129 break;
2130 default:
2131 break;
2132 }
2133 return FALSE;
2134 }
2135
2136 /**************************************************************************
2137 * ShellLink_IShellPropSheetExt interface
2138 */
2139
2140 HRESULT WINAPI ShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
2141 {
2142 HPROPSHEETPAGE hPage;
2143 BOOL bRet;
2144
2145 hPage = SH_CreatePropertySheetPage("SHELL_GENERAL_SHORTCUT_DLG", SH_ShellLinkDlgProc, (LPARAM)this, NULL);
2146 if (hPage == NULL)
2147 {
2148 ERR("failed to create property sheet page\n");
2149 return E_FAIL;
2150 }
2151
2152 bRet = pfnAddPage(hPage, lParam);
2153 if (bRet)
2154 return S_OK;
2155 else
2156 return E_FAIL;
2157 }
2158
2159 HRESULT WINAPI ShellLink::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
2160 {
2161 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID, pfnReplacePage, lParam);
2162 return E_NOTIMPL;
2163 }
2164
2165 HRESULT WINAPI ShellLink::SetSite(IUnknown *punk)
2166 {
2167 TRACE("%p %p\n", this, punk);
2168
2169 site = punk;
2170
2171 return S_OK;
2172 }
2173
2174 HRESULT WINAPI ShellLink::GetSite(REFIID iid, void ** ppvSite)
2175 {
2176 TRACE("%p %s %p\n", this, debugstr_guid(&iid), ppvSite );
2177
2178 if (site == NULL)
2179 return E_FAIL;
2180 return site->QueryInterface(iid, ppvSite );
2181 }
2182
2183 /**************************************************************************
2184 * IShellLink_ConstructFromFile
2185 */
2186 HRESULT WINAPI IShellLink_ConstructFromFile(IUnknown *pUnkOuter, REFIID riid, LPCITEMIDLIST pidl, LPVOID *ppv)
2187 {
2188 CComPtr<IUnknown> psl;
2189
2190 HRESULT hr = ShellLink::_CreatorClass::CreateInstance(NULL, riid, (void**)&psl);
2191
2192 if (SUCCEEDED(hr))
2193 {
2194 CComPtr<IPersistFile> ppf;
2195
2196 *ppv = NULL;
2197
2198 hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
2199
2200 if (SUCCEEDED(hr))
2201 {
2202 WCHAR path[MAX_PATH];
2203
2204 if (SHGetPathFromIDListW(pidl, path))
2205 hr = ppf->Load(path, 0);
2206 else
2207 hr = E_FAIL;
2208
2209 if (SUCCEEDED(hr))
2210 *ppv = psl.Detach();
2211 }
2212 }
2213
2214 return hr;
2215 }