shelllink.c 70.9 KB
Newer Older
1 2
/*
 *
3 4 5
 *      Copyright 1997  Marcus Meissner
 *      Copyright 1998  Juergen Schmied
 *      Copyright 2005  Mike McCormack
6
 *
7 8 9 10 11 12 13 14 15 16 17 18
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 *
 * NOTES
22
 *   Nearly complete information about the binary formats
23
 *   of .lnk files available at http://www.wotsit.org
24
 *
25 26 27 28 29 30 31 32
 *  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.
33 34
 */

35
#define COBJMACROS
36
#define NONAMELESSUNION
37

38
#include "wine/debug.h"
39
#include "winerror.h"
40
#include "windef.h"
41 42
#include "winbase.h"
#include "winnls.h"
43
#include "winreg.h"
44

45 46
#include "winuser.h"
#include "wingdi.h"
47
#include "shlobj.h"
48
#include "undocshell.h"
49

50
#include "pidl.h"
51
#include "shell32_main.h"
52
#include "shlguid.h"
53
#include "shlwapi.h"
54
#include "msi.h"
55
#include "appmgmt.h"
56

57 58
#include "initguid.h"

59
WINE_DEFAULT_DEBUG_CHANNEL(shell);
60

61 62 63 64 65
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);

66 67
/* link file formats */

68
#include "pshpack1.h"
69

70
typedef struct _LINK_HEADER
71 72 73 74 75
{
	DWORD    dwSize;	/* 0x00 size of the header - 0x4c */
	GUID     MagicGuid;	/* 0x04 is CLSID_ShellLink */
	DWORD    dwFlags;	/* 0x14 describes elements following */
	DWORD    dwFileAttr;	/* 0x18 attributes of the target file */
76 77 78
	FILETIME Time1;		/* 0x1c */
	FILETIME Time2;		/* 0x24 */
	FILETIME Time3;		/* 0x2c */
79 80
	DWORD    dwFileLength;	/* 0x34 File length */
	DWORD    nIcon;		/* 0x38 icon number */
81
	DWORD	fStartup;	/* 0x3c startup type */
82
	DWORD	wHotKey;	/* 0x40 hotkey */
83 84 85 86
	DWORD	Unknown5;	/* 0x44 */
	DWORD	Unknown6;	/* 0x48 */
} LINK_HEADER, * PLINK_HEADER;

87 88
#define SHLINK_LOCAL  0
#define SHLINK_REMOTE 1
89

90
typedef struct _LOCATION_INFO
91
{
92 93 94 95 96 97 98 99
    DWORD  dwTotalSize;
    DWORD  dwHeaderSize;
    DWORD  dwFlags;
    DWORD  dwVolTableOfs;
    DWORD  dwLocalPathOfs;
    DWORD  dwNetworkVolTableOfs;
    DWORD  dwFinalPathOfs;
} LOCATION_INFO;
100

101
typedef struct _LOCAL_VOLUME_INFO
102
{
103 104 105 106 107
    DWORD dwSize;
    DWORD dwType;
    DWORD dwVolSerial;
    DWORD dwVolLabelOfs;
} LOCAL_VOLUME_INFO;
108

109 110 111 112 113 114 115
typedef struct volume_info_t
{
    DWORD type;
    DWORD serial;
    WCHAR label[12];  /* assume 8.3 */
} volume_info;

116
#include "poppack.h"
117

118 119 120
static const IShellLinkAVtbl slvt;
static const IShellLinkWVtbl slvtw;
static const IPersistFileVtbl pfvt;
121
static const IPersistStreamVtbl psvt;
122
static const IShellLinkDataListVtbl dlvt;
123
static const IShellExtInitVtbl eivt;
124
static const IContextMenuVtbl cmvt;
125
static const IObjectWithSiteVtbl owsvt;
126 127 128

/* IShellLink Implementation */

129 130
typedef struct
{
131 132 133
	const IShellLinkAVtbl *lpVtbl;
	const IShellLinkWVtbl *lpvtblw;
	const IPersistFileVtbl *lpvtblPersistFile;
134
	const IPersistStreamVtbl *lpvtblPersistStream;
135
	const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
136
	const IShellExtInitVtbl *lpvtblShellExtInit;
137
	const IContextMenuVtbl *lpvtblContextMenu;
138
	const IObjectWithSiteVtbl *lpvtblObjectWithSite;
139

Mike McCormack's avatar
Mike McCormack committed
140
	LONG            ref;
141

142
	/* data structures according to the information in the link */
143
	LPITEMIDLIST	pPidl;
144
	WORD		wHotKey;
145 146 147
	SYSTEMTIME	time1;
	SYSTEMTIME	time2;
	SYSTEMTIME	time3;
148

149 150 151 152 153 154 155 156
	DWORD         iShowCmd;
	LPWSTR        sIcoPath;
	INT           iIcoNdx;
	LPWSTR        sPath;
	LPWSTR        sArgs;
	LPWSTR        sWorkDir;
	LPWSTR        sDescription;
	LPWSTR        sPathRel;
157 158 159
 	LPWSTR        sProduct;
 	LPWSTR        sComponent;
	volume_info   volume;
160

161
	BOOL          bDirty;
162
        INT           iIdOpen;  /* id of the "Open" entry in the context menu */
163
	IUnknown      *site;
164 165

	LPOLESTR      filepath; /* file path returned by IPersistFile::GetCurFile */
166
} IShellLinkImpl;
167

168 169 170 171
static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
}
172

173 174 175 176
static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
}
177

178 179 180 181 182 183 184 185 186
static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
}

static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
}
187

188 189 190 191
static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
}
192

193 194 195 196 197
static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
}

198 199 200 201 202
static inline IShellLinkImpl *impl_from_IObjectWithSite( IObjectWithSite *iface )
{
    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
}

203
static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
204 205

/* strdup on the process heap */
206
static inline LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
207
{
208 209 210 211 212
    INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
    LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
    if( !p )
        return p;
    MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
213 214 215
    return p;
}

216
static inline LPWSTR strdupW( LPCWSTR src )
217 218 219 220 221 222 223 224 225
{
    LPWSTR dest;
    if (!src) return NULL;
    dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
    if (dest)
        lstrcpyW(dest, src);
    return dest;
}

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
/**************************************************************************
 *  ShellLink::QueryInterface implementation
 */
static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
{
    TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));

    *ppvObj = NULL;

    if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
    {
        *ppvObj = This;
    }
    else if(IsEqualIID(riid, &IID_IShellLinkW))
    {
        *ppvObj = &(This->lpvtblw);
    }
    else if(IsEqualIID(riid, &IID_IPersistFile))
    {
        *ppvObj = &(This->lpvtblPersistFile);
    }
    else if(IsEqualIID(riid, &IID_IPersistStream))
    {
        *ppvObj = &(This->lpvtblPersistStream);
    }
    else if(IsEqualIID(riid, &IID_IShellLinkDataList))
    {
        *ppvObj = &(This->lpvtblShellLinkDataList);
    }
    else if(IsEqualIID(riid, &IID_IShellExtInit))
    {
        *ppvObj = &(This->lpvtblShellExtInit);
    }
    else if(IsEqualIID(riid, &IID_IContextMenu))
    {
        *ppvObj = &(This->lpvtblContextMenu);
    }
263 264 265 266
    else if(IsEqualIID(riid, &IID_IObjectWithSite))
    {
        *ppvObj = &(This->lpvtblObjectWithSite);
    }
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284

    if(*ppvObj)
    {
        IUnknown_AddRef((IUnknown*)(*ppvObj));
        TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
        return S_OK;
    }
    ERR("-- Interface: E_NOINTERFACE\n");
    return E_NOINTERFACE;
}

/**************************************************************************
 *  ShellLink::AddRef implementation
 */
static ULONG ShellLink_AddRef( IShellLinkImpl *This )
{
    ULONG refCount = InterlockedIncrement(&This->ref);

285
    TRACE("(%p)->(count=%u)\n", This, refCount - 1);
286 287 288 289 290 291 292 293 294 295 296

    return refCount;
}

/**************************************************************************
 *  ShellLink::Release implementation
 */
static ULONG ShellLink_Release( IShellLinkImpl *This )
{
    ULONG refCount = InterlockedDecrement(&This->ref);

297
    TRACE("(%p)->(count=%u)\n", This, refCount + 1);
298 299 300 301 302 303 304 305 306 307

    if (refCount)
        return refCount;

    TRACE("-- destroying IShellLink(%p)\n",This);

    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
    HeapFree(GetProcessHeap(), 0, This->sArgs);
    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
    HeapFree(GetProcessHeap(), 0, This->sDescription);
308 309 310 311
    HeapFree(GetProcessHeap(), 0, This->sPath);
    HeapFree(GetProcessHeap(), 0, This->sPathRel);
    HeapFree(GetProcessHeap(), 0, This->sProduct);
    HeapFree(GetProcessHeap(), 0, This->sComponent);
312
    HeapFree(GetProcessHeap(), 0, This->filepath);
313

314 315 316
    if (This->site)
        IUnknown_Release( This->site );

317 318 319
    if (This->pPidl)
        ILFree(This->pPidl);

320
    LocalFree(This);
321 322 323 324 325 326 327 328

    return 0;
}

static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
{
    TRACE("%p %p\n", This, pclsid);

329
    *pclsid = CLSID_ShellLink;
330 331 332
    return S_OK;
}

333 334 335
/**************************************************************************
 *  IPersistFile_QueryInterface
 */
336
static HRESULT WINAPI IPersistFile_fnQueryInterface(
337 338 339
	IPersistFile* iface,
	REFIID riid,
	LPVOID *ppvObj)
340
{
341
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
342
    return ShellLink_QueryInterface( This, riid, ppvObj );
343
}
344 345 346 347

/******************************************************************************
 * IPersistFile_AddRef
 */
348
static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
349
{
350
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
351
    return ShellLink_AddRef( This );
352
}
353

354 355 356
/******************************************************************************
 * IPersistFile_Release
 */
357
static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
358
{
359
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
360
    return IShellLinkA_Release((IShellLinkA*)This);
361 362
}

Patrik Stridvall's avatar
Patrik Stridvall committed
363
static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
364
{
365
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
366
    return ShellLink_GetClassID( This, pClassID );
367
}
368

Patrik Stridvall's avatar
Patrik Stridvall committed
369
static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
370
{
371
	IShellLinkImpl *This = impl_from_IPersistFile(iface);
372 373 374 375 376 377 378

	TRACE("(%p)\n",This);

	if (This->bDirty)
	    return S_OK;

	return S_FALSE;
379
}
380

381
static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
382
{
383
	IShellLinkImpl *This = impl_from_IPersistFile(iface);
384
	IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
385 386
        HRESULT r;
        IStream *stm;
387

388
        TRACE("(%p, %s, %x)\n",This, debugstr_w(pszFileName), dwMode);
389

390 391 392
        if( dwMode == 0 )
 		dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
        r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
393
        if( SUCCEEDED( r ) )
394
        {
395
            r = IPersistStream_Load(StreamThis, stm);
396
            ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
397
            IStream_Release( stm );
398 399 400 401 402

            /* update file path */
            HeapFree(GetProcessHeap(), 0, This->filepath);
            This->filepath = strdupW(pszFileName);

403
            This->bDirty = FALSE;
404
        }
405
        TRACE("-- returning hr %08x\n", r);
406
        return r;
407 408
}

409
BOOL run_winemenubuilder( const WCHAR *args )
410
{
411
    static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
412 413 414 415
    LONG len;
    LPWSTR buffer;
    STARTUPINFOW si;
    PROCESS_INFORMATION pi;
416
    BOOL ret;
417
    WCHAR app[MAX_PATH];
418
    void *redir;
419

420 421 422 423
    GetSystemDirectoryW( app, MAX_PATH - sizeof(menubuilder)/sizeof(WCHAR) );
    strcatW( app, menubuilder );

    len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR);
424 425 426
    buffer = HeapAlloc( GetProcessHeap(), 0, len );
    if( !buffer )
        return FALSE;
427

428 429
    strcpyW( buffer, app );
    strcatW( buffer, args );
430

431
    TRACE("starting %s\n",debugstr_w(buffer));
432

433 434
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
435

436
    Wow64DisableWow64FsRedirection( &redir );
437
    ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
438
    Wow64RevertWow64FsRedirection( redir );
439 440 441 442 443 444 445 446 447 448

    HeapFree( GetProcessHeap(), 0, buffer );

    if (ret)
    {
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
    }

    return ret;
449 450
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
static BOOL StartLinkProcessor( LPCOLESTR szLink )
{
    static const WCHAR szFormat[] = {' ','-','w',' ','"','%','s','"',0 };
    LONG len;
    LPWSTR buffer;
    BOOL ret;

    len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
    buffer = HeapAlloc( GetProcessHeap(), 0, len );
    if( !buffer )
        return FALSE;

    wsprintfW( buffer, szFormat, szLink );
    ret = run_winemenubuilder( buffer );
    HeapFree( GetProcessHeap(), 0, buffer );
    return ret;
}

469
static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
470
{
471
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
472
    IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
473 474
    HRESULT r;
    IStream *stm;
475 476 477

    TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));

478
    if (!pszFileName)
479
        return E_FAIL;
480

481
    r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
482
    if( SUCCEEDED( r ) )
483
    {
484 485
        r = IPersistStream_Save(StreamThis, stm, FALSE);
        IStream_Release( stm );
486

487
        if( SUCCEEDED( r ) )
488
	{
489
            StartLinkProcessor( pszFileName );
490

491 492 493 494
            /* update file path */
            HeapFree(GetProcessHeap(), 0, This->filepath);
            This->filepath = strdupW(pszFileName);

495
            This->bDirty = FALSE;
496 497
        }
	else
498
        {
499 500
            DeleteFileW( pszFileName );
            WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
501 502
        }
    }
503

504
    return r;
505
}
506

507
static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
508
{
509
	IShellLinkImpl *This = impl_from_IPersistFile(iface);
510
	FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
511
	return NOERROR;
512
}
513

514
static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *filename)
515
{
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    IShellLinkImpl *This = impl_from_IPersistFile(iface);
    IMalloc *pMalloc;

    TRACE("(%p)->(%p)\n", This, filename);

    if (!This->filepath)
    {
        *filename = NULL;
        return S_FALSE;
    }

    SHGetMalloc(&pMalloc);
    *filename = IMalloc_Alloc(pMalloc, (strlenW(This->filepath)+1)*sizeof(WCHAR));
    if (!*filename) return E_OUTOFMEMORY;

    strcpyW(*filename, This->filepath);

    return S_OK;
534
}
535

536
static const IPersistFileVtbl pfvt =
537
{
538 539 540 541 542 543 544 545 546
	IPersistFile_fnQueryInterface,
	IPersistFile_fnAddRef,
	IPersistFile_fnRelease,
	IPersistFile_fnGetClassID,
	IPersistFile_fnIsDirty,
	IPersistFile_fnLoad,
	IPersistFile_fnSave,
	IPersistFile_fnSaveCompleted,
	IPersistFile_fnGetCurFile
547
};
548

549 550
/************************************************************************
 * IPersistStream_QueryInterface
551
 */
552 553 554
static HRESULT WINAPI IPersistStream_fnQueryInterface(
	IPersistStream* iface,
	REFIID     riid,
555
	VOID**     ppvObj)
556
{
557
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
558
    return ShellLink_QueryInterface( This, riid, ppvObj );
559
}
560

561 562 563 564 565
/************************************************************************
 * IPersistStream_Release
 */
static ULONG WINAPI IPersistStream_fnRelease(
	IPersistStream* iface)
566
{
567
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
568
    return IShellLinkA_Release((IShellLinkA*)This);
569
}
570 571 572

/************************************************************************
 * IPersistStream_AddRef
573
 */
574 575
static ULONG WINAPI IPersistStream_fnAddRef(
	IPersistStream* iface)
576
{
577
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
578
    return ShellLink_AddRef( This );
579
}
580

581 582 583
/************************************************************************
 * IPersistStream_GetClassID
 *
584
 */
585
static HRESULT WINAPI IPersistStream_fnGetClassID(
Patrik Stridvall's avatar
Patrik Stridvall committed
586
	IPersistStream* iface,
587
	CLSID* pClassID)
588
{
589
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
590
    return ShellLink_GetClassID( This, pClassID );
591
}
592 593 594

/************************************************************************
 * IPersistStream_IsDirty (IPersistStream)
595
 */
596 597
static HRESULT WINAPI IPersistStream_fnIsDirty(
	IPersistStream*  iface)
598
{
599
	IShellLinkImpl *This = impl_from_IPersistStream(iface);
600

601 602 603
	TRACE("(%p)\n", This);

	return S_OK;
604
}
605 606 607 608 609 610 611 612 613 614 615 616 617


static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
{
    DWORD count;
    USHORT len;
    LPVOID temp;
    LPWSTR str;
    HRESULT r;

    TRACE("%p\n", stm);

    count = 0;
618 619
    r = IStream_Read(stm, &len, sizeof(len), &count);
    if ( FAILED (r) || ( count != sizeof(len) ) )
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
        return E_FAIL;

    if( unicode )
        len *= sizeof (WCHAR);

    TRACE("reading %d\n", len);
    temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
    if( !temp )
        return E_OUTOFMEMORY;
    count = 0;
    r = IStream_Read(stm, temp, len, &count);
    if( FAILED (r) || ( count != len ) )
    {
        HeapFree( GetProcessHeap(), 0, temp );
        return E_FAIL;
    }

    TRACE("read %s\n", debugstr_an(temp,len));

    /* convert to unicode if necessary */
    if( !unicode )
    {
642
        count = MultiByteToWideChar( CP_ACP, 0, temp, len, NULL, 0 );
643
        str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
644 645 646 647 648
        if( !str )
        {
            HeapFree( GetProcessHeap(), 0, temp );
            return E_OUTOFMEMORY;
        }
649
        MultiByteToWideChar( CP_ACP, 0, temp, len, str, count );
650 651 652 653 654
        HeapFree( GetProcessHeap(), 0, temp );
    }
    else
    {
        count /= 2;
655
        str = temp;
656 657 658 659 660 661 662 663
    }
    str[count] = 0;

    *pstr = str;

    return S_OK;
}

664
static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
665 666 667 668
{
    DWORD size;
    ULONG count;
    HRESULT r;
669 670 671 672
    struct sized_chunk {
        DWORD size;
        unsigned char data[1];
    } *chunk;
673 674 675

    TRACE("%p\n",stm);

676
    r = IStream_Read( stm, &size, sizeof(size), &count );
677
    if( FAILED( r )  || count != sizeof(size) )
678 679
        return E_FAIL;

680 681
    chunk = HeapAlloc( GetProcessHeap(), 0, size );
    if( !chunk )
682 683
        return E_OUTOFMEMORY;

684 685 686 687 688 689 690 691
    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;
    }

692
    TRACE("Read %d bytes\n",chunk->size);
693

694
    *data = chunk;
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720

    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;
}

721
static LPWSTR Stream_LoadPath( LPCSTR p, DWORD maxlen )
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
{
    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 )
{
Mike McCormack's avatar
Mike McCormack committed
740
    char *p = NULL;
741 742
    LOCATION_INFO *loc;
    HRESULT r;
743
    DWORD n;
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770

    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 );

771
    TRACE("type %d serial %08x name %s path %s\n", volume->type,
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
          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;
797
    EXP_DARWIN_LINK buffer;
798 799 800
    
    TRACE("%p\n",stm);

801
    r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
802
    if( FAILED( r ) )
803 804 805 806
        return r;

    /* make sure that we read the size of the structure even on error */
    size = sizeof buffer - sizeof (DWORD);
807
    if( buffer.dbh.cbSize != sizeof buffer )
808
    {
809
        ERR("Ooops.  This structure is not as expected...\n");
810
        return E_FAIL;
811 812
    }

813
    r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
814 815 816 817 818 819
    if( FAILED( r ) )
        return r;

    if( count != size )
        return E_FAIL;

820
    TRACE("magic %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
821

822
    if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
823
    {
824
        ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
825 826
        return E_FAIL;
    }
827

828
    *str = HeapAlloc( GetProcessHeap(), 0, 
829 830
                     (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
    lstrcpyW( *str, buffer.szwDarwinID );
831 832 833 834

    return S_OK;
}

835 836
/************************************************************************
 * IPersistStream_Load (IPersistStream)
837
 */
838
static HRESULT WINAPI IPersistStream_fnLoad(
839
    IPersistStream*  iface,
840
    IStream*         stm)
841
{
842 843 844 845
    LINK_HEADER hdr;
    ULONG    dwBytesRead;
    BOOL     unicode;
    HRESULT  r;
846
    DWORD    zero;
847

848
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
849

850
    TRACE("%p %p\n", This, stm);
851

852
    if( !stm )
853
        return STG_E_INVALIDPOINTER;
854

855
    dwBytesRead = 0;
856
    r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
857 858
    if( FAILED( r ) )
        return r;
859

860
    if( dwBytesRead != sizeof(hdr))
861
        return E_FAIL;
862
    if( hdr.dwSize != sizeof(hdr))
863 864 865
        return E_FAIL;
    if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
        return E_FAIL;
866

867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
    /* 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;
        
888
    This->wHotKey = (WORD)hdr.wHotKey;
889 890 891 892
    This->iIcoNdx = hdr.nIcon;
    FileTimeToSystemTime (&hdr.Time1, &This->time1);
    FileTimeToSystemTime (&hdr.Time2, &This->time2);
    FileTimeToSystemTime (&hdr.Time3, &This->time3);
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
    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 */
908
    if( hdr.dwFlags & SLDF_HAS_ID_LIST )
909 910 911 912 913 914 915 916
    {
        r = ILLoadFromStream( stm, &This->pPidl );
        if( FAILED( r ) )
            return r;
    }
    pdump(This->pPidl);

    /* load the location information */
917
    if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
918
        r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
919 920
    if( FAILED( r ) )
        goto end;
921

922 923
    unicode = hdr.dwFlags & SLDF_UNICODE;
    if( hdr.dwFlags & SLDF_HAS_NAME )
924 925 926 927 928 929
    {
        r = Stream_LoadString( stm, unicode, &This->sDescription );
        TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
    }
    if( FAILED( r ) )
        goto end;
930

931
    if( hdr.dwFlags & SLDF_HAS_RELPATH )
932 933 934 935 936 937
    {
        r = Stream_LoadString( stm, unicode, &This->sPathRel );
        TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
    }
    if( FAILED( r ) )
        goto end;
938

939
    if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
940
    {
941 942 943 944 945
        r = Stream_LoadString( stm, unicode, &This->sWorkDir );
        TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
    }
    if( FAILED( r ) )
        goto end;
946

947
    if( hdr.dwFlags & SLDF_HAS_ARGS )
948 949 950 951 952 953 954
    {
        r = Stream_LoadString( stm, unicode, &This->sArgs );
        TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
    }
    if( FAILED( r ) )
        goto end;

955
    if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
956 957 958 959 960 961 962
    {
        r = Stream_LoadString( stm, unicode, &This->sIcoPath );
        TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
    }
    if( FAILED( r ) )
        goto end;

963
    if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
964 965 966 967 968 969 970
    {
        r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
        TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
    }
    if( FAILED( r ) )
        goto end;

971
    if( hdr.dwFlags & SLDF_HAS_DARWINID )
972 973 974 975 976 977 978 979 980
    {
        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 )
981 982 983 984 985 986 987 988
    {
        /* Some lnk files have extra data blocks starting with a
         * DATABLOCK_HEADER. For instance EXP_SPECIAL_FOLDER and an unknown
         * one with a 0xa0000003 signature. However these don't seem to matter
         * too much.
         */
        WARN("Last word was not zero\n");
    }
989

990 991 992
    TRACE("OK\n");

    pdump (This->pPidl);
993

994
    return S_OK;
995
end:
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
    return r;
}

/************************************************************************
 * Stream_WriteString
 *
 * Helper function for IPersistStream_Save. Writes a unicode string 
 *  with terminating nul byte to a stream, preceded by the its length.
 */
static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
{
    USHORT len = lstrlenW( str ) + 1;
    DWORD count;
    HRESULT r;

1011
    r = IStream_Write( stm, &len, sizeof(len), &count );
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
    if( FAILED( r ) )
        return r;

    len *= sizeof(WCHAR);

    r = IStream_Write( stm, str, len, &count );
    if( FAILED( r ) )
        return r;

    return S_OK;
}
1023

1024 1025 1026 1027 1028 1029 1030
/************************************************************************
 * 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.
1031
 *        Figure out how Windows deals with unicode paths here.
1032 1033 1034 1035 1036 1037 1038 1039 1040
 */
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;
1041
    HRESULT hr;
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082

    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;

1083 1084 1085 1086
    hr = IStream_Write( stm, loc, total_size, &count );
    HeapFree(GetProcessHeap(), 0, loc);

    return hr;
1087 1088
}

1089 1090 1091 1092 1093
static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
{
    EXP_DARWIN_LINK *buffer;
    
    buffer = LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
1094
    buffer->dbh.cbSize = sizeof *buffer;
1095 1096 1097 1098 1099 1100 1101
    buffer->dbh.dwSignature = magic;
    lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
    WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );

    return buffer;
}

1102
static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1103
{
1104
    EXP_DARWIN_LINK *buffer;
1105
    ULONG count;
1106 1107
    
    TRACE("%p\n",stm);
1108

1109
    buffer = shelllink_build_darwinid( string, magic );
1110

1111
    return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
1112
}
1113 1114 1115

/************************************************************************
 * IPersistStream_Save (IPersistStream)
1116 1117
 *
 * FIXME: makes assumptions about byte order
1118
 */
1119 1120
static HRESULT WINAPI IPersistStream_fnSave(
	IPersistStream*  iface,
1121
	IStream*         stm,
1122
	BOOL             fClearDirty)
1123
{
1124
    LINK_HEADER header;
1125
    ULONG   count;
1126
    DWORD   zero;
1127
    HRESULT r;
1128

1129
    IShellLinkImpl *This = impl_from_IPersistStream(iface);
1130

1131
    TRACE("%p %p %x\n", This, stm, fClearDirty);
1132

1133 1134
    memset(&header, 0, sizeof(header));
    header.dwSize = sizeof(header);
1135
    header.fStartup = This->iShowCmd;
1136
    header.MagicGuid = CLSID_ShellLink;
1137 1138 1139

    header.wHotKey = This->wHotKey;
    header.nIcon = This->iIcoNdx;
1140
    header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1141
    if( This->pPidl )
1142
        header.dwFlags |= SLDF_HAS_ID_LIST;
1143
    if( This->sPath )
1144
        header.dwFlags |= SLDF_HAS_LINK_INFO;
1145
    if( This->sDescription )
1146
        header.dwFlags |= SLDF_HAS_NAME;
1147
    if( This->sWorkDir )
1148
        header.dwFlags |= SLDF_HAS_WORKINGDIR;
1149
    if( This->sArgs )
1150
        header.dwFlags |= SLDF_HAS_ARGS;
1151
    if( This->sIcoPath )
1152
        header.dwFlags |= SLDF_HAS_ICONLOCATION;
1153
    if( This->sProduct )
1154
        header.dwFlags |= SLDF_HAS_LOGO3ID;
1155
    if( This->sComponent )
1156
        header.dwFlags |= SLDF_HAS_DARWINID;
1157 1158 1159 1160 1161 1162

    SystemTimeToFileTime ( &This->time1, &header.Time1 );
    SystemTimeToFileTime ( &This->time2, &header.Time2 );
    SystemTimeToFileTime ( &This->time3, &header.Time3 );

    /* write the Shortcut header */
1163
    r = IStream_Write( stm, &header, sizeof(header), &count );
1164 1165 1166 1167 1168 1169
    if( FAILED( r ) )
    {
        ERR("Write failed at %d\n",__LINE__);
        return r;
    }

1170
    TRACE("Writing pidl\n");
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

    /* write the PIDL to the shortcut */
    if( This->pPidl )
    {
        r = ILSaveToStream( stm, This->pPidl );
        if( FAILED( r ) )
        {
            ERR("Failed to write PIDL at %d\n",__LINE__);
            return r;
        }
    }

1183
    if( This->sPath )
1184
        Stream_WriteLocationInfo( stm, This->sPath, &This->volume );
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200

    if( This->sDescription )
        r = Stream_WriteString( stm, This->sDescription );

    if( This->sPathRel )
        r = Stream_WriteString( stm, This->sPathRel );

    if( This->sWorkDir )
        r = Stream_WriteString( stm, This->sWorkDir );

    if( This->sArgs )
        r = Stream_WriteString( stm, This->sArgs );

    if( This->sIcoPath )
        r = Stream_WriteString( stm, This->sIcoPath );

1201
    if( This->sProduct )
1202
        r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1203 1204

    if( This->sComponent )
1205
        r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1206 1207 1208 1209 1210

    /* the last field is a single zero dword */
    zero = 0;
    r = IStream_Write( stm, &zero, sizeof zero, &count );

1211
    return S_OK;
1212 1213
}

1214 1215
/************************************************************************
 * IPersistStream_GetSizeMax (IPersistStream)
1216
 */
1217 1218 1219
static HRESULT WINAPI IPersistStream_fnGetSizeMax(
	IPersistStream*  iface,
	ULARGE_INTEGER*  pcbSize)
1220
{
1221
	IShellLinkImpl *This = impl_from_IPersistStream(iface);
1222

1223
	TRACE("(%p)\n", This);
1224

1225 1226
	return E_NOTIMPL;
}
1227

1228
static const IPersistStreamVtbl psvt =
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
{
	IPersistStream_fnQueryInterface,
	IPersistStream_fnAddRef,
	IPersistStream_fnRelease,
	IPersistStream_fnGetClassID,
	IPersistStream_fnIsDirty,
	IPersistStream_fnLoad,
	IPersistStream_fnSave,
	IPersistStream_fnGetSizeMax
};
1239 1240 1241 1242

/**************************************************************************
 *	  IShellLink_Constructor
 */
1243 1244
HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
               REFIID riid, LPVOID *ppv )
1245 1246
{
	IShellLinkImpl * sl;
1247
	HRESULT r;
1248 1249 1250 1251 1252

	TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));

	*ppv = NULL;

1253 1254 1255 1256 1257
	if (pUnkOuter)
            return CLASS_E_NOAGGREGATION;
	sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
	if (!sl)
            return E_OUTOFMEMORY;
1258 1259

	sl->ref = 1;
1260
	sl->lpVtbl = &slvt;
1261 1262 1263
	sl->lpvtblw = &slvtw;
	sl->lpvtblPersistFile = &pfvt;
	sl->lpvtblPersistStream = &psvt;
1264
	sl->lpvtblShellLinkDataList = &dlvt;
1265
	sl->lpvtblShellExtInit = &eivt;
1266
	sl->lpvtblContextMenu = &cmvt;
1267
	sl->lpvtblObjectWithSite = &owsvt;
1268
	sl->iShowCmd = SW_SHOWNORMAL;
1269
	sl->bDirty = FALSE;
1270
	sl->iIdOpen = -1;
1271
	sl->site = NULL;
1272
	sl->filepath = NULL;
1273

1274
	TRACE("(%p)->()\n",sl);
1275

1276 1277 1278
        r = ShellLink_QueryInterface( sl, riid, ppv );
        ShellLink_Release( sl );
        return r;
1279 1280
}

1281 1282 1283

static BOOL SHELL_ExistsFileW(LPCWSTR path)
{
1284
    if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1285
        return FALSE;
1286
    return TRUE;
1287 1288 1289
}

/**************************************************************************
1290
 *  ShellLink_UpdatePath
1291 1292
 *	update absolute path in sPath using relative path in sPathRel
 */
1293
static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1294 1295 1296 1297 1298 1299
{
    if (!path || !psPath)
	return E_INVALIDARG;

    if (!*psPath && sPathRel) {
	WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1300
	LPWSTR final = NULL;
1301 1302 1303

	/* first try if [directory of link file] + [relative path] finds an existing file */

1304 1305 1306 1307
        GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
        if( !final )
            final = buffer;
	lstrcpyW(final, sPathRel);
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342

	*abs_path = '\0';

	if (SHELL_ExistsFileW(buffer)) {
	    if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
		lstrcpyW(abs_path, buffer);
	} else {
	    /* try if [working directory] + [relative path] finds an existing file */
	    if (sWorkDir) {
		lstrcpyW(buffer, sWorkDir);
		lstrcpyW(PathAddBackslashW(buffer), sPathRel);

		if (SHELL_ExistsFileW(buffer))
		    if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
			lstrcpyW(abs_path, buffer);
	    }
	}

	/* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
	if (!*abs_path)
	    lstrcpyW(abs_path, sPathRel);

	*psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
	if (!*psPath)
	    return E_OUTOFMEMORY;

	lstrcpyW(*psPath, abs_path);
    }

    return S_OK;
}

/**************************************************************************
 *	  IShellLink_ConstructFromFile
 */
1343 1344
HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
               LPCITEMIDLIST pidl, LPVOID* ppv)
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
{
    IShellLinkW* psl;

    HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);

    if (SUCCEEDED(hr)) {
	IPersistFile* ppf;

	*ppv = NULL;

	hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);

	if (SUCCEEDED(hr)) {
	    WCHAR path[MAX_PATH];

1360
	    if (SHGetPathFromIDListW(pidl, path)) 
1361
		hr = IPersistFile_Load(ppf, path, 0);
1362 1363
            else
                hr = E_FAIL;
1364

1365
	    if (SUCCEEDED(hr))
1366
                *ppv = psl;
1367

1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
	    IPersistFile_Release(ppf);
	}

	if (!*ppv)
	    IShellLinkW_Release(psl);
    }

    return hr;
}

1378
/**************************************************************************
1379
 *  IShellLinkA_QueryInterface
1380
 */
1381
static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1382
{
1383 1384
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
    return ShellLink_QueryInterface( This, riid, ppvObj );
1385
}
1386

1387
/******************************************************************************
1388
 * IShellLinkA_AddRef
1389
 */
1390
static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1391
{
1392 1393
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
    return ShellLink_AddRef( This );
1394
}
1395

1396
/******************************************************************************
1397
 *	IShellLinkA_Release
1398
 */
1399
static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1400
{
1401
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1402
    return ShellLink_Release( This );
1403 1404
}

1405 1406
static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
                  INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1407
{
1408
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1409

1410
    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1411
          This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1412

1413 1414 1415 1416
    if (This->sComponent || This->sProduct)
        return S_FALSE;

    if (cchMaxPath)
1417 1418 1419 1420
        pszFile[0] = 0;
    if (This->sPath)
        WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
                             pszFile, cchMaxPath, NULL, NULL);
1421

1422 1423
    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);

1424
    return S_OK;
1425
}
1426

1427
static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1428
{
1429
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1430

1431
    TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1432

1433
    return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1434
}
1435

1436
static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1437
{
1438
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1439

1440
    TRACE("(%p)->(pidl=%p)\n",This, pidl);
1441

1442 1443 1444 1445
    if (This->pPidl)
	ILFree(This->pPidl);
    This->pPidl = ILClone (pidl);
    This->bDirty = TRUE;
1446 1447

    return S_OK;
1448
}
1449

1450
static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1451
{
1452
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1453

1454 1455 1456 1457 1458 1459 1460 1461 1462
    TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);

    if( cchMaxName )
        pszName[0] = 0;
    if( This->sDescription )
        WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
            pszName, cchMaxName, NULL, NULL);

    return S_OK;
1463
}
1464

1465
static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1466
{
1467
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1468

1469
    TRACE("(%p)->(pName=%s)\n", This, pszName);
1470

1471
    HeapFree(GetProcessHeap(), 0, This->sDescription);
1472 1473 1474 1475 1476 1477 1478 1479
    if (pszName)
    {
        This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
        if ( !This->sDescription )
            return E_OUTOFMEMORY;
    }
    else
        This->sDescription = NULL;
1480

1481 1482
    This->bDirty = TRUE;

1483
    return S_OK;
1484
}
1485

1486
static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1487
{
1488
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1489

1490
    TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1491

1492 1493 1494 1495 1496
    if( cchMaxPath )
        pszDir[0] = 0;
    if( This->sWorkDir )
        WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
                             pszDir, cchMaxPath, NULL, NULL);
1497

1498
    return S_OK;
1499
}
1500

1501
static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1502
{
1503
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1504

1505
    TRACE("(%p)->(dir=%s)\n",This, pszDir);
1506

1507
    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1508 1509 1510
    This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
    if ( !This->sWorkDir )
        return E_OUTOFMEMORY;
1511

1512 1513
    This->bDirty = TRUE;

1514
    return S_OK;
1515
}
1516

1517
static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1518
{
1519
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1520

1521
    TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1522

1523 1524 1525 1526 1527
    if( cchMaxPath )
        pszArgs[0] = 0;
    if( This->sArgs )
        WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
                             pszArgs, cchMaxPath, NULL, NULL);
1528

1529
    return S_OK;
1530
}
1531

1532
static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1533
{
1534
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1535

1536
    TRACE("(%p)->(args=%s)\n",This, pszArgs);
1537

1538
    HeapFree(GetProcessHeap(), 0, This->sArgs);
1539 1540 1541 1542 1543 1544 1545
    if (pszArgs)
    {
        This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
        if( !This->sArgs )
            return E_OUTOFMEMORY;
    }
    else This->sArgs = NULL;
1546

1547 1548
    This->bDirty = TRUE;

1549
    return S_OK;
1550
}
1551

1552
static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1553
{
1554
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1555

1556
    TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1557

1558
    *pwHotkey = This->wHotKey;
1559

1560
    return S_OK;
1561
}
1562

1563
static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1564
{
1565
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1566

1567
    TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1568

1569 1570
    This->wHotKey = wHotkey;
    This->bDirty = TRUE;
1571

1572
    return S_OK;
1573
}
1574

1575
static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1576
{
1577
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1578

1579 1580 1581
    TRACE("(%p)->(%p)\n",This, piShowCmd);
    *piShowCmd = This->iShowCmd;
    return S_OK;
1582
}
1583

1584
static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1585
{
1586
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1587

1588 1589 1590
    TRACE("(%p) %d\n",This, iShowCmd);

    This->iShowCmd = iShowCmd;
1591
    This->bDirty = TRUE;
1592 1593

    return NOERROR;
1594
}
1595

1596
static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1597
{
1598
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1599

1600
    TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1601

1602
    *piIcon = This->iIcoNdx;
1603

1604
    if (This->sIcoPath)
1605
        WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1606 1607
    else
        pszIconPath[0] = 0;
1608

1609
    return S_OK;
1610
}
1611

1612
static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1613
{
1614
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1615

1616
    TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1617

1618
    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1619 1620 1621
    This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
    if ( !This->sIcoPath )
        return E_OUTOFMEMORY;
1622

1623
    This->iIcoNdx = iIcon;
1624
    This->bDirty = TRUE;
1625

1626
    return S_OK;
1627
}
1628

1629
static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1630
{
1631
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1632

1633
    TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
1634

1635
    HeapFree(GetProcessHeap(), 0, This->sPathRel);
1636
    This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1637
    This->bDirty = TRUE;
1638

1639
    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1640
}
1641

1642
static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1643
{
1644 1645
    IShellLinkImpl *This = (IShellLinkImpl *)iface;

1646
    TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1647

1648
    return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1649
}
1650

1651
static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1652
{
1653 1654
    HRESULT r;
    LPWSTR str;
1655
    IShellLinkImpl *This = (IShellLinkImpl *)iface;
1656

1657
    TRACE("(%p)->(path=%s)\n",This, pszFile);
1658

1659 1660
    if (!pszFile) return E_INVALIDARG;

1661 1662
    str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
    if( !str ) 
1663
        return E_OUTOFMEMORY;
1664

1665 1666
    r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
    HeapFree( GetProcessHeap(), 0, str );
1667

1668
    return r;
1669 1670
}

1671 1672 1673 1674
/**************************************************************************
* IShellLink Implementation
*/

1675
static const IShellLinkAVtbl slvt =
1676
{
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
    IShellLinkA_fnQueryInterface,
    IShellLinkA_fnAddRef,
    IShellLinkA_fnRelease,
    IShellLinkA_fnGetPath,
    IShellLinkA_fnGetIDList,
    IShellLinkA_fnSetIDList,
    IShellLinkA_fnGetDescription,
    IShellLinkA_fnSetDescription,
    IShellLinkA_fnGetWorkingDirectory,
    IShellLinkA_fnSetWorkingDirectory,
    IShellLinkA_fnGetArguments,
    IShellLinkA_fnSetArguments,
    IShellLinkA_fnGetHotkey,
    IShellLinkA_fnSetHotkey,
    IShellLinkA_fnGetShowCmd,
    IShellLinkA_fnSetShowCmd,
    IShellLinkA_fnGetIconLocation,
    IShellLinkA_fnSetIconLocation,
    IShellLinkA_fnSetRelativePath,
    IShellLinkA_fnResolve,
    IShellLinkA_fnSetPath
1698 1699
};

1700 1701

/**************************************************************************
1702
 *  IShellLinkW_fnQueryInterface
1703
 */
1704 1705 1706
static HRESULT WINAPI IShellLinkW_fnQueryInterface(
  IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
{
1707
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1708
    return ShellLink_QueryInterface( This, riid, ppvObj );
1709
}
1710

1711
/******************************************************************************
1712
 * IShellLinkW_fnAddRef
1713
 */
1714 1715
static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
{
1716
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1717
    return ShellLink_AddRef( This );
1718
}
1719

1720
/******************************************************************************
1721
 * IShellLinkW_fnRelease
1722
 */
1723 1724
static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
{
1725
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1726
    return ShellLink_Release( This );
1727 1728
}

1729
static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1730
{
1731
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1732

1733
    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1734
          This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1735

1736 1737 1738 1739
    if (This->sComponent || This->sProduct)
        return S_FALSE;

    if (cchMaxPath)
1740
        pszFile[0] = 0;
1741
    if (This->sPath)
1742 1743
        lstrcpynW( pszFile, This->sPath, cchMaxPath );

1744 1745
    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);

1746
    return S_OK;
1747
}
1748 1749 1750

static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
{
1751
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1752

1753 1754
    TRACE("(%p)->(ppidl=%p)\n",This, ppidl);

1755
    if (!This->pPidl)
1756 1757
    {
	*ppidl = NULL;
1758
        return S_FALSE;
1759
    }
1760
    *ppidl = ILClone(This->pPidl);
1761
    return S_OK;
1762
}
1763 1764 1765

static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
{
1766
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1767

1768 1769 1770 1771 1772 1773 1774 1775
    TRACE("(%p)->(pidl=%p)\n",This, pidl);

    if( This->pPidl )
        ILFree( This->pPidl );
    This->pPidl = ILClone( pidl );
    if( !This->pPidl )
        return E_FAIL;

1776 1777
    This->bDirty = TRUE;

1778
    return S_OK;
1779
}
1780

1781
static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1782
{
1783
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1784

1785 1786
    TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);

1787
    pszName[0] = 0;
1788 1789 1790 1791
    if( This->sDescription )
        lstrcpynW( pszName, This->sDescription, cchMaxName );

    return S_OK;
1792
}
1793 1794 1795

static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
{
1796
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1797

1798
    TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1799

1800
    HeapFree(GetProcessHeap(), 0, This->sDescription);
1801 1802 1803 1804 1805 1806
    if (pszName)
    {
        This->sDescription = HeapAlloc( GetProcessHeap(), 0,
                                        (lstrlenW( pszName )+1)*sizeof(WCHAR) );
        if ( !This->sDescription )
            return E_OUTOFMEMORY;
1807

1808 1809 1810 1811
        lstrcpyW( This->sDescription, pszName );
    }
    else
        This->sDescription = NULL;
1812
    This->bDirty = TRUE;
1813

1814
    return S_OK;
1815
}
1816

1817
static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1818
{
1819
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1820

1821
    TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1822

1823 1824 1825 1826
    if( cchMaxPath )
        pszDir[0] = 0;
    if( This->sWorkDir )
        lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1827

1828
    return S_OK;
1829
}
1830 1831 1832

static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
{
1833
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1834

1835
    TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1836

1837
    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1838 1839 1840 1841 1842
    This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
                                (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
    if ( !This->sWorkDir )
        return E_OUTOFMEMORY;
    lstrcpyW( This->sWorkDir, pszDir );
1843
    This->bDirty = TRUE;
1844

1845
    return S_OK;
1846
}
1847

1848
static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1849
{
1850
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1851

1852
    TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1853

1854 1855 1856 1857
    if( cchMaxPath )
        pszArgs[0] = 0;
    if( This->sArgs )
        lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1858

1859
    return NOERROR;
1860
}
1861 1862 1863

static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
{
1864
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1865

1866
    TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1867

1868
    HeapFree(GetProcessHeap(), 0, This->sArgs);
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878
    if (pszArgs)
    {
        This->sArgs = HeapAlloc( GetProcessHeap(), 0,
                                 (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
        if ( !This->sArgs )
            return E_OUTOFMEMORY;
        lstrcpyW( This->sArgs, pszArgs );
    }
    else This->sArgs = NULL;

1879
    This->bDirty = TRUE;
1880

1881
    return S_OK;
1882
}
1883 1884 1885

static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
{
1886
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1887

1888 1889 1890 1891 1892
    TRACE("(%p)->(%p)\n",This, pwHotkey);

    *pwHotkey=This->wHotKey;

    return S_OK;
1893
}
1894 1895 1896

static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
{
1897
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1898

1899 1900 1901
    TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);

    This->wHotKey = wHotkey;
1902
    This->bDirty = TRUE;
1903 1904

    return S_OK;
1905
}
1906

1907
static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1908
{
1909
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1910

1911 1912 1913 1914 1915
    TRACE("(%p)->(%p)\n",This, piShowCmd);

    *piShowCmd = This->iShowCmd;

    return S_OK;
1916
}
1917

1918
static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1919
{
1920
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1921

1922
    This->iShowCmd = iShowCmd;
1923
    This->bDirty = TRUE;
1924 1925

    return S_OK;
1926
}
1927

1928
static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1929
{
1930
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1931

1932
    TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1933

1934
    *piIcon = This->iIcoNdx;
1935

1936
    if (This->sIcoPath)
1937
	lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1938 1939
    else
	pszIconPath[0] = 0;
1940

1941
    return S_OK;
1942
}
1943

1944
static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1945
{
1946
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1947

1948
    TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1949

1950
    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1951 1952 1953 1954 1955
    This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
                                (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
    if ( !This->sIcoPath )
        return E_OUTOFMEMORY;
    lstrcpyW( This->sIcoPath, pszIconPath );
1956

1957
    This->iIcoNdx = iIcon;
1958
    This->bDirty = TRUE;
1959 1960

    return S_OK;
1961
}
1962 1963 1964

static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
{
1965
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1966

1967
    TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
1968

1969
    HeapFree(GetProcessHeap(), 0, This->sPathRel);
1970 1971 1972 1973 1974
    This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
                                (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
    if ( !This->sPathRel )
        return E_OUTOFMEMORY;
    lstrcpyW( This->sPathRel, pszPathRel );
1975
    This->bDirty = TRUE;
1976

1977
    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1978
}
1979

1980
static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1981
{
1982
    HRESULT hr = S_OK;
1983
    BOOL bSuccess;
1984

1985
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1986

1987
    TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1988

1989 1990 1991 1992 1993
    /*FIXME: use IResolveShellLink interface */

    if (!This->sPath && This->pPidl) {
	WCHAR buffer[MAX_PATH];

1994
	bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
1995

1996
	if (bSuccess && *buffer) {
1997
	    This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1998 1999 2000 2001 2002 2003 2004
	    if (!This->sPath)
		return E_OUTOFMEMORY;

	    lstrcpyW(This->sPath, buffer);

	    This->bDirty = TRUE;
	} else
2005
	    hr = S_OK;    /* don't report an error occurred while just caching information */
2006 2007 2008
    }

    if (!This->sIcoPath && This->sPath) {
2009
	This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
	if (!This->sIcoPath)
	    return E_OUTOFMEMORY;

	lstrcpyW(This->sIcoPath, This->sPath);
	This->iIcoNdx = 0;

	This->bDirty = TRUE;
    }

    return hr;
2020
}
2021

2022 2023 2024 2025 2026 2027
static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
{
    LPWSTR ret;
    LPCWSTR p;
    DWORD len;

2028 2029 2030
    if( !str )
        return NULL;

2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091
    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;
    }

2092 2093
    /* we have to have a component for an advertised shortcut */
    if( !szComponent )
2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104
        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;
}

2105
static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, volume_info *volume)
2106 2107 2108 2109 2110 2111 2112 2113
{
    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);
2114
    TRACE("r = %d type %d serial %08x name %s\n", r,
2115 2116 2117 2118
          volume->type, volume->serial, debugstr_w(volume->label));
    return r;
}

2119 2120
static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
{
2121
    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2122
    WCHAR buffer[MAX_PATH];
2123
    LPWSTR fname, unquoted = NULL;
2124
    HRESULT hr = S_OK;
2125
    UINT len;
2126

2127
    TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2128

2129 2130
    if (!pszFile) return E_INVALIDARG;

2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
    /* quotes at the ends of the string are stripped */
    len = lstrlenW(pszFile);
    if (pszFile[0] == '"' && pszFile[len-1] == '"')
    {
        unquoted = strdupW(pszFile);
        PathUnquoteSpacesW(unquoted);
        pszFile = unquoted;
    }

    /* any other quote marks are invalid */
    if (strchrW(pszFile, '"'))
Huw Davies's avatar
Huw Davies committed
2142 2143
    {
        HeapFree(GetProcessHeap(), 0, unquoted);
2144
        return S_FALSE;
Huw Davies's avatar
Huw Davies committed
2145
    }
2146

2147
    HeapFree(GetProcessHeap(), 0, This->sPath);
2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162
    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;
2163 2164 2165
        else if(!PathFileExistsW(buffer) &&
		!SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
	  hr = S_FALSE;
2166 2167 2168 2169 2170

        This->pPidl = SHSimpleIDListFromPathW(pszFile);
        ShellLink_GetVolumeInfo(buffer, &This->volume);

        This->sPath = HeapAlloc( GetProcessHeap(), 0,
2171
                             (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2172
        if (!This->sPath)
Huw Davies's avatar
Huw Davies committed
2173 2174
        {
            HeapFree(GetProcessHeap(), 0, unquoted);
2175
            return E_OUTOFMEMORY;
Huw Davies's avatar
Huw Davies committed
2176
        }
2177

2178 2179
        lstrcpyW(This->sPath, buffer);
    }
2180
    This->bDirty = TRUE;
2181
    HeapFree(GetProcessHeap(), 0, unquoted);
2182

2183
    return hr;
2184 2185
}

2186 2187 2188 2189
/**************************************************************************
* IShellLinkW Implementation
*/

2190
static const IShellLinkWVtbl slvtw =
2191
{
2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212
    IShellLinkW_fnQueryInterface,
    IShellLinkW_fnAddRef,
    IShellLinkW_fnRelease,
    IShellLinkW_fnGetPath,
    IShellLinkW_fnGetIDList,
    IShellLinkW_fnSetIDList,
    IShellLinkW_fnGetDescription,
    IShellLinkW_fnSetDescription,
    IShellLinkW_fnGetWorkingDirectory,
    IShellLinkW_fnSetWorkingDirectory,
    IShellLinkW_fnGetArguments,
    IShellLinkW_fnSetArguments,
    IShellLinkW_fnGetHotkey,
    IShellLinkW_fnSetHotkey,
    IShellLinkW_fnGetShowCmd,
    IShellLinkW_fnSetShowCmd,
    IShellLinkW_fnGetIconLocation,
    IShellLinkW_fnSetIconLocation,
    IShellLinkW_fnSetRelativePath,
    IShellLinkW_fnResolve,
    IShellLinkW_fnSetPath
2213
};
2214 2215 2216 2217

static HRESULT WINAPI
ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
{
2218
    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2219 2220 2221 2222 2223 2224
    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
}

static ULONG WINAPI
ShellLink_DataList_AddRef( IShellLinkDataList* iface )
{
2225
    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2226 2227 2228 2229 2230 2231
    return IShellLinkA_AddRef((IShellLinkA*)This);
}

static ULONG WINAPI
ShellLink_DataList_Release( IShellLinkDataList* iface )
{
2232
    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2233
    return ShellLink_Release( This );
2234 2235 2236 2237 2238
}

static HRESULT WINAPI
ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
{
2239
    FIXME("(%p)->(%p): stub\n", iface, pDataBlock);
2240 2241 2242 2243 2244 2245
    return E_NOTIMPL;
}

static HRESULT WINAPI
ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
{
2246 2247 2248 2249
    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
    LPVOID block = NULL;
    HRESULT r = E_FAIL;

2250
    TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264

    switch (dwSig)
    {
    case EXP_DARWIN_ID_SIG:
        if (!This->sComponent)
            break;
        block = shelllink_build_darwinid( This->sComponent, dwSig );
        r = S_OK;
        break;
    case EXP_SZ_LINK_SIG:
    case NT_CONSOLE_PROPS_SIG:
    case NT_FE_CONSOLE_PROPS_SIG:
    case EXP_SPECIAL_FOLDER_SIG:
    case EXP_SZ_ICON_SIG:
2265
        FIXME("valid but unhandled datablock %08x\n", dwSig);
2266 2267
        break;
    default:
2268
        ERR("unknown datablock %08x\n", dwSig);
2269 2270 2271
    }
    *ppDataBlock = block;
    return r;
2272 2273 2274 2275 2276
}

static HRESULT WINAPI
ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
{
2277
    FIXME("(%p)->(%u): stub\n", iface, dwSig);
2278 2279 2280 2281 2282 2283
    return E_NOTIMPL;
}

static HRESULT WINAPI
ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
{
2284 2285 2286
    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
    DWORD flags = 0;

2287
    FIXME("(%p)->(%p): partially implemented\n", This, pdwFlags);
2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303

    /* FIXME: add more */
    if (This->sArgs)
        flags |= SLDF_HAS_ARGS;
    if (This->sComponent)
        flags |= SLDF_HAS_DARWINID;
    if (This->sIcoPath)
        flags |= SLDF_HAS_ICONLOCATION;
    if (This->sProduct)
        flags |= SLDF_HAS_LOGO3ID;
    if (This->pPidl)
        flags |= SLDF_HAS_ID_LIST;

    *pdwFlags = flags;

    return S_OK;
2304 2305 2306 2307 2308
}

static HRESULT WINAPI
ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
{
2309
    FIXME("(%p)->(%u): stub\n", iface, dwFlags);
2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323
    return E_NOTIMPL;
}

static const IShellLinkDataListVtbl dlvt =
{
    ShellLink_DataList_QueryInterface,
    ShellLink_DataList_AddRef,
    ShellLink_DataList_Release,
    ShellLink_AddDataBlock,
    ShellLink_CopyDataBlock,
    ShellLink_RemoveDataBlock,
    ShellLink_GetFlags,
    ShellLink_SetFlags
};
2324 2325 2326 2327

static HRESULT WINAPI
ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
{
2328
    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2329 2330 2331 2332 2333 2334
    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
}

static ULONG WINAPI
ShellLink_ExtInit_AddRef( IShellExtInit* iface )
{
2335
    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2336 2337 2338 2339 2340 2341
    return IShellLinkA_AddRef((IShellLinkA*)This);
}

static ULONG WINAPI
ShellLink_ExtInit_Release( IShellExtInit* iface )
{
2342
    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2343
    return ShellLink_Release( This );
2344 2345
}

2346 2347 2348 2349 2350
/**************************************************************************
 * ShellLink implementation of IShellExtInit::Initialize()
 *
 * Loads the shelllink from the dataobject the shell is pointing to.
 */
2351 2352 2353 2354
static HRESULT WINAPI
ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
                              IDataObject *pdtobj, HKEY hkeyProgID )
{
2355
    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394
    FORMATETC format;
    STGMEDIUM stgm;
    UINT count;
    HRESULT r = E_FAIL;

    TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );

    if( !pdtobj )
        return r;

    format.cfFormat = CF_HDROP;
    format.ptd = NULL;
    format.dwAspect = DVASPECT_CONTENT;
    format.lindex = -1;
    format.tymed = TYMED_HGLOBAL;

    if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
        return r;

    count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
    if( count == 1 )
    {
        LPWSTR path;

        count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
        count++;
        path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
        if( path )
        {
            IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;

            count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
            r = IPersistFile_Load( pf, path, 0 );
            HeapFree( GetProcessHeap(), 0, path );
        }
    }
    ReleaseStgMedium( &stgm );

    return r;
2395 2396 2397 2398 2399 2400 2401 2402 2403
}

static const IShellExtInitVtbl eivt =
{
    ShellLink_ExtInit_QueryInterface,
    ShellLink_ExtInit_AddRef,
    ShellLink_ExtInit_Release,
    ShellLink_ExtInit_Initialize
};
2404 2405 2406 2407

static HRESULT WINAPI
ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
{
2408
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
2409 2410 2411 2412 2413 2414
    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
}

static ULONG WINAPI
ShellLink_ContextMenu_AddRef( IContextMenu* iface )
{
2415
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
2416 2417 2418 2419 2420 2421
    return IShellLinkA_AddRef((IShellLinkA*)This);
}

static ULONG WINAPI
ShellLink_ContextMenu_Release( IContextMenu* iface )
{
2422
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
2423
    return ShellLink_Release( This );
2424 2425 2426 2427 2428 2429
}

static HRESULT WINAPI
ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
                            UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
{
2430
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
2431
    static WCHAR szOpen[] = { 'O','p','e','n',0 };
2432 2433
    MENUITEMINFOW mii;
    int id = 1;
2434

2435
    TRACE("%p %p %u %u %u %u\n", This,
2436 2437
          hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );

2438 2439 2440 2441 2442 2443
    if ( !hmenu )
        return E_INVALIDARG;

    memset( &mii, 0, sizeof mii );
    mii.cbSize = sizeof mii;
    mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2444
    mii.dwTypeData = szOpen;
2445 2446 2447 2448 2449 2450 2451 2452 2453
    mii.cch = strlenW( mii.dwTypeData );
    mii.wID = idCmdFirst + id++;
    mii.fState = MFS_DEFAULT | MFS_ENABLED;
    mii.fType = MFT_STRING;
    if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
        return E_FAIL;
    This->iIdOpen = 0;

    return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2454 2455
}

2456 2457
static LPWSTR
shelllink_get_msi_component_path( LPWSTR component )
2458
{
2459
    LPWSTR path;
2460
    DWORD r, sz = 0;
2461

2462 2463
    r = CommandLineFromMsiDescriptor( component, NULL, &sz );
    if (r != ERROR_SUCCESS)
2464
         return NULL;
2465

2466 2467 2468 2469
    sz++;
    path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
    r = CommandLineFromMsiDescriptor( component, path, &sz );
    if (r != ERROR_SUCCESS)
2470
    {
2471 2472
        HeapFree( GetProcessHeap(), 0, path );
        path = NULL;
2473 2474 2475 2476 2477 2478 2479
    }

    TRACE("returning %s\n", debugstr_w( path ) );

    return path;
}

2480 2481 2482
static HRESULT WINAPI
ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
{
2483
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
Mike McCormack's avatar
Mike McCormack committed
2484
    static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2485 2486 2487
    SHELLEXECUTEINFOW sei;
    HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
    LPWSTR args = NULL;
2488
    LPWSTR path = NULL;
2489
    HRESULT r;
2490

2491
    TRACE("%p %p\n", This, lpici );
2492

2493 2494 2495 2496 2497
    if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
        return E_INVALIDARG;

    if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
    {
2498
        ERR("Unknown id %p != %d\n", lpici->lpVerb, This->iIdOpen );
2499 2500 2501 2502 2503 2504 2505
        return E_INVALIDARG;
    }

    r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
    if ( FAILED( r ) )
        return r;

2506
    if ( This->sComponent )
2507
    {
2508 2509 2510
        path = shelllink_get_msi_component_path( This->sComponent );
        if (!path)
            return E_FAIL;
2511
    }
2512 2513
    else
        path = strdupW( This->sPath );
2514 2515 2516 2517 2518

    if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
         ( lpici->fMask & CMIC_MASK_UNICODE ) )
    {
        LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
Mike McCormack's avatar
Mike McCormack committed
2519
        DWORD len = 2;
2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530

        if ( This->sArgs )
            len += lstrlenW( This->sArgs );
        if ( iciex->lpParametersW )
            len += lstrlenW( iciex->lpParametersW );

        args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
        args[0] = 0;
        if ( This->sArgs )
            lstrcatW( args, This->sArgs );
        if ( iciex->lpParametersW )
Mike McCormack's avatar
Mike McCormack committed
2531 2532 2533
        {
            static const WCHAR space[] = { ' ', 0 };
            lstrcatW( args, space );
2534
            lstrcatW( args, iciex->lpParametersW );
Mike McCormack's avatar
Mike McCormack committed
2535
        }
2536 2537 2538 2539
    }

    memset( &sei, 0, sizeof sei );
    sei.cbSize = sizeof sei;
2540
    sei.fMask = SEE_MASK_UNICODE | (lpici->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
2541
    sei.lpFile = path;
2542 2543 2544 2545
    sei.nShow = This->iShowCmd;
    sei.lpIDList = This->pPidl;
    sei.lpDirectory = This->sWorkDir;
    sei.lpParameters = args;
Mike McCormack's avatar
Mike McCormack committed
2546
    sei.lpVerb = szOpen;
2547 2548 2549 2550 2551 2552 2553

    if( ShellExecuteExW( &sei ) )
        r = S_OK;
    else
        r = E_FAIL;

    HeapFree( GetProcessHeap(), 0, args );
2554
    HeapFree( GetProcessHeap(), 0, path );
2555 2556

    return r;
2557 2558 2559
}

static HRESULT WINAPI
Kevin Koltzau's avatar
Kevin Koltzau committed
2560
ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2561 2562
                            UINT* pwReserved, LPSTR pszName, UINT cchMax )
{
2563
    IShellLinkImpl *This = impl_from_IContextMenu(iface);
2564

2565
    FIXME("(%p)->(%lu %u %p %p %u): stub\n", This,
2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579
          idCmd, uType, pwReserved, pszName, cchMax );

    return E_NOTIMPL;
}

static const IContextMenuVtbl cmvt =
{
    ShellLink_ContextMenu_QueryInterface,
    ShellLink_ContextMenu_AddRef,
    ShellLink_ContextMenu_Release,
    ShellLink_QueryContextMenu,
    ShellLink_InvokeCommand,
    ShellLink_GetCommandString
};
2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635

static HRESULT WINAPI
ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
{
    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
    return ShellLink_QueryInterface( This, riid, ppvObject );
}

static ULONG WINAPI
ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
{
    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
    return ShellLink_AddRef( This );
}

static ULONG WINAPI
ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
{
    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
    return ShellLink_Release( This );
}

static HRESULT WINAPI
ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
{
    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);

    TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );

    if ( !This->site )
        return E_FAIL;
    return IUnknown_QueryInterface( This->site, iid, ppvSite );
}

static HRESULT WINAPI
ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
{
    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);

    TRACE("%p %p\n", iface, punk);

    if ( punk )
        IUnknown_AddRef( punk );
    This->site = punk;

    return S_OK;
}

static const IObjectWithSiteVtbl owsvt =
{
    ShellLink_ObjectWithSite_QueryInterface,
    ShellLink_ObjectWithSite_AddRef,
    ShellLink_ObjectWithSite_Release,
    ShellLink_SetSite,
    ShellLink_GetSite,
};