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()
112 memset(&time1
, 0, sizeof(time1
));
113 memset(&time2
, 0, sizeof(time2
));
114 memset(&time3
, 0, sizeof(time3
));
115 iShowCmd
= SW_SHOWNORMAL
;
121 ZeroMemory(&volume
, sizeof(volume
));
123 m_sDescription
= NULL
;
134 /**/sProduct
= sComponent
= NULL
;/**/
137 CShellLink::~CShellLink()
139 TRACE("-- destroying IShellLink(%p)\n", this);
143 HeapFree(GetProcessHeap(), 0, m_sPath
);
145 HeapFree(GetProcessHeap(), 0, m_sDescription
);
146 HeapFree(GetProcessHeap(), 0, m_sPathRel
);
147 HeapFree(GetProcessHeap(), 0, m_sWorkDir
);
148 HeapFree(GetProcessHeap(), 0, m_sArgs
);
149 HeapFree(GetProcessHeap(), 0, m_sIcoPath
);
150 HeapFree(GetProcessHeap(), 0, m_sLinkPath
);
153 HRESULT STDMETHODCALLTYPE
CShellLink::GetClassID(CLSID
*pclsid
)
155 TRACE("%p %p\n", this, pclsid
);
159 *pclsid
= CLSID_ShellLink
;
163 /************************************************************************
164 * IPersistStream_IsDirty (IPersistStream)
166 HRESULT STDMETHODCALLTYPE
CShellLink::IsDirty()
168 TRACE("(%p)\n", this);
169 return (m_bDirty
? S_OK
: S_FALSE
);
172 HRESULT STDMETHODCALLTYPE
CShellLink::Load(LPCOLESTR pszFileName
, DWORD dwMode
)
174 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName
), dwMode
);
177 dwMode
= STGM_READ
| STGM_SHARE_DENY_WRITE
;
179 CComPtr
<IStream
> stm
;
180 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, dwMode
, &stm
);
183 HeapFree(GetProcessHeap(), 0, m_sLinkPath
);
184 m_sLinkPath
= strdupW(pszFileName
);
186 ShellLink_UpdatePath(m_sPathRel
, pszFileName
, m_sWorkDir
, &m_sPath
);
189 TRACE("-- returning hr %08x\n", hr
);
193 HRESULT STDMETHODCALLTYPE
CShellLink::Save(LPCOLESTR pszFileName
, BOOL fRemember
)
195 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
200 CComPtr
<IStream
> stm
;
201 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, STGM_READWRITE
| STGM_CREATE
| STGM_SHARE_EXCLUSIVE
, &stm
);
204 hr
= Save(stm
, FALSE
);
209 HeapFree(GetProcessHeap(), 0, m_sLinkPath
);
211 m_sLinkPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName
) + 1) * sizeof(WCHAR
));
213 wcscpy(m_sLinkPath
, pszFileName
);
219 DeleteFileW(pszFileName
);
220 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName
));
227 HRESULT STDMETHODCALLTYPE
CShellLink::SaveCompleted(LPCOLESTR pszFileName
)
229 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
233 HRESULT STDMETHODCALLTYPE
CShellLink::GetCurFile(LPOLESTR
*ppszFileName
)
235 *ppszFileName
= NULL
;
239 /* IPersistFile::GetCurFile called before IPersistFile::Save */
243 *ppszFileName
= (LPOLESTR
)CoTaskMemAlloc((wcslen(m_sLinkPath
) + 1) * sizeof(WCHAR
));
247 return E_OUTOFMEMORY
;
250 /* copy last saved filename */
251 wcscpy(*ppszFileName
, m_sLinkPath
);
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
);
299 count
/= sizeof(WCHAR
);
311 * NOTE: The following 5 functions are part of LINKINFO.DLL
313 static BOOL
ShellLink_GetVolumeInfo(LPCWSTR path
, CShellLink::volume_info
*volume
)
315 WCHAR drive
[4] = { path
[0], ':', '\\', 0 };
317 volume
->type
= GetDriveTypeW(drive
);
318 BOOL bRet
= GetVolumeInformationW(drive
, volume
->label
, _countof(volume
->label
), &volume
->serial
, NULL
, NULL
, NULL
, 0);
319 TRACE("ret = %d type %d serial %08x name %s\n", bRet
,
320 volume
->type
, volume
->serial
, debugstr_w(volume
->label
));
324 static HRESULT
Stream_ReadChunk(IStream
* stm
, LPVOID
*data
)
329 unsigned char data
[1];
336 HRESULT hr
= stm
->Read(&size
, sizeof(size
), &count
);
337 if (FAILED(hr
) || count
!= sizeof(size
))
340 chunk
= static_cast<sized_chunk
*>(HeapAlloc(GetProcessHeap(), 0, size
));
342 return E_OUTOFMEMORY
;
345 hr
= stm
->Read(chunk
->data
, size
- sizeof(size
), &count
);
346 if (FAILED(hr
) || count
!= (size
- sizeof(size
)))
348 HeapFree(GetProcessHeap(), 0, chunk
);
352 TRACE("Read %d bytes\n", chunk
->size
);
359 static BOOL
Stream_LoadVolume(LOCAL_VOLUME_INFO
*vol
, CShellLink::volume_info
*volume
)
361 volume
->serial
= vol
->dwVolSerial
;
362 volume
->type
= vol
->dwType
;
364 if (!vol
->dwVolLabelOfs
)
366 if (vol
->dwSize
<= vol
->dwVolLabelOfs
)
368 INT len
= vol
->dwSize
- vol
->dwVolLabelOfs
;
370 LPSTR label
= (LPSTR
)vol
;
371 label
+= vol
->dwVolLabelOfs
;
372 MultiByteToWideChar(CP_ACP
, 0, label
, len
, volume
->label
, _countof(volume
->label
));
377 static LPWSTR
Stream_LoadPath(LPCSTR p
, DWORD maxlen
)
381 while (p
[len
] && len
< maxlen
)
384 UINT wlen
= MultiByteToWideChar(CP_ACP
, 0, p
, len
, NULL
, 0);
385 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wlen
+ 1) * sizeof(WCHAR
));
388 MultiByteToWideChar(CP_ACP
, 0, p
, len
, path
, wlen
);
394 static HRESULT
Stream_LoadLocation(IStream
*stm
,
395 CShellLink::volume_info
*volume
, LPWSTR
*path
)
398 HRESULT hr
= Stream_ReadChunk(stm
, (LPVOID
*) &p
);
402 LOCATION_INFO
*loc
= reinterpret_cast<LOCATION_INFO
*>(p
);
403 if (loc
->dwTotalSize
< sizeof(LOCATION_INFO
))
405 HeapFree(GetProcessHeap(), 0, p
);
409 /* if there's valid local volume information, load it */
410 if (loc
->dwVolTableOfs
&&
411 ((loc
->dwVolTableOfs
+ sizeof(LOCAL_VOLUME_INFO
)) <= loc
->dwTotalSize
))
413 LOCAL_VOLUME_INFO
*volume_info
;
415 volume_info
= (LOCAL_VOLUME_INFO
*) &p
[loc
->dwVolTableOfs
];
416 Stream_LoadVolume(volume_info
, volume
);
419 /* if there's a local path, load it */
420 DWORD n
= loc
->dwLocalPathOfs
;
421 if (n
&& n
< loc
->dwTotalSize
)
422 *path
= Stream_LoadPath(&p
[n
], loc
->dwTotalSize
- n
);
424 TRACE("type %d serial %08x name %s path %s\n", volume
->type
,
425 volume
->serial
, debugstr_w(volume
->label
), debugstr_w(*path
));
427 HeapFree(GetProcessHeap(), 0, p
);
432 * The format of the advertised shortcut info seems to be:
437 * 0 Length of the block (4 bytes, usually 0x314)
439 * 8 string data in ASCII
440 * 8+0x104 string data in UNICODE
442 * In the original Win32 implementation the buffers are not initialized
443 * to zero, so data trailing the string is random garbage.
445 static HRESULT
Stream_LoadAdvertiseInfo(IStream
* stm
, LPWSTR
*str
)
450 EXP_DARWIN_LINK buffer
;
451 HRESULT hr
= stm
->Read(&buffer
.dbh
.cbSize
, sizeof (DWORD
), &count
);
455 /* make sure that we read the size of the structure even on error */
456 DWORD size
= sizeof buffer
- sizeof (DWORD
);
457 if (buffer
.dbh
.cbSize
!= sizeof buffer
)
459 ERR("Ooops. This structure is not as expected...\n");
463 hr
= stm
->Read(&buffer
.dbh
.dwSignature
, size
, &count
);
470 TRACE("magic %08x string = %s\n", buffer
.dbh
.dwSignature
, debugstr_w(buffer
.szwDarwinID
));
472 if ((buffer
.dbh
.dwSignature
& 0xffff0000) != 0xa0000000)
474 ERR("Unknown magic number %08x in advertised shortcut\n", buffer
.dbh
.dwSignature
);
478 *str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
479 (wcslen(buffer
.szwDarwinID
) + 1) * sizeof(WCHAR
));
480 wcscpy(*str
, buffer
.szwDarwinID
);
485 /************************************************************************
486 * IPersistStream_Load (IPersistStream)
488 HRESULT STDMETHODCALLTYPE
CShellLink::Load(IStream
*stm
)
490 TRACE("%p %p\n", this, stm
);
493 return STG_E_INVALIDPOINTER
;
495 SHELL_LINK_HEADER ShlLnkHeader
;
496 ULONG dwBytesRead
= 0;
497 HRESULT hr
= stm
->Read(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &dwBytesRead
);
501 if (dwBytesRead
!= sizeof(ShlLnkHeader
))
503 if (ShlLnkHeader
.dwSize
!= sizeof(ShlLnkHeader
))
505 if (!IsEqualIID(ShlLnkHeader
.clsid
, CLSID_ShellLink
))
508 /* free all the old stuff */
511 memset(&volume
, 0, sizeof volume
);
512 HeapFree(GetProcessHeap(), 0, m_sPath
);
514 HeapFree(GetProcessHeap(), 0, m_sDescription
);
515 m_sDescription
= NULL
;
516 HeapFree(GetProcessHeap(), 0, m_sPathRel
);
518 HeapFree(GetProcessHeap(), 0, m_sWorkDir
);
520 HeapFree(GetProcessHeap(), 0, m_sArgs
);
522 HeapFree(GetProcessHeap(), 0, m_sIcoPath
);
524 HeapFree(GetProcessHeap(), 0, sProduct
);
526 HeapFree(GetProcessHeap(), 0, sComponent
);
529 BOOL unicode
= FALSE
;
530 iShowCmd
= ShlLnkHeader
.nShowCommand
;
531 wHotKey
= ShlLnkHeader
.wHotKey
;
532 iIcoNdx
= ShlLnkHeader
.nIconIndex
;
533 FileTimeToSystemTime (&ShlLnkHeader
.ftCreationTime
, &time1
);
534 FileTimeToSystemTime (&ShlLnkHeader
.ftLastAccessTime
, &time2
);
535 FileTimeToSystemTime (&ShlLnkHeader
.ftLastWriteTime
, &time3
);
538 WCHAR sTemp
[MAX_PATH
];
539 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time1
,
540 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
541 TRACE("-- time1: %s\n", debugstr_w(sTemp
));
542 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time2
,
543 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
544 TRACE("-- time2: %s\n", debugstr_w(sTemp
));
545 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time3
,
546 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
547 TRACE("-- time3: %s\n", debugstr_w(sTemp
));
550 /* load all the new stuff */
551 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ID_LIST
)
553 hr
= ILLoadFromStream(stm
, &m_pPidl
);
559 /* load the location information */
560 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LINK_INFO
)
561 hr
= Stream_LoadLocation(stm
, &volume
, &m_sPath
);
565 if (ShlLnkHeader
.dwFlags
& SLDF_UNICODE
)
568 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_NAME
)
570 hr
= Stream_LoadString(stm
, unicode
, &m_sDescription
);
571 TRACE("Description -> %s\n", debugstr_w(m_sDescription
));
576 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_RELPATH
)
578 hr
= Stream_LoadString(stm
, unicode
, &m_sPathRel
);
579 TRACE("Relative Path-> %s\n", debugstr_w(m_sPathRel
));
584 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_WORKINGDIR
)
586 hr
= Stream_LoadString(stm
, unicode
, &m_sWorkDir
);
587 PathRemoveBackslash(m_sWorkDir
);
588 TRACE("Working Dir -> %s\n", debugstr_w(m_sWorkDir
));
593 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ARGS
)
595 hr
= Stream_LoadString(stm
, unicode
, &m_sArgs
);
596 TRACE("Arguments -> %s\n", debugstr_w(m_sArgs
));
601 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ICONLOCATION
)
603 hr
= Stream_LoadString(stm
, unicode
, &m_sIcoPath
);
604 TRACE("Icon file -> %s\n", debugstr_w(m_sIcoPath
));
609 #if (NTDDI_VERSION < NTDDI_LONGHORN)
610 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LOGO3ID
)
612 hr
= Stream_LoadAdvertiseInfo(stm
, &sProduct
);
613 TRACE("Product -> %s\n", debugstr_w(sProduct
));
619 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_DARWINID
)
621 hr
= Stream_LoadAdvertiseInfo(stm
, &sComponent
);
622 TRACE("Component -> %s\n", debugstr_w(sComponent
));
624 if (ShlLnkHeader
.dwFlags
& SLDF_RUNAS_USER
)
637 hr
= stm
->Read(&dwZero
, sizeof(dwZero
), &dwBytesRead
);
638 if (FAILED(hr
) || dwZero
|| dwBytesRead
!= sizeof(dwZero
))
639 ERR("Last word was not zero\n");
651 /************************************************************************
654 * Helper function for IPersistStream_Save. Writes a unicode string
655 * with terminating nul byte to a stream, preceded by the its length.
657 static HRESULT
Stream_WriteString(IStream
* stm
, LPCWSTR str
)
659 USHORT len
= wcslen(str
) + 1;
662 HRESULT hr
= stm
->Write(&len
, sizeof(len
), &count
);
666 len
*= sizeof(WCHAR
);
668 hr
= stm
->Write(str
, len
, &count
);
675 /************************************************************************
676 * Stream_WriteLocationInfo
678 * Writes the location info to a stream
680 * FIXME: One day we might want to write the network volume information
681 * and the final path.
682 * Figure out how Windows deals with unicode paths here.
684 static HRESULT
Stream_WriteLocationInfo(IStream
* stm
, LPCWSTR path
,
685 CShellLink::volume_info
*volume
)
687 LOCAL_VOLUME_INFO
*vol
;
690 TRACE("%p %s %p\n", stm
, debugstr_w(path
), volume
);
692 /* figure out the size of everything */
693 DWORD label_size
= WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
694 NULL
, 0, NULL
, NULL
);
695 DWORD path_size
= WideCharToMultiByte(CP_ACP
, 0, path
, -1,
696 NULL
, 0, NULL
, NULL
);
697 DWORD volume_info_size
= sizeof(*vol
) + label_size
;
698 DWORD final_path_size
= 1;
699 DWORD total_size
= sizeof(*loc
) + volume_info_size
+ path_size
+ final_path_size
;
701 /* create pointers to everything */
702 loc
= static_cast<LOCATION_INFO
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, total_size
));
703 vol
= (LOCAL_VOLUME_INFO
*) &loc
[1];
704 LPSTR szLabel
= (LPSTR
) &vol
[1];
705 LPSTR szPath
= &szLabel
[label_size
];
706 LPSTR szFinalPath
= &szPath
[path_size
];
708 /* fill in the location information header */
709 loc
->dwTotalSize
= total_size
;
710 loc
->dwHeaderSize
= sizeof(*loc
);
712 loc
->dwVolTableOfs
= sizeof(*loc
);
713 loc
->dwLocalPathOfs
= sizeof(*loc
) + volume_info_size
;
714 loc
->dwNetworkVolTableOfs
= 0;
715 loc
->dwFinalPathOfs
= sizeof(*loc
) + volume_info_size
+ path_size
;
717 /* fill in the volume information */
718 vol
->dwSize
= volume_info_size
;
719 vol
->dwType
= volume
->type
;
720 vol
->dwVolSerial
= volume
->serial
;
721 vol
->dwVolLabelOfs
= sizeof(*vol
);
723 /* copy in the strings */
724 WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
725 szLabel
, label_size
, NULL
, NULL
);
726 WideCharToMultiByte(CP_ACP
, 0, path
, -1,
727 szPath
, path_size
, NULL
, NULL
);
731 HRESULT hr
= stm
->Write(loc
, total_size
, &count
);
732 HeapFree(GetProcessHeap(), 0, loc
);
737 static EXP_DARWIN_LINK
* shelllink_build_darwinid(LPCWSTR string
, DWORD magic
)
739 EXP_DARWIN_LINK
*buffer
= (EXP_DARWIN_LINK
*)LocalAlloc(LMEM_ZEROINIT
, sizeof * buffer
);
740 buffer
->dbh
.cbSize
= sizeof * buffer
;
741 buffer
->dbh
.dwSignature
= magic
;
742 lstrcpynW(buffer
->szwDarwinID
, string
, MAX_PATH
);
743 WideCharToMultiByte(CP_ACP
, 0, string
, -1, buffer
->szDarwinID
, MAX_PATH
, NULL
, NULL
);
748 static HRESULT
Stream_WriteAdvertiseInfo(IStream
* stm
, LPCWSTR string
, DWORD magic
)
752 EXP_DARWIN_LINK
*buffer
= shelllink_build_darwinid(string
, magic
);
755 return stm
->Write(buffer
, buffer
->dbh
.cbSize
, &count
);
758 /************************************************************************
759 * IPersistStream_Save (IPersistStream)
761 * FIXME: makes assumptions about byte order
763 HRESULT STDMETHODCALLTYPE
CShellLink::Save(IStream
*stm
, BOOL fClearDirty
)
765 TRACE("%p %p %x\n", this, stm
, fClearDirty
);
767 SHELL_LINK_HEADER ShlLnkHeader
;
768 memset(&ShlLnkHeader
, 0, sizeof(ShlLnkHeader
));
769 ShlLnkHeader
.dwSize
= sizeof(ShlLnkHeader
);
770 ShlLnkHeader
.nShowCommand
= iShowCmd
;
771 ShlLnkHeader
.clsid
= CLSID_ShellLink
;
773 ShlLnkHeader
.wHotKey
= wHotKey
;
774 ShlLnkHeader
.nIconIndex
= iIcoNdx
;
775 ShlLnkHeader
.dwFlags
= SLDF_UNICODE
; /* strings are in unicode */
777 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ID_LIST
;
779 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LINK_INFO
;
781 ShlLnkHeader
.dwFlags
|= SLDF_HAS_NAME
;
783 ShlLnkHeader
.dwFlags
|= SLDF_HAS_WORKINGDIR
;
785 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ARGS
;
787 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ICONLOCATION
;
788 #if (NTDDI_VERSION < NTDDI_LONGHORN)
790 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LOGO3ID
;
793 ShlLnkHeader
.dwFlags
|= SLDF_HAS_DARWINID
;
795 ShlLnkHeader
.dwFlags
|= SLDF_RUNAS_USER
;
797 SystemTimeToFileTime (&time1
, &ShlLnkHeader
.ftCreationTime
);
798 SystemTimeToFileTime (&time2
, &ShlLnkHeader
.ftLastAccessTime
);
799 SystemTimeToFileTime (&time3
, &ShlLnkHeader
.ftLastWriteTime
);
801 /* write the Shortcut header */
803 HRESULT hr
= stm
->Write(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &count
);
806 ERR("Write failed\n");
810 TRACE("Writing pidl\n");
812 /* write the PIDL to the shortcut */
815 hr
= ILSaveToStream(stm
, m_pPidl
);
818 ERR("Failed to write PIDL\n");
824 Stream_WriteLocationInfo(stm
, m_sPath
, &volume
);
827 hr
= Stream_WriteString(stm
, m_sDescription
);
830 hr
= Stream_WriteString(stm
, m_sPathRel
);
833 hr
= Stream_WriteString(stm
, m_sWorkDir
);
836 hr
= Stream_WriteString(stm
, m_sArgs
);
839 hr
= Stream_WriteString(stm
, m_sIcoPath
);
842 hr
= Stream_WriteAdvertiseInfo(stm
, sProduct
, EXP_SZ_ICON_SIG
);
845 hr
= Stream_WriteAdvertiseInfo(stm
, sComponent
, EXP_DARWIN_ID_SIG
);
847 /* the last field is a single zero dword */
849 hr
= stm
->Write(&zero
, sizeof zero
, &count
);
854 /************************************************************************
855 * IPersistStream_GetSizeMax (IPersistStream)
857 HRESULT STDMETHODCALLTYPE
CShellLink::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
859 TRACE("(%p)\n", this);
863 static BOOL
SHELL_ExistsFileW(LPCWSTR path
)
865 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(path
))
871 /**************************************************************************
872 * ShellLink_UpdatePath
873 * update absolute path in sPath using relative path in sPathRel
875 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
)
877 if (!path
|| !psPath
)
880 if (!*psPath
&& sPathRel
)
882 WCHAR buffer
[2*MAX_PATH
], abs_path
[2*MAX_PATH
];
885 /* first try if [directory of link file] + [relative path] finds an existing file */
887 GetFullPathNameW(path
, MAX_PATH
* 2, buffer
, &final
);
890 wcscpy(final
, sPathRel
);
894 if (SHELL_ExistsFileW(buffer
))
896 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
897 wcscpy(abs_path
, buffer
);
901 /* try if [working directory] + [relative path] finds an existing file */
904 wcscpy(buffer
, sWorkDir
);
905 wcscpy(PathAddBackslashW(buffer
), sPathRel
);
907 if (SHELL_ExistsFileW(buffer
))
908 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
909 wcscpy(abs_path
, buffer
);
913 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
915 wcscpy(abs_path
, sPathRel
);
917 *psPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path
) + 1) * sizeof(WCHAR
));
919 return E_OUTOFMEMORY
;
921 wcscpy(*psPath
, abs_path
);
927 HRESULT STDMETHODCALLTYPE
CShellLink::GetPath(LPSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAA
*pfd
, DWORD fFlags
)
929 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
930 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(m_sPath
));
932 if (sComponent
|| sProduct
)
939 WideCharToMultiByte(CP_ACP
, 0, m_sPath
, -1,
940 pszFile
, cchMaxPath
, NULL
, NULL
);
942 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
947 HRESULT STDMETHODCALLTYPE
CShellLink::GetIDList(LPITEMIDLIST
*ppidl
)
949 TRACE("(%p)->(ppidl=%p)\n", this, ppidl
);
957 *ppidl
= ILClone(m_pPidl
);
961 HRESULT STDMETHODCALLTYPE
CShellLink::SetIDList(LPCITEMIDLIST pidl
)
963 TRACE("(%p)->(pidl=%p)\n", this, pidl
);
968 m_pPidl
= ILClone(pidl
);
977 HRESULT STDMETHODCALLTYPE
CShellLink::GetDescription(LPSTR pszName
, INT cchMaxName
)
979 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
985 WideCharToMultiByte(CP_ACP
, 0, m_sDescription
, -1,
986 pszName
, cchMaxName
, NULL
, NULL
);
991 HRESULT STDMETHODCALLTYPE
CShellLink::SetDescription(LPCSTR pszName
)
993 TRACE("(%p)->(pName=%s)\n", this, pszName
);
995 HeapFree(GetProcessHeap(), 0, m_sDescription
);
996 m_sDescription
= NULL
;
1000 m_sDescription
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszName
);
1001 if (!m_sDescription
)
1002 return E_OUTOFMEMORY
;
1009 HRESULT STDMETHODCALLTYPE
CShellLink::GetWorkingDirectory(LPSTR pszDir
, INT cchMaxPath
)
1011 TRACE("(%p)->(%p len=%u)\n", this, pszDir
, cchMaxPath
);
1017 WideCharToMultiByte(CP_ACP
, 0, m_sWorkDir
, -1,
1018 pszDir
, cchMaxPath
, NULL
, NULL
);
1023 HRESULT STDMETHODCALLTYPE
CShellLink::SetWorkingDirectory(LPCSTR pszDir
)
1025 TRACE("(%p)->(dir=%s)\n", this, pszDir
);
1027 HeapFree(GetProcessHeap(), 0, m_sWorkDir
);
1032 m_sWorkDir
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir
);
1034 return E_OUTOFMEMORY
;
1041 HRESULT STDMETHODCALLTYPE
CShellLink::GetArguments(LPSTR pszArgs
, INT cchMaxPath
)
1043 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1049 WideCharToMultiByte(CP_ACP
, 0, m_sArgs
, -1,
1050 pszArgs
, cchMaxPath
, NULL
, NULL
);
1055 HRESULT STDMETHODCALLTYPE
CShellLink::SetArguments(LPCSTR pszArgs
)
1057 TRACE("(%p)->(args=%s)\n", this, pszArgs
);
1059 HeapFree(GetProcessHeap(), 0, m_sArgs
);
1064 m_sArgs
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs
);
1066 return E_OUTOFMEMORY
;
1074 HRESULT STDMETHODCALLTYPE
CShellLink::GetHotkey(WORD
*pwHotkey
)
1076 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey
, wHotKey
);
1077 *pwHotkey
= wHotKey
;
1081 HRESULT STDMETHODCALLTYPE
CShellLink::SetHotkey(WORD wHotkey
)
1083 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey
);
1091 HRESULT STDMETHODCALLTYPE
CShellLink::GetShowCmd(INT
*piShowCmd
)
1093 TRACE("(%p)->(%p) %d\n", this, piShowCmd
, iShowCmd
);
1094 *piShowCmd
= iShowCmd
;
1098 HRESULT STDMETHODCALLTYPE
CShellLink::SetShowCmd(INT iShowCmd
)
1100 TRACE("(%p) %d\n", this, iShowCmd
);
1102 this->iShowCmd
= iShowCmd
;
1108 HRESULT STDMETHODCALLTYPE
CShellLink::GetIconLocation(LPSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1111 LPWSTR pszIconPathW
;
1113 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1115 /* Allocate a temporary UNICODE buffer */
1116 pszIconPathW
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, cchIconPath
* sizeof(WCHAR
));
1118 return E_OUTOFMEMORY
;
1120 /* Call the UNICODE function */
1121 hr
= GetIconLocation(pszIconPathW
, cchIconPath
, piIcon
);
1123 /* Convert the file path back to ANSI */
1124 WideCharToMultiByte(CP_ACP
, 0, pszIconPathW
, -1,
1125 pszIconPath
, cchIconPath
, NULL
, NULL
);
1127 /* Free the temporary buffer */
1128 HeapFree(GetProcessHeap(), 0, pszIconPathW
);
1133 HRESULT STDMETHODCALLTYPE
CShellLink::SetIconLocation(LPCSTR pszIconPath
, INT iIcon
)
1135 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath
, iIcon
);
1137 HeapFree(GetProcessHeap(), 0, m_sIcoPath
);
1142 m_sIcoPath
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath
);
1144 return E_OUTOFMEMORY
;
1153 HRESULT STDMETHODCALLTYPE
CShellLink::SetRelativePath(LPCSTR pszPathRel
, DWORD dwReserved
)
1155 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel
, dwReserved
);
1157 HeapFree(GetProcessHeap(), 0, m_sPathRel
);
1162 m_sPathRel
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel
);
1166 return ShellLink_UpdatePath(m_sPathRel
, m_sPath
, m_sWorkDir
, &m_sPath
);
1169 HRESULT STDMETHODCALLTYPE
CShellLink::Resolve(HWND hwnd
, DWORD fFlags
)
1174 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd
, fFlags
);
1176 /*FIXME: use IResolveShellLink interface */
1178 if (!m_sPath
&& m_pPidl
)
1180 WCHAR buffer
[MAX_PATH
];
1182 bSuccess
= SHGetPathFromIDListW(m_pPidl
, buffer
);
1184 if (bSuccess
&& *buffer
)
1186 m_sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
) + 1) * sizeof(WCHAR
));
1189 return E_OUTOFMEMORY
;
1191 wcscpy(m_sPath
, buffer
);
1196 hr
= S_OK
; /* don't report an error occurred while just caching information */
1199 if (!m_sIcoPath
&& m_sPath
)
1201 m_sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(m_sPath
) + 1) * sizeof(WCHAR
));
1204 return E_OUTOFMEMORY
;
1206 wcscpy(m_sIcoPath
, m_sPath
);
1215 HRESULT STDMETHODCALLTYPE
CShellLink::SetPath(LPCSTR pszFile
)
1217 TRACE("(%p)->(path=%s)\n", this, pszFile
);
1220 return E_INVALIDARG
;
1222 LPWSTR str
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile
);
1224 return E_OUTOFMEMORY
;
1226 HRESULT hr
= SetPath(str
);
1227 HeapFree(GetProcessHeap(), 0, str
);
1232 HRESULT STDMETHODCALLTYPE
CShellLink::GetPath(LPWSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAW
*pfd
, DWORD fFlags
)
1234 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1235 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(m_sPath
));
1237 if (sComponent
|| sProduct
)
1244 lstrcpynW(pszFile
, m_sPath
, cchMaxPath
);
1246 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1251 HRESULT STDMETHODCALLTYPE
CShellLink::GetDescription(LPWSTR pszName
, INT cchMaxName
)
1253 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
1257 lstrcpynW(pszName
, m_sDescription
, cchMaxName
);
1262 HRESULT STDMETHODCALLTYPE
CShellLink::SetDescription(LPCWSTR pszName
)
1264 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName
));
1266 HeapFree(GetProcessHeap(), 0, m_sDescription
);
1269 m_sDescription
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1270 (wcslen(pszName
) + 1) * sizeof(WCHAR
));
1271 if (!m_sDescription
)
1272 return E_OUTOFMEMORY
;
1274 wcscpy(m_sDescription
, pszName
);
1277 m_sDescription
= NULL
;
1284 HRESULT STDMETHODCALLTYPE
CShellLink::GetWorkingDirectory(LPWSTR pszDir
, INT cchMaxPath
)
1286 TRACE("(%p)->(%p len %u)\n", this, pszDir
, cchMaxPath
);
1292 lstrcpynW(pszDir
, m_sWorkDir
, cchMaxPath
);
1297 HRESULT STDMETHODCALLTYPE
CShellLink::SetWorkingDirectory(LPCWSTR pszDir
)
1299 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir
));
1301 HeapFree(GetProcessHeap(), 0, m_sWorkDir
);
1304 m_sWorkDir
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1305 (wcslen(pszDir
) + 1) * sizeof(WCHAR
));
1307 return E_OUTOFMEMORY
;
1308 wcscpy(m_sWorkDir
, pszDir
);
1318 HRESULT STDMETHODCALLTYPE
CShellLink::GetArguments(LPWSTR pszArgs
, INT cchMaxPath
)
1320 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1326 lstrcpynW(pszArgs
, m_sArgs
, cchMaxPath
);
1331 HRESULT STDMETHODCALLTYPE
CShellLink::SetArguments(LPCWSTR pszArgs
)
1333 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs
));
1335 HeapFree(GetProcessHeap(), 0, m_sArgs
);
1338 m_sArgs
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1339 (wcslen(pszArgs
) + 1) * sizeof(WCHAR
));
1341 return E_OUTOFMEMORY
;
1343 wcscpy(m_sArgs
, pszArgs
);
1353 static HRESULT
SHELL_PidlGetIconLocationW(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1354 PWSTR pszIconFile
, UINT cchMax
, int *piIndex
)
1356 LPCITEMIDLIST pidlLast
;
1359 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
1362 CComPtr
<IExtractIconW
> pei
;
1364 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_NULL_PPV_ARG(IExtractIconW
, &pei
));
1366 hr
= pei
->GetIconLocation(0, pszIconFile
, cchMax
, piIndex
, &wFlags
);
1374 HRESULT STDMETHODCALLTYPE
CShellLink::GetIconLocation(LPWSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1376 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1385 lstrcpynW(pszIconPath
, m_sIcoPath
, cchIconPath
);
1389 if (m_pPidl
|| m_sPath
)
1391 CComPtr
<IShellFolder
> pdsk
;
1393 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1397 /* first look for an icon using the PIDL (if present) */
1399 hr
= SHELL_PidlGetIconLocationW(pdsk
, m_pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1403 /* if we couldn't find an icon yet, look for it using the file system path */
1404 if (FAILED(hr
) && m_sPath
)
1408 hr
= pdsk
->ParseDisplayName(0, NULL
, m_sPath
, NULL
, &pidl
, NULL
);
1412 hr
= SHELL_PidlGetIconLocationW(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1423 HRESULT STDMETHODCALLTYPE
CShellLink::SetIconLocation(LPCWSTR pszIconPath
, INT iIcon
)
1425 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath
), iIcon
);
1427 HeapFree(GetProcessHeap(), 0, m_sIcoPath
);
1430 m_sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1431 (wcslen(pszIconPath
) + 1) * sizeof (WCHAR
));
1433 return E_OUTOFMEMORY
;
1434 wcscpy(m_sIcoPath
, pszIconPath
);
1445 HRESULT STDMETHODCALLTYPE
CShellLink::SetRelativePath(LPCWSTR pszPathRel
, DWORD dwReserved
)
1447 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel
), dwReserved
);
1449 HeapFree(GetProcessHeap(), 0, m_sPathRel
);
1452 m_sPathRel
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1453 (wcslen(pszPathRel
) + 1) * sizeof(WCHAR
));
1455 return E_OUTOFMEMORY
;
1456 wcscpy(m_sPathRel
, pszPathRel
);
1463 return ShellLink_UpdatePath(m_sPathRel
, m_sPath
, m_sWorkDir
, &m_sPath
);
1466 static LPWSTR
GetAdvertisedArg(LPCWSTR str
)
1471 LPCWSTR p
= wcschr(str
, L
':');
1475 DWORD len
= p
- str
;
1476 LPWSTR ret
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * (len
+ 1));
1480 memcpy(ret
, str
, sizeof(WCHAR
)*len
);
1485 HRESULT
CShellLink::SetAdvertiseInfo(LPCWSTR str
)
1487 LPCWSTR szComponent
= NULL
, szProduct
= NULL
;
1491 /* each segment must start with two colons */
1492 if (str
[0] != ':' || str
[1] != ':')
1495 /* the last segment is just two colons */
1500 /* there must be a colon straight after a guid */
1501 LPCWSTR p
= wcschr(str
, L
':');
1508 /* get the guid, and check if it's validly formatted */
1510 memcpy(szGuid
, str
, sizeof(WCHAR
)*len
);
1514 HRESULT hr
= CLSIDFromString(szGuid
, &guid
);
1519 /* match it up to a guid that we care about */
1520 if (IsEqualGUID(guid
, SHELL32_AdvtShortcutComponent
) && !szComponent
)
1522 else if (IsEqualGUID(guid
, SHELL32_AdvtShortcutProduct
) && !szProduct
)
1527 /* skip to the next field */
1528 str
= wcschr(str
, L
':');
1533 /* we have to have a component for an advertised shortcut */
1537 sComponent
= GetAdvertisedArg(szComponent
);
1538 sProduct
= GetAdvertisedArg(szProduct
);
1540 TRACE("Component = %s\n", debugstr_w(sComponent
));
1541 TRACE("Product = %s\n", debugstr_w(sProduct
));
1546 HRESULT STDMETHODCALLTYPE
CShellLink::SetPath(LPCWSTR pszFile
)
1548 LPWSTR unquoted
= NULL
;
1551 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile
));
1554 return E_INVALIDARG
;
1556 /* quotes at the ends of the string are stripped */
1557 SIZE_T len
= wcslen(pszFile
);
1558 if (pszFile
[0] == L
'"' && pszFile
[len
-1] == L
'"')
1560 unquoted
= strdupW(pszFile
);
1561 PathUnquoteSpacesW(unquoted
);
1565 /* any other quote marks are invalid */
1566 if (wcschr(pszFile
, L
'"'))
1568 HeapFree(GetProcessHeap(), 0, unquoted
);
1572 HeapFree(GetProcessHeap(), 0, m_sPath
);
1575 HeapFree(GetProcessHeap(), 0, sComponent
);
1582 if (S_OK
!= SetAdvertiseInfo(pszFile
))
1584 WCHAR buffer
[MAX_PATH
];
1587 if (*pszFile
== '\0')
1589 else if (!GetFullPathNameW(pszFile
, MAX_PATH
, buffer
, &fname
))
1591 else if(!PathFileExistsW(buffer
) &&
1592 !SearchPathW(NULL
, pszFile
, NULL
, MAX_PATH
, buffer
, NULL
))
1595 m_pPidl
= SHSimpleIDListFromPathW(pszFile
);
1596 ShellLink_GetVolumeInfo(buffer
, &volume
);
1598 m_sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1599 (wcslen(buffer
) + 1) * sizeof (WCHAR
));
1601 return E_OUTOFMEMORY
;
1603 wcscpy(m_sPath
, buffer
);
1607 HeapFree(GetProcessHeap(), 0, unquoted
);
1612 HRESULT STDMETHODCALLTYPE
CShellLink::AddDataBlock(void* pDataBlock
)
1618 HRESULT STDMETHODCALLTYPE
CShellLink::CopyDataBlock(DWORD dwSig
, void** ppDataBlock
)
1620 LPVOID block
= NULL
;
1621 HRESULT hr
= E_FAIL
;
1623 TRACE("%p %08x %p\n", this, dwSig
, ppDataBlock
);
1627 case EXP_DARWIN_ID_SIG
:
1630 block
= shelllink_build_darwinid(sComponent
, dwSig
);
1633 case EXP_SZ_LINK_SIG
:
1634 case NT_CONSOLE_PROPS_SIG
:
1635 case NT_FE_CONSOLE_PROPS_SIG
:
1636 case EXP_SPECIAL_FOLDER_SIG
:
1637 case EXP_SZ_ICON_SIG
:
1638 FIXME("valid but unhandled datablock %08x\n", dwSig
);
1641 ERR("unknown datablock %08x\n", dwSig
);
1643 *ppDataBlock
= block
;
1647 HRESULT STDMETHODCALLTYPE
CShellLink::RemoveDataBlock(DWORD dwSig
)
1653 HRESULT STDMETHODCALLTYPE
CShellLink::GetFlags(DWORD
*pdwFlags
)
1657 FIXME("%p %p\n", this, pdwFlags
);
1659 /* FIXME: add more */
1661 flags
|= SLDF_HAS_ARGS
;
1663 flags
|= SLDF_HAS_DARWINID
;
1665 flags
|= SLDF_HAS_ICONLOCATION
;
1666 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1668 flags
|= SLDF_HAS_LOGO3ID
;
1671 flags
|= SLDF_HAS_ID_LIST
;
1678 HRESULT STDMETHODCALLTYPE
CShellLink::SetFlags(DWORD dwFlags
)
1684 /**************************************************************************
1685 * CShellLink implementation of IShellExtInit::Initialize()
1687 * Loads the shelllink from the dataobject the shell is pointing to.
1689 HRESULT STDMETHODCALLTYPE
CShellLink::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pdtobj
, HKEY hkeyProgID
)
1691 TRACE("%p %p %p %p\n", this, pidlFolder
, pdtobj
, hkeyProgID
);
1697 format
.cfFormat
= CF_HDROP
;
1699 format
.dwAspect
= DVASPECT_CONTENT
;
1701 format
.tymed
= TYMED_HGLOBAL
;
1704 HRESULT hr
= pdtobj
->GetData(&format
, &stgm
);
1708 UINT count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, -1, NULL
, 0);
1711 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, NULL
, 0);
1713 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, count
* sizeof(WCHAR
));
1716 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, path
, count
);
1718 HeapFree(GetProcessHeap(), 0, path
);
1721 ReleaseStgMedium(&stgm
);
1726 HRESULT STDMETHODCALLTYPE
CShellLink::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
1730 TRACE("%p %p %u %u %u %u\n", this,
1731 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1734 return E_INVALIDARG
;
1737 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_VERB
, wszOpen
, _countof(wszOpen
)))
1741 ZeroMemory(&mii
, sizeof(mii
));
1742 mii
.cbSize
= sizeof(mii
);
1743 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
;
1744 mii
.dwTypeData
= wszOpen
;
1745 mii
.cch
= wcslen(mii
.dwTypeData
);
1746 mii
.wID
= idCmdFirst
+ id
++;
1747 mii
.fState
= MFS_DEFAULT
| MFS_ENABLED
;
1748 mii
.fType
= MFT_STRING
;
1749 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
1753 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, id
);
1757 shelllink_get_msi_component_path(LPWSTR component
)
1759 DWORD Result
, sz
= 0;
1761 Result
= CommandLineFromMsiDescriptor(component
, NULL
, &sz
);
1762 if (Result
!= ERROR_SUCCESS
)
1766 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sz
* sizeof(WCHAR
));
1767 Result
= CommandLineFromMsiDescriptor(component
, path
, &sz
);
1768 if (Result
!= ERROR_SUCCESS
)
1770 HeapFree(GetProcessHeap(), 0, path
);
1774 TRACE("returning %s\n", debugstr_w(path
));
1779 HRESULT STDMETHODCALLTYPE
CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1781 HWND hwnd
= NULL
; /* FIXME: get using interface set from IObjectWithSite */
1785 TRACE("%p %p\n", this, lpici
);
1787 if (lpici
->cbSize
< sizeof(CMINVOKECOMMANDINFO
))
1788 return E_INVALIDARG
;
1790 HRESULT hr
= Resolve(hwnd
, 0);
1793 TRACE("failed to resolve component with error 0x%08x", hr
);
1798 path
= shelllink_get_msi_component_path(sComponent
);
1803 path
= strdupW(m_sPath
);
1805 if ( lpici
->cbSize
== sizeof(CMINVOKECOMMANDINFOEX
) &&
1806 (lpici
->fMask
& CMIC_MASK_UNICODE
) )
1808 LPCMINVOKECOMMANDINFOEX iciex
= (LPCMINVOKECOMMANDINFOEX
)lpici
;
1812 len
+= wcslen(m_sArgs
);
1813 if (iciex
->lpParametersW
)
1814 len
+= wcslen(iciex
->lpParametersW
);
1816 args
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1819 wcscat(args
, m_sArgs
);
1820 if (iciex
->lpParametersW
)
1823 wcscat(args
, iciex
->lpParametersW
);
1826 else if (m_sArgs
!= NULL
)
1828 args
= strdupW(m_sArgs
);
1831 SHELLEXECUTEINFOW sei
;
1832 ZeroMemory(&sei
, sizeof(sei
));
1833 sei
.cbSize
= sizeof(sei
);
1834 sei
.fMask
= SEE_MASK_HASLINKNAME
| SEE_MASK_UNICODE
|
1835 (lpici
->fMask
& (SEE_MASK_NOASYNC
| SEE_MASK_ASYNCOK
| SEE_MASK_FLAG_NO_UI
));
1837 sei
.lpClass
= m_sLinkPath
;
1838 sei
.nShow
= iShowCmd
;
1839 sei
.lpDirectory
= m_sWorkDir
;
1840 sei
.lpParameters
= args
;
1841 sei
.lpVerb
= L
"open";
1843 // HACK for ShellExecuteExW
1844 if (m_sPath
&& wcsstr(m_sPath
, L
".cpl"))
1845 sei
.lpVerb
= L
"cplopen";
1847 if (ShellExecuteExW(&sei
))
1852 HeapFree(GetProcessHeap(), 0, args
);
1853 HeapFree(GetProcessHeap(), 0, path
);
1858 HRESULT STDMETHODCALLTYPE
CShellLink::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1860 FIXME("%p %lu %u %p %p %u\n", this, idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1864 INT_PTR CALLBACK
ExtendedShortcutProc(HWND hwndDlg
, UINT uMsg
,
1865 WPARAM wParam
, LPARAM lParam
)
1872 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1873 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1878 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1879 if (LOWORD(wParam
) == IDOK
)
1881 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1882 EndDialog(hwndDlg
, 1);
1884 EndDialog(hwndDlg
, 0);
1886 else if (LOWORD(wParam
) == IDCANCEL
)
1888 EndDialog(hwndDlg
, -1);
1890 else if (LOWORD(wParam
) == 14000)
1892 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1893 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_UNCHECKED
, 0);
1895 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1904 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder
,
1906 PCUITEMID_CHILD_ARRAY apidl
,
1909 /**************************************************************************
1910 * SH_GetTargetTypeByPath
1912 * Function to get target type by passing full path to it
1914 LPWSTR
SH_GetTargetTypeByPath(LPCWSTR lpcwFullPath
)
1917 static WCHAR wszBuf
[MAX_PATH
];
1919 /* Get file information */
1921 if (!SHGetFileInfoW(lpcwFullPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
| SHGFI_USEFILEATTRIBUTES
))
1923 ERR("SHGetFileInfoW failed for %ls (%lu)\n", lpcwFullPath
, GetLastError());
1924 fi
.szTypeName
[0] = L
'\0';
1928 pwszExt
= PathFindExtensionW(lpcwFullPath
);
1931 if (!fi
.szTypeName
[0])
1933 /* The file type is unknown, so default to string "FileExtension File" */
1934 size_t cchRemaining
= 0;
1935 LPWSTR pwszEnd
= NULL
;
1937 StringCchPrintfExW(wszBuf
, _countof(wszBuf
), &pwszEnd
, &cchRemaining
, 0, L
"%s ", pwszExt
+ 1);
1940 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s (%s)", fi
.szTypeName
, pwszExt
); /* Update file type */
1946 /**************************************************************************
1947 * SH_ShellLinkDlgProc
1949 * dialog proc of the shortcut property dialog
1952 INT_PTR CALLBACK
CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1954 CShellLink
*pThis
= reinterpret_cast<CShellLink
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
1960 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
1964 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg
, lParam
, ppsp
->lParam
);
1966 pThis
= reinterpret_cast<CShellLink
*>(ppsp
->lParam
);
1967 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pThis
);
1969 TRACE("m_sArgs: %S sComponent: %S m_sDescription: %S m_sIcoPath: %S m_sPath: %S m_sPathRel: %S sProduct: %S m_sWorkDir: %S\n", pThis
->m_sArgs
, pThis
->sComponent
, pThis
->m_sDescription
,
1970 pThis
->m_sIcoPath
, pThis
->m_sPath
, pThis
->m_sPathRel
, pThis
->sProduct
, pThis
->m_sWorkDir
);
1972 /* Get file information */
1974 if (!SHGetFileInfoW(pThis
->m_sLinkPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
1976 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis
->m_sLinkPath
, GetLastError());
1977 fi
.szTypeName
[0] = L
'\0';
1981 if (fi
.hIcon
) // TODO: destroy icon
1982 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
1984 ERR("ExtractIconW failed %ls %u\n", pThis
->m_sIcoPath
, pThis
->iIcoNdx
);
1988 SetDlgItemTextW(hwndDlg
, 14005, SH_GetTargetTypeByPath(pThis
->m_sPath
));
1990 /* target location */
1991 if (pThis
->m_sWorkDir
)
1992 SetDlgItemTextW(hwndDlg
, 14007, PathFindFileName(pThis
->m_sWorkDir
));
1997 WCHAR newpath
[2*MAX_PATH
] = L
"\0";
1998 if (wcschr(pThis
->m_sPath
, ' '))
1999 StringCchPrintfExW(newpath
, _countof(newpath
), NULL
, NULL
, 0, L
"\"%ls\"", pThis
->m_sPath
);
2001 StringCchCopyExW(newpath
, _countof(newpath
), pThis
->m_sPath
, NULL
, NULL
, 0);
2003 if (pThis
->m_sArgs
&& pThis
->m_sArgs
[0])
2005 StringCchCatW(newpath
, _countof(newpath
), L
" ");
2006 StringCchCatW(newpath
, _countof(newpath
), pThis
->m_sArgs
);
2008 SetDlgItemTextW(hwndDlg
, 14009, newpath
);
2011 if (pThis
->m_sWorkDir
)
2012 SetDlgItemTextW(hwndDlg
, 14011, pThis
->m_sWorkDir
);
2015 if (pThis
->m_sDescription
)
2016 SetDlgItemTextW(hwndDlg
, 14019, pThis
->m_sDescription
);
2022 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
2023 if (lppsn
->hdr
.code
== PSN_APPLY
)
2025 WCHAR wszBuf
[MAX_PATH
];
2026 /* set working directory */
2027 GetDlgItemTextW(hwndDlg
, 14011, wszBuf
, _countof(wszBuf
));
2028 pThis
->SetWorkingDirectory(wszBuf
);
2029 /* set link destination */
2030 GetDlgItemTextW(hwndDlg
, 14009, wszBuf
, _countof(wszBuf
));
2031 LPWSTR lpszArgs
= NULL
;
2032 LPWSTR unquoted
= strdupW(wszBuf
);
2033 StrTrimW(unquoted
, L
" ");
2034 if (!PathFileExistsW(unquoted
))
2036 lpszArgs
= PathGetArgsW(unquoted
);
2037 PathRemoveArgsW(unquoted
);
2038 StrTrimW(lpszArgs
, L
" ");
2040 if (unquoted
[0] == '"' && unquoted
[wcslen(unquoted
)-1] == '"')
2041 PathUnquoteSpacesW(unquoted
);
2044 WCHAR
*pwszExt
= PathFindExtensionW(unquoted
);
2045 if (!wcsicmp(pwszExt
, L
".lnk"))
2047 // FIXME load localized error msg
2048 MessageBoxW(hwndDlg
, L
"You cannot create a link to a shortcut", L
"Error", MB_ICONERROR
);
2049 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
2053 if (!PathFileExistsW(unquoted
))
2055 //FIXME load localized error msg
2056 MessageBoxW(hwndDlg
, L
"The specified file name in the target box is invalid", L
"Error", MB_ICONERROR
);
2057 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
2061 pThis
->SetPath(unquoted
);
2063 pThis
->SetArguments(lpszArgs
);
2065 pThis
->SetArguments(L
"\0");
2067 HeapFree(GetProcessHeap(), 0, unquoted
);
2069 TRACE("This %p m_sLinkPath %S\n", pThis
, pThis
->m_sLinkPath
);
2070 pThis
->Save(pThis
->m_sLinkPath
, TRUE
);
2071 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
2077 switch(LOWORD(wParam
))
2080 SHOpenFolderAndSelectItems(pThis
->m_pPidl
, 0, NULL
, 0);
2083 /// open target directory
2088 WCHAR wszPath
[MAX_PATH
] = L
"";
2090 if (pThis
->m_sIcoPath
)
2091 wcscpy(wszPath
, pThis
->m_sIcoPath
);
2092 INT IconIndex
= pThis
->iIcoNdx
;
2093 if (PickIconDlg(hwndDlg
, wszPath
, _countof(wszPath
), &IconIndex
))
2095 pThis
->SetIconLocation(wszPath
, IconIndex
);
2097 /// FIXME redraw icon
2104 INT_PTR result
= DialogBoxParamW(shell32_hInstance
, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES
), hwndDlg
, ExtendedShortcutProc
, (LPARAM
)pThis
->m_bRunAs
);
2105 if (result
== 1 || result
== 0)
2107 if (pThis
->m_bRunAs
!= result
)
2109 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2112 pThis
->m_bRunAs
= result
;
2117 if (HIWORD(wParam
) == EN_CHANGE
)
2118 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2126 /**************************************************************************
2127 * ShellLink_IShellPropSheetExt interface
2130 HRESULT STDMETHODCALLTYPE
CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
2132 HPROPSHEETPAGE hPage
= SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES
, SH_ShellLinkDlgProc
, (LPARAM
)this, NULL
);
2135 ERR("failed to create property sheet page\n");
2139 if (!pfnAddPage(hPage
, lParam
))
2145 HRESULT STDMETHODCALLTYPE
CShellLink::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
2147 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID
, pfnReplacePage
, lParam
);
2151 HRESULT STDMETHODCALLTYPE
CShellLink::SetSite(IUnknown
*punk
)
2153 TRACE("%p %p\n", this, punk
);
2160 HRESULT STDMETHODCALLTYPE
CShellLink::GetSite(REFIID iid
, void ** ppvSite
)
2162 TRACE("%p %s %p\n", this, debugstr_guid(&iid
), ppvSite
);
2167 return m_site
->QueryInterface(iid
, ppvSite
);
2170 HRESULT STDMETHODCALLTYPE
CShellLink::DragEnter(IDataObject
*pDataObject
,
2171 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
2173 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject
);
2174 LPCITEMIDLIST pidlLast
;
2175 CComPtr
<IShellFolder
> psf
;
2177 HRESULT hr
= SHBindToParent(m_pPidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
2181 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_NULL_PPV_ARG(IDropTarget
, &m_DropTarget
));
2184 hr
= m_DropTarget
->DragEnter(pDataObject
, dwKeyState
, pt
, pdwEffect
);
2186 *pdwEffect
= DROPEFFECT_NONE
;
2189 *pdwEffect
= DROPEFFECT_NONE
;
2194 HRESULT STDMETHODCALLTYPE
CShellLink::DragOver(DWORD dwKeyState
, POINTL pt
,
2197 TRACE("(%p)\n", this);
2200 hr
= m_DropTarget
->DragOver(dwKeyState
, pt
, pdwEffect
);
2204 HRESULT STDMETHODCALLTYPE
CShellLink::DragLeave()
2206 TRACE("(%p)\n", this);
2210 hr
= m_DropTarget
->DragLeave();
2211 m_DropTarget
.Release();
2217 HRESULT STDMETHODCALLTYPE
CShellLink::Drop(IDataObject
*pDataObject
,
2218 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
2220 TRACE("(%p)\n", this);
2223 hr
= m_DropTarget
->Drop(pDataObject
, dwKeyState
, pt
, pdwEffect
);
2228 /**************************************************************************
2229 * IShellLink_ConstructFromFile
2231 HRESULT WINAPI
IShellLink_ConstructFromPath(WCHAR
*path
, REFIID riid
, LPVOID
*ppv
)
2233 CComPtr
<IPersistFile
> ppf
;
2234 HRESULT hr
= CShellLink::_CreatorClass::CreateInstance(NULL
, IID_PPV_ARG(IPersistFile
, &ppf
));
2238 hr
= ppf
->Load(path
, 0);
2242 return ppf
->QueryInterface(riid
, ppv
);
2245 HRESULT WINAPI
IShellLink_ConstructFromFile(IShellFolder
* psf
, LPCITEMIDLIST pidl
, REFIID riid
, LPVOID
*ppv
)
2247 WCHAR path
[MAX_PATH
];
2248 if (!ILGetDisplayNameExW(psf
, pidl
, path
, 0))
2251 return IShellLink_ConstructFromPath(path
, riid
, ppv
);