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 /**************************************************************************
80 * SH_GetTargetTypeByPath
82 * Function to get target type by passing full path to it
84 LPWSTR
SH_GetTargetTypeByPath(LPCWSTR lpcwFullPath
)
87 static WCHAR wszBuf
[MAX_PATH
];
89 /* Get file information */
91 if (!SHGetFileInfoW(lpcwFullPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
| SHGFI_USEFILEATTRIBUTES
))
93 ERR("SHGetFileInfoW failed for %ls (%lu)\n", lpcwFullPath
, GetLastError());
94 fi
.szTypeName
[0] = L
'\0';
98 pwszExt
= PathFindExtensionW(lpcwFullPath
);
101 if (!fi
.szTypeName
[0])
103 /* The file type is unknown, so default to string "FileExtension File" */
104 size_t cchRemaining
= 0;
105 LPWSTR pwszEnd
= NULL
;
107 StringCchPrintfExW(wszBuf
, _countof(wszBuf
), &pwszEnd
, &cchRemaining
, 0, L
"%s ", pwszExt
+ 1);
110 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s (%s)", fi
.szTypeName
, pwszExt
); /* Update file type */
116 /* IShellLink Implementation */
118 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
);
120 /* strdup on the process heap */
121 static LPWSTR __inline
HEAP_strdupAtoW(HANDLE heap
, DWORD flags
, LPCSTR str
)
128 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
129 p
= (LPWSTR
)HeapAlloc(heap
, flags
, len
* sizeof(WCHAR
));
132 MultiByteToWideChar(CP_ACP
, 0, str
, -1, p
, len
);
136 static LPWSTR __inline
strdupW(LPCWSTR src
)
139 if (!src
) return NULL
;
140 dest
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(src
) + 1) * sizeof(WCHAR
));
146 CShellLink::CShellLink()
150 memset(&time1
, 0, sizeof(time1
));
151 memset(&time2
, 0, sizeof(time2
));
152 memset(&time3
, 0, sizeof(time3
));
153 iShowCmd
= SW_SHOWNORMAL
;
163 memset(&volume
, 0, sizeof(volume
));
170 CShellLink::~CShellLink()
172 TRACE("-- destroying IShellLink(%p)\n", this);
174 HeapFree(GetProcessHeap(), 0, sIcoPath
);
175 HeapFree(GetProcessHeap(), 0, sArgs
);
176 HeapFree(GetProcessHeap(), 0, sWorkDir
);
177 HeapFree(GetProcessHeap(), 0, sDescription
);
178 HeapFree(GetProcessHeap(), 0, sPath
);
179 HeapFree(GetProcessHeap(), 0, sLinkPath
);
185 HRESULT WINAPI
CShellLink::GetClassID(CLSID
*pclsid
)
187 TRACE("%p %p\n", this, pclsid
);
191 *pclsid
= CLSID_ShellLink
;
195 HRESULT WINAPI
CShellLink::IsDirty()
197 TRACE("(%p)\n", this);
205 HRESULT WINAPI
CShellLink::Load(LPCOLESTR pszFileName
, DWORD dwMode
)
207 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName
), dwMode
);
210 dwMode
= STGM_READ
| STGM_SHARE_DENY_WRITE
;
212 CComPtr
<IStream
> stm
;
213 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, dwMode
, &stm
);
216 HeapFree(GetProcessHeap(), 0, sLinkPath
);
217 sLinkPath
= strdupW(pszFileName
);
219 ShellLink_UpdatePath(sPathRel
, pszFileName
, sWorkDir
, &sPath
);
222 TRACE("-- returning hr %08x\n", hr
);
226 HRESULT WINAPI
CShellLink::Save(LPCOLESTR pszFileName
, BOOL fRemember
)
228 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
233 CComPtr
<IStream
> stm
;
234 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, STGM_READWRITE
| STGM_CREATE
| STGM_SHARE_EXCLUSIVE
, &stm
);
237 hr
= Save(stm
, FALSE
);
242 HeapFree(GetProcessHeap(), 0, sLinkPath
);
244 sLinkPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName
) + 1) * sizeof(WCHAR
));
246 wcscpy(sLinkPath
, pszFileName
);
252 DeleteFileW(pszFileName
);
253 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName
));
260 HRESULT WINAPI
CShellLink::SaveCompleted(LPCOLESTR pszFileName
)
262 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
266 HRESULT WINAPI
CShellLink::GetCurFile(LPOLESTR
*ppszFileName
)
268 *ppszFileName
= NULL
;
272 /* IPersistFile::GetCurFile called before IPersistFile::Save */
276 *ppszFileName
= (LPOLESTR
)CoTaskMemAlloc((wcslen(sLinkPath
) + 1) * sizeof(WCHAR
));
280 return E_OUTOFMEMORY
;
283 /* copy last saved filename */
284 wcscpy(*ppszFileName
, sLinkPath
);
289 /************************************************************************
290 * IPersistStream_IsDirty (IPersistStream)
293 static HRESULT
Stream_LoadString(IStream
* stm
, BOOL unicode
, LPWSTR
*pstr
)
299 HRESULT hr
= stm
->Read(&len
, sizeof(len
), &count
);
300 if (FAILED(hr
) || count
!= sizeof(len
))
304 len
*= sizeof (WCHAR
);
306 TRACE("reading %d\n", len
);
307 LPSTR temp
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
+ sizeof(WCHAR
));
309 return E_OUTOFMEMORY
;
311 hr
= stm
->Read(temp
, len
, &count
);
312 if(FAILED(hr
) || count
!= len
)
314 HeapFree(GetProcessHeap(), 0, temp
);
318 TRACE("read %s\n", debugstr_an(temp
, len
));
320 /* convert to unicode if necessary */
324 count
= MultiByteToWideChar(CP_ACP
, 0, temp
, len
, NULL
, 0);
325 str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (count
+ 1) * sizeof (WCHAR
));
328 HeapFree(GetProcessHeap(), 0, temp
);
329 return E_OUTOFMEMORY
;
331 MultiByteToWideChar(CP_ACP
, 0, temp
, len
, str
, count
);
332 HeapFree(GetProcessHeap(), 0, temp
);
346 static HRESULT
Stream_ReadChunk(IStream
* stm
, LPVOID
*data
)
351 unsigned char data
[1];
358 HRESULT hr
= stm
->Read(&size
, sizeof(size
), &count
);
359 if (FAILED(hr
) || count
!= sizeof(size
))
362 chunk
= static_cast<sized_chunk
*>(HeapAlloc(GetProcessHeap(), 0, size
));
364 return E_OUTOFMEMORY
;
367 hr
= stm
->Read(chunk
->data
, size
- sizeof(size
), &count
);
368 if (FAILED(hr
) || count
!= (size
- sizeof(size
)))
370 HeapFree(GetProcessHeap(), 0, chunk
);
374 TRACE("Read %d bytes\n", chunk
->size
);
381 static BOOL
Stream_LoadVolume(LOCAL_VOLUME_INFO
*vol
, CShellLink::volume_info
*volume
)
383 volume
->serial
= vol
->dwVolSerial
;
384 volume
->type
= vol
->dwType
;
386 if (!vol
->dwVolLabelOfs
)
388 if (vol
->dwSize
<= vol
->dwVolLabelOfs
)
390 INT len
= vol
->dwSize
- vol
->dwVolLabelOfs
;
392 LPSTR label
= (LPSTR
)vol
;
393 label
+= vol
->dwVolLabelOfs
;
394 MultiByteToWideChar(CP_ACP
, 0, label
, len
, volume
->label
, _countof(volume
->label
));
399 static LPWSTR
Stream_LoadPath(LPCSTR p
, DWORD maxlen
)
403 while (p
[len
] && len
< maxlen
)
406 UINT wlen
= MultiByteToWideChar(CP_ACP
, 0, p
, len
, NULL
, 0);
407 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wlen
+ 1) * sizeof(WCHAR
));
410 MultiByteToWideChar(CP_ACP
, 0, p
, len
, path
, wlen
);
416 static HRESULT
Stream_LoadLocation(IStream
*stm
,
417 CShellLink::volume_info
*volume
, LPWSTR
*path
)
420 HRESULT hr
= Stream_ReadChunk(stm
, (LPVOID
*) &p
);
424 LOCATION_INFO
*loc
= reinterpret_cast<LOCATION_INFO
*>(p
);
425 if (loc
->dwTotalSize
< sizeof(LOCATION_INFO
))
427 HeapFree(GetProcessHeap(), 0, p
);
431 /* if there's valid local volume information, load it */
432 if (loc
->dwVolTableOfs
&&
433 ((loc
->dwVolTableOfs
+ sizeof(LOCAL_VOLUME_INFO
)) <= loc
->dwTotalSize
))
435 LOCAL_VOLUME_INFO
*volume_info
;
437 volume_info
= (LOCAL_VOLUME_INFO
*) &p
[loc
->dwVolTableOfs
];
438 Stream_LoadVolume(volume_info
, volume
);
441 /* if there's a local path, load it */
442 DWORD n
= loc
->dwLocalPathOfs
;
443 if (n
&& n
< loc
->dwTotalSize
)
444 *path
= Stream_LoadPath(&p
[n
], loc
->dwTotalSize
- n
);
446 TRACE("type %d serial %08x name %s path %s\n", volume
->type
,
447 volume
->serial
, debugstr_w(volume
->label
), debugstr_w(*path
));
449 HeapFree(GetProcessHeap(), 0, p
);
454 * The format of the advertised shortcut info seems to be:
459 * 0 Length of the block (4 bytes, usually 0x314)
461 * 8 string data in ASCII
462 * 8+0x104 string data in UNICODE
464 * In the original Win32 implementation the buffers are not initialized
465 * to zero, so data trailing the string is random garbage.
467 static HRESULT
Stream_LoadAdvertiseInfo(IStream
* stm
, LPWSTR
*str
)
472 EXP_DARWIN_LINK buffer
;
473 HRESULT hr
= stm
->Read(&buffer
.dbh
.cbSize
, sizeof (DWORD
), &count
);
477 /* make sure that we read the size of the structure even on error */
478 DWORD size
= sizeof buffer
- sizeof (DWORD
);
479 if (buffer
.dbh
.cbSize
!= sizeof buffer
)
481 ERR("Ooops. This structure is not as expected...\n");
485 hr
= stm
->Read(&buffer
.dbh
.dwSignature
, size
, &count
);
492 TRACE("magic %08x string = %s\n", buffer
.dbh
.dwSignature
, debugstr_w(buffer
.szwDarwinID
));
494 if ((buffer
.dbh
.dwSignature
& 0xffff0000) != 0xa0000000)
496 ERR("Unknown magic number %08x in advertised shortcut\n", buffer
.dbh
.dwSignature
);
500 *str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
501 (wcslen(buffer
.szwDarwinID
) + 1) * sizeof(WCHAR
));
502 wcscpy(*str
, buffer
.szwDarwinID
);
507 /************************************************************************
508 * IPersistStream_Load (IPersistStream)
510 HRESULT WINAPI
CShellLink::Load(IStream
*stm
)
512 TRACE("%p %p\n", this, stm
);
515 return STG_E_INVALIDPOINTER
;
517 SHELL_LINK_HEADER ShlLnkHeader
;
518 ULONG dwBytesRead
= 0;
519 HRESULT hr
= stm
->Read(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &dwBytesRead
);
523 if (dwBytesRead
!= sizeof(ShlLnkHeader
))
525 if (ShlLnkHeader
.dwSize
!= sizeof(ShlLnkHeader
))
527 if (!IsEqualIID(ShlLnkHeader
.clsid
, CLSID_ShellLink
))
530 /* free all the old stuff */
533 memset(&volume
, 0, sizeof volume
);
534 HeapFree(GetProcessHeap(), 0, sPath
);
536 HeapFree(GetProcessHeap(), 0, sDescription
);
538 HeapFree(GetProcessHeap(), 0, sPathRel
);
540 HeapFree(GetProcessHeap(), 0, sWorkDir
);
542 HeapFree(GetProcessHeap(), 0, sArgs
);
544 HeapFree(GetProcessHeap(), 0, sIcoPath
);
546 HeapFree(GetProcessHeap(), 0, sProduct
);
548 HeapFree(GetProcessHeap(), 0, sComponent
);
551 BOOL unicode
= FALSE
;
552 iShowCmd
= ShlLnkHeader
.nShowCommand
;
553 wHotKey
= ShlLnkHeader
.wHotKey
;
554 iIcoNdx
= ShlLnkHeader
.nIconIndex
;
555 FileTimeToSystemTime (&ShlLnkHeader
.ftCreationTime
, &time1
);
556 FileTimeToSystemTime (&ShlLnkHeader
.ftLastAccessTime
, &time2
);
557 FileTimeToSystemTime (&ShlLnkHeader
.ftLastWriteTime
, &time3
);
560 WCHAR sTemp
[MAX_PATH
];
561 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time1
,
562 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
563 TRACE("-- time1: %s\n", debugstr_w(sTemp
));
564 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time2
,
565 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
566 TRACE("-- time2: %s\n", debugstr_w(sTemp
));
567 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time3
,
568 NULL
, sTemp
, sizeof(sTemp
) / sizeof(*sTemp
));
569 TRACE("-- time3: %s\n", debugstr_w(sTemp
));
572 /* load all the new stuff */
573 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ID_LIST
)
575 hr
= ILLoadFromStream(stm
, &pPidl
);
581 /* load the location information */
582 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LINK_INFO
)
583 hr
= Stream_LoadLocation(stm
, &volume
, &sPath
);
587 if (ShlLnkHeader
.dwFlags
& SLDF_UNICODE
)
590 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_NAME
)
592 hr
= Stream_LoadString(stm
, unicode
, &sDescription
);
593 TRACE("Description -> %s\n", debugstr_w(sDescription
));
598 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_RELPATH
)
600 hr
= Stream_LoadString(stm
, unicode
, &sPathRel
);
601 TRACE("Relative Path-> %s\n", debugstr_w(sPathRel
));
606 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_WORKINGDIR
)
608 hr
= Stream_LoadString(stm
, unicode
, &sWorkDir
);
609 PathRemoveBackslash(sWorkDir
);
610 TRACE("Working Dir -> %s\n", debugstr_w(sWorkDir
));
615 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ARGS
)
617 hr
= Stream_LoadString(stm
, unicode
, &sArgs
);
618 TRACE("Arguments -> %s\n", debugstr_w(sArgs
));
623 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_ICONLOCATION
)
625 hr
= Stream_LoadString(stm
, unicode
, &sIcoPath
);
626 TRACE("Icon file -> %s\n", debugstr_w(sIcoPath
));
631 #if (NTDDI_VERSION < NTDDI_LONGHORN)
632 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_LOGO3ID
)
634 hr
= Stream_LoadAdvertiseInfo(stm
, &sProduct
);
635 TRACE("Product -> %s\n", debugstr_w(sProduct
));
641 if (ShlLnkHeader
.dwFlags
& SLDF_HAS_DARWINID
)
643 hr
= Stream_LoadAdvertiseInfo(stm
, &sComponent
);
644 TRACE("Component -> %s\n", debugstr_w(sComponent
));
646 if (ShlLnkHeader
.dwFlags
& SLDF_RUNAS_USER
)
659 hr
= stm
->Read(&dwZero
, sizeof(dwZero
), &dwBytesRead
);
660 if (FAILED(hr
) || dwZero
|| dwBytesRead
!= sizeof(dwZero
))
661 ERR("Last word was not zero\n");
673 /************************************************************************
676 * Helper function for IPersistStream_Save. Writes a unicode string
677 * with terminating nul byte to a stream, preceded by the its length.
679 static HRESULT
Stream_WriteString(IStream
* stm
, LPCWSTR str
)
681 USHORT len
= wcslen(str
) + 1;
684 HRESULT hr
= stm
->Write(&len
, sizeof(len
), &count
);
688 len
*= sizeof(WCHAR
);
690 hr
= stm
->Write(str
, len
, &count
);
697 /************************************************************************
698 * Stream_WriteLocationInfo
700 * Writes the location info to a stream
702 * FIXME: One day we might want to write the network volume information
703 * and the final path.
704 * Figure out how Windows deals with unicode paths here.
706 static HRESULT
Stream_WriteLocationInfo(IStream
* stm
, LPCWSTR path
,
707 CShellLink::volume_info
*volume
)
709 LOCAL_VOLUME_INFO
*vol
;
712 TRACE("%p %s %p\n", stm
, debugstr_w(path
), volume
);
714 /* figure out the size of everything */
715 DWORD label_size
= WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
716 NULL
, 0, NULL
, NULL
);
717 DWORD path_size
= WideCharToMultiByte(CP_ACP
, 0, path
, -1,
718 NULL
, 0, NULL
, NULL
);
719 DWORD volume_info_size
= sizeof(*vol
) + label_size
;
720 DWORD final_path_size
= 1;
721 DWORD total_size
= sizeof(*loc
) + volume_info_size
+ path_size
+ final_path_size
;
723 /* create pointers to everything */
724 loc
= static_cast<LOCATION_INFO
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, total_size
));
725 vol
= (LOCAL_VOLUME_INFO
*) &loc
[1];
726 LPSTR szLabel
= (LPSTR
) &vol
[1];
727 LPSTR szPath
= &szLabel
[label_size
];
728 LPSTR szFinalPath
= &szPath
[path_size
];
730 /* fill in the location information header */
731 loc
->dwTotalSize
= total_size
;
732 loc
->dwHeaderSize
= sizeof (*loc
);
734 loc
->dwVolTableOfs
= sizeof (*loc
);
735 loc
->dwLocalPathOfs
= sizeof (*loc
) + volume_info_size
;
736 loc
->dwNetworkVolTableOfs
= 0;
737 loc
->dwFinalPathOfs
= sizeof (*loc
) + volume_info_size
+ path_size
;
739 /* fill in the volume information */
740 vol
->dwSize
= volume_info_size
;
741 vol
->dwType
= volume
->type
;
742 vol
->dwVolSerial
= volume
->serial
;
743 vol
->dwVolLabelOfs
= sizeof (*vol
);
745 /* copy in the strings */
746 WideCharToMultiByte(CP_ACP
, 0, volume
->label
, -1,
747 szLabel
, label_size
, NULL
, NULL
);
748 WideCharToMultiByte(CP_ACP
, 0, path
, -1,
749 szPath
, path_size
, NULL
, NULL
);
753 HRESULT hr
= stm
->Write(loc
, total_size
, &count
);
754 HeapFree(GetProcessHeap(), 0, loc
);
759 static EXP_DARWIN_LINK
* shelllink_build_darwinid(LPCWSTR string
, DWORD magic
)
761 EXP_DARWIN_LINK
*buffer
= (EXP_DARWIN_LINK
*)LocalAlloc(LMEM_ZEROINIT
, sizeof * buffer
);
762 buffer
->dbh
.cbSize
= sizeof * buffer
;
763 buffer
->dbh
.dwSignature
= magic
;
764 lstrcpynW(buffer
->szwDarwinID
, string
, MAX_PATH
);
765 WideCharToMultiByte(CP_ACP
, 0, string
, -1, buffer
->szDarwinID
, MAX_PATH
, NULL
, NULL
);
770 static HRESULT
Stream_WriteAdvertiseInfo(IStream
* stm
, LPCWSTR string
, DWORD magic
)
774 EXP_DARWIN_LINK
*buffer
= shelllink_build_darwinid(string
, magic
);
777 return stm
->Write(buffer
, buffer
->dbh
.cbSize
, &count
);
780 /************************************************************************
781 * IPersistStream_Save (IPersistStream)
783 * FIXME: makes assumptions about byte order
785 HRESULT WINAPI
CShellLink::Save(IStream
*stm
, BOOL fClearDirty
)
787 TRACE("%p %p %x\n", this, stm
, fClearDirty
);
789 SHELL_LINK_HEADER ShlLnkHeader
;
790 memset(&ShlLnkHeader
, 0, sizeof(ShlLnkHeader
));
791 ShlLnkHeader
.dwSize
= sizeof(ShlLnkHeader
);
792 ShlLnkHeader
.nShowCommand
= iShowCmd
;
793 ShlLnkHeader
.clsid
= CLSID_ShellLink
;
795 ShlLnkHeader
.wHotKey
= wHotKey
;
796 ShlLnkHeader
.nIconIndex
= iIcoNdx
;
797 ShlLnkHeader
.dwFlags
= SLDF_UNICODE
; /* strings are in unicode */
799 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ID_LIST
;
801 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LINK_INFO
;
803 ShlLnkHeader
.dwFlags
|= SLDF_HAS_NAME
;
805 ShlLnkHeader
.dwFlags
|= SLDF_HAS_WORKINGDIR
;
807 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ARGS
;
809 ShlLnkHeader
.dwFlags
|= SLDF_HAS_ICONLOCATION
;
810 #if (NTDDI_VERSION < NTDDI_LONGHORN)
812 ShlLnkHeader
.dwFlags
|= SLDF_HAS_LOGO3ID
;
815 ShlLnkHeader
.dwFlags
|= SLDF_HAS_DARWINID
;
817 ShlLnkHeader
.dwFlags
|= SLDF_RUNAS_USER
;
819 SystemTimeToFileTime (&time1
, &ShlLnkHeader
.ftCreationTime
);
820 SystemTimeToFileTime (&time2
, &ShlLnkHeader
.ftLastAccessTime
);
821 SystemTimeToFileTime (&time3
, &ShlLnkHeader
.ftLastWriteTime
);
823 /* write the Shortcut header */
825 HRESULT hr
= stm
->Write(&ShlLnkHeader
, sizeof(ShlLnkHeader
), &count
);
828 ERR("Write failed\n");
832 TRACE("Writing pidl\n");
834 /* write the PIDL to the shortcut */
837 hr
= ILSaveToStream(stm
, pPidl
);
840 ERR("Failed to write PIDL\n");
846 Stream_WriteLocationInfo(stm
, sPath
, &volume
);
849 hr
= Stream_WriteString(stm
, sDescription
);
852 hr
= Stream_WriteString(stm
, sPathRel
);
855 hr
= Stream_WriteString(stm
, sWorkDir
);
858 hr
= Stream_WriteString(stm
, sArgs
);
861 hr
= Stream_WriteString(stm
, sIcoPath
);
864 hr
= Stream_WriteAdvertiseInfo(stm
, sProduct
, EXP_SZ_ICON_SIG
);
867 hr
= Stream_WriteAdvertiseInfo(stm
, sComponent
, EXP_DARWIN_ID_SIG
);
869 /* the last field is a single zero dword */
871 hr
= stm
->Write(&zero
, sizeof zero
, &count
);
876 /************************************************************************
877 * IPersistStream_GetSizeMax (IPersistStream)
879 HRESULT WINAPI
CShellLink::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
881 TRACE("(%p)\n", this);
886 static BOOL
SHELL_ExistsFileW(LPCWSTR path
)
888 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(path
))
894 /**************************************************************************
895 * ShellLink_UpdatePath
896 * update absolute path in sPath using relative path in sPathRel
898 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
)
900 if (!path
|| !psPath
)
903 if (!*psPath
&& sPathRel
)
905 WCHAR buffer
[2*MAX_PATH
], abs_path
[2*MAX_PATH
];
908 /* first try if [directory of link file] + [relative path] finds an existing file */
910 GetFullPathNameW(path
, MAX_PATH
* 2, buffer
, &final
);
913 wcscpy(final
, sPathRel
);
917 if (SHELL_ExistsFileW(buffer
))
919 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
920 wcscpy(abs_path
, buffer
);
924 /* try if [working directory] + [relative path] finds an existing file */
927 wcscpy(buffer
, sWorkDir
);
928 wcscpy(PathAddBackslashW(buffer
), sPathRel
);
930 if (SHELL_ExistsFileW(buffer
))
931 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
932 wcscpy(abs_path
, buffer
);
936 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
938 wcscpy(abs_path
, sPathRel
);
940 *psPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path
) + 1) * sizeof(WCHAR
));
942 return E_OUTOFMEMORY
;
944 wcscpy(*psPath
, abs_path
);
950 HRESULT WINAPI
CShellLink::GetPath(LPSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAA
*pfd
, DWORD fFlags
)
952 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
953 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
955 if (sComponent
|| sProduct
)
962 WideCharToMultiByte(CP_ACP
, 0, sPath
, -1,
963 pszFile
, cchMaxPath
, NULL
, NULL
);
965 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
970 HRESULT WINAPI
CShellLink::GetIDList(LPITEMIDLIST
* ppidl
)
972 TRACE("(%p)->(ppidl=%p)\n", this, ppidl
);
980 *ppidl
= ILClone(pPidl
);
984 HRESULT WINAPI
CShellLink::SetIDList(LPCITEMIDLIST pidl
)
986 TRACE("(%p)->(pidl=%p)\n", this, pidl
);
991 pPidl
= ILClone(pidl
);
1000 HRESULT WINAPI
CShellLink::GetDescription(LPSTR pszName
, INT cchMaxName
)
1002 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
1008 WideCharToMultiByte(CP_ACP
, 0, sDescription
, -1,
1009 pszName
, cchMaxName
, NULL
, NULL
);
1014 HRESULT WINAPI
CShellLink::SetDescription(LPCSTR pszName
)
1016 TRACE("(%p)->(pName=%s)\n", this, pszName
);
1018 HeapFree(GetProcessHeap(), 0, sDescription
);
1019 sDescription
= NULL
;
1023 sDescription
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszName
);
1025 return E_OUTOFMEMORY
;
1032 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPSTR pszDir
, INT cchMaxPath
)
1034 TRACE("(%p)->(%p len=%u)\n", this, pszDir
, cchMaxPath
);
1040 WideCharToMultiByte(CP_ACP
, 0, sWorkDir
, -1,
1041 pszDir
, cchMaxPath
, NULL
, NULL
);
1046 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCSTR pszDir
)
1048 TRACE("(%p)->(dir=%s)\n", this, pszDir
);
1050 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1055 sWorkDir
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir
);
1057 return E_OUTOFMEMORY
;
1064 HRESULT WINAPI
CShellLink::GetArguments(LPSTR pszArgs
, INT cchMaxPath
)
1066 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1071 WideCharToMultiByte(CP_ACP
, 0, sArgs
, -1,
1072 pszArgs
, cchMaxPath
, NULL
, NULL
);
1077 HRESULT WINAPI
CShellLink::SetArguments(LPCSTR pszArgs
)
1079 TRACE("(%p)->(args=%s)\n", this, pszArgs
);
1081 HeapFree(GetProcessHeap(), 0, sArgs
);
1086 sArgs
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs
);
1088 return E_OUTOFMEMORY
;
1096 HRESULT WINAPI
CShellLink::GetHotkey(WORD
*pwHotkey
)
1098 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey
, wHotKey
);
1100 *pwHotkey
= wHotKey
;
1105 HRESULT WINAPI
CShellLink::SetHotkey(WORD wHotkey
)
1107 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey
);
1115 HRESULT WINAPI
CShellLink::GetShowCmd(INT
*piShowCmd
)
1117 TRACE("(%p)->(%p)\n", this, piShowCmd
);
1118 *piShowCmd
= iShowCmd
;
1122 HRESULT WINAPI
CShellLink::SetShowCmd(INT iShowCmd
)
1124 TRACE("(%p) %d\n", this, iShowCmd
);
1126 this->iShowCmd
= iShowCmd
;
1132 static HRESULT
SHELL_PidlGeticonLocationA(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1133 LPSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1135 LPCITEMIDLIST pidlLast
;
1137 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
1141 CComPtr
<IExtractIconA
> pei
;
1143 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_NULL_PPV_ARG(IExtractIconA
, &pei
));
1146 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, NULL
);
1154 HRESULT WINAPI
CShellLink::GetIconLocation(LPSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1156 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1163 WideCharToMultiByte(CP_ACP
, 0, sIcoPath
, -1, pszIconPath
, cchIconPath
, NULL
, NULL
);
1169 CComPtr
<IShellFolder
> pdsk
;
1171 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1175 /* first look for an icon using the PIDL (if present) */
1177 hr
= SHELL_PidlGeticonLocationA(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1181 /* if we couldn't find an icon yet, look for it using the file system path */
1182 if (FAILED(hr
) && sPath
)
1186 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1190 hr
= SHELL_PidlGeticonLocationA(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1202 HRESULT WINAPI
CShellLink::SetIconLocation(LPCSTR pszIconPath
, INT iIcon
)
1204 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath
, iIcon
);
1206 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1211 sIcoPath
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath
);
1213 return E_OUTOFMEMORY
;
1222 HRESULT WINAPI
CShellLink::SetRelativePath(LPCSTR pszPathRel
, DWORD dwReserved
)
1224 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel
, dwReserved
);
1226 HeapFree(GetProcessHeap(), 0, sPathRel
);
1231 sPathRel
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel
);
1235 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1238 HRESULT WINAPI
CShellLink::Resolve(HWND hwnd
, DWORD fFlags
)
1243 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd
, fFlags
);
1245 /*FIXME: use IResolveShellLink interface */
1247 if (!sPath
&& pPidl
)
1249 WCHAR buffer
[MAX_PATH
];
1251 bSuccess
= SHGetPathFromIDListW(pPidl
, buffer
);
1253 if (bSuccess
&& *buffer
)
1255 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
) + 1) * sizeof(WCHAR
));
1258 return E_OUTOFMEMORY
;
1260 wcscpy(sPath
, buffer
);
1265 hr
= S_OK
; /* don't report an error occurred while just caching information */
1268 if (!sIcoPath
&& sPath
)
1270 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(sPath
) + 1) * sizeof(WCHAR
));
1273 return E_OUTOFMEMORY
;
1275 wcscpy(sIcoPath
, sPath
);
1284 HRESULT WINAPI
CShellLink::SetPath(LPCSTR pszFile
)
1286 TRACE("(%p)->(path=%s)\n", this, pszFile
);
1287 if (pszFile
== NULL
)
1288 return E_INVALIDARG
;
1290 LPWSTR str
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile
);
1292 return E_OUTOFMEMORY
;
1294 HRESULT hr
= SetPath(str
);
1295 HeapFree(GetProcessHeap(), 0, str
);
1300 HRESULT WINAPI
CShellLink::GetPath(LPWSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAW
*pfd
, DWORD fFlags
)
1302 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1303 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
1305 if (sComponent
|| sProduct
)
1312 lstrcpynW(pszFile
, sPath
, cchMaxPath
);
1314 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1319 HRESULT WINAPI
CShellLink::GetDescription(LPWSTR pszName
, INT cchMaxName
)
1321 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
1325 lstrcpynW(pszName
, sDescription
, cchMaxName
);
1330 HRESULT WINAPI
CShellLink::SetDescription(LPCWSTR pszName
)
1332 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName
));
1334 HeapFree(GetProcessHeap(), 0, sDescription
);
1335 sDescription
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1336 (wcslen(pszName
) + 1) * sizeof(WCHAR
));
1338 return E_OUTOFMEMORY
;
1340 wcscpy(sDescription
, pszName
);
1346 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPWSTR pszDir
, INT cchMaxPath
)
1348 TRACE("(%p)->(%p len %u)\n", this, pszDir
, cchMaxPath
);
1353 lstrcpynW(pszDir
, sWorkDir
, cchMaxPath
);
1358 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCWSTR pszDir
)
1360 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir
));
1362 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1363 sWorkDir
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1364 (wcslen(pszDir
) + 1) * sizeof (WCHAR
));
1366 return E_OUTOFMEMORY
;
1367 wcscpy(sWorkDir
, pszDir
);
1373 HRESULT WINAPI
CShellLink::GetArguments(LPWSTR pszArgs
, INT cchMaxPath
)
1375 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1380 lstrcpynW(pszArgs
, sArgs
, cchMaxPath
);
1385 HRESULT WINAPI
CShellLink::SetArguments(LPCWSTR pszArgs
)
1387 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs
));
1389 HeapFree(GetProcessHeap(), 0, sArgs
);
1390 sArgs
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1391 (wcslen(pszArgs
) + 1) * sizeof (WCHAR
));
1393 return E_OUTOFMEMORY
;
1395 wcscpy(sArgs
, pszArgs
);
1401 static HRESULT
SHELL_PidlGeticonLocationW(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1402 LPWSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1404 LPCITEMIDLIST pidlLast
;
1407 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
1411 CComPtr
<IExtractIconW
> pei
;
1413 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_NULL_PPV_ARG(IExtractIconW
, &pei
));
1416 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, &wFlags
);
1424 HRESULT WINAPI
CShellLink::GetIconLocation(LPWSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1426 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1433 lstrcpynW(pszIconPath
, sIcoPath
, cchIconPath
);
1439 CComPtr
<IShellFolder
> pdsk
;
1441 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1445 /* first look for an icon using the PIDL (if present) */
1447 hr
= SHELL_PidlGeticonLocationW(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1451 /* if we couldn't find an icon yet, look for it using the file system path */
1452 if (FAILED(hr
) && sPath
)
1456 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1460 hr
= SHELL_PidlGeticonLocationW(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1471 HRESULT WINAPI
CShellLink::SetIconLocation(LPCWSTR pszIconPath
, INT iIcon
)
1473 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath
), iIcon
);
1475 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1476 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1477 (wcslen(pszIconPath
) + 1) * sizeof (WCHAR
));
1479 return E_OUTOFMEMORY
;
1480 wcscpy(sIcoPath
, pszIconPath
);
1488 HRESULT WINAPI
CShellLink::SetRelativePath(LPCWSTR pszPathRel
, DWORD dwReserved
)
1490 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel
), dwReserved
);
1492 HeapFree(GetProcessHeap(), 0, sPathRel
);
1493 sPathRel
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1494 (wcslen(pszPathRel
) + 1) * sizeof (WCHAR
));
1496 return E_OUTOFMEMORY
;
1497 wcscpy(sPathRel
, pszPathRel
);
1500 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1503 LPWSTR
CShellLink::ShellLink_GetAdvertisedArg(LPCWSTR str
)
1508 LPCWSTR p
= wcschr(str
, L
':');
1511 DWORD len
= p
- str
;
1512 LPWSTR ret
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * (len
+ 1));
1515 memcpy(ret
, str
, sizeof(WCHAR
)*len
);
1520 HRESULT
CShellLink::ShellLink_SetAdvertiseInfo(LPCWSTR str
)
1522 LPCWSTR szComponent
= NULL
, szProduct
= NULL
;
1526 /* each segment must start with two colons */
1527 if (str
[0] != ':' || str
[1] != ':')
1530 /* the last segment is just two colons */
1535 /* there must be a colon straight after a guid */
1536 LPCWSTR p
= wcschr(str
, L
':');
1543 /* get the guid, and check it's validly formatted */
1545 memcpy(szGuid
, str
, sizeof(WCHAR
)*len
);
1549 HRESULT hr
= CLSIDFromString(szGuid
, &guid
);
1554 /* match it up to a guid that we care about */
1555 if (IsEqualGUID(guid
, SHELL32_AdvtShortcutComponent
) && !szComponent
)
1557 else if (IsEqualGUID(guid
, SHELL32_AdvtShortcutProduct
) && !szProduct
)
1562 /* skip to the next field */
1563 str
= wcschr(str
, L
':');
1568 /* we have to have a component for an advertised shortcut */
1572 sComponent
= ShellLink_GetAdvertisedArg(szComponent
);
1573 sProduct
= ShellLink_GetAdvertisedArg(szProduct
);
1575 TRACE("Component = %s\n", debugstr_w(sComponent
));
1576 TRACE("Product = %s\n", debugstr_w(sProduct
));
1581 static BOOL
ShellLink_GetVolumeInfo(LPCWSTR path
, CShellLink::volume_info
*volume
)
1583 WCHAR drive
[4] = { path
[0], ':', '\\', 0 };
1585 volume
->type
= GetDriveTypeW(drive
);
1586 BOOL bRet
= GetVolumeInformationW(drive
, volume
->label
, _countof(volume
->label
), &volume
->serial
, NULL
, NULL
, NULL
, 0);
1587 TRACE("ret = %d type %d serial %08x name %s\n", bRet
,
1588 volume
->type
, volume
->serial
, debugstr_w(volume
->label
));
1592 HRESULT WINAPI
CShellLink::SetPath(LPCWSTR pszFile
)
1594 LPWSTR unquoted
= NULL
;
1597 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile
));
1600 return E_INVALIDARG
;
1602 /* quotes at the ends of the string are stripped */
1603 UINT len
= wcslen(pszFile
);
1604 if (pszFile
[0] == '"' && pszFile
[len
-1] == '"')
1606 unquoted
= strdupW(pszFile
);
1607 PathUnquoteSpacesW(unquoted
);
1611 /* any other quote marks are invalid */
1612 if (wcschr(pszFile
, '"'))
1614 HeapFree(GetProcessHeap(), 0, unquoted
);
1618 HeapFree(GetProcessHeap(), 0, sPath
);
1621 HeapFree(GetProcessHeap(), 0, sComponent
);
1628 if (S_OK
!= ShellLink_SetAdvertiseInfo(pszFile
))
1630 WCHAR buffer
[MAX_PATH
];
1633 if (*pszFile
== '\0')
1635 else if (!GetFullPathNameW(pszFile
, MAX_PATH
, buffer
, &fname
))
1637 else if(!PathFileExistsW(buffer
) &&
1638 !SearchPathW(NULL
, pszFile
, NULL
, MAX_PATH
, buffer
, NULL
))
1641 pPidl
= SHSimpleIDListFromPathW(pszFile
);
1642 ShellLink_GetVolumeInfo(buffer
, &volume
);
1644 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1645 (wcslen(buffer
) + 1) * sizeof (WCHAR
));
1647 return E_OUTOFMEMORY
;
1649 wcscpy(sPath
, buffer
);
1653 HeapFree(GetProcessHeap(), 0, unquoted
);
1658 HRESULT WINAPI
CShellLink::AddDataBlock(void* pDataBlock
)
1664 HRESULT WINAPI
CShellLink::CopyDataBlock(DWORD dwSig
, void** ppDataBlock
)
1666 LPVOID block
= NULL
;
1667 HRESULT hr
= E_FAIL
;
1669 TRACE("%p %08x %p\n", this, dwSig
, ppDataBlock
);
1673 case EXP_DARWIN_ID_SIG
:
1676 block
= shelllink_build_darwinid(sComponent
, dwSig
);
1679 case EXP_SZ_LINK_SIG
:
1680 case NT_CONSOLE_PROPS_SIG
:
1681 case NT_FE_CONSOLE_PROPS_SIG
:
1682 case EXP_SPECIAL_FOLDER_SIG
:
1683 case EXP_SZ_ICON_SIG
:
1684 FIXME("valid but unhandled datablock %08x\n", dwSig
);
1687 ERR("unknown datablock %08x\n", dwSig
);
1689 *ppDataBlock
= block
;
1693 HRESULT WINAPI
CShellLink::RemoveDataBlock(DWORD dwSig
)
1699 HRESULT WINAPI
CShellLink::GetFlags(DWORD
*pdwFlags
)
1703 FIXME("%p %p\n", this, pdwFlags
);
1705 /* FIXME: add more */
1707 flags
|= SLDF_HAS_ARGS
;
1709 flags
|= SLDF_HAS_DARWINID
;
1711 flags
|= SLDF_HAS_ICONLOCATION
;
1712 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1714 flags
|= SLDF_HAS_LOGO3ID
;
1717 flags
|= SLDF_HAS_ID_LIST
;
1724 HRESULT WINAPI
CShellLink::SetFlags(DWORD dwFlags
)
1730 /**************************************************************************
1731 * CShellLink implementation of IShellExtInit::Initialize()
1733 * Loads the shelllink from the dataobject the shell is pointing to.
1735 HRESULT WINAPI
CShellLink::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pdtobj
, HKEY hkeyProgID
)
1737 TRACE("%p %p %p %p\n", this, pidlFolder
, pdtobj
, hkeyProgID
);
1743 format
.cfFormat
= CF_HDROP
;
1745 format
.dwAspect
= DVASPECT_CONTENT
;
1747 format
.tymed
= TYMED_HGLOBAL
;
1750 HRESULT hr
= pdtobj
->GetData(&format
, &stgm
);
1754 UINT count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, -1, NULL
, 0);
1757 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, NULL
, 0);
1759 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, count
* sizeof(WCHAR
));
1762 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, path
, count
);
1764 HeapFree(GetProcessHeap(), 0, path
);
1767 ReleaseStgMedium(&stgm
);
1772 HRESULT WINAPI
CShellLink::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
1776 TRACE("%p %p %u %u %u %u\n", this,
1777 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1780 return E_INVALIDARG
;
1783 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_VERB
, wszOpen
, _countof(wszOpen
)))
1787 memset(&mii
, 0, sizeof(mii
));
1788 mii
.cbSize
= sizeof (mii
);
1789 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
;
1790 mii
.dwTypeData
= wszOpen
;
1791 mii
.cch
= wcslen(mii
.dwTypeData
);
1792 mii
.wID
= idCmdFirst
+ id
++;
1793 mii
.fState
= MFS_DEFAULT
| MFS_ENABLED
;
1794 mii
.fType
= MFT_STRING
;
1795 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
1799 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, id
);
1803 shelllink_get_msi_component_path(LPWSTR component
)
1805 DWORD Result
, sz
= 0;
1807 Result
= CommandLineFromMsiDescriptor(component
, NULL
, &sz
);
1808 if (Result
!= ERROR_SUCCESS
)
1812 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sz
* sizeof(WCHAR
));
1813 Result
= CommandLineFromMsiDescriptor(component
, path
, &sz
);
1814 if (Result
!= ERROR_SUCCESS
)
1816 HeapFree(GetProcessHeap(), 0, path
);
1820 TRACE("returning %s\n", debugstr_w(path
));
1825 HRESULT WINAPI
CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1827 HWND hwnd
= NULL
; /* FIXME: get using interface set from IObjectWithSite */
1831 TRACE("%p %p\n", this, lpici
);
1833 if (lpici
->cbSize
< sizeof (CMINVOKECOMMANDINFO
))
1834 return E_INVALIDARG
;
1836 HRESULT hr
= Resolve(hwnd
, 0);
1839 TRACE("failed to resolve component with error 0x%08x", hr
);
1844 path
= shelllink_get_msi_component_path(sComponent
);
1849 path
= strdupW(sPath
);
1851 if ( lpici
->cbSize
== sizeof(CMINVOKECOMMANDINFOEX
) &&
1852 (lpici
->fMask
& CMIC_MASK_UNICODE
) )
1854 LPCMINVOKECOMMANDINFOEX iciex
= (LPCMINVOKECOMMANDINFOEX
)lpici
;
1858 len
+= wcslen(sArgs
);
1859 if (iciex
->lpParametersW
)
1860 len
+= wcslen(iciex
->lpParametersW
);
1862 args
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1865 wcscat(args
, sArgs
);
1866 if (iciex
->lpParametersW
)
1869 wcscat(args
, iciex
->lpParametersW
);
1872 else if (sArgs
!= NULL
)
1874 args
= strdupW(sArgs
);
1877 SHELLEXECUTEINFOW sei
;
1878 memset(&sei
, 0, sizeof sei
);
1879 sei
.cbSize
= sizeof sei
;
1880 sei
.fMask
= SEE_MASK_HASLINKNAME
| SEE_MASK_UNICODE
|
1881 (lpici
->fMask
& (SEE_MASK_NOASYNC
| SEE_MASK_ASYNCOK
| SEE_MASK_FLAG_NO_UI
));
1883 sei
.lpClass
= sLinkPath
;
1884 sei
.nShow
= iShowCmd
;
1885 sei
.lpDirectory
= sWorkDir
;
1886 sei
.lpParameters
= args
;
1887 sei
.lpVerb
= L
"open";
1889 // HACK for ShellExecuteExW
1890 if (wcsstr(sPath
, L
".cpl"))
1891 sei
.lpVerb
= L
"cplopen";
1893 if (ShellExecuteExW(&sei
))
1898 HeapFree(GetProcessHeap(), 0, args
);
1899 HeapFree(GetProcessHeap(), 0, path
);
1904 HRESULT WINAPI
CShellLink::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1906 FIXME("%p %lu %u %p %p %u\n", this, idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1911 INT_PTR CALLBACK
ExtendedShortcutProc(HWND hwndDlg
, UINT uMsg
,
1912 WPARAM wParam
, LPARAM lParam
)
1919 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1920 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1925 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1926 if (LOWORD(wParam
) == IDOK
)
1928 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1929 EndDialog(hwndDlg
, 1);
1931 EndDialog(hwndDlg
, 0);
1933 else if (LOWORD(wParam
) == IDCANCEL
)
1935 EndDialog(hwndDlg
, -1);
1937 else if (LOWORD(wParam
) == 14000)
1939 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1940 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_UNCHECKED
, 0);
1942 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1949 /**************************************************************************
1950 * SH_ShellLinkDlgProc
1952 * dialog proc of the shortcut property dialog
1955 INT_PTR CALLBACK
CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1957 CShellLink
*pThis
= reinterpret_cast<CShellLink
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
1963 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
1967 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg
, lParam
, ppsp
->lParam
);
1969 pThis
= reinterpret_cast<CShellLink
*>(ppsp
->lParam
);
1970 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pThis
);
1972 TRACE("sArgs: %S sComponent: %S sDescription: %S sIcoPath: %S sPath: %S sPathRel: %S sProduct: %S sWorkDir: %S\n", pThis
->sArgs
, pThis
->sComponent
, pThis
->sDescription
,
1973 pThis
->sIcoPath
, pThis
->sPath
, pThis
->sPathRel
, pThis
->sProduct
, pThis
->sWorkDir
);
1975 /* Get file information */
1977 if (!SHGetFileInfoW(pThis
->sLinkPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
1979 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis
->sLinkPath
, GetLastError());
1980 fi
.szTypeName
[0] = L
'\0';
1984 if (fi
.hIcon
) // TODO: destroy icon
1985 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
1987 ERR("ExtractIconW failed %ls %u\n", pThis
->sIcoPath
, pThis
->iIcoNdx
);
1991 SetDlgItemTextW(hwndDlg
, 14005, SH_GetTargetTypeByPath(pThis
->sPath
));
1993 /* target location */
1994 if (pThis
->sWorkDir
)
1995 SetDlgItemTextW(hwndDlg
, 14007, PathFindFileName(pThis
->sWorkDir
));
2000 WCHAR newpath
[2*MAX_PATH
] = L
"\0";
2001 if (wcschr(pThis
->sPath
, ' '))
2002 StringCchPrintfExW(newpath
, 2*MAX_PATH
, NULL
, NULL
, 0, L
"\"%ls\"", pThis
->sPath
);
2004 StringCchCopyExW(newpath
, 2*MAX_PATH
, pThis
->sPath
, NULL
, NULL
, 0);
2006 if (pThis
->sArgs
&& pThis
->sArgs
[0])
2008 StringCchCatW(newpath
, 2*MAX_PATH
, L
" ");
2009 StringCchCatW(newpath
, 2*MAX_PATH
, pThis
->sArgs
);
2011 SetDlgItemTextW(hwndDlg
, 14009, newpath
);
2014 if (pThis
->sWorkDir
)
2015 SetDlgItemTextW(hwndDlg
, 14011, pThis
->sWorkDir
);
2018 if (pThis
->sDescription
)
2019 SetDlgItemTextW(hwndDlg
, 14019, pThis
->sDescription
);
2025 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
2026 if (lppsn
->hdr
.code
== PSN_APPLY
)
2028 WCHAR wszBuf
[MAX_PATH
];
2029 /* set working directory */
2030 GetDlgItemTextW(hwndDlg
, 14011, wszBuf
, MAX_PATH
);
2031 pThis
->SetWorkingDirectory(wszBuf
);
2032 /* set link destination */
2033 GetDlgItemTextW(hwndDlg
, 14009, wszBuf
, MAX_PATH
);
2034 LPWSTR lpszArgs
= NULL
;
2035 LPWSTR unquoted
= strdupW(wszBuf
);
2036 StrTrimW(unquoted
, L
" ");
2037 if (!PathFileExistsW(unquoted
))
2039 lpszArgs
= PathGetArgsW(unquoted
);
2040 PathRemoveArgsW(unquoted
);
2041 StrTrimW(lpszArgs
, L
" ");
2043 if (unquoted
[0] == '"' && unquoted
[wcslen(unquoted
)-1] == '"')
2044 PathUnquoteSpacesW(unquoted
);
2047 WCHAR
*pwszExt
= PathFindExtensionW(unquoted
);
2048 if (!wcsicmp(pwszExt
, L
".lnk"))
2050 // FIXME load localized error msg
2051 MessageBoxW(hwndDlg
, L
"You cannot create a link to a shortcut", L
"Error", MB_ICONERROR
);
2052 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
2056 if (!PathFileExistsW(unquoted
))
2058 //FIXME load localized error msg
2059 MessageBoxW(hwndDlg
, L
"The specified file name in the target box is invalid", L
"Error", MB_ICONERROR
);
2060 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
2064 pThis
->SetPath(unquoted
);
2066 pThis
->SetArguments(lpszArgs
);
2068 pThis
->SetArguments(L
"\0");
2070 HeapFree(GetProcessHeap(), 0, unquoted
);
2072 TRACE("This %p sLinkPath %S\n", pThis
, pThis
->sLinkPath
);
2073 pThis
->Save(pThis
->sLinkPath
, TRUE
);
2074 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
); return TRUE
;
2079 switch(LOWORD(wParam
))
2084 /// open target directory
2089 WCHAR wszPath
[MAX_PATH
] = L
"";
2091 if (pThis
->sIcoPath
)
2092 wcscpy(wszPath
, pThis
->sIcoPath
);
2093 INT IconIndex
= pThis
->iIcoNdx
;
2094 if (PickIconDlg(hwndDlg
, wszPath
, MAX_PATH
, &IconIndex
))
2096 pThis
->SetIconLocation(wszPath
, IconIndex
);
2098 /// FIXME redraw icon
2105 INT_PTR result
= DialogBoxParamW(shell32_hInstance
, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES
), hwndDlg
, ExtendedShortcutProc
, (LPARAM
)pThis
->bRunAs
);
2106 if (result
== 1 || result
== 0)
2108 if (pThis
->bRunAs
!= result
)
2110 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2113 pThis
->bRunAs
= result
;
2118 if(HIWORD(wParam
) == EN_CHANGE
)
2119 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2127 /**************************************************************************
2128 * ShellLink_IShellPropSheetExt interface
2131 HRESULT WINAPI
CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
2133 HPROPSHEETPAGE hPage
= SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES
, SH_ShellLinkDlgProc
, (LPARAM
)this, NULL
);
2136 ERR("failed to create property sheet page\n");
2140 if (!pfnAddPage(hPage
, lParam
))
2146 HRESULT WINAPI
CShellLink::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
2148 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID
, pfnReplacePage
, lParam
);
2152 HRESULT WINAPI
CShellLink::SetSite(IUnknown
*punk
)
2154 TRACE("%p %p\n", this, punk
);
2161 HRESULT WINAPI
CShellLink::GetSite(REFIID iid
, void ** ppvSite
)
2163 TRACE("%p %s %p\n", this, debugstr_guid(&iid
), ppvSite
);
2168 return site
->QueryInterface(iid
, ppvSite
);
2171 HRESULT WINAPI
CShellLink::DragEnter(IDataObject
*pDataObject
,
2172 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
2174 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject
);
2175 LPCITEMIDLIST pidlLast
;
2178 HRESULT hr
= SHBindToParent(pPidl
, IID_PPV_ARG(IShellFolder
, &psf
), &pidlLast
);
2182 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_NULL_PPV_ARG(IDropTarget
, &mDropTarget
));
2185 hr
= mDropTarget
->DragEnter(pDataObject
, dwKeyState
, pt
, pdwEffect
);
2187 *pdwEffect
= DROPEFFECT_NONE
;
2192 *pdwEffect
= DROPEFFECT_NONE
;
2199 HRESULT WINAPI
CShellLink::DragOver(DWORD dwKeyState
, POINTL pt
,
2202 TRACE("(%p)\n", this);
2205 hr
= mDropTarget
->DragOver(dwKeyState
, pt
, pdwEffect
);
2209 HRESULT WINAPI
CShellLink::DragLeave()
2211 TRACE("(%p)\n", this);
2215 hr
= mDropTarget
->DragLeave();
2216 mDropTarget
->Release();
2222 HRESULT WINAPI
CShellLink::Drop(IDataObject
*pDataObject
,
2223 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
2225 TRACE("(%p)\n", this);
2228 hr
= mDropTarget
->Drop(pDataObject
, dwKeyState
, pt
, pdwEffect
);
2233 /**************************************************************************
2234 * IShellLink_ConstructFromFile
2236 HRESULT WINAPI
IShellLink_ConstructFromFile(IUnknown
*pUnkOuter
, REFIID riid
, LPCITEMIDLIST pidl
, LPVOID
*ppv
)
2238 CComPtr
<IUnknown
> psl
;
2240 HRESULT hr
= CShellLink::_CreatorClass::CreateInstance(NULL
, riid
, (void**)&psl
);
2244 CComPtr
<IPersistFile
> ppf
;
2248 hr
= psl
->QueryInterface(IID_PPV_ARG(IPersistFile
, &ppf
));
2252 WCHAR path
[MAX_PATH
];
2254 if (SHGetPathFromIDListW(pidl
, path
))
2255 hr
= ppf
->Load(path
, 0);
2260 *ppv
= psl
.Detach();