- 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

@ -2,6 +2,7 @@
* *
* Copyright 1997 Marcus Meissner * Copyright 1997 Marcus Meissner
* Copyright 1998 Juergen Schmied * Copyright 1998 Juergen Schmied
* Copyright 2005 Mike McCormack
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -21,6 +22,14 @@
* Nearly complete informations about the binary formats * Nearly complete informations about the binary formats
* of .lnk files available at http://www.wotsit.org * 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" #include "config.h"
@ -58,19 +67,28 @@
#include "shlguid.h" #include "shlguid.h"
#include "shlwapi.h" #include "shlwapi.h"
#include "initguid.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell); 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 */ /* link file formats */
/* flag1: lnk elements: simple link has 0x0B */ /* flag1: lnk elements: simple link has 0x0B */
#define SCF_PIDL 1 #define SCF_PIDL 1
#define SCF_NORMAL 2 #define SCF_LOCATION 2
#define SCF_DESCRIPTION 4 #define SCF_DESCRIPTION 4
#define SCF_RELATIVE 8 #define SCF_RELATIVE 8
#define SCF_WORKDIR 0x10 #define SCF_WORKDIR 0x10
#define SCF_ARGS 0x20 #define SCF_ARGS 0x20
#define SCF_CUSTOMICON 0x40 #define SCF_CUSTOMICON 0x40
#define SCF_UNICODE 0x80 #define SCF_UNICODE 0x80
#define SCF_PRODUCT 0x800
#define SCF_COMPONENT 0x1000
#include "pshpack1.h" #include "pshpack1.h"
@ -113,6 +131,21 @@ typedef struct _LOCAL_VOLUME_INFO
DWORD dwVolLabelOfs; DWORD dwVolLabelOfs;
} LOCAL_VOLUME_INFO; } 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" #include "poppack.h"
static IShellLinkAVtbl slvt; static IShellLinkAVtbl slvt;
@ -146,6 +179,9 @@ typedef struct
LPWSTR sWorkDir; LPWSTR sWorkDir;
LPWSTR sDescription; LPWSTR sDescription;
LPWSTR sPathRel; LPWSTR sPathRel;
LPWSTR sProduct;
LPWSTR sComponent;
volume_info volume;
BOOL bDirty; BOOL bDirty;
} IShellLinkImpl; } IShellLinkImpl;
@ -172,7 +208,6 @@ inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
return p; return p;
} }
/************************************************************************** /**************************************************************************
* IPersistFile_QueryInterface * IPersistFile_QueryInterface
*/ */
@ -251,7 +286,8 @@ static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFile
static BOOL StartLinkProcessor( LPCOLESTR szLink ) 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', 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 }; ' ','-','r',' ','"','%','s','"',0 };
LONG len; LONG len;
LPWSTR buffer; LPWSTR buffer;
@ -290,7 +326,7 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile
TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName)); TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
if (!pszFileName || !This->sPath) if (!pszFileName)
return E_FAIL; return E_FAIL;
r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm); 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; return S_OK;
} }
static HRESULT Stream_LoadLocation( IStream* stm ) static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
{ {
DWORD size; DWORD size;
ULONG count; ULONG count;
HRESULT r; HRESULT r;
LOCATION_INFO *loc; struct sized_chunk {
DWORD size;
unsigned char data[1];
} *chunk;
TRACE("%p\n",stm); TRACE("%p\n",stm);
r = IStream_Read( stm, &size, sizeof(size), &count ); r = IStream_Read( stm, &size, sizeof(size), &count );
if( FAILED( r ) ) if( FAILED( r ) || count != sizeof(size) )
return r;
if( count != sizeof(loc->dwTotalSize) )
return E_FAIL; return E_FAIL;
loc = HeapAlloc( GetProcessHeap(), 0, size ); chunk = HeapAlloc( GetProcessHeap(), 0, size );
if( ! loc ) if( !chunk )
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count ); chunk->size = size;
if( FAILED( r ) ) r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
goto end; if( FAILED( r ) || count != (size - sizeof(size)) )
if( count != (size - sizeof(size)) )
{ {
r = E_FAIL; HeapFree( GetProcessHeap(), 0, chunk );
goto end; 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 */ *data = (LPVOID) chunk;
HeapFree( GetProcessHeap(), 0, loc );
return S_OK; return S_OK;
end: }
HeapFree( GetProcessHeap(), 0, loc );
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; 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;
} }
/************************************************************************ /************************************************************************
@ -519,12 +686,12 @@ static HRESULT WINAPI IPersistStream_fnLoad(
LINK_HEADER hdr; LINK_HEADER hdr;
ULONG dwBytesRead; ULONG dwBytesRead;
BOOL unicode; BOOL unicode;
WCHAR sTemp[MAX_PATH];
HRESULT r; HRESULT r;
DWORD zero;
_ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface); _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
TRACE("(%p)(%p)\n", This, stm); TRACE("%p %p\n", This, stm);
if( !stm ) if( !stm )
return STG_E_INVALIDPOINTER; return STG_E_INVALIDPOINTER;
@ -541,30 +708,61 @@ static HRESULT WINAPI IPersistStream_fnLoad(
if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) ) if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
return E_FAIL; return E_FAIL;
/* if( hdr.dwFlags & SCF_PIDL ) */ /* FIXME: seems to always have a PIDL */ /* free all the old stuff */
{ ILFree(This->pPidl);
r = ILLoadFromStream( stm, &This->pPidl ); This->pPidl = NULL;
if( FAILED( r ) ) memset( &This->volume, 0, sizeof This->volume );
return r; 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->wHotKey = (WORD)hdr.wHotKey;
This->iIcoNdx = hdr.nIcon; This->iIcoNdx = hdr.nIcon;
FileTimeToSystemTime (&hdr.Time1, &This->time1); FileTimeToSystemTime (&hdr.Time1, &This->time1);
FileTimeToSystemTime (&hdr.Time2, &This->time2); FileTimeToSystemTime (&hdr.Time2, &This->time2);
FileTimeToSystemTime (&hdr.Time3, &This->time3); FileTimeToSystemTime (&hdr.Time3, &This->time3);
#if 1 if (TRACE_ON(shell))
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256); {
TRACE("-- time1: %s\n", debugstr_w(sTemp) ); WCHAR sTemp[MAX_PATH];
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256); GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
TRACE("-- time1: %s\n", debugstr_w(sTemp) ); NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
TRACE("-- time1: %s\n", debugstr_w(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); pdump(This->pPidl);
#endif
if( hdr.dwFlags & SCF_NORMAL ) /* load the location information */
r = Stream_LoadLocation( stm ); if( hdr.dwFlags & SCF_LOCATION )
r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
if( FAILED( r ) ) if( FAILED( r ) )
goto end; goto end;
unicode = hdr.dwFlags & SCF_UNICODE; unicode = hdr.dwFlags & SCF_UNICODE;
if( hdr.dwFlags & SCF_DESCRIPTION ) if( hdr.dwFlags & SCF_DESCRIPTION )
{ {
@ -606,6 +804,26 @@ static HRESULT WINAPI IPersistStream_fnLoad(
if( FAILED( r ) ) if( FAILED( r ) )
goto end; 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"); TRACE("OK\n");
pdump (This->pPidl); pdump (This->pPidl);
@ -640,19 +858,81 @@ static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
return S_OK; 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; ULONG count;
LINK_ADVERTISEINFO buffer;
FIXME("writing empty location info\n"); TRACE("%p\n",stm);
memset( &loc, 0, sizeof(loc) ); memset( &buffer, 0, sizeof buffer );
loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize); 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 );
/* FIXME: fill this in */ return IStream_Write( stm, &buffer, buffer.size, &count );
return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
} }
/************************************************************************ /************************************************************************
@ -670,17 +950,19 @@ static HRESULT WINAPI IPersistStream_fnSave(
LINK_HEADER header; LINK_HEADER header;
WCHAR exePath[MAX_PATH]; WCHAR exePath[MAX_PATH];
ULONG count; ULONG count;
DWORD zero;
HRESULT r; HRESULT r;
_ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface); _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
TRACE("(%p) %p %x\n", This, stm, fClearDirty); TRACE("%p %p %x\n", This, stm, fClearDirty);
*exePath = '\0'; *exePath = '\0';
if (This->sPath) 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 * 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 * so if the executable does not exist the just trust the path they
@ -689,19 +971,18 @@ static HRESULT WINAPI IPersistStream_fnSave(
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)); memset(&header, 0, sizeof(header));
header.dwSize = sizeof(header); header.dwSize = sizeof(header);
header.fStartup = This->iShowCmd;
memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) ); memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
header.wHotKey = This->wHotKey; header.wHotKey = This->wHotKey;
header.nIcon = This->iIcoNdx; header.nIcon = This->iIcoNdx;
header.dwFlags = SCF_UNICODE; /* strings are in unicode */ header.dwFlags = SCF_UNICODE; /* strings are in unicode */
header.dwFlags |= SCF_NORMAL; /* how do we determine this ? */
if( This->pPidl ) if( This->pPidl )
header.dwFlags |= SCF_PIDL; header.dwFlags |= SCF_PIDL;
if( This->sPath )
header.dwFlags |= SCF_LOCATION;
if( This->sDescription ) if( This->sDescription )
header.dwFlags |= SCF_DESCRIPTION; header.dwFlags |= SCF_DESCRIPTION;
if( This->sWorkDir ) if( This->sWorkDir )
@ -710,6 +991,10 @@ static HRESULT WINAPI IPersistStream_fnSave(
header.dwFlags |= SCF_ARGS; header.dwFlags |= SCF_ARGS;
if( This->sIcoPath ) if( This->sIcoPath )
header.dwFlags |= SCF_CUSTOMICON; 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->time1, &header.Time1 );
SystemTimeToFileTime ( &This->time2, &header.Time2 ); 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 ) if( This->sDescription )
r = Stream_WriteString( stm, This->sDescription ); r = Stream_WriteString( stm, This->sDescription );
@ -754,6 +1039,16 @@ static HRESULT WINAPI IPersistStream_fnSave(
if( This->sIcoPath ) if( This->sIcoPath )
r = Stream_WriteString( stm, 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; return S_OK;
} }
@ -828,13 +1123,9 @@ HRESULT WINAPI IShellLink_Constructor (
static BOOL SHELL_ExistsFileW(LPCWSTR path) static BOOL SHELL_ExistsFileW(LPCWSTR path)
{ {
HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
if (hfile != INVALID_HANDLE_VALUE) {
CloseHandle(hfile);
return TRUE;
} else
return FALSE; return FALSE;
return TRUE;
} }
/************************************************************************** /**************************************************************************
@ -1017,6 +1308,9 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n", TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath)); This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
if (This->sComponent || This->sProduct)
return S_FALSE;
if (cchMaxPath) if (cchMaxPath)
pszFile[0] = 0; pszFile[0] = 0;
if (This->sPath) if (This->sPath)
@ -1025,7 +1319,7 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This); 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) static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
@ -1034,9 +1328,7 @@ static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST
TRACE("(%p)->(ppidl=%p)\n",This, ppidl); TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
*ppidl = ILClone(This->pPidl); return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
return NOERROR;
} }
static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl) 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); TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
if (cchIconPath)
pszIconPath[0] = 0; pszIconPath[0] = 0;
if (This->sIcoPath) {
WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
*piIcon = This->iIcoNdx; *piIcon = This->iIcoNdx;
if (This->sIcoPath)
{
WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
return S_OK; return S_OK;
} }
if (This->pPidl || This->sPath) { if (This->pPidl || This->sPath)
{
IShellFolder* pdsk; IShellFolder* pdsk;
HRESULT hr = SHGetDesktopFolder(&pdsk); HRESULT hr = SHGetDesktopFolder(&pdsk);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
{
/* first look for an icon using the PIDL (if present) */ /* first look for an icon using the PIDL (if present) */
if (This->pPidl) if (This->pPidl)
hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon); 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; hr = E_FAIL;
/* if we couldn't find an icon yet, look for it using the file system path */ /* 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; LPITEMIDLIST pidl;
hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL); 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; return hr;
} else }
return E_FAIL; return S_OK;
} }
static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon) 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) static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
{ {
HRESULT r;
LPWSTR str;
IShellLinkImpl *This = (IShellLinkImpl *)iface; IShellLinkImpl *This = (IShellLinkImpl *)iface;
char buffer[MAX_PATH];
LPSTR fname;
HRESULT hr = S_OK;
TRACE("(%p)->(path=%s)\n",This, pszFile); TRACE("(%p)->(path=%s)\n",This, pszFile);
if(*pszFile == '\0') str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
*buffer = '\0'; if( !str )
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 )
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
This->bDirty = TRUE; r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
HeapFree( GetProcessHeap(), 0, str );
return hr; return r;
} }
/************************************************************************** /**************************************************************************
@ -1426,8 +1713,11 @@ static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,
{ {
_ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface); _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n", TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
This, pszFile, cchMaxPath, pfd, fFlags); This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
if (This->sComponent || This->sProduct)
return S_FALSE;
if (cchMaxPath) if (cchMaxPath)
pszFile[0] = 0; pszFile[0] = 0;
@ -1436,7 +1726,7 @@ static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,
if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This); 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) 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); TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
if( This->pPidl) if (!This->pPidl)
return S_FALSE;
*ppidl = ILClone(This->pPidl); *ppidl = ILClone(This->pPidl);
else
*ppidl = NULL;
return S_OK; return S_OK;
} }
@ -1476,7 +1764,6 @@ static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR p
TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName); TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
if( cchMaxName )
pszName[0] = 0; pszName[0] = 0;
if( This->sDescription ) if( This->sDescription )
lstrcpynW( pszName, This->sDescription, cchMaxName ); 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); TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
if (cchIconPath)
pszIconPath[0] = 0; pszIconPath[0] = 0;
if (This->sIcoPath) {
lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
*piIcon = This->iIcoNdx; *piIcon = This->iIcoNdx;
if (This->sIcoPath)
{
lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
return S_OK; return S_OK;
} }
if (This->pPidl || This->sPath) { if (This->pPidl || This->sPath)
{
IShellFolder* pdsk; IShellFolder* pdsk;
HRESULT hr = SHGetDesktopFolder(&pdsk); HRESULT hr = SHGetDesktopFolder(&pdsk);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
{
/* first look for an icon using the PIDL (if present) */ /* first look for an icon using the PIDL (if present) */
if (This->pPidl) if (This->pPidl)
hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon); hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
@ -1659,12 +1948,14 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
hr = E_FAIL; hr = E_FAIL;
/* if we couldn't find an icon yet, look for it using the file system path */ /* 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; LPITEMIDLIST pidl;
hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL); 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); hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
SHFree(pidl); SHFree(pidl);
@ -1673,10 +1964,9 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
IShellFolder_Release(pdsk); IShellFolder_Release(pdsk);
} }
return hr; return hr;
} else }
return E_FAIL; return S_OK;
} }
static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon) 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; 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) static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
{ {
_ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface); _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
@ -1765,6 +2149,18 @@ static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile
TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile)); TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
HeapFree(GetProcessHeap(), 0, This->sPath);
This->sPath = NULL;
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') if (*pszFile == '\0')
*buffer = '\0'; *buffer = '\0';
else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname)) else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
@ -1772,13 +2168,16 @@ static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile
else if(!PathFileExistsW(buffer)) else if(!PathFileExistsW(buffer))
hr = S_FALSE; hr = S_FALSE;
HeapFree(GetProcessHeap(), 0, This->sPath); This->pPidl = SHSimpleIDListFromPathW(pszFile);
ShellLink_GetVolumeInfo(buffer, &This->volume);
This->sPath = HeapAlloc( GetProcessHeap(), 0, This->sPath = HeapAlloc( GetProcessHeap(), 0,
(lstrlenW( buffer )+1) * sizeof (WCHAR) ); (lstrlenW( buffer )+1) * sizeof (WCHAR) );
if (!This->sPath) if (!This->sPath)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
lstrcpyW(This->sPath, buffer); lstrcpyW(This->sPath, buffer);
}
This->bDirty = TRUE; This->bDirty = TRUE;
return hr; return hr;

View File

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