3 * Copyright 1997 Marcus Meissner
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2005 Mike McCormack
6 * Copyright 2009 Andrew Hill
7 * Copyright 2013 Dominik Hornung
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 * Nearly complete information about the binary formats
25 * of .lnk files available at http://www.wotsit.org
27 * You can use winedump to examine the contents of a link file:
30 * MSI advertised shortcuts are totally undocumented. They provide an
31 * icon for a program that is not yet installed, and invoke MSI to
32 * install the program when the shortcut is clicked on. They are
33 * created by passing a special string to SetPath, and the information
34 * in that string is parsed an stored.
41 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
43 #define SHLINK_LOCAL 0
44 #define SHLINK_REMOTE 1
45 #define MAX_PROPERTY_SHEET_PAGE 32
47 /* link file formats */
58 DWORD dwNetworkVolTableOfs
;
62 struct LOCAL_VOLUME_INFO
74 WCHAR label
[12]; /* assume 8.3 */
79 /* IShellLink Implementation */
81 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
);
83 /* strdup on the process heap */
84 static LPWSTR __inline
HEAP_strdupAtoW(HANDLE heap
, DWORD flags
, LPCSTR str
)
91 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
92 p
= (LPWSTR
)HeapAlloc(heap
, flags
, len
* sizeof(WCHAR
));
95 MultiByteToWideChar(CP_ACP
, 0, str
, -1, p
, len
);
99 static LPWSTR __inline
strdupW(LPCWSTR src
)
102 if (!src
) return NULL
;
103 dest
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(src
) + 1) * sizeof(WCHAR
));
109 CShellLink::CShellLink()
113 memset(&time1
, 0, sizeof(time1
));
114 memset(&time2
, 0, sizeof(time2
));
115 memset(&time3
, 0, sizeof(time3
));
116 iShowCmd
= SW_SHOWNORMAL
;
126 memset(&volume
, 0, sizeof(volume
));
133 CShellLink::~CShellLink()
135 TRACE("-- destroying IShellLink(%p)\n", this);
137 HeapFree(GetProcessHeap(), 0, sIcoPath
);
138 HeapFree(GetProcessHeap(), 0, sArgs
);
139 HeapFree(GetProcessHeap(), 0, sWorkDir
);
140 HeapFree(GetProcessHeap(), 0, sDescription
);
141 HeapFree(GetProcessHeap(), 0, sPath
);
142 HeapFree(GetProcessHeap(), 0, sLinkPath
);
148 HRESULT WINAPI
CShellLink::GetClassID(CLSID
*pclsid
)
150 TRACE("%p %p\n", this, pclsid
);
154 *pclsid
= CLSID_ShellLink
;
158 HRESULT WINAPI
CShellLink::IsDirty()
160 TRACE("(%p)\n", this);
168 HRESULT WINAPI
CShellLink::Load(LPCOLESTR pszFileName
, DWORD dwMode
)
170 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName
), dwMode
);
173 dwMode
= STGM_READ
| STGM_SHARE_DENY_WRITE
;
175 CComPtr
<IStream
> stm
;
176 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, dwMode
, &stm
);
179 HeapFree(GetProcessHeap(), 0, sLinkPath
);
180 sLinkPath
= strdupW(pszFileName
);
182 ShellLink_UpdatePath(sPathRel
, pszFileName
, sWorkDir
, &sPath
);
185 TRACE("-- returning hr %08x\n", hr
);
189 HRESULT WINAPI
CShellLink::Save(LPCOLESTR pszFileName
, BOOL fRemember
)
191 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
196 CComPtr
<IStream
> stm
;
197 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, STGM_READWRITE
| STGM_CREATE
| STGM_SHARE_EXCLUSIVE
, &stm
);
200 hr
= Save(stm
, FALSE
);
205 HeapFree(GetProcessHeap(), 0, sLinkPath
);
207 sLinkPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName
) + 1) * sizeof(WCHAR
));
209 wcscpy(sLinkPath
, pszFileName
);
215 DeleteFileW(pszFileName
);
216 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName
));
223 HRESULT WINAPI
CShellLink::SaveCompleted(LPCOLESTR pszFileName
)
225 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
229 HRESULT WINAPI
CShellLink::GetCurFile(LPOLESTR
*ppszFileName
)
231 *ppszFileName
= NULL
;
235 /* IPersistFile::GetCurFile called before IPersistFile::Save */
239 *ppszFileName
= (LPOLESTR
)CoTaskMemAlloc((wcslen(sLinkPath
) + 1) * sizeof(WCHAR
));
243 return E_OUTOFMEMORY
;
246 /* copy last saved filename */
247 wcscpy(*ppszFileName
, sLinkPath
);
252 /************************************************************************
253 * IPersistStream_IsDirty (IPersistStream)
256 static HRESULT
Stream_LoadString(IStream
* stm
, BOOL unicode
, LPWSTR
*pstr
)
262 HRESULT hr
= stm
->Read(&len
, sizeof(len
), &count
);
263 if (FAILED(hr
) || count
!= sizeof(len
))
267 len
*= sizeof (WCHAR
);
269 TRACE("reading %d\n", len
);
270 LPSTR temp
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
+ sizeof(WCHAR
));
272 return E_OUTOFMEMORY
;
274 hr
= stm
->Read(temp
, len
, &count
);
275 if(FAILED(hr
) || count
!= len
)
277 HeapFree(GetProcessHeap(), 0, temp
);
281 TRACE("read %s\n", debugstr_an(temp
, len
));
283 /* convert to unicode if necessary */
287 count
= MultiByteToWideChar(CP_ACP
, 0, temp
, len
, NULL
, 0);
288 str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (count
+ 1) * sizeof (WCHAR
));
291 HeapFree(GetProcessHeap(), 0, temp
);
292 return E_OUTOFMEMORY
;
294 MultiByteToWideChar(CP_ACP
, 0, temp
, len
, str
, count
);
295 HeapFree(GetProcessHeap(), 0, temp
);
309 static HRESULT
Stream_ReadChunk(IStream
* stm
, LPVOID
*data
)
314 unsigned char data
[1];
321 HRESULT hr
= stm
->Read(&size
, sizeof(size
), &count
);
322 if (FAILED(hr
) || count
!= sizeof(size
))
325 chunk
= (sized_chunk
*)HeapAlloc(GetProcessHeap(), 0, size
);
327 return E_OUTOFMEMORY
;
330 hr
= stm
->Read(chunk
->data
, size
- sizeof(size
), &count
);
331 if (FAILED(hr
) || count
!= (size
- sizeof(size
)))
333 HeapFree(GetProcessHeap(), 0, chunk
);
337 TRACE("Read %d bytes\n", chunk
->size
);
344 static BOOL
Stream_LoadVolume(LOCAL_VOLUME_INFO
*vol
, CShellLink::volume_info
*volume
)
346 volume
->serial
= vol
->dwVolSerial
;
347 volume
->type
= vol
->dwType
;
349 if (!vol
->dwVolLabelOfs
)
351 if (vol
->dwSize
<= vol
->dwVolLabelOfs
)
353 INT len
= vol
->dwSize
- vol
->dwVolLabelOfs
;
355 LPSTR label
= (LPSTR
)vol
;
356 label
+= vol
->dwVolLabelOfs
;
357 MultiByteToWideChar(CP_ACP
, 0, label
, len
, volume
->label
, _countof(volume
->label
));
362 static LPWSTR
Stream_LoadPath(LPCSTR p
, DWORD maxlen
)
366 while (p
[len
] && len
< maxlen
)
369 UINT wlen
= MultiByteToWideChar(CP_ACP
, 0, p
, len
, NULL
, 0);
370 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wlen
+ 1) * sizeof(WCHAR
));
373 MultiByteToWideChar(CP_ACP
, 0, p
, len
, path
, wlen
);
379 static HRESULT
Stream_LoadLocation(IStream
*stm
,
380 CShellLink::volume_info
*volume
, LPWSTR
*path
)
383 HRESULT hr
= Stream_ReadChunk(stm
, (LPVOID
*) &p
);
387 LOCATION_INFO
*loc
= (LOCATION_INFO
*) p
;
388 if (loc
->dwTotalSize
< sizeof(LOCATION_INFO
))
390 HeapFree(GetProcessHeap(), 0, p
);
394 /* if there's valid local volume information, load it */
395 if (loc
->dwVolTableOfs
&&
396 ((loc
->dwVolTableOfs
+ sizeof(LOCAL_VOLUME_INFO
)) <= loc
->dwTotalSize
))
398 LOCAL_VOLUME_INFO
*volume_info
;
400 volume_info
= (LOCAL_VOLUME_INFO
*) &p
[loc
->dwVolTableOfs
];
401 Stream_LoadVolume(volume_info
, volume
);
404 /* if there's a local path, load it */
405 DWORD n
= loc
->dwLocalPathOfs
;
406 if (n
&& n
< loc
->dwTotalSize
)
407 *path
= Stream_LoadPath(&p
[n
], loc
->dwTotalSize
- n
);
409 TRACE("type %d serial %08x name %s path %s\n", volume
->type
,
410 volume
->serial
, debugstr_w(volume
->label
), debugstr_w(*path
));
412 HeapFree(GetProcessHeap(), 0, p
);
417 * The format of the advertised shortcut info seems to be:
422 * 0 Length of the block (4 bytes, usually 0x314)
424 * 8 string data in ASCII
425 * 8+0x104 string data in UNICODE
427 * In the original Win32 implementation the buffers are not initialized
428 * to zero, so data trailing the string is random garbage.
430 static HRESULT
Stream_LoadAdvertiseInfo(IStream
* stm
, LPWSTR
*str
)
435 EXP_DARWIN_LINK buffer
;
436 HRESULT hr
= stm
->Read(&buffer
.dbh
.cbSize
, sizeof (DWORD
), &count
);
440 /* make sure that we read the size of the structure even on error */
441 DWORD size
= sizeof buffer
- sizeof (DWORD
);
442 if (buffer
.dbh
.cbSize
!= sizeof buffer
)
444 ERR("Ooops. This structure is not as expected...\n");
448 hr
= stm
->Read(&buffer
.dbh
.dwSignature
, size
, &count
);
455 TRACE("magic %08x string = %s\n", buffer
.dbh
.dwSignature
, debugstr_w(buffer
.szwDarwinID
));
457 if ((buffer
.dbh
.dwSignature
& 0xffff0000) != 0xa0000000)
459 ERR("Unknown magic number %08x in advertised shortcut\n", buffer
.dbh
.dwSignature
);
463 *str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
464 (wcslen(buffer
.szwDarwinID
) + 1) * sizeof(WCHAR
));
465 wcscpy(*str
, buffer
.szwDarwinID
);
470 /************************************************************************
471 * IPersistStream_Load (IPersistStream)
473 HRESULT WINAPI
CShellLink::Load(IStream
*stm
)
475 TRACE("%p %p\n", this, stm
);
478 return STG_E_INVALIDPOINTER
;
480 SHELL_LINK_HEADER ShlLnkHeader
;
481 ULONG dwBytesRead
= 0;
482 HRESULT hr
= stm
->Read(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &dwBytesRead
);
486 if (dwBytesRead
!= sizeof(ShlLnkHeader
))
488 if (ShlLnkHeader
.dwSize
!= sizeof(ShlLnkHeader
))
490 if (!IsEqualIID(ShlLnkHeader
.clsid
, CLSID_ShellLink
))
493 /* free all the old stuff */
496 memset(&volume
, 0, sizeof volume
);
497 HeapFree(GetProcessHeap(), 0, sPath
);
499 HeapFree(GetProcessHeap(), 0, sDescription
);
501 HeapFree(GetProcessHeap(), 0, sPathRel
);
503 HeapFree(GetProcessHeap(), 0, sWorkDir
);
505 HeapFree(GetProcessHeap(), 0, sArgs
);
507 HeapFree(GetProcessHeap(), 0, sIcoPath
);
509 HeapFree(GetProcessHeap(), 0, sProduct
);
511 HeapFree(GetProcessHeap(), 0, sComponent
);
514 BOOL unicode
= FALSE
;
515 iShowCmd
= ShlLnkHeader
.nShowCommand
;
516 wHotKey
= ShlLnkHeader
.wHotKey
;
517 iIcoNdx
= ShlLnkHeader
.nIconIndex
;
518 FileTimeToSystemTime (&ShlLnkHeader
.ftCreationTime
, &time1
);
519 FileTimeToSystemTime (&ShlLnkHeader
.ftLastAccessTime
, &time2
);
520 FileTimeToSystemTime (&ShlLnkHeader
.ftLastWriteTime
, &time3
);
523 WCHAR sTemp
[MAX_PATH
];
524 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time1
,
525 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
526 TRACE("-- time1: %s\n", debugstr_w(sTemp
));
527 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time2
,
528 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
529 TRACE("-- time2: %s\n", debugstr_w(sTemp
));
530 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time3
,
531 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
532 TRACE("-- time3: %s\n", debugstr_w(sTemp
));
535 /* load all the new stuff */
536 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ID_LIST
)
538 hr
= ILLoadFromStream(stm
, &pPidl
);
544 /* load the location information */
545 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LINK_INFO
)
546 hr
= Stream_LoadLocation(stm
, &volume
, &sPath
);
550 if (ShlLnkHeader
.dwFlags
& SLDF_UNICODE
)
553 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_NAME
)
555 hr
= Stream_LoadString(stm
, unicode
, &sDescription
);
556 TRACE("Description -> %s\n", debugstr_w(sDescription
));
561 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_RELPATH
)
563 hr
= Stream_LoadString(stm
, unicode
, &sPathRel
);
564 TRACE("Relative Path-> %s\n", debugstr_w(sPathRel
));
569 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_WORKINGDIR
)
571 hr
= Stream_LoadString(stm
, unicode
, &sWorkDir
);
572 PathRemoveBackslash(sWorkDir
);
573 TRACE("Working Dir -> %s\n", debugstr_w(sWorkDir
));
578 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ARGS
)
580 hr
= Stream_LoadString(stm
, unicode
, &sArgs
);
581 TRACE("Arguments -> %s\n", debugstr_w(sArgs
));
586 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ICONLOCATION
)
588 hr
= Stream_LoadString(stm
, unicode
, &sIcoPath
);
589 TRACE("Icon file -> %s\n", debugstr_w(sIcoPath
));
594 #if (NTDDI_VERSION < NTDDI_LONGHORN)
595 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LOGO3ID
)
597 hr
= Stream_LoadAdvertiseInfo(stm
, &sProduct
);
598 TRACE("Product -> %s\n", debugstr_w(sProduct
));
604 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_DARWINID
)
606 hr
= Stream_LoadAdvertiseInfo(stm
, &sComponent
);
607 TRACE("Component -> %s\n", debugstr_w(sComponent
));
609 if (ShlLnkHeader
.dwFlags
& SLDF_RUNAS_USER
)
622 hr
= stm
->Read(&dwZero
, sizeof(dwZero
), &dwBytesRead
);
623 if (FAILED(hr
) || dwZero
|| dwBytesRead
!= sizeof(dwZero
))
624 ERR("Last word was not zero\n");
636 /************************************************************************
639 * Helper function for IPersistStream_Save. Writes a unicode string
640 * with terminating nul byte to a stream, preceded by the its length.
642 static HRESULT
Stream_WriteString(IStream
* stm
, LPCWSTR str
)
644 USHORT len
= wcslen(str
) + 1;
647 HRESULT hr
= stm
->Write(&len
, sizeof(len
), &count
);
651 len
*= sizeof(WCHAR
);
653 hr
= stm
->Write(str
, len
, &count
);
660 /************************************************************************
661 * Stream_WriteLocationInfo
663 * Writes the location info to a stream
665 * FIXME: One day we might want to write the network volume information
666 * and the final path.
667 * Figure out how Windows deals with unicode paths here.
669 static HRESULT
Stream_WriteLocationInfo(IStream
* stm
, LPCWSTR path
,
670 CShellLink::volume_info
*volume
)
672 LOCAL_VOLUME_INFO
*vol
;
675 TRACE("%p %s %p\n", stm
, debugstr_w(path
), volume
);
677 /* figure out the size of everything */
678 DWORD label_size
= WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
679 NULL
, 0, NULL
, NULL
);
680 DWORD path_size
= WideCharToMultiByte(CP_ACP
, 0, path
, -1,
681 NULL
, 0, NULL
, NULL
);
682 DWORD volume_info_size
= sizeof(*vol
) + label_size
;
683 DWORD final_path_size
= 1;
684 DWORD total_size
= sizeof(*loc
) + volume_info_size
+ path_size
+ final_path_size
;
686 /* create pointers to everything */
687 loc
= (LOCATION_INFO
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, total_size
);
688 vol
= (LOCAL_VOLUME_INFO
*) &loc
[1];
689 LPSTR szLabel
= (LPSTR
) &vol
[1];
690 LPSTR szPath
= &szLabel
[label_size
];
691 LPSTR szFinalPath
= &szPath
[path_size
];
693 /* fill in the location information header */
694 loc
->dwTotalSize
= total_size
;
695 loc
->dwHeaderSize
= sizeof (*loc
);
697 loc
->dwVolTableOfs
= sizeof (*loc
);
698 loc
->dwLocalPathOfs
= sizeof (*loc
) + volume_info_size
;
699 loc
->dwNetworkVolTableOfs
= 0;
700 loc
->dwFinalPathOfs
= sizeof (*loc
) + volume_info_size
+ path_size
;
702 /* fill in the volume information */
703 vol
->dwSize
= volume_info_size
;
704 vol
->dwType
= volume
->type
;
705 vol
->dwVolSerial
= volume
->serial
;
706 vol
->dwVolLabelOfs
= sizeof (*vol
);
708 /* copy in the strings */
709 WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
710 szLabel
, label_size
, NULL
, NULL
);
711 WideCharToMultiByte(CP_ACP
, 0, path
, -1,
712 szPath
, path_size
, NULL
, NULL
);
716 HRESULT hr
= stm
->Write(loc
, total_size
, &count
);
717 HeapFree(GetProcessHeap(), 0, loc
);
722 static EXP_DARWIN_LINK
* shelllink_build_darwinid(LPCWSTR string
, DWORD magic
)
724 EXP_DARWIN_LINK
*buffer
= (EXP_DARWIN_LINK
*)LocalAlloc(LMEM_ZEROINIT
, sizeof * buffer
);
725 buffer
->dbh
.cbSize
= sizeof * buffer
;
726 buffer
->dbh
.dwSignature
= magic
;
727 lstrcpynW(buffer
->szwDarwinID
, string
, MAX_PATH
);
728 WideCharToMultiByte(CP_ACP
, 0, string
, -1, buffer
->szDarwinID
, MAX_PATH
, NULL
, NULL
);
733 static HRESULT
Stream_WriteAdvertiseInfo(IStream
* stm
, LPCWSTR string
, DWORD magic
)
737 EXP_DARWIN_LINK
*buffer
= shelllink_build_darwinid(string
, magic
);
740 return stm
->Write(buffer
, buffer
->dbh
.cbSize
, &count
);
743 /************************************************************************
744 * IPersistStream_Save (IPersistStream)
746 * FIXME: makes assumptions about byte order
748 HRESULT WINAPI
CShellLink::Save(IStream
*stm
, BOOL fClearDirty
)
750 TRACE("%p %p %x\n", this, stm
, fClearDirty
);
752 SHELL_LINK_HEADER ShlLnkHeader
;
753 memset(&ShlLnkHeader
, 0, sizeof(ShlLnkHeader
));
754 ShlLnkHeader
.dwSize
= sizeof(ShlLnkHeader
);
755 ShlLnkHeader
.nShowCommand
= iShowCmd
;
756 ShlLnkHeader
.clsid
= CLSID_ShellLink
;
758 ShlLnkHeader
.wHotKey
= wHotKey
;
759 ShlLnkHeader
.nIconIndex
= iIcoNdx
;
760 ShlLnkHeader
.dwFlags
= SLDF_UNICODE
; /* strings are in unicode */
762 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ID_LIST
;
764 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LINK_INFO
;
766 ShlLnkHeader
.dwFlags
|= SLDF_HAS_NAME
;
768 ShlLnkHeader
.dwFlags
|= SLDF_HAS_WORKINGDIR
;
770 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ARGS
;
772 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ICONLOCATION
;
773 #if (NTDDI_VERSION < NTDDI_LONGHORN)
775 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LOGO3ID
;
778 ShlLnkHeader
.dwFlags
|= SLDF_HAS_DARWINID
;
780 ShlLnkHeader
.dwFlags
|= SLDF_RUNAS_USER
;
782 SystemTimeToFileTime (&time1
, &ShlLnkHeader
.ftCreationTime
);
783 SystemTimeToFileTime (&time2
, &ShlLnkHeader
.ftLastAccessTime
);
784 SystemTimeToFileTime (&time3
, &ShlLnkHeader
.ftLastWriteTime
);
786 /* write the Shortcut header */
788 HRESULT hr
= stm
->Write(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &count
);
791 ERR("Write failed\n");
795 TRACE("Writing pidl\n");
797 /* write the PIDL to the shortcut */
800 hr
= ILSaveToStream(stm
, pPidl
);
803 ERR("Failed to write PIDL\n");
809 Stream_WriteLocationInfo(stm
, sPath
, &volume
);
812 hr
= Stream_WriteString(stm
, sDescription
);
815 hr
= Stream_WriteString(stm
, sPathRel
);
818 hr
= Stream_WriteString(stm
, sWorkDir
);
821 hr
= Stream_WriteString(stm
, sArgs
);
824 hr
= Stream_WriteString(stm
, sIcoPath
);
827 hr
= Stream_WriteAdvertiseInfo(stm
, sProduct
, EXP_SZ_ICON_SIG
);
830 hr
= Stream_WriteAdvertiseInfo(stm
, sComponent
, EXP_DARWIN_ID_SIG
);
832 /* the last field is a single zero dword */
834 hr
= stm
->Write(&zero
, sizeof zero
, &count
);
839 /************************************************************************
840 * IPersistStream_GetSizeMax (IPersistStream)
842 HRESULT WINAPI
CShellLink::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
844 TRACE("(%p)\n", this);
849 static BOOL
SHELL_ExistsFileW(LPCWSTR path
)
851 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(path
))
857 /**************************************************************************
858 * ShellLink_UpdatePath
859 * update absolute path in sPath using relative path in sPathRel
861 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
)
863 if (!path
|| !psPath
)
866 if (!*psPath
&& sPathRel
)
868 WCHAR buffer
[2*MAX_PATH
], abs_path
[2*MAX_PATH
];
871 /* first try if [directory of link file] + [relative path] finds an existing file */
873 GetFullPathNameW(path
, MAX_PATH
* 2, buffer
, &final
);
876 wcscpy(final
, sPathRel
);
880 if (SHELL_ExistsFileW(buffer
))
882 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
883 wcscpy(abs_path
, buffer
);
887 /* try if [working directory] + [relative path] finds an existing file */
890 wcscpy(buffer
, sWorkDir
);
891 wcscpy(PathAddBackslashW(buffer
), sPathRel
);
893 if (SHELL_ExistsFileW(buffer
))
894 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
895 wcscpy(abs_path
, buffer
);
899 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
901 wcscpy(abs_path
, sPathRel
);
903 *psPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path
) + 1) * sizeof(WCHAR
));
905 return E_OUTOFMEMORY
;
907 wcscpy(*psPath
, abs_path
);
913 HRESULT WINAPI
CShellLink::GetPath(LPSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAA
*pfd
, DWORD fFlags
)
915 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
916 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
918 if (sComponent
|| sProduct
)
925 WideCharToMultiByte(CP_ACP
, 0, sPath
, -1,
926 pszFile
, cchMaxPath
, NULL
, NULL
);
928 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
933 HRESULT WINAPI
CShellLink::GetIDList(LPITEMIDLIST
* ppidl
)
935 TRACE("(%p)->(ppidl=%p)\n", this, ppidl
);
943 *ppidl
= ILClone(pPidl
);
947 HRESULT WINAPI
CShellLink::SetIDList(LPCITEMIDLIST pidl
)
949 TRACE("(%p)->(pidl=%p)\n", this, pidl
);
954 pPidl
= ILClone(pidl
);
963 HRESULT WINAPI
CShellLink::GetDescription(LPSTR pszName
, INT cchMaxName
)
965 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
971 WideCharToMultiByte(CP_ACP
, 0, sDescription
, -1,
972 pszName
, cchMaxName
, NULL
, NULL
);
977 HRESULT WINAPI
CShellLink::SetDescription(LPCSTR pszName
)
979 TRACE("(%p)->(pName=%s)\n", this, pszName
);
981 HeapFree(GetProcessHeap(), 0, sDescription
);
986 sDescription
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszName
);
988 return E_OUTOFMEMORY
;
995 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPSTR pszDir
, INT cchMaxPath
)
997 TRACE("(%p)->(%p len=%u)\n", this, pszDir
, cchMaxPath
);
1003 WideCharToMultiByte(CP_ACP
, 0, sWorkDir
, -1,
1004 pszDir
, cchMaxPath
, NULL
, NULL
);
1009 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCSTR pszDir
)
1011 TRACE("(%p)->(dir=%s)\n", this, pszDir
);
1013 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1018 sWorkDir
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir
);
1020 return E_OUTOFMEMORY
;
1027 HRESULT WINAPI
CShellLink::GetArguments(LPSTR pszArgs
, INT cchMaxPath
)
1029 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1034 WideCharToMultiByte(CP_ACP
, 0, sArgs
, -1,
1035 pszArgs
, cchMaxPath
, NULL
, NULL
);
1040 HRESULT WINAPI
CShellLink::SetArguments(LPCSTR pszArgs
)
1042 TRACE("(%p)->(args=%s)\n", this, pszArgs
);
1044 HeapFree(GetProcessHeap(), 0, sArgs
);
1049 sArgs
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs
);
1051 return E_OUTOFMEMORY
;
1059 HRESULT WINAPI
CShellLink::GetHotkey(WORD
*pwHotkey
)
1061 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey
, wHotKey
);
1063 *pwHotkey
= wHotKey
;
1068 HRESULT WINAPI
CShellLink::SetHotkey(WORD wHotkey
)
1070 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey
);
1078 HRESULT WINAPI
CShellLink::GetShowCmd(INT
*piShowCmd
)
1080 TRACE("(%p)->(%p)\n", this, piShowCmd
);
1081 *piShowCmd
= iShowCmd
;
1085 HRESULT WINAPI
CShellLink::SetShowCmd(INT iShowCmd
)
1087 TRACE("(%p) %d\n", this, iShowCmd
);
1089 this->iShowCmd
= iShowCmd
;
1095 static HRESULT
SHELL_PidlGeticonLocationA(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1096 LPSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1098 LPCITEMIDLIST pidlLast
;
1100 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
1104 CComPtr
<IExtractIconA
> pei
;
1106 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_IExtractIconA
, NULL
, (LPVOID
*)&pei
);
1109 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, NULL
);
1117 HRESULT WINAPI
CShellLink::GetIconLocation(LPSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1119 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1126 WideCharToMultiByte(CP_ACP
, 0, sIcoPath
, -1, pszIconPath
, cchIconPath
, NULL
, NULL
);
1132 CComPtr
<IShellFolder
> pdsk
;
1134 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1138 /* first look for an icon using the PIDL (if present) */
1140 hr
= SHELL_PidlGeticonLocationA(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1144 /* if we couldn't find an icon yet, look for it using the file system path */
1145 if (FAILED(hr
) && sPath
)
1149 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1153 hr
= SHELL_PidlGeticonLocationA(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1165 HRESULT WINAPI
CShellLink::SetIconLocation(LPCSTR pszIconPath
, INT iIcon
)
1167 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath
, iIcon
);
1169 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1174 sIcoPath
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath
);
1176 return E_OUTOFMEMORY
;
1185 HRESULT WINAPI
CShellLink::SetRelativePath(LPCSTR pszPathRel
, DWORD dwReserved
)
1187 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel
, dwReserved
);
1189 HeapFree(GetProcessHeap(), 0, sPathRel
);
1194 sPathRel
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel
);
1198 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1201 HRESULT WINAPI
CShellLink::Resolve(HWND hwnd
, DWORD fFlags
)
1206 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd
, fFlags
);
1208 /*FIXME: use IResolveShellLink interface */
1210 if (!sPath
&& pPidl
)
1212 WCHAR buffer
[MAX_PATH
];
1214 bSuccess
= SHGetPathFromIDListW(pPidl
, buffer
);
1216 if (bSuccess
&& *buffer
)
1218 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
) + 1) * sizeof(WCHAR
));
1221 return E_OUTOFMEMORY
;
1223 wcscpy(sPath
, buffer
);
1228 hr
= S_OK
; /* don't report an error occurred while just caching information */
1231 if (!sIcoPath
&& sPath
)
1233 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(sPath
) + 1) * sizeof(WCHAR
));
1236 return E_OUTOFMEMORY
;
1238 wcscpy(sIcoPath
, sPath
);
1247 HRESULT WINAPI
CShellLink::SetPath(LPCSTR pszFile
)
1249 TRACE("(%p)->(path=%s)\n", this, pszFile
);
1250 if (pszFile
== NULL
)
1251 return E_INVALIDARG
;
1253 LPWSTR str
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile
);
1255 return E_OUTOFMEMORY
;
1257 HRESULT hr
= SetPath(str
);
1258 HeapFree(GetProcessHeap(), 0, str
);
1263 HRESULT WINAPI
CShellLink::GetPath(LPWSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAW
*pfd
, DWORD fFlags
)
1265 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1266 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
1268 if (sComponent
|| sProduct
)
1275 lstrcpynW(pszFile
, sPath
, cchMaxPath
);
1277 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1282 HRESULT WINAPI
CShellLink::GetDescription(LPWSTR pszName
, INT cchMaxName
)
1284 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
1288 lstrcpynW(pszName
, sDescription
, cchMaxName
);
1293 HRESULT WINAPI
CShellLink::SetDescription(LPCWSTR pszName
)
1295 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName
));
1297 HeapFree(GetProcessHeap(), 0, sDescription
);
1298 sDescription
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1299 (wcslen(pszName
) + 1) * sizeof(WCHAR
));
1301 return E_OUTOFMEMORY
;
1303 wcscpy(sDescription
, pszName
);
1309 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPWSTR pszDir
, INT cchMaxPath
)
1311 TRACE("(%p)->(%p len %u)\n", this, pszDir
, cchMaxPath
);
1316 lstrcpynW(pszDir
, sWorkDir
, cchMaxPath
);
1321 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCWSTR pszDir
)
1323 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir
));
1325 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1326 sWorkDir
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1327 (wcslen(pszDir
) + 1) * sizeof (WCHAR
));
1329 return E_OUTOFMEMORY
;
1330 wcscpy(sWorkDir
, pszDir
);
1336 HRESULT WINAPI
CShellLink::GetArguments(LPWSTR pszArgs
, INT cchMaxPath
)
1338 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1343 lstrcpynW(pszArgs
, sArgs
, cchMaxPath
);
1348 HRESULT WINAPI
CShellLink::SetArguments(LPCWSTR pszArgs
)
1350 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs
));
1352 HeapFree(GetProcessHeap(), 0, sArgs
);
1353 sArgs
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1354 (wcslen(pszArgs
) + 1) * sizeof (WCHAR
));
1356 return E_OUTOFMEMORY
;
1358 wcscpy(sArgs
, pszArgs
);
1364 static HRESULT
SHELL_PidlGeticonLocationW(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1365 LPWSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1367 LPCITEMIDLIST pidlLast
;
1370 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
1374 CComPtr
<IExtractIconW
> pei
;
1376 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_IExtractIconW
, NULL
, (LPVOID
*)&pei
);
1379 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, &wFlags
);
1387 HRESULT WINAPI
CShellLink::GetIconLocation(LPWSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1389 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1396 lstrcpynW(pszIconPath
, sIcoPath
, cchIconPath
);
1402 CComPtr
<IShellFolder
> pdsk
;
1404 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1408 /* first look for an icon using the PIDL (if present) */
1410 hr
= SHELL_PidlGeticonLocationW(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1414 /* if we couldn't find an icon yet, look for it using the file system path */
1415 if (FAILED(hr
) && sPath
)
1419 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1423 hr
= SHELL_PidlGeticonLocationW(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1434 HRESULT WINAPI
CShellLink::SetIconLocation(LPCWSTR pszIconPath
, INT iIcon
)
1436 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath
), iIcon
);
1438 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1439 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1440 (wcslen(pszIconPath
) + 1) * sizeof (WCHAR
));
1442 return E_OUTOFMEMORY
;
1443 wcscpy(sIcoPath
, pszIconPath
);
1451 HRESULT WINAPI
CShellLink::SetRelativePath(LPCWSTR pszPathRel
, DWORD dwReserved
)
1453 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel
), dwReserved
);
1455 HeapFree(GetProcessHeap(), 0, sPathRel
);
1456 sPathRel
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1457 (wcslen(pszPathRel
) + 1) * sizeof (WCHAR
));
1459 return E_OUTOFMEMORY
;
1460 wcscpy(sPathRel
, pszPathRel
);
1463 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1466 LPWSTR
CShellLink::ShellLink_GetAdvertisedArg(LPCWSTR str
)
1471 LPCWSTR p
= wcschr(str
, L
':');
1474 DWORD len
= p
- str
;
1475 LPWSTR ret
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * (len
+ 1));
1478 memcpy(ret
, str
, sizeof(WCHAR
)*len
);
1483 HRESULT
CShellLink::ShellLink_SetAdvertiseInfo(LPCWSTR str
)
1485 LPCWSTR szComponent
= NULL
, szProduct
= NULL
;
1489 /* each segment must start with two colons */
1490 if (str
[0] != ':' || str
[1] != ':')
1493 /* the last segment is just two colons */
1498 /* there must be a colon straight after a guid */
1499 LPCWSTR p
= wcschr(str
, L
':');
1506 /* get the guid, and check it's validly formatted */
1508 memcpy(szGuid
, str
, sizeof(WCHAR
)*len
);
1512 HRESULT hr
= CLSIDFromString(szGuid
, &guid
);
1517 /* match it up to a guid that we care about */
1518 if (IsEqualGUID(guid
, SHELL32_AdvtShortcutComponent
) && !szComponent
)
1520 else if (IsEqualGUID(guid
, SHELL32_AdvtShortcutProduct
) && !szProduct
)
1525 /* skip to the next field */
1526 str
= wcschr(str
, L
':');
1531 /* we have to have a component for an advertised shortcut */
1535 sComponent
= ShellLink_GetAdvertisedArg(szComponent
);
1536 sProduct
= ShellLink_GetAdvertisedArg(szProduct
);
1538 TRACE("Component = %s\n", debugstr_w(sComponent
));
1539 TRACE("Product = %s\n", debugstr_w(sProduct
));
1544 static BOOL
ShellLink_GetVolumeInfo(LPCWSTR path
, CShellLink::volume_info
*volume
)
1546 WCHAR drive
[4] = { path
[0], ':', '\\', 0 };
1548 volume
->type
= GetDriveTypeW(drive
);
1549 BOOL bRet
= GetVolumeInformationW(drive
, volume
->label
, _countof(volume
->label
), &volume
->serial
, NULL
, NULL
, NULL
, 0);
1550 TRACE("ret = %d type %d serial %08x name %s\n", bRet
,
1551 volume
->type
, volume
->serial
, debugstr_w(volume
->label
));
1555 HRESULT WINAPI
CShellLink::SetPath(LPCWSTR pszFile
)
1557 LPWSTR unquoted
= NULL
;
1560 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile
));
1563 return E_INVALIDARG
;
1565 /* quotes at the ends of the string are stripped */
1566 UINT len
= wcslen(pszFile
);
1567 if (pszFile
[0] == '"' && pszFile
[len
-1] == '"')
1569 unquoted
= strdupW(pszFile
);
1570 PathUnquoteSpacesW(unquoted
);
1574 /* any other quote marks are invalid */
1575 if (wcschr(pszFile
, '"'))
1577 HeapFree(GetProcessHeap(), 0, unquoted
);
1581 HeapFree(GetProcessHeap(), 0, sPath
);
1584 HeapFree(GetProcessHeap(), 0, sComponent
);
1591 if (S_OK
!= ShellLink_SetAdvertiseInfo(pszFile
))
1593 WCHAR buffer
[MAX_PATH
];
1596 if (*pszFile
== '\0')
1598 else if (!GetFullPathNameW(pszFile
, MAX_PATH
, buffer
, &fname
))
1600 else if(!PathFileExistsW(buffer
) &&
1601 !SearchPathW(NULL
, pszFile
, NULL
, MAX_PATH
, buffer
, NULL
))
1604 pPidl
= SHSimpleIDListFromPathW(pszFile
);
1605 ShellLink_GetVolumeInfo(buffer
, &volume
);
1607 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1608 (wcslen(buffer
) + 1) * sizeof (WCHAR
));
1610 return E_OUTOFMEMORY
;
1612 wcscpy(sPath
, buffer
);
1616 HeapFree(GetProcessHeap(), 0, unquoted
);
1621 HRESULT WINAPI
CShellLink::AddDataBlock(void* pDataBlock
)
1627 HRESULT WINAPI
CShellLink::CopyDataBlock(DWORD dwSig
, void** ppDataBlock
)
1629 LPVOID block
= NULL
;
1630 HRESULT hr
= E_FAIL
;
1632 TRACE("%p %08x %p\n", this, dwSig
, ppDataBlock
);
1636 case EXP_DARWIN_ID_SIG
:
1639 block
= shelllink_build_darwinid(sComponent
, dwSig
);
1642 case EXP_SZ_LINK_SIG
:
1643 case NT_CONSOLE_PROPS_SIG
:
1644 case NT_FE_CONSOLE_PROPS_SIG
:
1645 case EXP_SPECIAL_FOLDER_SIG
:
1646 case EXP_SZ_ICON_SIG
:
1647 FIXME("valid but unhandled datablock %08x\n", dwSig
);
1650 ERR("unknown datablock %08x\n", dwSig
);
1652 *ppDataBlock
= block
;
1656 HRESULT WINAPI
CShellLink::RemoveDataBlock(DWORD dwSig
)
1662 HRESULT WINAPI
CShellLink::GetFlags(DWORD
*pdwFlags
)
1666 FIXME("%p %p\n", this, pdwFlags
);
1668 /* FIXME: add more */
1670 flags
|= SLDF_HAS_ARGS
;
1672 flags
|= SLDF_HAS_DARWINID
;
1674 flags
|= SLDF_HAS_ICONLOCATION
;
1675 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1677 flags
|= SLDF_HAS_LOGO3ID
;
1680 flags
|= SLDF_HAS_ID_LIST
;
1687 HRESULT WINAPI
CShellLink::SetFlags(DWORD dwFlags
)
1693 /**************************************************************************
1694 * CShellLink implementation of IShellExtInit::Initialize()
1696 * Loads the shelllink from the dataobject the shell is pointing to.
1698 HRESULT WINAPI
CShellLink::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pdtobj
, HKEY hkeyProgID
)
1700 TRACE("%p %p %p %p\n", this, pidlFolder
, pdtobj
, hkeyProgID
);
1706 format
.cfFormat
= CF_HDROP
;
1708 format
.dwAspect
= DVASPECT_CONTENT
;
1710 format
.tymed
= TYMED_HGLOBAL
;
1713 HRESULT hr
= pdtobj
->GetData(&format
, &stgm
);
1717 UINT count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, -1, NULL
, 0);
1720 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, NULL
, 0);
1722 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, count
* sizeof(WCHAR
));
1725 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, path
, count
);
1727 HeapFree(GetProcessHeap(), 0, path
);
1730 ReleaseStgMedium(&stgm
);
1735 HRESULT WINAPI
CShellLink::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
1739 TRACE("%p %p %u %u %u %u\n", this,
1740 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1743 return E_INVALIDARG
;
1746 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_VERB
, wszOpen
, _countof(wszOpen
)))
1750 memset(&mii
, 0, sizeof(mii
));
1751 mii
.cbSize
= sizeof (mii
);
1752 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
;
1753 mii
.dwTypeData
= wszOpen
;
1754 mii
.cch
= wcslen(mii
.dwTypeData
);
1755 mii
.wID
= idCmdFirst
+ id
++;
1756 mii
.fState
= MFS_DEFAULT
| MFS_ENABLED
;
1757 mii
.fType
= MFT_STRING
;
1758 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
1762 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, id
);
1766 shelllink_get_msi_component_path(LPWSTR component
)
1768 DWORD Result
, sz
= 0;
1770 Result
= CommandLineFromMsiDescriptor(component
, NULL
, &sz
);
1771 if (Result
!= ERROR_SUCCESS
)
1775 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sz
* sizeof(WCHAR
));
1776 Result
= CommandLineFromMsiDescriptor(component
, path
, &sz
);
1777 if (Result
!= ERROR_SUCCESS
)
1779 HeapFree(GetProcessHeap(), 0, path
);
1783 TRACE("returning %s\n", debugstr_w(path
));
1788 HRESULT WINAPI
CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1790 HWND hwnd
= NULL
; /* FIXME: get using interface set from IObjectWithSite */
1794 TRACE("%p %p\n", this, lpici
);
1796 if (lpici
->cbSize
< sizeof (CMINVOKECOMMANDINFO
))
1797 return E_INVALIDARG
;
1799 HRESULT hr
= Resolve(hwnd
, 0);
1802 TRACE("failed to resolve component with error 0x%08x", hr
);
1807 path
= shelllink_get_msi_component_path(sComponent
);
1812 path
= strdupW(sPath
);
1814 if ( lpici
->cbSize
== sizeof(CMINVOKECOMMANDINFOEX
) &&
1815 (lpici
->fMask
& CMIC_MASK_UNICODE
) )
1817 LPCMINVOKECOMMANDINFOEX iciex
= (LPCMINVOKECOMMANDINFOEX
)lpici
;
1821 len
+= wcslen(sArgs
);
1822 if (iciex
->lpParametersW
)
1823 len
+= wcslen(iciex
->lpParametersW
);
1825 args
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1828 wcscat(args
, sArgs
);
1829 if (iciex
->lpParametersW
)
1832 wcscat(args
, iciex
->lpParametersW
);
1835 else if (sArgs
!= NULL
)
1837 args
= strdupW(sArgs
);
1840 SHELLEXECUTEINFOW sei
;
1841 memset(&sei
, 0, sizeof sei
);
1842 sei
.cbSize
= sizeof sei
;
1843 sei
.fMask
= SEE_MASK_HASLINKNAME
| SEE_MASK_UNICODE
|
1844 (lpici
->fMask
& (SEE_MASK_NOASYNC
| SEE_MASK_ASYNCOK
| SEE_MASK_FLAG_NO_UI
));
1846 sei
.lpClass
= sLinkPath
;
1847 sei
.nShow
= iShowCmd
;
1848 sei
.lpDirectory
= sWorkDir
;
1849 sei
.lpParameters
= args
;
1850 sei
.lpVerb
= L
"open";
1852 // HACK for ShellExecuteExW
1853 if (wcsstr(sPath
, L
".cpl"))
1854 sei
.lpVerb
= L
"cplopen";
1856 if (ShellExecuteExW(&sei
))
1861 HeapFree(GetProcessHeap(), 0, args
);
1862 HeapFree(GetProcessHeap(), 0, path
);
1867 HRESULT WINAPI
CShellLink::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1869 FIXME("%p %lu %u %p %p %u\n", this, idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1874 INT_PTR CALLBACK
ExtendedShortcutProc(HWND hwndDlg
, UINT uMsg
,
1875 WPARAM wParam
, LPARAM lParam
)
1882 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1883 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1888 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1889 if (LOWORD(wParam
) == IDOK
)
1891 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1892 EndDialog(hwndDlg
, 1);
1894 EndDialog(hwndDlg
, 0);
1896 else if (LOWORD(wParam
) == IDCANCEL
)
1898 EndDialog(hwndDlg
, -1);
1900 else if (LOWORD(wParam
) == 14000)
1902 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1903 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_UNCHECKED
, 0);
1905 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1912 /**************************************************************************
1913 * SH_ShellLinkDlgProc
1915 * dialog proc of the shortcut property dialog
1918 INT_PTR CALLBACK
CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1920 CShellLink
*pThis
= (CShellLink
*)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
1926 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
1930 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg
, lParam
, ppsp
->lParam
);
1932 pThis
= (CShellLink
*)ppsp
->lParam
;
1933 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pThis
);
1935 TRACE("sArgs: %S sComponent: %S sDescription: %S sIcoPath: %S sPath: %S sPathRel: %S sProduct: %S sWorkDir: %S\n", pThis
->sArgs
, pThis
->sComponent
, pThis
->sDescription
,
1936 pThis
->sIcoPath
, pThis
->sPath
, pThis
->sPathRel
, pThis
->sProduct
, pThis
->sWorkDir
);
1938 /* Get file information */
1940 if (!SHGetFileInfoW(pThis
->sLinkPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
1942 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis
->sLinkPath
, GetLastError());
1943 fi
.szTypeName
[0] = L
'\0';
1947 if (fi
.hIcon
) // TODO: destroy icon
1948 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
1950 ERR("ExtractIconW failed %ls %u\n", pThis
->sIcoPath
, pThis
->iIcoNdx
);
1952 /* target location */
1953 if (pThis
->sWorkDir
)
1954 SetDlgItemTextW(hwndDlg
, 14007, PathFindFileName(pThis
->sWorkDir
));
1958 SetDlgItemTextW(hwndDlg
, 14009, pThis
->sPath
);
1961 if (pThis
->sWorkDir
)
1962 SetDlgItemTextW(hwndDlg
, 14011, pThis
->sWorkDir
);
1965 if (pThis
->sDescription
)
1966 SetDlgItemTextW(hwndDlg
, 14019, pThis
->sDescription
);
1972 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
1973 if (lppsn
->hdr
.code
== PSN_APPLY
)
1975 WCHAR wszBuf
[MAX_PATH
];
1977 /* set working directory */
1978 GetDlgItemTextW(hwndDlg
, 14011, wszBuf
, MAX_PATH
);
1979 pThis
->SetWorkingDirectory(wszBuf
);
1980 /* set link destination */
1981 GetDlgItemTextW(hwndDlg
, 14009, wszBuf
, MAX_PATH
);
1982 if (!PathFileExistsW(wszBuf
))
1984 //FIXME load localized error msg
1985 MessageBoxW(hwndDlg
, L
"file not existing", wszBuf
, MB_OK
);
1986 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
1990 WCHAR
*pwszExt
= PathFindExtensionW(wszBuf
);
1991 if (!wcsicmp(pwszExt
, L
".lnk"))
1993 // FIXME load localized error msg
1994 MessageBoxW(hwndDlg
, L
"You cannot create a link to a shortcut", L
"Error", MB_ICONERROR
);
1995 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
1999 pThis
->SetPath(wszBuf
);
2001 TRACE("This %p sLinkPath %S\n", pThis
, pThis
->sLinkPath
);
2002 pThis
->Save(pThis
->sLinkPath
, TRUE
);
2003 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
2009 switch(LOWORD(wParam
))
2014 /// open target directory
2019 WCHAR wszPath
[MAX_PATH
] = L
"";
2021 if (pThis
->sIcoPath
)
2022 wcscpy(wszPath
, pThis
->sIcoPath
);
2023 INT IconIndex
= pThis
->iIcoNdx
;
2024 if (PickIconDlg(hwndDlg
, wszPath
, MAX_PATH
, &IconIndex
))
2026 pThis
->SetIconLocation(wszPath
, IconIndex
);
2028 /// FIXME redraw icon
2035 INT_PTR result
= DialogBoxParamW(shell32_hInstance
, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES
), hwndDlg
, ExtendedShortcutProc
, (LPARAM
)pThis
->bRunAs
);
2036 if (result
== 1 || result
== 0)
2038 if (pThis
->bRunAs
!= result
)
2040 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2043 pThis
->bRunAs
= result
;
2048 if(HIWORD(wParam
) == EN_CHANGE
)
2049 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2057 /**************************************************************************
2058 * ShellLink_IShellPropSheetExt interface
2061 HRESULT WINAPI
CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
2063 HPROPSHEETPAGE hPage
= SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES
, SH_ShellLinkDlgProc
, (LPARAM
)this, NULL
);
2066 ERR("failed to create property sheet page\n");
2070 if (!pfnAddPage(hPage
, lParam
))
2076 HRESULT WINAPI
CShellLink::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
2078 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID
, pfnReplacePage
, lParam
);
2082 HRESULT WINAPI
CShellLink::SetSite(IUnknown
*punk
)
2084 TRACE("%p %p\n", this, punk
);
2091 HRESULT WINAPI
CShellLink::GetSite(REFIID iid
, void ** ppvSite
)
2093 TRACE("%p %s %p\n", this, debugstr_guid(&iid
), ppvSite
);
2098 return site
->QueryInterface(iid
, ppvSite
);
2101 /**************************************************************************
2102 * IShellLink_ConstructFromFile
2104 HRESULT WINAPI
IShellLink_ConstructFromFile(IUnknown
*pUnkOuter
, REFIID riid
, LPCITEMIDLIST pidl
, LPVOID
*ppv
)
2106 CComPtr
<IUnknown
> psl
;
2108 HRESULT hr
= CShellLink::_CreatorClass::CreateInstance(NULL
, riid
, (void**)&psl
);
2112 CComPtr
<IPersistFile
> ppf
;
2116 hr
= psl
->QueryInterface(IID_PPV_ARG(IPersistFile
, &ppf
));
2120 WCHAR path
[MAX_PATH
];
2122 if (SHGetPathFromIDListW(pidl
, path
))
2123 hr
= ppf
->Load(path
, 0);
2128 *ppv
= psl
.Detach();