SHELL32: implement loading and saving of MSI advertised shortcut
info
Mike McCormack
mike at codeweavers.com
Fri Feb 25 06:40:48 CST 2005
ChangeLog:
* 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
-------------- next part --------------
Index: dlls/shell32/shelllink.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/shelllink.c,v
retrieving revision 1.83
diff -u -p -r1.83 shelllink.c
--- dlls/shell32/shelllink.c 17 Feb 2005 11:51:44 -0000 1.83
+++ dlls/shell32/shelllink.c 25 Feb 2005 12:34:22 -0000
@@ -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( HA
return p;
}
-
/**************************************************************************
* IPersistFile_QueryInterface
*/
@@ -251,8 +286,9 @@ static HRESULT WINAPI IPersistFile_fnLoa
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_fnSav
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( IStrea
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 );
+ chunk->size = size;
+ r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
+ if( FAILED( r ) || count != (size - sizeof(size)) )
+ {
+ HeapFree( GetProcessHeap(), 0, chunk );
+ return E_FAIL;
+ }
+
+ TRACE("Read %ld bytes\n",chunk->size);
+
+ *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 ) )
- goto end;
- if( count != (size - sizeof(size)) )
+ 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 )
{
- r = E_FAIL;
- goto end;
+ ERR("Ooops. This structure is different to expected...\n");
+ return E_FAIL;
}
- loc->dwTotalSize = size;
- TRACE("Read %ld bytes\n",count);
+ r = IStream_Read( stm, &buffer.magic, size, &count );
+ if( FAILED( r ) )
+ return r;
- /* FIXME: do something useful with it */
- HeapFree( GetProcessHeap(), 0, loc );
+ 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_fnL
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_fnL
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_fnL
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_fnL
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( IStre
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 )
{
- LOCATION_INFO loc;
- ULONG count;
+ 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;
- FIXME("writing empty location info\n");
+ TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
- memset( &loc, 0, sizeof(loc) );
- loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
+ /* 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;
- /* FIXME: fill this in */
+ return IStream_Write( stm, loc, total_size, &count );
+}
- return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
+static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
+{
+ ULONG count;
+ LINK_ADVERTISEINFO buffer;
+
+ TRACE("%p\n",stm);
+
+ 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 );
+
+ return IStream_Write( stm, &buffer, buffer.size, &count );
}
/************************************************************************
@@ -670,38 +950,39 @@ static HRESULT WINAPI IPersistStream_fnS
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_fnS
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_fnS
}
}
- 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_fnS
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_fnGetP
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_fnGetP
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;
-
- TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+ IShellLinkImpl *This = (IShellLinkImpl *)iface;
- *ppidl = ILClone(This->pPidl);
+ TRACE("(%p)->(ppidl=%p)\n",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_fnGetI
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_fnGetI
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_fnGetI
}
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_fnReso
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_fnGetP
{
_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_fnGetI
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_fnGetD
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_fnGetI
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_fnGetI
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_fnGetI
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_fnReso
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_fnSetP
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,
+ 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')
+ *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;
+ if (!This->sPath)
+ return E_OUTOFMEMORY;
- lstrcpyW(This->sPath, buffer);
+ lstrcpyW(This->sPath, buffer);
+ }
This->bDirty = TRUE;
return hr;
Index: dlls/shell32/tests/shelllink.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/tests/shelllink.c,v
retrieving revision 1.3
diff -u -p -r1.3 shelllink.c
--- dlls/shell32/tests/shelllink.c 16 Feb 2005 16:27:42 -0000 1.3
+++ dlls/shell32/tests/shelllink.c 25 Feb 2005 12:34:22 -0000
@@ -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 W
}
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="";
More information about the wine-patches
mailing list