- implement loading and saving of MSI advertised shortcut info

- make more test cases pass
- read and write the location block
- improve the binary compatibility of lnk files
This commit is contained in:
Mike McCormack 2005-02-25 16:19:57 +00:00 committed by Alexandre Julliard
parent 73227aa568
commit 64c3208e10
2 changed files with 538 additions and 143 deletions

View File

@ -1,7 +1,8 @@
/*
*
* Copyright 1997 Marcus Meissner
* Copyright 1998 Juergen Schmied
* Copyright 1997 Marcus Meissner
* Copyright 1998 Juergen Schmied
* Copyright 2005 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -21,6 +22,14 @@
* Nearly complete informations about the binary formats
* of .lnk files available at http://www.wotsit.org
*
* You can use winedump to examine the contents of a link file:
* winedump lnk sc.lnk
*
* MSI advertised shortcuts are totally undocumented. They provide an
* icon for a program that is not yet installed, and invoke MSI to
* install the program when the shortcut is clicked on. They are
* created by passing a special string to SetPath, and the information
* in that string is parsed an stored.
*/
#include "config.h"
@ -58,19 +67,28 @@
#include "shlguid.h"
#include "shlwapi.h"
#include "initguid.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
DEFINE_GUID( SHELL32_AdvtShortcutProduct,
0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
DEFINE_GUID( SHELL32_AdvtShortcutComponent,
0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
/* link file formats */
/* flag1: lnk elements: simple link has 0x0B */
#define SCF_PIDL 1
#define SCF_NORMAL 2
#define SCF_PIDL 1
#define SCF_LOCATION 2
#define SCF_DESCRIPTION 4
#define SCF_RELATIVE 8
#define SCF_WORKDIR 0x10
#define SCF_ARGS 0x20
#define SCF_CUSTOMICON 0x40
#define SCF_UNICODE 0x80
#define SCF_PRODUCT 0x800
#define SCF_COMPONENT 0x1000
#include "pshpack1.h"
@ -113,6 +131,21 @@ typedef struct _LOCAL_VOLUME_INFO
DWORD dwVolLabelOfs;
} LOCAL_VOLUME_INFO;
typedef struct tagLINK_ADVERTISEINFO
{
DWORD size;
DWORD magic;
CHAR bufA[MAX_PATH];
WCHAR bufW[MAX_PATH];
} LINK_ADVERTISEINFO;
typedef struct volume_info_t
{
DWORD type;
DWORD serial;
WCHAR label[12]; /* assume 8.3 */
} volume_info;
#include "poppack.h"
static IShellLinkAVtbl slvt;
@ -146,6 +179,9 @@ typedef struct
LPWSTR sWorkDir;
LPWSTR sDescription;
LPWSTR sPathRel;
LPWSTR sProduct;
LPWSTR sComponent;
volume_info volume;
BOOL bDirty;
} IShellLinkImpl;
@ -172,7 +208,6 @@ inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
return p;
}
/**************************************************************************
* IPersistFile_QueryInterface
*/
@ -251,8 +286,9 @@ static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFile
static BOOL StartLinkProcessor( LPCOLESTR szLink )
{
static const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
' ','-','r',' ','"','%','s','"',0 };
static const WCHAR szFormat[] = {
'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
' ','-','r',' ','"','%','s','"',0 };
LONG len;
LPWSTR buffer;
STARTUPINFOW si;
@ -290,7 +326,7 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile
TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
if (!pszFileName || !This->sPath)
if (!pszFileName)
return E_FAIL;
r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
@ -469,44 +505,175 @@ static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
return S_OK;
}
static HRESULT Stream_LoadLocation( IStream* stm )
static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
{
DWORD size;
ULONG count;
HRESULT r;
LOCATION_INFO *loc;
struct sized_chunk {
DWORD size;
unsigned char data[1];
} *chunk;
TRACE("%p\n",stm);
r = IStream_Read( stm, &size, sizeof(size), &count );
if( FAILED( r ) )
return r;
if( count != sizeof(loc->dwTotalSize) )
if( FAILED( r ) || count != sizeof(size) )
return E_FAIL;
loc = HeapAlloc( GetProcessHeap(), 0, size );
if( ! loc )
chunk = HeapAlloc( GetProcessHeap(), 0, size );
if( !chunk )
return E_OUTOFMEMORY;
r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count );
if( FAILED( r ) )
goto end;
if( count != (size - sizeof(size)) )
chunk->size = size;
r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
if( FAILED( r ) || count != (size - sizeof(size)) )
{
r = E_FAIL;
goto end;
HeapFree( GetProcessHeap(), 0, chunk );
return E_FAIL;
}
loc->dwTotalSize = size;
TRACE("Read %ld bytes\n",count);
TRACE("Read %ld bytes\n",chunk->size);
/* FIXME: do something useful with it */
HeapFree( GetProcessHeap(), 0, loc );
*data = (LPVOID) chunk;
return S_OK;
}
static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
{
const int label_sz = sizeof volume->label/sizeof volume->label[0];
LPSTR label;
int len;
volume->serial = vol->dwVolSerial;
volume->type = vol->dwType;
if( !vol->dwVolLabelOfs )
return FALSE;
if( vol->dwSize <= vol->dwVolLabelOfs )
return FALSE;
len = vol->dwSize - vol->dwVolLabelOfs;
label = (LPSTR) vol;
label += vol->dwVolLabelOfs;
MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
return TRUE;
}
static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
{
int len = 0, wlen;
LPWSTR path;
while( p[len] && (len < maxlen) )
len++;
wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
path[wlen] = 0;
return path;
}
static HRESULT Stream_LoadLocation( IStream *stm,
volume_info *volume, LPWSTR *path )
{
unsigned char *p = NULL;
LOCATION_INFO *loc;
HRESULT r;
int n;
r = Stream_ReadChunk( stm, (LPVOID*) &p );
if( FAILED(r) )
return r;
loc = (LOCATION_INFO*) p;
if (loc->dwTotalSize < sizeof(LOCATION_INFO))
{
HeapFree( GetProcessHeap(), 0, p );
return E_FAIL;
}
/* if there's valid local volume information, load it */
if( loc->dwVolTableOfs &&
((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
{
LOCAL_VOLUME_INFO *volume_info;
volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
Stream_LoadVolume( volume_info, volume );
}
/* if there's a local path, load it */
n = loc->dwLocalPathOfs;
if( n && (n < loc->dwTotalSize) )
*path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
TRACE("type %ld serial %08lx name %s path %s\n", volume->type,
volume->serial, debugstr_w(volume->label), debugstr_w(*path));
HeapFree( GetProcessHeap(), 0, p );
return S_OK;
}
/*
* The format of the advertised shortcut info seems to be:
*
* Offset Description
* ------ -----------
*
* 0 Length of the block (4 bytes, usually 0x314)
* 4 tag (dword)
* 8 string data in ASCII
* 8+0x104 string data in UNICODE
*
* In the original Win32 implementation the buffers are not initialized
* to zero, so data trailing the string is random garbage.
*/
static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
{
DWORD size;
ULONG count;
HRESULT r;
LINK_ADVERTISEINFO buffer;
TRACE("%p\n",stm);
r = IStream_Read( stm, &buffer.size, sizeof (DWORD), &count );
if( FAILED( r ) )
return r;
/* make sure that we read the size of the structure even on error */
size = sizeof buffer - sizeof (DWORD);
if( buffer.size != sizeof buffer )
{
ERR("Ooops. This structure is different to expected...\n");
return E_FAIL;
}
r = IStream_Read( stm, &buffer.magic, size, &count );
if( FAILED( r ) )
return r;
if( count != size )
return E_FAIL;
TRACE("magic %08lx string = %s\n", buffer.magic, debugstr_w(buffer.bufW));
if( (buffer.magic&0xffff0000) != 0xa0000000 )
{
ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.magic);
return E_FAIL;
}
*str = HeapAlloc( GetProcessHeap(), 0,
(strlenW(buffer.bufW)+1) * sizeof(WCHAR) );
strcpyW( *str, buffer.bufW );
return S_OK;
end:
HeapFree( GetProcessHeap(), 0, loc );
return r;
}
/************************************************************************
@ -519,12 +686,12 @@ static HRESULT WINAPI IPersistStream_fnLoad(
LINK_HEADER hdr;
ULONG dwBytesRead;
BOOL unicode;
WCHAR sTemp[MAX_PATH];
HRESULT r;
DWORD zero;
_ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
TRACE("(%p)(%p)\n", This, stm);
TRACE("%p %p\n", This, stm);
if( !stm )
return STG_E_INVALIDPOINTER;
@ -541,30 +708,61 @@ static HRESULT WINAPI IPersistStream_fnLoad(
if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
return E_FAIL;
/* if( hdr.dwFlags & SCF_PIDL ) */ /* FIXME: seems to always have a PIDL */
{
r = ILLoadFromStream( stm, &This->pPidl );
if( FAILED( r ) )
return r;
}
/* free all the old stuff */
ILFree(This->pPidl);
This->pPidl = NULL;
memset( &This->volume, 0, sizeof This->volume );
HeapFree(GetProcessHeap(), 0, This->sPath);
This->sPath = NULL;
HeapFree(GetProcessHeap(), 0, This->sDescription);
This->sDescription = NULL;
HeapFree(GetProcessHeap(), 0, This->sPathRel);
This->sPathRel = NULL;
HeapFree(GetProcessHeap(), 0, This->sWorkDir);
This->sWorkDir = NULL;
HeapFree(GetProcessHeap(), 0, This->sArgs);
This->sArgs = NULL;
HeapFree(GetProcessHeap(), 0, This->sIcoPath);
This->sIcoPath = NULL;
HeapFree(GetProcessHeap(), 0, This->sProduct);
This->sProduct = NULL;
HeapFree(GetProcessHeap(), 0, This->sComponent);
This->sComponent = NULL;
This->wHotKey = (WORD)hdr.wHotKey;
This->iIcoNdx = hdr.nIcon;
FileTimeToSystemTime (&hdr.Time1, &This->time1);
FileTimeToSystemTime (&hdr.Time2, &This->time2);
FileTimeToSystemTime (&hdr.Time3, &This->time3);
#if 1
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
TRACE("-- time1: %s\n", debugstr_w(sTemp) );
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
TRACE("-- time1: %s\n", debugstr_w(sTemp) );
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
TRACE("-- time1: %s\n", debugstr_w(sTemp) );
pdump (This->pPidl);
#endif
if( hdr.dwFlags & SCF_NORMAL )
r = Stream_LoadLocation( stm );
if (TRACE_ON(shell))
{
WCHAR sTemp[MAX_PATH];
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
TRACE("-- time1: %s\n", debugstr_w(sTemp) );
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
TRACE("-- time2: %s\n", debugstr_w(sTemp) );
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
TRACE("-- time3: %s\n", debugstr_w(sTemp) );
}
/* load all the new stuff */
if( hdr.dwFlags & SCF_PIDL )
{
r = ILLoadFromStream( stm, &This->pPidl );
if( FAILED( r ) )
return r;
}
pdump(This->pPidl);
/* load the location information */
if( hdr.dwFlags & SCF_LOCATION )
r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
if( FAILED( r ) )
goto end;
unicode = hdr.dwFlags & SCF_UNICODE;
if( hdr.dwFlags & SCF_DESCRIPTION )
{
@ -583,7 +781,7 @@ static HRESULT WINAPI IPersistStream_fnLoad(
goto end;
if( hdr.dwFlags & SCF_WORKDIR )
{
{
r = Stream_LoadString( stm, unicode, &This->sWorkDir );
TRACE("Working Dir -> %s\n",debugstr_w(This->sWorkDir));
}
@ -606,6 +804,26 @@ static HRESULT WINAPI IPersistStream_fnLoad(
if( FAILED( r ) )
goto end;
if( hdr.dwFlags & SCF_PRODUCT )
{
r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
TRACE("Product -> %s\n",debugstr_w(This->sProduct));
}
if( FAILED( r ) )
goto end;
if( hdr.dwFlags & SCF_COMPONENT )
{
r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
TRACE("Component -> %s\n",debugstr_w(This->sComponent));
}
if( FAILED( r ) )
goto end;
r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
ERR("Last word was not zero\n");
TRACE("OK\n");
pdump (This->pPidl);
@ -640,19 +858,81 @@ static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
return S_OK;
}
static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
/************************************************************************
* Stream_WriteLocationInfo
*
* Writes the location info to a stream
*
* FIXME: One day we might want to write the network volume information
* and the final path.
* Figure out how Windows deals with unicode pathes here.
*/
static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
volume_info *volume )
{
DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
LOCAL_VOLUME_INFO *vol;
LOCATION_INFO *loc;
LPSTR szLabel, szPath, szFinalPath;
ULONG count = 0;
TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
/* figure out the size of everything */
label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
NULL, 0, NULL, NULL );
path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
NULL, 0, NULL, NULL );
volume_info_size = sizeof *vol + label_size;
final_path_size = 1;
total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
/* create pointers to everything */
loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
vol = (LOCAL_VOLUME_INFO*) &loc[1];
szLabel = (LPSTR) &vol[1];
szPath = &szLabel[label_size];
szFinalPath = &szPath[path_size];
/* fill in the location information header */
loc->dwTotalSize = total_size;
loc->dwHeaderSize = sizeof (*loc);
loc->dwFlags = 1;
loc->dwVolTableOfs = sizeof (*loc);
loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
loc->dwNetworkVolTableOfs = 0;
loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
/* fill in the volume information */
vol->dwSize = volume_info_size;
vol->dwType = volume->type;
vol->dwVolSerial = volume->serial;
vol->dwVolLabelOfs = sizeof (*vol);
/* copy in the strings */
WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
szLabel, label_size, NULL, NULL );
WideCharToMultiByte( CP_ACP, 0, path, -1,
szPath, path_size, NULL, NULL );
szFinalPath[0] = 0;
return IStream_Write( stm, loc, total_size, &count );
}
static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
{
LOCATION_INFO loc;
ULONG count;
LINK_ADVERTISEINFO buffer;
TRACE("%p\n",stm);
FIXME("writing empty location info\n");
memset( &buffer, 0, sizeof buffer );
buffer.size = sizeof buffer;
buffer.magic = magic;
strncpyW( buffer.bufW, string, MAX_PATH );
WideCharToMultiByte(CP_ACP, 0, string, -1, buffer.bufA, MAX_PATH, NULL, NULL );
memset( &loc, 0, sizeof(loc) );
loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
/* FIXME: fill this in */
return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
return IStream_Write( stm, &buffer, buffer.size, &count );
}
/************************************************************************
@ -670,38 +950,39 @@ static HRESULT WINAPI IPersistStream_fnSave(
LINK_HEADER header;
WCHAR exePath[MAX_PATH];
ULONG count;
DWORD zero;
HRESULT r;
_ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
TRACE("(%p) %p %x\n", This, stm, fClearDirty);
TRACE("%p %p %x\n", This, stm, fClearDirty);
*exePath = '\0';
if (This->sPath)
{
SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL);
SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH,
NULL, NULL, NULL, NULL);
/*
* windows can create lnk files to executables that do not exist yet
* so if the executable does not exist the just trust the path they
* gave us
*/
if( !*exePath ) strcpyW(exePath,This->sPath);
if (!*exePath) strcpyW(exePath,This->sPath);
}
/* if there's no PIDL, generate one */
if( ! This->pPidl ) This->pPidl = ILCreateFromPathW(exePath);
memset(&header, 0, sizeof(header));
header.dwSize = sizeof(header);
header.fStartup = This->iShowCmd;
memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
header.wHotKey = This->wHotKey;
header.nIcon = This->iIcoNdx;
header.dwFlags = SCF_UNICODE; /* strings are in unicode */
header.dwFlags |= SCF_NORMAL; /* how do we determine this ? */
if( This->pPidl )
header.dwFlags |= SCF_PIDL;
if( This->sPath )
header.dwFlags |= SCF_LOCATION;
if( This->sDescription )
header.dwFlags |= SCF_DESCRIPTION;
if( This->sWorkDir )
@ -710,6 +991,10 @@ static HRESULT WINAPI IPersistStream_fnSave(
header.dwFlags |= SCF_ARGS;
if( This->sIcoPath )
header.dwFlags |= SCF_CUSTOMICON;
if( This->sProduct )
header.dwFlags |= SCF_PRODUCT;
if( This->sComponent )
header.dwFlags |= SCF_COMPONENT;
SystemTimeToFileTime ( &This->time1, &header.Time1 );
SystemTimeToFileTime ( &This->time2, &header.Time2 );
@ -736,9 +1021,9 @@ static HRESULT WINAPI IPersistStream_fnSave(
}
}
Stream_WriteLocationInfo( stm, exePath );
if( This->sPath )
Stream_WriteLocationInfo( stm, exePath, &This->volume );
TRACE("Description = %s\n", debugstr_w(This->sDescription));
if( This->sDescription )
r = Stream_WriteString( stm, This->sDescription );
@ -754,6 +1039,16 @@ static HRESULT WINAPI IPersistStream_fnSave(
if( This->sIcoPath )
r = Stream_WriteString( stm, This->sIcoPath );
if( This->sProduct )
r = Stream_WriteAdvertiseInfo( stm, This->sProduct, 0xa0000007 );
if( This->sComponent )
r = Stream_WriteAdvertiseInfo( stm, This->sComponent, 0xa0000006 );
/* the last field is a single zero dword */
zero = 0;
r = IStream_Write( stm, &zero, sizeof zero, &count );
return S_OK;
}
@ -828,13 +1123,9 @@ HRESULT WINAPI IShellLink_Constructor (
static BOOL SHELL_ExistsFileW(LPCWSTR path)
{
HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hfile != INVALID_HANDLE_VALUE) {
CloseHandle(hfile);
return TRUE;
} else
if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
return FALSE;
return TRUE;
}
/**************************************************************************
@ -1017,7 +1308,10 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
if( cchMaxPath )
if (This->sComponent || This->sProduct)
return S_FALSE;
if (cchMaxPath)
pszFile[0] = 0;
if (This->sPath)
WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
@ -1025,18 +1319,16 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
return NOERROR;
return S_OK;
}
static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
{
IShellLinkImpl *This = (IShellLinkImpl *)iface;
IShellLinkImpl *This = (IShellLinkImpl *)iface;
TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
*ppidl = ILClone(This->pPidl);
return NOERROR;
return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
}
static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
@ -1218,21 +1510,23 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
if (cchIconPath)
pszIconPath[0] = 0;
pszIconPath[0] = 0;
*piIcon = This->iIcoNdx;
if (This->sIcoPath) {
if (This->sIcoPath)
{
WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
*piIcon = This->iIcoNdx;
return S_OK;
}
if (This->pPidl || This->sPath) {
if (This->pPidl || This->sPath)
{
IShellFolder* pdsk;
HRESULT hr = SHGetDesktopFolder(&pdsk);
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr))
{
/* first look for an icon using the PIDL (if present) */
if (This->pPidl)
hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
@ -1240,7 +1534,8 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
hr = E_FAIL;
/* if we couldn't find an icon yet, look for it using the file system path */
if (FAILED(hr) && This->sPath) {
if (FAILED(hr) && This->sPath)
{
LPITEMIDLIST pidl;
hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
@ -1256,8 +1551,8 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
}
return hr;
} else
return E_FAIL;
}
return S_OK;
}
static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
@ -1333,28 +1628,20 @@ static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWOR
static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
{
HRESULT r;
LPWSTR str;
IShellLinkImpl *This = (IShellLinkImpl *)iface;
char buffer[MAX_PATH];
LPSTR fname;
HRESULT hr = S_OK;
TRACE("(%p)->(path=%s)\n",This, pszFile);
if(*pszFile == '\0')
*buffer = '\0';
else if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname))
return E_FAIL;
else if(!PathFileExistsA(buffer))
hr = S_FALSE;
HeapFree(GetProcessHeap(), 0, This->sPath);
This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer);
if( !This->sPath )
str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
if( !str )
return E_OUTOFMEMORY;
This->bDirty = TRUE;
r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
HeapFree( GetProcessHeap(), 0, str );
return hr;
return r;
}
/**************************************************************************
@ -1426,17 +1713,20 @@ static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,
{
_ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",
This, pszFile, cchMaxPath, pfd, fFlags);
TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
if( cchMaxPath )
if (This->sComponent || This->sProduct)
return S_FALSE;
if (cchMaxPath)
pszFile[0] = 0;
if( This->sPath )
if (This->sPath)
lstrcpynW( pszFile, This->sPath, cchMaxPath );
if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
return NOERROR;
return S_OK;
}
static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
@ -1445,11 +1735,9 @@ static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST
TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
if( This->pPidl)
*ppidl = ILClone( This->pPidl );
else
*ppidl = NULL;
if (!This->pPidl)
return S_FALSE;
*ppidl = ILClone(This->pPidl);
return S_OK;
}
@ -1476,8 +1764,7 @@ static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR p
TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
if( cchMaxName )
pszName[0] = 0;
pszName[0] = 0;
if( This->sDescription )
lstrcpynW( pszName, This->sDescription, cchMaxName );
@ -1637,21 +1924,23 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
if (cchIconPath)
pszIconPath[0] = 0;
pszIconPath[0] = 0;
*piIcon = This->iIcoNdx;
if (This->sIcoPath) {
if (This->sIcoPath)
{
lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
*piIcon = This->iIcoNdx;
return S_OK;
}
if (This->pPidl || This->sPath) {
if (This->pPidl || This->sPath)
{
IShellFolder* pdsk;
HRESULT hr = SHGetDesktopFolder(&pdsk);
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr))
{
/* first look for an icon using the PIDL (if present) */
if (This->pPidl)
hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
@ -1659,12 +1948,14 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
hr = E_FAIL;
/* if we couldn't find an icon yet, look for it using the file system path */
if (FAILED(hr) && This->sPath) {
if (FAILED(hr) && This->sPath)
{
LPITEMIDLIST pidl;
hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr))
{
hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
SHFree(pidl);
@ -1673,10 +1964,9 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
IShellFolder_Release(pdsk);
}
return hr;
} else
return E_FAIL;
}
return S_OK;
}
static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
@ -1756,6 +2046,100 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR
return hr;
}
static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
{
LPWSTR ret;
LPCWSTR p;
DWORD len;
p = strchrW( str, ':' );
if( !p )
return NULL;
len = p - str;
ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
if( !ret )
return ret;
memcpy( ret, str, sizeof(WCHAR)*len );
ret[len] = 0;
return ret;
}
static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
{
LPCWSTR szComponent = NULL, szProduct = NULL, p;
WCHAR szGuid[39];
HRESULT r;
GUID guid;
int len;
while( str[0] )
{
/* each segment must start with two colons */
if( str[0] != ':' || str[1] != ':' )
return E_FAIL;
/* the last segment is just two colons */
if( !str[2] )
break;
str += 2;
/* there must be a colon straight after a guid */
p = strchrW( str, ':' );
if( !p )
return E_FAIL;
len = p - str;
if( len != 38 )
return E_FAIL;
/* get the guid, and check it's validly formatted */
memcpy( szGuid, str, sizeof(WCHAR)*len );
szGuid[len] = 0;
r = CLSIDFromString( szGuid, &guid );
if( r != S_OK )
return r;
str = p + 1;
/* match it up to a guid that we care about */
if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
szComponent = str;
else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
szProduct = str;
else
return E_FAIL;
/* skip to the next field */
str = strchrW( str, ':' );
if( !str )
return E_FAIL;
}
/* we have to have at least one of these two for an advertised shortcut */
if( !szComponent && !szProduct )
return E_FAIL;
This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
TRACE("Component = %s\n", debugstr_w(This->sComponent));
TRACE("Product = %s\n", debugstr_w(This->sProduct));
return S_OK;
}
static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
{
const int label_sz = sizeof volume->label/sizeof volume->label[0];
WCHAR drive[4] = { path[0], ':', '\\', 0 };
BOOL r;
volume->type = GetDriveTypeW(drive);
r = GetVolumeInformationW(drive, volume->label, label_sz,
&volume->serial, NULL, NULL, NULL, 0);
TRACE("r = %d type %ld serial %08lx name %s\n", r,
volume->type, volume->serial, debugstr_w(volume->label));
return r;
}
static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
{
_ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
@ -1765,20 +2149,35 @@ static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile
TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
if (*pszFile == '\0')
*buffer = '\0';
else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
return E_FAIL;
else if(!PathFileExistsW(buffer))
hr = S_FALSE;
HeapFree(GetProcessHeap(), 0, This->sPath);
This->sPath = HeapAlloc( GetProcessHeap(), 0,
(lstrlenW( buffer )+1) * sizeof (WCHAR) );
if (!This->sPath)
return E_OUTOFMEMORY;
This->sPath = NULL;
lstrcpyW(This->sPath, buffer);
HeapFree(GetProcessHeap(), 0, This->sComponent);
This->sComponent = NULL;
if (This->pPidl)
ILFree(This->pPidl);
This->pPidl = NULL;
if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
{
if (*pszFile == '\0')
*buffer = '\0';
else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
return E_FAIL;
else if(!PathFileExistsW(buffer))
hr = S_FALSE;
This->pPidl = SHSimpleIDListFromPathW(pszFile);
ShellLink_GetVolumeInfo(buffer, &This->volume);
This->sPath = HeapAlloc( GetProcessHeap(), 0,
(lstrlenW( buffer )+1) * sizeof (WCHAR) );
if (!This->sPath)
return E_OUTOFMEMORY;
lstrcpyW(This->sPath, buffer);
}
This->bDirty = TRUE;
return hr;

View File

@ -234,9 +234,7 @@ static void test_get_set()
ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
}
ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
todo_wine {
ok(i==0, "GetIconLocation returned %d\n", i);
}
str="c:\\nonexistent\\file";
r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
@ -383,9 +381,7 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
}
r = IPersistFile_Load(pf, path, STGM_READ);
todo_wine {
lok(SUCCEEDED(r), "load failed (0x%08lx)\n", r);
}
IPersistFile_Release(pf);
if (!SUCCEEDED(r))
{
@ -473,7 +469,7 @@ static void test_load_save()
/* Save an empty .lnk file */
memset(&desc, 0, sizeof(desc));
create_lnk(lnkfile, &desc, 1);
create_lnk(lnkfile, &desc, 0);
/* It should come back as a bunch of empty strings */
desc.description="";