Keep track of per-column information inside the listview.
[wine] / dlls / shell32 / shelllink.c
1 /*
2  *
3  *      Copyright 1997  Marcus Meissner
4  *      Copyright 1998  Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <stdio.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include <errno.h>
29 #ifdef HAVE_SYS_WAIT_H
30 # include <sys/wait.h>
31 #endif
32 #include "wine/debug.h"
33 #include "wine/port.h"
34 #include "winerror.h"
35 #include "winbase.h"
36 #include "winnls.h"
37 #include "winreg.h"
38
39 #include "shlobj.h"
40 #include "undocshell.h"
41 #include "bitmaps/wine.xpm"
42
43 #include "heap.h"
44 #include "pidl.h"
45 #include "shell32_main.h"
46 #include "shlguid.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49
50 /* link file formats */
51
52 #include "pshpack1.h"
53
54 /* flag1: lnk elements: simple link has 0x0B */
55 #define WORKDIR         0x10
56 #define ARGUMENT        0x20
57 #define ICON            0x40
58 #define UNC             0x80
59
60 /* fStartup */
61 #define NORMAL          0x01
62 #define MAXIMIZED       0x03
63 #define MINIMIZED       0x07
64
65 typedef struct _LINK_HEADER
66 {       DWORD   MagicStr;       /* 0x00 'L','\0','\0','\0' */
67         GUID    MagicGuid;      /* 0x04 is CLSID_ShellLink */
68         DWORD   Flag1;          /* 0x14 describes elements following */
69         DWORD   Flag2;          /* 0x18 */
70         FILETIME Time1;         /* 0x1c */
71         FILETIME Time2;         /* 0x24 */
72         FILETIME Time3;         /* 0x2c */
73         DWORD   Unknown1;       /* 0x34 */
74         DWORD   Unknown2;       /* 0x38 icon number */
75         DWORD   fStartup;       /* 0x3c startup type */
76         DWORD   wHotKey;        /* 0x40 hotkey */
77         DWORD   Unknown5;       /* 0x44 */
78         DWORD   Unknown6;       /* 0x48 */
79         USHORT  PidlSize;       /* 0x4c */
80         ITEMIDLIST Pidl;        /* 0x4e */
81 } LINK_HEADER, * PLINK_HEADER;
82
83 #define LINK_HEADER_SIZE (sizeof(LINK_HEADER)-sizeof(ITEMIDLIST))
84
85 typedef struct
86 {
87         BYTE bWidth;
88         BYTE bHeight;
89         BYTE bColorCount;
90         BYTE bReserved;
91         WORD wPlanes;
92         WORD wBitCount;
93         DWORD dwBytesInRes;
94         WORD nID;
95 } GRPICONDIRENTRY;
96
97 typedef struct
98 {
99         WORD idReserved;
100         WORD idType;
101         WORD idCount;
102         GRPICONDIRENTRY idEntries[1];
103 } GRPICONDIR;
104
105 typedef struct
106 {
107         BYTE bWidth;
108         BYTE bHeight;
109         BYTE bColorCount;
110         BYTE bReserved;
111         WORD wPlanes;
112         WORD wBitCount;
113         DWORD dwBytesInRes;
114         DWORD dwImageOffset;
115 } ICONDIRENTRY;
116
117 typedef struct
118 {
119         WORD idReserved;
120         WORD idType;
121         WORD idCount;
122 } ICONDIR;
123
124
125 #include "poppack.h"
126
127 typedef struct
128 {
129         HRSRC *pResInfo;
130         int   nIndex;
131 } ENUMRESSTRUCT;
132
133 static ICOM_VTABLE(IShellLinkA)         slvt;
134 static ICOM_VTABLE(IShellLinkW)         slvtw;
135 static ICOM_VTABLE(IPersistFile)        pfvt;
136 static ICOM_VTABLE(IPersistStream)      psvt;
137
138 /* IShellLink Implementation */
139
140 typedef struct
141 {
142         ICOM_VFIELD(IShellLinkA);
143         DWORD                           ref;
144
145         ICOM_VTABLE(IShellLinkW)*       lpvtblw;
146         ICOM_VTABLE(IPersistFile)*      lpvtblPersistFile;
147         ICOM_VTABLE(IPersistStream)*    lpvtblPersistStream;
148
149         /* internal stream of the IPersistFile interface */
150         IStream*                        lpFileStream;
151
152         /* data structures according to the informations in the lnk */
153         LPSTR           sPath;
154         LPITEMIDLIST    pPidl;
155         WORD            wHotKey;
156         SYSTEMTIME      time1;
157         SYSTEMTIME      time2;
158         SYSTEMTIME      time3;
159
160         LPSTR           sIcoPath;
161         INT             iIcoNdx;
162         LPSTR           sArgs;
163         LPSTR           sWorkDir;
164         LPSTR           sDescription;
165 } IShellLinkImpl;
166
167 #define _IShellLinkW_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblw)))
168 #define _ICOM_THIS_From_IShellLinkW(class, name) class* This = (class*)(((char*)name)-_IShellLinkW_Offset);
169
170 #define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
171 #define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset);
172
173 #define _IPersistStream_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistStream)))
174 #define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset);
175 #define _IPersistStream_From_ICOM_THIS(class, name) class* StreamThis = (class*)(((char*)name)+_IPersistStream_Offset);
176
177
178 /* strdup on the process heap */
179 inline static LPSTR heap_strdup( LPCSTR str )
180 {
181     INT len = strlen(str) + 1;
182     LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
183     if (p) memcpy( p, str, len );
184     return p;
185 }
186
187
188 /**************************************************************************
189  *  IPersistFile_QueryInterface
190  */
191 static HRESULT WINAPI IPersistFile_fnQueryInterface(
192         IPersistFile* iface,
193         REFIID riid,
194         LPVOID *ppvObj)
195 {
196         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
197
198         TRACE("(%p)\n",This);
199
200         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
201 }
202
203 /******************************************************************************
204  * IPersistFile_AddRef
205  */
206 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
207 {
208         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
209
210         TRACE("(%p)->(count=%lu)\n",This,This->ref);
211
212         return IShellLinkA_AddRef((IShellLinkA*)This);
213 }
214 /******************************************************************************
215  * IPersistFile_Release
216  */
217 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
218 {
219         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
220
221         TRACE("(%p)->(count=%lu)\n",This,This->ref);
222
223         return IShellLinkA_Release((IShellLinkA*)This);
224 }
225
226 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
227 {
228         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
229         FIXME("(%p)\n",This);
230         return NOERROR;
231 }
232 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
233 {
234         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
235         FIXME("(%p)\n",This);
236         return NOERROR;
237 }
238 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
239 {
240         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
241         _IPersistStream_From_ICOM_THIS(IPersistStream, This)
242
243         LPSTR           sFile = HEAP_strdupWtoA ( GetProcessHeap(), 0, pszFileName);
244         HRESULT         hRet = E_FAIL;
245
246         TRACE("(%p, %s)\n",This, sFile);
247
248
249         if (This->lpFileStream)
250           IStream_Release(This->lpFileStream);
251
252         if SUCCEEDED(CreateStreamOnFile(sFile, &(This->lpFileStream)))
253         {
254           if SUCCEEDED (IPersistStream_Load(StreamThis, This->lpFileStream))
255           {
256             return NOERROR;
257           }
258         }
259
260         return hRet;
261 }
262
263
264 /* Icon extraction routines
265  *
266  * FIXME: should use PrivateExtractIcons and friends
267  * FIXME: should not use stdio
268  */
269
270 static BOOL SaveIconResAsXPM(const BITMAPINFO *pIcon, const char *szXPMFileName)
271 {
272     FILE *fXPMFile;
273     int nHeight;
274     int nXORWidthBytes;
275     int nANDWidthBytes;
276     BOOL b8BitColors;
277     int nColors;
278     BYTE *pXOR;
279     BYTE *pAND;
280     BOOL aColorUsed[256] = {0};
281     int nColorsUsed = 0;
282     int i,j;
283
284     if (!((pIcon->bmiHeader.biBitCount == 4) || (pIcon->bmiHeader.biBitCount == 8)))
285         return 0;
286
287     if (!(fXPMFile = fopen(szXPMFileName, "w")))
288         return 0;
289
290     nHeight = pIcon->bmiHeader.biHeight / 2;
291     nXORWidthBytes = 4 * ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount / 32)
292                           + ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount % 32) > 0));
293     nANDWidthBytes = 4 * ((pIcon->bmiHeader.biWidth / 32)
294                           + ((pIcon->bmiHeader.biWidth % 32) > 0));
295     b8BitColors = pIcon->bmiHeader.biBitCount == 8;
296     nColors = pIcon->bmiHeader.biClrUsed ? pIcon->bmiHeader.biClrUsed
297         : 1 << pIcon->bmiHeader.biBitCount;
298     pXOR = (BYTE*) pIcon + sizeof (BITMAPINFOHEADER) + (nColors * sizeof (RGBQUAD));
299     pAND = pXOR + nHeight * nXORWidthBytes;
300
301 #define MASK(x,y) (pAND[(x) / 8 + (nHeight - (y) - 1) * nANDWidthBytes] & (1 << (7 - (x) % 8)))
302 #define COLOR(x,y) (b8BitColors ? pXOR[(x) + (nHeight - (y) - 1) * nXORWidthBytes] : (x) % 2 ? pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF : (pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF0) >> 4)
303
304     for (i = 0; i < nHeight; i++)
305         for (j = 0; j < pIcon->bmiHeader.biWidth; j++)
306             if (!aColorUsed[COLOR(j,i)] && !MASK(j,i))
307             {
308                 aColorUsed[COLOR(j,i)] = TRUE;
309                 nColorsUsed++;
310             }
311
312     if (fprintf(fXPMFile, "/* XPM */\nstatic char *icon[] = {\n") <= 0)
313         goto error;
314     if (fprintf(fXPMFile, "\"%d %d %d %d\",\n",
315                 (int) pIcon->bmiHeader.biWidth, nHeight, nColorsUsed + 1, 2) <=0)
316         goto error;
317
318     for (i = 0; i < nColors; i++)
319         if (aColorUsed[i])
320             if (fprintf(fXPMFile, "\"%.2X c #%.2X%.2X%.2X\",\n", i, pIcon->bmiColors[i].rgbRed,
321                         pIcon->bmiColors[i].rgbGreen, pIcon->bmiColors[i].rgbBlue) <= 0)
322                 goto error;
323     if (fprintf(fXPMFile, "\"   c None\"") <= 0)
324         goto error;
325
326     for (i = 0; i < nHeight; i++)
327     {
328         if (fprintf(fXPMFile, ",\n\"") <= 0)
329             goto error;
330         for (j = 0; j < pIcon->bmiHeader.biWidth; j++)
331         {
332             if MASK(j,i)
333                 {
334                     if (fprintf(fXPMFile, "  ") <= 0)
335                         goto error;
336                 }
337             else
338                 if (fprintf(fXPMFile, "%.2X", COLOR(j,i)) <= 0)
339                     goto error;
340         }
341         if (fprintf(fXPMFile, "\"") <= 0)
342             goto error;
343     }
344     if (fprintf(fXPMFile, "};\n") <= 0)
345         goto error;
346
347 #undef MASK
348 #undef COLOR
349
350     fclose(fXPMFile);
351     return 1;
352
353  error:
354     fclose(fXPMFile);
355     unlink( szXPMFileName );
356     return 0;
357 }
358
359 static BOOL CALLBACK EnumResNameProc(HANDLE hModule, const char *lpszType, char *lpszName, LONG lParam)
360 {
361     ENUMRESSTRUCT *sEnumRes = (ENUMRESSTRUCT *) lParam;
362
363     if (!sEnumRes->nIndex--)
364     {
365       *sEnumRes->pResInfo = FindResourceA(hModule, lpszName, RT_GROUP_ICONA);
366       return FALSE;
367     }
368     else
369       return TRUE;
370 }
371
372 static int ExtractFromEXEDLL(const char *szFileName, int nIndex, const char *szXPMFileName)
373 {
374     HMODULE hModule;
375     HRSRC hResInfo;
376     char *lpName = NULL;
377     HGLOBAL hResData;
378     GRPICONDIR *pIconDir;
379     BITMAPINFO *pIcon;
380     ENUMRESSTRUCT sEnumRes;
381     int nMax = 0;
382     int nMaxBits = 0;
383     int i;
384
385     if (!(hModule = LoadLibraryExA(szFileName, 0, LOAD_LIBRARY_AS_DATAFILE)))
386     {
387         TRACE("LoadLibraryExA (%s) failed, error %ld\n", szFileName, GetLastError());
388         goto error1;
389     }
390
391     if (nIndex < 0)
392     {
393         hResInfo = FindResourceA(hModule, MAKEINTRESOURCEA(-nIndex), RT_GROUP_ICONA);
394         TRACE("FindResourceA (%s) called, return 0x%x, error %ld\n", szFileName, hResInfo, GetLastError());
395     }
396     else
397     {
398         sEnumRes.pResInfo = &hResInfo;
399         sEnumRes.nIndex = nIndex;
400         if (EnumResourceNamesA(hModule, RT_GROUP_ICONA, &EnumResNameProc, (LONG) &sEnumRes))
401         {
402             TRACE("EnumResourceNamesA failed, error %ld\n", GetLastError());
403             goto error2;
404         }
405     }
406
407     if (!hResInfo)
408     {
409         TRACE("ExtractFromEXEDLL failed, error %ld\n", GetLastError());
410         goto error2;
411     }
412
413     if (!(hResData = LoadResource(hModule, hResInfo)))
414     {
415         TRACE("LoadResource failed, error %ld\n", GetLastError());
416         goto error2;
417     }
418     if (!(pIconDir = LockResource(hResData)))
419     {
420         TRACE("LockResource failed, error %ld\n", GetLastError());
421         goto error3;
422     }
423
424     for (i = 0; i < pIconDir->idCount; i++)
425         if ((pIconDir->idEntries[i].wBitCount >= nMaxBits) && (pIconDir->idEntries[i].wBitCount <= 8))
426         {
427           if (pIconDir->idEntries[i].wBitCount > nMaxBits)
428           {
429               nMaxBits = pIconDir->idEntries[i].wBitCount;
430               nMax = 0;
431           }
432           if ((pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth) > nMax)
433           {
434               lpName = MAKEINTRESOURCEA(pIconDir->idEntries[i].nID);
435               nMax = pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth;
436           }
437         }
438
439     FreeResource(hResData);
440
441     if (!(hResInfo = FindResourceA(hModule, lpName, RT_ICONA)))
442     {
443         TRACE("Second FindResourceA failed, error %ld\n", GetLastError());
444         goto error2;
445     }
446     if (!(hResData = LoadResource(hModule, hResInfo)))
447     {
448         TRACE("Second LoadResource failed, error %ld\n", GetLastError());
449         goto error2;
450     }
451     if (!(pIcon = LockResource(hResData)))
452     {
453         TRACE("Second LockResource failed, error %ld\n", GetLastError());
454         goto error3;
455     }
456
457     if(!SaveIconResAsXPM(pIcon, szXPMFileName))
458     {
459         TRACE("Failed saving icon as XPM, error %ld\n", GetLastError());
460         goto error3;
461     }
462
463     FreeResource(hResData);
464     FreeLibrary(hModule);
465
466     return 1;
467
468  error3:
469     FreeResource(hResData);
470  error2:
471     FreeLibrary(hModule);
472  error1:
473     return 0;
474 }
475
476 /* get the Unix file name for a given path, allocating the string */
477 inline static char *get_unix_file_name( const char *dos )
478 {
479     char buffer[MAX_PATH];
480
481     if (!wine_get_unix_file_name( dos, buffer, sizeof(buffer) )) return NULL;
482     return heap_strdup( buffer );
483 }
484
485 static int ExtractFromICO(const char *szFileName, const char *szXPMFileName)
486 {
487     FILE *fICOFile;
488     ICONDIR iconDir;
489     ICONDIRENTRY *pIconDirEntry;
490     int nMax = 0;
491     int nIndex = 0;
492     void *pIcon;
493     int i;
494     char *filename;
495
496     filename = get_unix_file_name(szFileName);
497     if (!(fICOFile = fopen(filename, "r")))
498         goto error1;
499
500     if (fread(&iconDir, sizeof (ICONDIR), 1, fICOFile) != 1)
501         goto error2;
502     if ((iconDir.idReserved != 0) || (iconDir.idType != 1))
503         goto error2;
504
505     if ((pIconDirEntry = malloc(iconDir.idCount * sizeof (ICONDIRENTRY))) == NULL)
506         goto error2;
507     if (fread(pIconDirEntry, sizeof (ICONDIRENTRY), iconDir.idCount, fICOFile) != iconDir.idCount)
508         goto error3;
509
510     for (i = 0; i < iconDir.idCount; i++)
511         if ((pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth) > nMax)
512         {
513             nIndex = i;
514             nMax = pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth;
515         }
516     if ((pIcon = malloc(pIconDirEntry[nIndex].dwBytesInRes)) == NULL)
517         goto error3;
518     if (fseek(fICOFile, pIconDirEntry[nIndex].dwImageOffset, SEEK_SET))
519         goto error4;
520     if (fread(pIcon, pIconDirEntry[nIndex].dwBytesInRes, 1, fICOFile) != 1)
521         goto error4;
522
523     if(!SaveIconResAsXPM(pIcon, szXPMFileName))
524         goto error4;
525
526     free(pIcon);
527     free(pIconDirEntry);
528     fclose(fICOFile);
529
530     return 1;
531
532  error4:
533     free(pIcon);
534  error3:
535     free(pIconDirEntry);
536  error2:
537     fclose(fICOFile);
538  error1:
539     HeapFree(GetProcessHeap(), 0, filename);
540     return 0;
541 }
542
543 static BOOL create_default_icon( const char *filename )
544 {
545     FILE *fXPM;
546     int i;
547
548     if (!(fXPM = fopen(filename, "w"))) return FALSE;
549     fprintf(fXPM, "/* XPM */\nstatic char * icon[] = {");
550     for (i = 0; i < sizeof(wine_xpm)/sizeof(wine_xpm[0]); i++)
551         fprintf( fXPM, "\n\"%s\",", wine_xpm[i]);
552     fprintf( fXPM, "};\n" );
553     fclose( fXPM );
554     return TRUE;
555 }
556
557 /* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
558 static char *extract_icon( const char *path, int index)
559 {
560     int fd, nodefault = 1;
561     char *filename, tmpfn[25];
562
563     strcpy(tmpfn,"/tmp/icon.XXXXXX");
564     fd = mkstemp( tmpfn );
565     if (fd == -1)
566         return NULL;
567     filename = heap_strdup( tmpfn );
568     close(fd); /* not needed */
569
570     /* If icon path begins with a '*' then this is a deferred call */
571     if (path[0] == '*')
572     {
573         path++;
574         nodefault = 0;
575     }
576     if (ExtractFromEXEDLL( path, index, filename )) return filename;
577     if (ExtractFromICO( path, filename )) return filename;
578     if (!nodefault)
579         if (create_default_icon( filename )) return filename;
580     HeapFree( GetProcessHeap(), 0, filename );
581     return NULL;
582 }
583
584
585 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
586 {
587     HRESULT ret = NOERROR;
588     int pid, status;
589     char buffer[MAX_PATH], buff2[MAX_PATH], ascii_filename[MAX_PATH];
590     char *filename, *link_name, *p;
591     char *shell_link_app = NULL;
592     char *icon_name = NULL;
593     char *work_dir = NULL;
594     BOOL bDesktop;
595     HKEY hkey;
596
597     _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
598
599     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
600
601     if (!pszFileName || !This->sPath)
602         return ERROR_UNKNOWN;
603
604     /* check for .exe extension */
605     if (!(p = strrchr( This->sPath, '.' ))) return NOERROR;
606     if (strchr( p, '\\' ) || strchr( p, '/' )) return NOERROR;
607     if (strcasecmp( p, ".exe" )) return NOERROR;
608
609     /* check if ShellLinker configured */
610     buffer[0] = 0;
611     if (!RegOpenKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Wine",
612                         0, KEY_ALL_ACCESS, &hkey ))
613     {
614         DWORD type, count = sizeof(buffer);
615         if (RegQueryValueExA( hkey, "ShellLinker", 0, &type, buffer, &count )) buffer[0] = 0;
616         RegCloseKey( hkey );
617     }
618     if (!*buffer) return NOERROR;
619     shell_link_app = heap_strdup( buffer );
620
621     if (!WideCharToMultiByte( CP_ACP, 0, pszFileName, -1, ascii_filename, sizeof(ascii_filename), NULL, NULL))
622         return ERROR_UNKNOWN;
623     GetFullPathNameA( ascii_filename, sizeof(buff2), buff2, NULL );
624     filename = heap_strdup( buff2 );
625
626     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTUP, FALSE ))
627     {
628         /* ignore startup for now */
629         if (!strncasecmp( filename, buffer, strlen(buffer) )) goto done;
630     }
631     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_DESKTOPDIRECTORY, FALSE ))
632     {
633         if (!strncasecmp( filename, buffer, strlen(buffer) ))
634         {
635             link_name = filename + strlen(buffer);
636             bDesktop = TRUE;
637             goto found;
638         }
639     }
640     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTMENU, FALSE ))
641     {
642         if (!strncasecmp( filename, buffer, strlen(buffer) ))
643         {
644             link_name = filename + strlen(buffer);
645             bDesktop = FALSE;
646             goto found;
647         }
648     }
649     goto done;
650
651  found:
652     /* make link name a Unix name */
653     for (p = link_name; *p; p++) if (*p == '\\') *p = '/';
654     /* strip leading slashes */
655     while (*link_name == '/') link_name++;
656     /* remove extension */
657     if ((p = strrchr( link_name, '.' ))) *p = 0;
658
659     /* convert app working dir */
660     if (This->sWorkDir) work_dir = get_unix_file_name( This->sWorkDir );
661
662     /* extract the icon */
663     if (!(icon_name = extract_icon( This->sIcoPath && strlen(This->sIcoPath) ?
664                                       This->sIcoPath : This->sPath,
665                                       This->iIcoNdx )))
666     {
667         /* Couldn't extract icon --  defer this menu entry to runonce. */
668         HKEY hRunOnce;
669         char* buffer = NULL;
670
671         TRACE("Deferring icon creation to reboot.\n");
672         if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", 0,
673                 NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRunOnce, NULL) != ERROR_SUCCESS)
674         {
675             ret = ERROR_UNKNOWN;
676             goto done;
677         }
678         buffer = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * 3 + (This->sArgs ? strlen(This->sArgs) : 0) +
679                            (This->sDescription ? strlen(This->sDescription) : 0) + 200);
680         sprintf(buffer, "link:%s\xff*%s\xff%d\xff%s\xff%s\xff%s", This->sPath, This->sIcoPath, This->iIcoNdx,
681             This->sArgs ? This->sArgs : "", This->sDescription ? This->sDescription : "",
682             This->sWorkDir ? This->sWorkDir : "");
683         if (RegSetValueExA(hRunOnce, ascii_filename, 0, REG_SZ, buffer, strlen(buffer) + 1) != ERROR_SUCCESS)
684         {
685             HeapFree(GetProcessHeap(), 0, buffer);
686             RegCloseKey(hRunOnce);
687             ret = ERROR_UNKNOWN;
688             goto done;
689         }
690         HeapFree(GetProcessHeap(), 0, buffer);
691         RegCloseKey(hRunOnce);
692         goto done;
693     }
694
695     TRACE("linker app='%s' link='%s' mode=%s path='%s' args='%s' icon='%s' workdir='%s' descr='%s'\n",
696         shell_link_app, link_name, bDesktop ? "desktop" : "menu", This->sPath,
697         This->sArgs ? This->sArgs : "", icon_name, work_dir ? work_dir : "",
698         This->sDescription ? This->sDescription : "" );
699
700     if ((pid = fork()) == -1) goto done;
701     if (!pid)
702     {
703         int pos = 0;
704         char *argv[20];
705         argv[pos++] = shell_link_app;
706         argv[pos++] = "--link";
707         argv[pos++] = link_name;
708         argv[pos++] = "--path";
709         argv[pos++] = This->sPath;
710         argv[pos++] = bDesktop ? "--desktop" : "--menu";
711         if (This->sArgs && strlen(This->sArgs))
712         {
713             argv[pos++] = "--args";
714             argv[pos++] = This->sArgs;
715         }
716         if (icon_name)
717         {
718             argv[pos++] = "--icon";
719             argv[pos++] = icon_name;
720         }
721         if (This->sWorkDir && strlen(This->sWorkDir))
722         {
723             argv[pos++] = "--workdir";
724             argv[pos++] = This->sWorkDir;
725         }
726         if (This->sDescription && strlen(This->sDescription))
727         {
728             argv[pos++] = "--descr";
729             argv[pos++] = This->sDescription;
730         }
731         argv[pos] = NULL;
732         execvp( shell_link_app, argv );
733         _exit(1);
734     }
735
736     while (waitpid( pid, &status, 0 ) == -1)
737     {
738         if (errno != EINTR)
739         {
740             ret = ERROR_UNKNOWN;
741             goto done;
742         }
743     }
744     if (status) ret = E_ACCESSDENIED;
745
746  done:
747     if (icon_name) unlink( icon_name );
748     HeapFree( GetProcessHeap(), 0, shell_link_app );
749     HeapFree( GetProcessHeap(), 0, filename );
750     HeapFree( GetProcessHeap(), 0, icon_name );
751     HeapFree( GetProcessHeap(), 0, work_dir );
752     return ret;
753 }
754
755 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
756 {
757         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
758         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
759         return NOERROR;
760 }
761 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
762 {
763         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
764         FIXME("(%p)\n",This);
765         return NOERROR;
766 }
767
768 static ICOM_VTABLE(IPersistFile) pfvt =
769 {
770         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
771         IPersistFile_fnQueryInterface,
772         IPersistFile_fnAddRef,
773         IPersistFile_fnRelease,
774         IPersistFile_fnGetClassID,
775         IPersistFile_fnIsDirty,
776         IPersistFile_fnLoad,
777         IPersistFile_fnSave,
778         IPersistFile_fnSaveCompleted,
779         IPersistFile_fnGetCurFile
780 };
781
782 /************************************************************************
783  * IPersistStream_QueryInterface
784  */
785 static HRESULT WINAPI IPersistStream_fnQueryInterface(
786         IPersistStream* iface,
787         REFIID     riid,
788         VOID**     ppvoid)
789 {
790         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
791
792         TRACE("(%p)\n",This);
793
794         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
795 }
796
797 /************************************************************************
798  * IPersistStream_Release
799  */
800 static ULONG WINAPI IPersistStream_fnRelease(
801         IPersistStream* iface)
802 {
803         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
804
805         TRACE("(%p)\n",This);
806
807         return IShellLinkA_Release((IShellLinkA*)This);
808 }
809
810 /************************************************************************
811  * IPersistStream_AddRef
812  */
813 static ULONG WINAPI IPersistStream_fnAddRef(
814         IPersistStream* iface)
815 {
816         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
817
818         TRACE("(%p)\n",This);
819
820         return IShellLinkA_AddRef((IShellLinkA*)This);
821 }
822
823 /************************************************************************
824  * IPersistStream_GetClassID
825  *
826  */
827 static HRESULT WINAPI IPersistStream_fnGetClassID(
828         IPersistStream* iface,
829         CLSID* pClassID)
830 {
831         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
832
833         TRACE("(%p)\n", This);
834
835         if (pClassID==0)
836           return E_POINTER;
837
838 /*      memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
839
840         return S_OK;
841 }
842
843 /************************************************************************
844  * IPersistStream_IsDirty (IPersistStream)
845  */
846 static HRESULT WINAPI IPersistStream_fnIsDirty(
847         IPersistStream*  iface)
848 {
849         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
850
851         TRACE("(%p)\n", This);
852
853         return S_OK;
854 }
855 /************************************************************************
856  * IPersistStream_Load (IPersistStream)
857  */
858
859 static HRESULT WINAPI IPersistStream_fnLoad(
860         IPersistStream*  iface,
861         IStream*         pLoadStream)
862 {
863         PLINK_HEADER lpLinkHeader = HeapAlloc(GetProcessHeap(), 0, LINK_HEADER_SIZE);
864         ULONG   dwBytesRead;
865         DWORD   ret = E_FAIL;
866         char    sTemp[MAX_PATH];
867
868         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
869
870         TRACE("(%p)(%p)\n", This, pLoadStream);
871
872         if ( ! pLoadStream)
873         {
874           return STG_E_INVALIDPOINTER;
875         }
876
877         IStream_AddRef (pLoadStream);
878         if(!lpLinkHeader)
879           goto end;
880
881         dwBytesRead = 0;
882         if (!(SUCCEEDED(IStream_Read(pLoadStream, lpLinkHeader, LINK_HEADER_SIZE, &dwBytesRead))))
883           goto end;
884
885         if (dwBytesRead != LINK_HEADER_SIZE)
886           goto end;
887
888         if ( (lpLinkHeader->MagicStr != 0x0000004CL) || !IsEqualIID(&lpLinkHeader->MagicGuid, &CLSID_ShellLink) )
889           goto end;
890
891         if(lpLinkHeader->PidlSize)
892         {
893           lpLinkHeader = HeapReAlloc(GetProcessHeap(), 0, lpLinkHeader, LINK_HEADER_SIZE+lpLinkHeader->PidlSize);
894           if (!lpLinkHeader)
895             goto end;
896           dwBytesRead = 0;
897           if (!(SUCCEEDED(IStream_Read(pLoadStream, &(lpLinkHeader->Pidl), lpLinkHeader->PidlSize, &dwBytesRead))))
898             goto end;
899           if(dwBytesRead != lpLinkHeader->PidlSize)
900             goto end;
901
902           if (pcheck (&lpLinkHeader->Pidl))
903           {
904             This->pPidl = ILClone (&lpLinkHeader->Pidl);
905
906             SHGetPathFromIDListA(&lpLinkHeader->Pidl, sTemp);
907             This->sPath = heap_strdup( sTemp );
908           }
909         }
910         This->wHotKey = lpLinkHeader->wHotKey;
911         FileTimeToSystemTime (&lpLinkHeader->Time1, &This->time1);
912         FileTimeToSystemTime (&lpLinkHeader->Time2, &This->time2);
913         FileTimeToSystemTime (&lpLinkHeader->Time3, &This->time3);
914 #if 1
915         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
916         TRACE("-- time1: %s\n", sTemp);
917         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
918         TRACE("-- time1: %s\n", sTemp);
919         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
920         TRACE("-- time1: %s\n", sTemp);
921         pdump (This->pPidl);
922 #endif
923         ret = S_OK;
924
925 end:
926         IStream_Release (pLoadStream);
927
928         pdump(This->pPidl);
929
930         HeapFree(GetProcessHeap(), 0, lpLinkHeader);
931
932         return ret;
933 }
934
935 /************************************************************************
936  * IPersistStream_Save (IPersistStream)
937  */
938 static HRESULT WINAPI IPersistStream_fnSave(
939         IPersistStream*  iface,
940         IStream*         pOutStream,
941         BOOL             fClearDirty)
942 {
943         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
944
945         TRACE("(%p) %p %x\n", This, pOutStream, fClearDirty);
946
947         return E_NOTIMPL;
948 }
949
950 /************************************************************************
951  * IPersistStream_GetSizeMax (IPersistStream)
952  */
953 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
954         IPersistStream*  iface,
955         ULARGE_INTEGER*  pcbSize)
956 {
957         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
958
959         TRACE("(%p)\n", This);
960
961         return E_NOTIMPL;
962 }
963
964 static ICOM_VTABLE(IPersistStream) psvt =
965 {
966         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
967         IPersistStream_fnQueryInterface,
968         IPersistStream_fnAddRef,
969         IPersistStream_fnRelease,
970         IPersistStream_fnGetClassID,
971         IPersistStream_fnIsDirty,
972         IPersistStream_fnLoad,
973         IPersistStream_fnSave,
974         IPersistStream_fnGetSizeMax
975 };
976
977 /**************************************************************************
978  *        IShellLink_Constructor
979  */
980 HRESULT WINAPI IShellLink_Constructor (
981         IUnknown * pUnkOuter,
982         REFIID riid,
983         LPVOID * ppv)
984 {
985         IShellLinkImpl * sl;
986
987         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
988
989         *ppv = NULL;
990
991         if(pUnkOuter) return CLASS_E_NOAGGREGATION;
992         sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
993         if (!sl) return E_OUTOFMEMORY;
994
995         sl->ref = 1;
996         ICOM_VTBL(sl) = &slvt;
997         sl->lpvtblw = &slvtw;
998         sl->lpvtblPersistFile = &pfvt;
999         sl->lpvtblPersistStream = &psvt;
1000
1001         TRACE("(%p)->()\n",sl);
1002
1003         if (IsEqualIID(riid, &IID_IUnknown) ||
1004             IsEqualIID(riid, &IID_IShellLinkA))
1005             *ppv = sl;
1006         else if (IsEqualIID(riid, &IID_IShellLinkW))
1007             *ppv = &(sl->lpvtblw);
1008         else {
1009             LocalFree((HLOCAL)sl);
1010             ERR("E_NOINTERFACE\n");
1011             return E_NOINTERFACE;
1012         }
1013
1014         return S_OK;
1015 }
1016
1017 /**************************************************************************
1018  *  IShellLinkA_QueryInterface
1019  */
1020 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1021 {
1022         ICOM_THIS(IShellLinkImpl, iface);
1023
1024         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
1025
1026         *ppvObj = NULL;
1027
1028         if(IsEqualIID(riid, &IID_IUnknown) ||
1029            IsEqualIID(riid, &IID_IShellLinkA))
1030         {
1031           *ppvObj = This;
1032         }
1033         else if(IsEqualIID(riid, &IID_IShellLinkW))
1034         {
1035           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
1036         }
1037         else if(IsEqualIID(riid, &IID_IPersistFile))
1038         {
1039           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
1040         }
1041         else if(IsEqualIID(riid, &IID_IPersistStream))
1042         {
1043           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
1044         }
1045
1046         if(*ppvObj)
1047         {
1048           IUnknown_AddRef((IUnknown*)(*ppvObj));
1049           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
1050           return S_OK;
1051         }
1052         TRACE("-- Interface: E_NOINTERFACE\n");
1053         return E_NOINTERFACE;
1054 }
1055 /******************************************************************************
1056  * IShellLinkA_AddRef
1057  */
1058 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1059 {
1060         ICOM_THIS(IShellLinkImpl, iface);
1061
1062         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1063
1064         return ++(This->ref);
1065 }
1066 /******************************************************************************
1067  *      IShellLinkA_Release
1068  */
1069 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1070 {
1071         ICOM_THIS(IShellLinkImpl, iface);
1072
1073         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1074
1075         if (!--(This->ref))
1076         { TRACE("-- destroying IShellLink(%p)\n",This);
1077
1078           if (This->sIcoPath)
1079             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1080
1081           if (This->sArgs)
1082             HeapFree(GetProcessHeap(), 0, This->sArgs);
1083
1084           if (This->sWorkDir)
1085             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1086
1087           if (This->sDescription)
1088             HeapFree(GetProcessHeap(), 0, This->sDescription);
1089
1090           if (This->sPath)
1091             HeapFree(GetProcessHeap(),0,This->sPath);
1092
1093           if (This->pPidl)
1094             SHFree(This->pPidl);
1095
1096           if (This->lpFileStream)
1097             IStream_Release(This->lpFileStream);
1098
1099           This->iIcoNdx = 0;
1100
1101           LocalFree((HANDLE)This);
1102           return 0;
1103         }
1104         return This->ref;
1105 }
1106
1107 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1108 {
1109         ICOM_THIS(IShellLinkImpl, iface);
1110
1111         TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",This, pszFile, cchMaxPath, pfd, fFlags, debugstr_a(This->sPath));
1112
1113         if (This->sPath)
1114           lstrcpynA(pszFile,This->sPath, cchMaxPath);
1115         else
1116           return E_FAIL;
1117
1118         return NOERROR;
1119 }
1120 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1121 {
1122         ICOM_THIS(IShellLinkImpl, iface);
1123
1124         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1125
1126         *ppidl = ILClone(This->pPidl);
1127         return NOERROR;
1128 }
1129 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1130 {
1131         ICOM_THIS(IShellLinkImpl, iface);
1132
1133         TRACE("(%p)->(pidl=%p)\n",This, pidl);
1134
1135         if (This->pPidl)
1136             SHFree(This->pPidl);
1137         This->pPidl = ILClone (pidl);
1138         return NOERROR;
1139 }
1140 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1141 {
1142         ICOM_THIS(IShellLinkImpl, iface);
1143
1144         FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1145         lstrcpynA(pszName,"Description, FIXME",cchMaxName);
1146         return NOERROR;
1147 }
1148 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1149 {
1150         ICOM_THIS(IShellLinkImpl, iface);
1151
1152         TRACE("(%p)->(pName=%s)\n", This, pszName);
1153
1154         if (This->sDescription)
1155             HeapFree(GetProcessHeap(), 0, This->sDescription);
1156         if (!(This->sDescription = heap_strdup(pszName)))
1157             return E_OUTOFMEMORY;
1158
1159         return NOERROR;
1160 }
1161 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1162 {
1163         ICOM_THIS(IShellLinkImpl, iface);
1164
1165         TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1166
1167         lstrcpynA( pszDir, This->sWorkDir ? This->sWorkDir : "", cchMaxPath );
1168
1169         return NOERROR;
1170 }
1171 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1172 {
1173         ICOM_THIS(IShellLinkImpl, iface);
1174
1175         TRACE("(%p)->(dir=%s)\n",This, pszDir);
1176
1177         if (This->sWorkDir)
1178             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1179         if (!(This->sWorkDir = heap_strdup(pszDir)))
1180             return E_OUTOFMEMORY;
1181
1182         return NOERROR;
1183 }
1184 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1185 {
1186         ICOM_THIS(IShellLinkImpl, iface);
1187
1188         TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1189
1190         lstrcpynA( pszArgs, This->sArgs ? This->sArgs : "", cchMaxPath );
1191
1192         return NOERROR;
1193 }
1194 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1195 {
1196         ICOM_THIS(IShellLinkImpl, iface);
1197
1198         TRACE("(%p)->(args=%s)\n",This, pszArgs);
1199
1200         if (This->sArgs)
1201             HeapFree(GetProcessHeap(), 0, This->sArgs);
1202         if (!(This->sArgs = heap_strdup(pszArgs)))
1203             return E_OUTOFMEMORY;
1204
1205         return NOERROR;
1206 }
1207 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1208 {
1209         ICOM_THIS(IShellLinkImpl, iface);
1210
1211         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1212
1213         *pwHotkey = This->wHotKey;
1214
1215         return NOERROR;
1216 }
1217 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1218 {
1219         ICOM_THIS(IShellLinkImpl, iface);
1220
1221         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1222
1223         This->wHotKey = wHotkey;
1224
1225         return NOERROR;
1226 }
1227 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1228 {
1229         ICOM_THIS(IShellLinkImpl, iface);
1230
1231         FIXME("(%p)->(%p)\n",This, piShowCmd);
1232         *piShowCmd=0;
1233         return NOERROR;
1234 }
1235 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1236 {
1237         ICOM_THIS(IShellLinkImpl, iface);
1238
1239         FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
1240         return NOERROR;
1241 }
1242 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1243 {
1244         ICOM_THIS(IShellLinkImpl, iface);
1245
1246         TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1247
1248         lstrcpynA( pszIconPath, This->sIcoPath ? This->sIcoPath : "", cchIconPath );
1249         *piIcon = This->iIcoNdx;
1250
1251         return NOERROR;
1252 }
1253 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1254 {
1255         ICOM_THIS(IShellLinkImpl, iface);
1256
1257         TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1258
1259         if (This->sIcoPath)
1260             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1261         if (!(This->sIcoPath = heap_strdup(pszIconPath)))
1262             return E_OUTOFMEMORY;
1263         This->iIcoNdx = iIcon;
1264
1265         return NOERROR;
1266 }
1267 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1268 {
1269         ICOM_THIS(IShellLinkImpl, iface);
1270
1271         FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1272         return NOERROR;
1273 }
1274 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1275 {
1276         ICOM_THIS(IShellLinkImpl, iface);
1277
1278         FIXME("(%p)->(hwnd=%x flags=%lx)\n",This, hwnd, fFlags);
1279         return NOERROR;
1280 }
1281 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1282 {
1283         ICOM_THIS(IShellLinkImpl, iface);
1284
1285         TRACE("(%p)->(path=%s)\n",This, pszFile);
1286
1287         if (This->sPath)
1288             HeapFree(GetProcessHeap(), 0, This->sPath);
1289         if (!(This->sPath = heap_strdup(pszFile)))
1290             return E_OUTOFMEMORY;
1291
1292         return NOERROR;
1293 }
1294
1295 /**************************************************************************
1296 * IShellLink Implementation
1297 */
1298
1299 static ICOM_VTABLE(IShellLinkA) slvt =
1300 {
1301         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1302         IShellLinkA_fnQueryInterface,
1303         IShellLinkA_fnAddRef,
1304         IShellLinkA_fnRelease,
1305         IShellLinkA_fnGetPath,
1306         IShellLinkA_fnGetIDList,
1307         IShellLinkA_fnSetIDList,
1308         IShellLinkA_fnGetDescription,
1309         IShellLinkA_fnSetDescription,
1310         IShellLinkA_fnGetWorkingDirectory,
1311         IShellLinkA_fnSetWorkingDirectory,
1312         IShellLinkA_fnGetArguments,
1313         IShellLinkA_fnSetArguments,
1314         IShellLinkA_fnGetHotkey,
1315         IShellLinkA_fnSetHotkey,
1316         IShellLinkA_fnGetShowCmd,
1317         IShellLinkA_fnSetShowCmd,
1318         IShellLinkA_fnGetIconLocation,
1319         IShellLinkA_fnSetIconLocation,
1320         IShellLinkA_fnSetRelativePath,
1321         IShellLinkA_fnResolve,
1322         IShellLinkA_fnSetPath
1323 };
1324
1325
1326 /**************************************************************************
1327  *  IShellLinkW_fnQueryInterface
1328  */
1329 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1330   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1331 {
1332         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1333
1334         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1335 }
1336
1337 /******************************************************************************
1338  * IShellLinkW_fnAddRef
1339  */
1340 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1341 {
1342         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1343
1344         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1345
1346         return IShellLinkA_AddRef((IShellLinkA*)This);
1347 }
1348 /******************************************************************************
1349  * IShellLinkW_fnRelease
1350  */
1351
1352 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1353 {
1354         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1355
1356         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1357
1358         return IShellLinkA_Release((IShellLinkA*)This);
1359 }
1360
1361 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1362 {
1363         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1364
1365         FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
1366         MultiByteToWideChar( CP_ACP, 0, "c:\\foo.bar", -1, pszFile, cchMaxPath );
1367         return NOERROR;
1368 }
1369
1370 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1371 {
1372         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1373
1374         FIXME("(%p)->(ppidl=%p)\n",This, ppidl);
1375         *ppidl = _ILCreateDesktop();
1376         return NOERROR;
1377 }
1378
1379 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1380 {
1381         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1382
1383         FIXME("(%p)->(pidl=%p)\n",This, pidl);
1384         return NOERROR;
1385 }
1386
1387 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1388 {
1389         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1390
1391         FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1392         MultiByteToWideChar( CP_ACP, 0, "Description, FIXME", -1, pszName, cchMaxName );
1393         return NOERROR;
1394 }
1395
1396 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1397 {
1398         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1399
1400         TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1401
1402         if (This->sDescription)
1403             HeapFree(GetProcessHeap(), 0, This->sDescription);
1404         if (!(This->sDescription = HEAP_strdupWtoA(GetProcessHeap(), 0, pszName)))
1405             return E_OUTOFMEMORY;
1406
1407         return NOERROR;
1408 }
1409
1410 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1411 {
1412         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1413
1414         TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1415
1416         MultiByteToWideChar( CP_ACP, 0, This->sWorkDir ? This->sWorkDir : "", -1, pszDir, cchMaxPath );
1417
1418         return NOERROR;
1419 }
1420
1421 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1422 {
1423         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1424
1425         TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1426
1427         if (This->sWorkDir)
1428             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1429         if (!(This->sWorkDir = HEAP_strdupWtoA(GetProcessHeap(), 0, pszDir)))
1430             return E_OUTOFMEMORY;
1431
1432         return NOERROR;
1433 }
1434
1435 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1436 {
1437         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1438
1439         TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1440
1441         MultiByteToWideChar( CP_ACP, 0, This->sArgs ? This->sArgs : "", -1, pszArgs, cchMaxPath );
1442
1443         return NOERROR;
1444 }
1445
1446 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1447 {
1448         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1449
1450         TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1451
1452         if (This->sArgs)
1453             HeapFree(GetProcessHeap(), 0, This->sArgs);
1454         if (!(This->sArgs = HEAP_strdupWtoA(GetProcessHeap(), 0, pszArgs)))
1455             return E_OUTOFMEMORY;
1456
1457         return NOERROR;
1458 }
1459
1460 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1461 {
1462         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1463
1464         FIXME("(%p)->(%p)\n",This, pwHotkey);
1465         *pwHotkey=0x0;
1466         return NOERROR;
1467 }
1468
1469 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1470 {
1471         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1472
1473         FIXME("(%p)->(hotkey=%x)\n",This, wHotkey);
1474         return NOERROR;
1475 }
1476
1477 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1478 {
1479         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1480
1481         FIXME("(%p)->(%p)\n",This, piShowCmd);
1482         *piShowCmd=0;
1483         return NOERROR;
1484 }
1485
1486 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1487 {
1488         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1489
1490         FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
1491         return NOERROR;
1492 }
1493
1494 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1495 {
1496         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1497
1498         TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1499
1500         MultiByteToWideChar( CP_ACP, 0, This->sIcoPath ? This->sIcoPath : "", -1, pszIconPath, cchIconPath );
1501         *piIcon = This->iIcoNdx;
1502
1503         return NOERROR;
1504 }
1505
1506 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1507 {
1508         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1509
1510         TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1511
1512         if (This->sIcoPath)
1513             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1514         if (!(This->sIcoPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszIconPath)))
1515             return E_OUTOFMEMORY;
1516         This->iIcoNdx = iIcon;
1517
1518         return NOERROR;
1519 }
1520
1521 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1522 {
1523         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1524
1525         FIXME("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1526         return NOERROR;
1527 }
1528
1529 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1530 {
1531         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1532
1533         FIXME("(%p)->(hwnd=%x flags=%lx)\n",This, hwnd, fFlags);
1534         return NOERROR;
1535 }
1536
1537 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1538 {
1539         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1540
1541         TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1542
1543         if (This->sPath)
1544             HeapFree(GetProcessHeap(), 0, This->sPath);
1545         if (!(This->sPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszFile)))
1546             return E_OUTOFMEMORY;
1547
1548         return NOERROR;
1549 }
1550
1551 /**************************************************************************
1552 * IShellLinkW Implementation
1553 */
1554
1555 static ICOM_VTABLE(IShellLinkW) slvtw =
1556 {
1557         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1558         IShellLinkW_fnQueryInterface,
1559         IShellLinkW_fnAddRef,
1560         IShellLinkW_fnRelease,
1561         IShellLinkW_fnGetPath,
1562         IShellLinkW_fnGetIDList,
1563         IShellLinkW_fnSetIDList,
1564         IShellLinkW_fnGetDescription,
1565         IShellLinkW_fnSetDescription,
1566         IShellLinkW_fnGetWorkingDirectory,
1567         IShellLinkW_fnSetWorkingDirectory,
1568         IShellLinkW_fnGetArguments,
1569         IShellLinkW_fnSetArguments,
1570         IShellLinkW_fnGetHotkey,
1571         IShellLinkW_fnSetHotkey,
1572         IShellLinkW_fnGetShowCmd,
1573         IShellLinkW_fnSetShowCmd,
1574         IShellLinkW_fnGetIconLocation,
1575         IShellLinkW_fnSetIconLocation,
1576         IShellLinkW_fnSetRelativePath,
1577         IShellLinkW_fnResolve,
1578         IShellLinkW_fnSetPath
1579 };