Eliminate segfault due to NULL This->sIcoPath.
[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 %p, 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,
401                                (ENUMRESNAMEPROCA)&EnumResNameProc,
402                                (LONG) &sEnumRes))
403         {
404             TRACE("EnumResourceNamesA failed, error %ld\n", GetLastError());
405             goto error2;
406         }
407     }
408
409     if (!hResInfo)
410     {
411         TRACE("ExtractFromEXEDLL failed, error %ld\n", GetLastError());
412         goto error2;
413     }
414
415     if (!(hResData = LoadResource(hModule, hResInfo)))
416     {
417         TRACE("LoadResource failed, error %ld\n", GetLastError());
418         goto error2;
419     }
420     if (!(pIconDir = LockResource(hResData)))
421     {
422         TRACE("LockResource failed, error %ld\n", GetLastError());
423         goto error3;
424     }
425
426     for (i = 0; i < pIconDir->idCount; i++)
427         if ((pIconDir->idEntries[i].wBitCount >= nMaxBits) && (pIconDir->idEntries[i].wBitCount <= 8))
428         {
429           if (pIconDir->idEntries[i].wBitCount > nMaxBits)
430           {
431               nMaxBits = pIconDir->idEntries[i].wBitCount;
432               nMax = 0;
433           }
434           if ((pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth) > nMax)
435           {
436               lpName = MAKEINTRESOURCEA(pIconDir->idEntries[i].nID);
437               nMax = pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth;
438           }
439         }
440
441     FreeResource(hResData);
442
443     if (!(hResInfo = FindResourceA(hModule, lpName, RT_ICONA)))
444     {
445         TRACE("Second FindResourceA failed, error %ld\n", GetLastError());
446         goto error2;
447     }
448     if (!(hResData = LoadResource(hModule, hResInfo)))
449     {
450         TRACE("Second LoadResource failed, error %ld\n", GetLastError());
451         goto error2;
452     }
453     if (!(pIcon = LockResource(hResData)))
454     {
455         TRACE("Second LockResource failed, error %ld\n", GetLastError());
456         goto error3;
457     }
458
459     if(!SaveIconResAsXPM(pIcon, szXPMFileName))
460     {
461         TRACE("Failed saving icon as XPM, error %ld\n", GetLastError());
462         goto error3;
463     }
464
465     FreeResource(hResData);
466     FreeLibrary(hModule);
467
468     return 1;
469
470  error3:
471     FreeResource(hResData);
472  error2:
473     FreeLibrary(hModule);
474  error1:
475     return 0;
476 }
477
478 /* get the Unix file name for a given path, allocating the string */
479 inline static char *get_unix_file_name( const char *dos )
480 {
481     char buffer[MAX_PATH];
482
483     if (!wine_get_unix_file_name( dos, buffer, sizeof(buffer) )) return NULL;
484     return heap_strdup( buffer );
485 }
486
487 static int ExtractFromICO(const char *szFileName, const char *szXPMFileName)
488 {
489     FILE *fICOFile;
490     ICONDIR iconDir;
491     ICONDIRENTRY *pIconDirEntry;
492     int nMax = 0;
493     int nIndex = 0;
494     void *pIcon;
495     int i;
496     char *filename;
497
498     filename = get_unix_file_name(szFileName);
499     if (!(fICOFile = fopen(filename, "r")))
500         goto error1;
501
502     if (fread(&iconDir, sizeof (ICONDIR), 1, fICOFile) != 1)
503         goto error2;
504     if ((iconDir.idReserved != 0) || (iconDir.idType != 1))
505         goto error2;
506
507     if ((pIconDirEntry = malloc(iconDir.idCount * sizeof (ICONDIRENTRY))) == NULL)
508         goto error2;
509     if (fread(pIconDirEntry, sizeof (ICONDIRENTRY), iconDir.idCount, fICOFile) != iconDir.idCount)
510         goto error3;
511
512     for (i = 0; i < iconDir.idCount; i++)
513         if ((pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth) > nMax)
514         {
515             nIndex = i;
516             nMax = pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth;
517         }
518     if ((pIcon = malloc(pIconDirEntry[nIndex].dwBytesInRes)) == NULL)
519         goto error3;
520     if (fseek(fICOFile, pIconDirEntry[nIndex].dwImageOffset, SEEK_SET))
521         goto error4;
522     if (fread(pIcon, pIconDirEntry[nIndex].dwBytesInRes, 1, fICOFile) != 1)
523         goto error4;
524
525     if(!SaveIconResAsXPM(pIcon, szXPMFileName))
526         goto error4;
527
528     free(pIcon);
529     free(pIconDirEntry);
530     fclose(fICOFile);
531
532     return 1;
533
534  error4:
535     free(pIcon);
536  error3:
537     free(pIconDirEntry);
538  error2:
539     fclose(fICOFile);
540  error1:
541     HeapFree(GetProcessHeap(), 0, filename);
542     return 0;
543 }
544
545 static BOOL create_default_icon( const char *filename )
546 {
547     FILE *fXPM;
548     int i;
549
550     if (!(fXPM = fopen(filename, "w"))) return FALSE;
551     fprintf(fXPM, "/* XPM */\nstatic char * icon[] = {");
552     for (i = 0; i < sizeof(wine_xpm)/sizeof(wine_xpm[0]); i++)
553         fprintf( fXPM, "\n\"%s\",", wine_xpm[i]);
554     fprintf( fXPM, "};\n" );
555     fclose( fXPM );
556     return TRUE;
557 }
558
559 /* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
560 static char *extract_icon( const char *path, int index)
561 {
562     int fd, nodefault = 1;
563     char *filename, tmpfn[25];
564
565     strcpy(tmpfn,"/tmp/icon.XXXXXX.xpm");
566     fd = mkstemps( tmpfn, 4 );
567     if (fd == -1)
568         return NULL;
569     filename = heap_strdup( tmpfn );
570     close(fd); /* not needed */
571
572     /* If icon path begins with a '*' then this is a deferred call */
573     if (path[0] == '*')
574     {
575         path++;
576         nodefault = 0;
577     }
578     if (ExtractFromEXEDLL( path, index, filename )) return filename;
579     if (ExtractFromICO( path, filename )) return filename;
580     if (!nodefault)
581         if (create_default_icon( filename )) return filename;
582     HeapFree( GetProcessHeap(), 0, filename );
583     return NULL;
584 }
585
586 /* This escapes \ in filenames */
587 static LPSTR
588 escape(LPCSTR arg) {
589     LPSTR      narg, x;
590
591     narg = HeapAlloc(GetProcessHeap(),0,2*strlen(arg)+2);
592     x = narg;
593     while (*arg) {
594         *x++ = *arg;
595         if (*arg == '\\')
596             *x++='\\'; /* escape \ */
597         arg++;
598     }
599     *x = 0;
600     return narg;
601 }
602
603 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
604 {
605     HRESULT ret = NOERROR;
606     int pid, status;
607     char buffer[MAX_PATH], buff2[MAX_PATH], ascii_filename[MAX_PATH];
608     char *filename, *link_name, *p;
609     char *shell_link_app = NULL;
610     char *icon_name = NULL;
611     char *work_dir = NULL;
612     char *escaped_path = NULL;
613     char *escaped_args = NULL;
614     BOOL bDesktop;
615     HKEY hkey;
616
617     _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
618
619     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
620
621     if (!pszFileName || !This->sPath)
622         return ERROR_UNKNOWN;
623
624     /* check for .exe extension */
625     if (!(p = strrchr( This->sPath, '.' ))) return NOERROR;
626     if (strchr( p, '\\' ) || strchr( p, '/' )) return NOERROR;
627     if (strcasecmp( p, ".exe" )) return NOERROR;
628
629     /* check if ShellLinker configured */
630     buffer[0] = 0;
631     if (!RegOpenKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Wine",
632                         0, KEY_ALL_ACCESS, &hkey ))
633     {
634         DWORD type, count = sizeof(buffer);
635         if (RegQueryValueExA( hkey, "ShellLinker", 0, &type, buffer, &count )) buffer[0] = 0;
636         RegCloseKey( hkey );
637     }
638     if (!*buffer) return NOERROR;
639     shell_link_app = heap_strdup( buffer );
640
641     if (!WideCharToMultiByte( CP_ACP, 0, pszFileName, -1, ascii_filename, sizeof(ascii_filename), NULL, NULL))
642         return ERROR_UNKNOWN;
643     GetFullPathNameA( ascii_filename, sizeof(buff2), buff2, NULL );
644     filename = heap_strdup( buff2 );
645
646     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTUP, FALSE ))
647     {
648         /* ignore startup for now */
649         if (!strncasecmp( filename, buffer, strlen(buffer) )) goto done;
650     }
651     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_DESKTOPDIRECTORY, FALSE ))
652     {
653         if (!strncasecmp( filename, buffer, strlen(buffer) ))
654         {
655             link_name = filename + strlen(buffer);
656             bDesktop = TRUE;
657             goto found;
658         }
659     }
660     if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTMENU, FALSE ))
661     {
662         if (!strncasecmp( filename, buffer, strlen(buffer) ))
663         {
664             link_name = filename + strlen(buffer);
665             bDesktop = FALSE;
666             goto found;
667         }
668     }
669     goto done;
670
671  found:
672     /* make link name a Unix name */
673     for (p = link_name; *p; p++) if (*p == '\\') *p = '/';
674     /* strip leading slashes */
675     while (*link_name == '/') link_name++;
676     /* remove extension */
677     if ((p = strrchr( link_name, '.' ))) *p = 0;
678
679     /* convert app working dir */
680     if (This->sWorkDir) work_dir = get_unix_file_name( This->sWorkDir );
681
682     /* extract the icon */
683     if (!(icon_name = extract_icon( This->sIcoPath && strlen(This->sIcoPath) ?
684                                       This->sIcoPath : This->sPath,
685                                       This->iIcoNdx )))
686     {
687         /* Couldn't extract icon --  defer this menu entry to runonce. */
688         HKEY hRunOnce;
689         char* buffer = NULL;
690
691         TRACE("Deferring icon creation to reboot.\n");
692         if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", 0,
693                 NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRunOnce, NULL) != ERROR_SUCCESS)
694         {
695             ret = ERROR_UNKNOWN;
696             goto done;
697         }
698         buffer = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * 3 + (This->sArgs ? strlen(This->sArgs) : 0) +
699                            (This->sDescription ? strlen(This->sDescription) : 0) + 200);
700         sprintf(buffer, "link:%s\xff*%s\xff%d\xff%s\xff%s\xff%s", This->sPath, This->sIcoPath ? This->sIcoPath : "", This->iIcoNdx,
701             This->sArgs ? This->sArgs : "", This->sDescription ? This->sDescription : "",
702             This->sWorkDir ? This->sWorkDir : "");
703         if (RegSetValueExA(hRunOnce, ascii_filename, 0, REG_SZ, buffer, strlen(buffer) + 1) != ERROR_SUCCESS)
704         {
705             HeapFree(GetProcessHeap(), 0, buffer);
706             RegCloseKey(hRunOnce);
707             ret = ERROR_UNKNOWN;
708             goto done;
709         }
710         HeapFree(GetProcessHeap(), 0, buffer);
711         RegCloseKey(hRunOnce);
712         goto done;
713     }
714
715     TRACE("linker app='%s' link='%s' mode=%s path='%s' args='%s' icon='%s' workdir='%s' descr='%s'\n",
716         shell_link_app, link_name, bDesktop ? "desktop" : "menu", This->sPath,
717         This->sArgs ? This->sArgs : "", icon_name, work_dir ? work_dir : "",
718         This->sDescription ? This->sDescription : "" );
719
720     if ((pid = fork()) == -1) goto done;
721
722     escaped_path = escape(This->sPath);
723     if (This->sArgs)
724         escaped_args = escape(This->sArgs);
725
726     if (!pid)
727     {
728         int pos = 0;
729         char *argv[20];
730         argv[pos++] = shell_link_app;
731         argv[pos++] = "--link";
732         argv[pos++] = link_name;
733         argv[pos++] = "--path";
734         argv[pos++] = escaped_path;
735         argv[pos++] = bDesktop ? "--desktop" : "--menu";
736         if (This->sArgs && strlen(This->sArgs))
737         {
738             argv[pos++] = "--args";
739             argv[pos++] = escaped_args;
740         }
741         if (icon_name)
742         {
743             argv[pos++] = "--icon";
744             argv[pos++] = icon_name;
745         }
746         if (This->sWorkDir && strlen(This->sWorkDir))
747         {
748             argv[pos++] = "--workdir";
749             argv[pos++] = work_dir;
750         }
751         if (This->sDescription && strlen(This->sDescription))
752         {
753             argv[pos++] = "--descr";
754             argv[pos++] = This->sDescription;
755         }
756         argv[pos] = NULL;
757         execvp( shell_link_app, argv );
758         _exit(1);
759     }
760
761     while (waitpid( pid, &status, 0 ) == -1)
762     {
763         if (errno != EINTR)
764         {
765             ret = ERROR_UNKNOWN;
766             goto done;
767         }
768     }
769     if (status) ret = E_ACCESSDENIED;
770
771  done:
772     if (icon_name) unlink( icon_name );
773     HeapFree( GetProcessHeap(), 0, shell_link_app );
774     HeapFree( GetProcessHeap(), 0, filename );
775     HeapFree( GetProcessHeap(), 0, icon_name );
776     HeapFree( GetProcessHeap(), 0, work_dir );
777     if (escaped_args) HeapFree( GetProcessHeap(), 0, escaped_args );
778     if (escaped_path) HeapFree( GetProcessHeap(), 0, escaped_path );
779     return ret;
780 }
781
782 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
783 {
784         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
785         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
786         return NOERROR;
787 }
788 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
789 {
790         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
791         FIXME("(%p)\n",This);
792         return NOERROR;
793 }
794
795 static ICOM_VTABLE(IPersistFile) pfvt =
796 {
797         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
798         IPersistFile_fnQueryInterface,
799         IPersistFile_fnAddRef,
800         IPersistFile_fnRelease,
801         IPersistFile_fnGetClassID,
802         IPersistFile_fnIsDirty,
803         IPersistFile_fnLoad,
804         IPersistFile_fnSave,
805         IPersistFile_fnSaveCompleted,
806         IPersistFile_fnGetCurFile
807 };
808
809 /************************************************************************
810  * IPersistStream_QueryInterface
811  */
812 static HRESULT WINAPI IPersistStream_fnQueryInterface(
813         IPersistStream* iface,
814         REFIID     riid,
815         VOID**     ppvoid)
816 {
817         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
818
819         TRACE("(%p)\n",This);
820
821         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
822 }
823
824 /************************************************************************
825  * IPersistStream_Release
826  */
827 static ULONG WINAPI IPersistStream_fnRelease(
828         IPersistStream* iface)
829 {
830         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
831
832         TRACE("(%p)\n",This);
833
834         return IShellLinkA_Release((IShellLinkA*)This);
835 }
836
837 /************************************************************************
838  * IPersistStream_AddRef
839  */
840 static ULONG WINAPI IPersistStream_fnAddRef(
841         IPersistStream* iface)
842 {
843         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
844
845         TRACE("(%p)\n",This);
846
847         return IShellLinkA_AddRef((IShellLinkA*)This);
848 }
849
850 /************************************************************************
851  * IPersistStream_GetClassID
852  *
853  */
854 static HRESULT WINAPI IPersistStream_fnGetClassID(
855         IPersistStream* iface,
856         CLSID* pClassID)
857 {
858         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
859
860         TRACE("(%p)\n", This);
861
862         if (pClassID==0)
863           return E_POINTER;
864
865 /*      memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
866
867         return S_OK;
868 }
869
870 /************************************************************************
871  * IPersistStream_IsDirty (IPersistStream)
872  */
873 static HRESULT WINAPI IPersistStream_fnIsDirty(
874         IPersistStream*  iface)
875 {
876         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
877
878         TRACE("(%p)\n", This);
879
880         return S_OK;
881 }
882 /************************************************************************
883  * IPersistStream_Load (IPersistStream)
884  */
885
886 static HRESULT WINAPI IPersistStream_fnLoad(
887         IPersistStream*  iface,
888         IStream*         pLoadStream)
889 {
890         PLINK_HEADER lpLinkHeader = HeapAlloc(GetProcessHeap(), 0, LINK_HEADER_SIZE);
891         ULONG   dwBytesRead;
892         DWORD   ret = E_FAIL;
893         char    sTemp[MAX_PATH];
894
895         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
896
897         TRACE("(%p)(%p)\n", This, pLoadStream);
898
899         if ( ! pLoadStream)
900         {
901           return STG_E_INVALIDPOINTER;
902         }
903
904         IStream_AddRef (pLoadStream);
905         if(!lpLinkHeader)
906           goto end;
907
908         dwBytesRead = 0;
909         if (!(SUCCEEDED(IStream_Read(pLoadStream, lpLinkHeader, LINK_HEADER_SIZE, &dwBytesRead))))
910           goto end;
911
912         if (dwBytesRead != LINK_HEADER_SIZE)
913           goto end;
914
915         if ( (lpLinkHeader->MagicStr != 0x0000004CL) || !IsEqualIID(&lpLinkHeader->MagicGuid, &CLSID_ShellLink) )
916           goto end;
917
918         if(lpLinkHeader->PidlSize)
919         {
920           lpLinkHeader = HeapReAlloc(GetProcessHeap(), 0, lpLinkHeader, LINK_HEADER_SIZE+lpLinkHeader->PidlSize);
921           if (!lpLinkHeader)
922             goto end;
923           dwBytesRead = 0;
924           if (!(SUCCEEDED(IStream_Read(pLoadStream, &(lpLinkHeader->Pidl), lpLinkHeader->PidlSize, &dwBytesRead))))
925             goto end;
926           if(dwBytesRead != lpLinkHeader->PidlSize)
927             goto end;
928
929           if (pcheck (&lpLinkHeader->Pidl))
930           {
931             This->pPidl = ILClone (&lpLinkHeader->Pidl);
932
933             SHGetPathFromIDListA(&lpLinkHeader->Pidl, sTemp);
934             This->sPath = heap_strdup( sTemp );
935           }
936         }
937         This->wHotKey = lpLinkHeader->wHotKey;
938         FileTimeToSystemTime (&lpLinkHeader->Time1, &This->time1);
939         FileTimeToSystemTime (&lpLinkHeader->Time2, &This->time2);
940         FileTimeToSystemTime (&lpLinkHeader->Time3, &This->time3);
941 #if 1
942         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
943         TRACE("-- time1: %s\n", sTemp);
944         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
945         TRACE("-- time1: %s\n", sTemp);
946         GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
947         TRACE("-- time1: %s\n", sTemp);
948         pdump (This->pPidl);
949 #endif
950         ret = S_OK;
951
952 end:
953         IStream_Release (pLoadStream);
954
955         pdump(This->pPidl);
956
957         HeapFree(GetProcessHeap(), 0, lpLinkHeader);
958
959         return ret;
960 }
961
962 /************************************************************************
963  * IPersistStream_Save (IPersistStream)
964  */
965 static HRESULT WINAPI IPersistStream_fnSave(
966         IPersistStream*  iface,
967         IStream*         pOutStream,
968         BOOL             fClearDirty)
969 {
970         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
971
972         TRACE("(%p) %p %x\n", This, pOutStream, fClearDirty);
973
974         return E_NOTIMPL;
975 }
976
977 /************************************************************************
978  * IPersistStream_GetSizeMax (IPersistStream)
979  */
980 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
981         IPersistStream*  iface,
982         ULARGE_INTEGER*  pcbSize)
983 {
984         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
985
986         TRACE("(%p)\n", This);
987
988         return E_NOTIMPL;
989 }
990
991 static ICOM_VTABLE(IPersistStream) psvt =
992 {
993         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
994         IPersistStream_fnQueryInterface,
995         IPersistStream_fnAddRef,
996         IPersistStream_fnRelease,
997         IPersistStream_fnGetClassID,
998         IPersistStream_fnIsDirty,
999         IPersistStream_fnLoad,
1000         IPersistStream_fnSave,
1001         IPersistStream_fnGetSizeMax
1002 };
1003
1004 /**************************************************************************
1005  *        IShellLink_Constructor
1006  */
1007 HRESULT WINAPI IShellLink_Constructor (
1008         IUnknown * pUnkOuter,
1009         REFIID riid,
1010         LPVOID * ppv)
1011 {
1012         IShellLinkImpl * sl;
1013
1014         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1015
1016         *ppv = NULL;
1017
1018         if(pUnkOuter) return CLASS_E_NOAGGREGATION;
1019         sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
1020         if (!sl) return E_OUTOFMEMORY;
1021
1022         sl->ref = 1;
1023         sl->lpVtbl = &slvt;
1024         sl->lpvtblw = &slvtw;
1025         sl->lpvtblPersistFile = &pfvt;
1026         sl->lpvtblPersistStream = &psvt;
1027
1028         TRACE("(%p)->()\n",sl);
1029
1030         if (IsEqualIID(riid, &IID_IUnknown) ||
1031             IsEqualIID(riid, &IID_IShellLinkA))
1032             *ppv = sl;
1033         else if (IsEqualIID(riid, &IID_IShellLinkW))
1034             *ppv = &(sl->lpvtblw);
1035         else {
1036             LocalFree((HLOCAL)sl);
1037             ERR("E_NOINTERFACE\n");
1038             return E_NOINTERFACE;
1039         }
1040
1041         return S_OK;
1042 }
1043
1044 /**************************************************************************
1045  *  IShellLinkA_QueryInterface
1046  */
1047 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1048 {
1049         ICOM_THIS(IShellLinkImpl, iface);
1050
1051         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
1052
1053         *ppvObj = NULL;
1054
1055         if(IsEqualIID(riid, &IID_IUnknown) ||
1056            IsEqualIID(riid, &IID_IShellLinkA))
1057         {
1058           *ppvObj = This;
1059         }
1060         else if(IsEqualIID(riid, &IID_IShellLinkW))
1061         {
1062           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
1063         }
1064         else if(IsEqualIID(riid, &IID_IPersistFile))
1065         {
1066           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
1067         }
1068         else if(IsEqualIID(riid, &IID_IPersistStream))
1069         {
1070           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
1071         }
1072
1073         if(*ppvObj)
1074         {
1075           IUnknown_AddRef((IUnknown*)(*ppvObj));
1076           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
1077           return S_OK;
1078         }
1079         TRACE("-- Interface: E_NOINTERFACE\n");
1080         return E_NOINTERFACE;
1081 }
1082 /******************************************************************************
1083  * IShellLinkA_AddRef
1084  */
1085 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1086 {
1087         ICOM_THIS(IShellLinkImpl, iface);
1088
1089         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1090
1091         return ++(This->ref);
1092 }
1093 /******************************************************************************
1094  *      IShellLinkA_Release
1095  */
1096 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1097 {
1098         ICOM_THIS(IShellLinkImpl, iface);
1099
1100         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1101
1102         if (!--(This->ref))
1103         { TRACE("-- destroying IShellLink(%p)\n",This);
1104
1105           if (This->sIcoPath)
1106             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1107
1108           if (This->sArgs)
1109             HeapFree(GetProcessHeap(), 0, This->sArgs);
1110
1111           if (This->sWorkDir)
1112             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1113
1114           if (This->sDescription)
1115             HeapFree(GetProcessHeap(), 0, This->sDescription);
1116
1117           if (This->sPath)
1118             HeapFree(GetProcessHeap(),0,This->sPath);
1119
1120           if (This->pPidl)
1121             SHFree(This->pPidl);
1122
1123           if (This->lpFileStream)
1124             IStream_Release(This->lpFileStream);
1125
1126           This->iIcoNdx = 0;
1127
1128           LocalFree((HANDLE)This);
1129           return 0;
1130         }
1131         return This->ref;
1132 }
1133
1134 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1135 {
1136         ICOM_THIS(IShellLinkImpl, iface);
1137
1138         TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",This, pszFile, cchMaxPath, pfd, fFlags, debugstr_a(This->sPath));
1139
1140         if (This->sPath)
1141           lstrcpynA(pszFile,This->sPath, cchMaxPath);
1142         else
1143           return E_FAIL;
1144
1145         return NOERROR;
1146 }
1147 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1148 {
1149         ICOM_THIS(IShellLinkImpl, iface);
1150
1151         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1152
1153         *ppidl = ILClone(This->pPidl);
1154         return NOERROR;
1155 }
1156 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1157 {
1158         ICOM_THIS(IShellLinkImpl, iface);
1159
1160         TRACE("(%p)->(pidl=%p)\n",This, pidl);
1161
1162         if (This->pPidl)
1163             SHFree(This->pPidl);
1164         This->pPidl = ILClone (pidl);
1165         return NOERROR;
1166 }
1167 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1168 {
1169         ICOM_THIS(IShellLinkImpl, iface);
1170
1171         FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1172         lstrcpynA(pszName,"Description, FIXME",cchMaxName);
1173         return NOERROR;
1174 }
1175 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1176 {
1177         ICOM_THIS(IShellLinkImpl, iface);
1178
1179         TRACE("(%p)->(pName=%s)\n", This, pszName);
1180
1181         if (This->sDescription)
1182             HeapFree(GetProcessHeap(), 0, This->sDescription);
1183         if (!(This->sDescription = heap_strdup(pszName)))
1184             return E_OUTOFMEMORY;
1185
1186         return NOERROR;
1187 }
1188 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1189 {
1190         ICOM_THIS(IShellLinkImpl, iface);
1191
1192         TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1193
1194         lstrcpynA( pszDir, This->sWorkDir ? This->sWorkDir : "", cchMaxPath );
1195
1196         return NOERROR;
1197 }
1198 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1199 {
1200         ICOM_THIS(IShellLinkImpl, iface);
1201
1202         TRACE("(%p)->(dir=%s)\n",This, pszDir);
1203
1204         if (This->sWorkDir)
1205             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1206         if (!(This->sWorkDir = heap_strdup(pszDir)))
1207             return E_OUTOFMEMORY;
1208
1209         return NOERROR;
1210 }
1211 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1212 {
1213         ICOM_THIS(IShellLinkImpl, iface);
1214
1215         TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1216
1217         lstrcpynA( pszArgs, This->sArgs ? This->sArgs : "", cchMaxPath );
1218
1219         return NOERROR;
1220 }
1221 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1222 {
1223         ICOM_THIS(IShellLinkImpl, iface);
1224
1225         TRACE("(%p)->(args=%s)\n",This, pszArgs);
1226
1227         if (This->sArgs)
1228             HeapFree(GetProcessHeap(), 0, This->sArgs);
1229         if (!(This->sArgs = heap_strdup(pszArgs)))
1230             return E_OUTOFMEMORY;
1231
1232         return NOERROR;
1233 }
1234 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1235 {
1236         ICOM_THIS(IShellLinkImpl, iface);
1237
1238         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1239
1240         *pwHotkey = This->wHotKey;
1241
1242         return NOERROR;
1243 }
1244 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1245 {
1246         ICOM_THIS(IShellLinkImpl, iface);
1247
1248         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1249
1250         This->wHotKey = wHotkey;
1251
1252         return NOERROR;
1253 }
1254 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1255 {
1256         ICOM_THIS(IShellLinkImpl, iface);
1257
1258         FIXME("(%p)->(%p)\n",This, piShowCmd);
1259         *piShowCmd=0;
1260         return NOERROR;
1261 }
1262 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1263 {
1264         ICOM_THIS(IShellLinkImpl, iface);
1265
1266         /* SW_SHOWNORMAL is the default ... The others would have 
1267          * to be somehow passed through the link file ... We can't 
1268          * do that currently.
1269          */
1270         if (iShowCmd != SW_SHOWNORMAL)
1271                 FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
1272         return NOERROR;
1273 }
1274 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1275 {
1276         ICOM_THIS(IShellLinkImpl, iface);
1277
1278         TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1279
1280         lstrcpynA( pszIconPath, This->sIcoPath ? This->sIcoPath : "", cchIconPath );
1281         *piIcon = This->iIcoNdx;
1282
1283         return NOERROR;
1284 }
1285 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1286 {
1287         ICOM_THIS(IShellLinkImpl, iface);
1288
1289         TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1290
1291         if (This->sIcoPath)
1292             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1293         if (!(This->sIcoPath = heap_strdup(pszIconPath)))
1294             return E_OUTOFMEMORY;
1295         This->iIcoNdx = iIcon;
1296
1297         return NOERROR;
1298 }
1299 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1300 {
1301         ICOM_THIS(IShellLinkImpl, iface);
1302
1303         FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1304         return NOERROR;
1305 }
1306 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1307 {
1308         ICOM_THIS(IShellLinkImpl, iface);
1309
1310         FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1311         return NOERROR;
1312 }
1313 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1314 {
1315         ICOM_THIS(IShellLinkImpl, iface);
1316
1317         TRACE("(%p)->(path=%s)\n",This, pszFile);
1318
1319         if (This->sPath)
1320             HeapFree(GetProcessHeap(), 0, This->sPath);
1321         if (!(This->sPath = heap_strdup(pszFile)))
1322             return E_OUTOFMEMORY;
1323
1324         return NOERROR;
1325 }
1326
1327 /**************************************************************************
1328 * IShellLink Implementation
1329 */
1330
1331 static ICOM_VTABLE(IShellLinkA) slvt =
1332 {
1333         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1334         IShellLinkA_fnQueryInterface,
1335         IShellLinkA_fnAddRef,
1336         IShellLinkA_fnRelease,
1337         IShellLinkA_fnGetPath,
1338         IShellLinkA_fnGetIDList,
1339         IShellLinkA_fnSetIDList,
1340         IShellLinkA_fnGetDescription,
1341         IShellLinkA_fnSetDescription,
1342         IShellLinkA_fnGetWorkingDirectory,
1343         IShellLinkA_fnSetWorkingDirectory,
1344         IShellLinkA_fnGetArguments,
1345         IShellLinkA_fnSetArguments,
1346         IShellLinkA_fnGetHotkey,
1347         IShellLinkA_fnSetHotkey,
1348         IShellLinkA_fnGetShowCmd,
1349         IShellLinkA_fnSetShowCmd,
1350         IShellLinkA_fnGetIconLocation,
1351         IShellLinkA_fnSetIconLocation,
1352         IShellLinkA_fnSetRelativePath,
1353         IShellLinkA_fnResolve,
1354         IShellLinkA_fnSetPath
1355 };
1356
1357
1358 /**************************************************************************
1359  *  IShellLinkW_fnQueryInterface
1360  */
1361 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1362   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1363 {
1364         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1365
1366         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1367 }
1368
1369 /******************************************************************************
1370  * IShellLinkW_fnAddRef
1371  */
1372 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1373 {
1374         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1375
1376         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1377
1378         return IShellLinkA_AddRef((IShellLinkA*)This);
1379 }
1380 /******************************************************************************
1381  * IShellLinkW_fnRelease
1382  */
1383
1384 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1385 {
1386         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1387
1388         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1389
1390         return IShellLinkA_Release((IShellLinkA*)This);
1391 }
1392
1393 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1394 {
1395         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1396
1397         FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
1398         MultiByteToWideChar( CP_ACP, 0, "c:\\foo.bar", -1, pszFile, cchMaxPath );
1399         return NOERROR;
1400 }
1401
1402 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1403 {
1404         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1405
1406         FIXME("(%p)->(ppidl=%p)\n",This, ppidl);
1407         *ppidl = _ILCreateDesktop();
1408         return NOERROR;
1409 }
1410
1411 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1412 {
1413         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1414
1415         FIXME("(%p)->(pidl=%p)\n",This, pidl);
1416         return NOERROR;
1417 }
1418
1419 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1420 {
1421         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1422
1423         FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1424         MultiByteToWideChar( CP_ACP, 0, "Description, FIXME", -1, pszName, cchMaxName );
1425         return NOERROR;
1426 }
1427
1428 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1429 {
1430         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1431
1432         TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1433
1434         if (This->sDescription)
1435             HeapFree(GetProcessHeap(), 0, This->sDescription);
1436         if (!(This->sDescription = HEAP_strdupWtoA(GetProcessHeap(), 0, pszName)))
1437             return E_OUTOFMEMORY;
1438
1439         return NOERROR;
1440 }
1441
1442 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1443 {
1444         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1445
1446         TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1447
1448         MultiByteToWideChar( CP_ACP, 0, This->sWorkDir ? This->sWorkDir : "", -1, pszDir, cchMaxPath );
1449
1450         return NOERROR;
1451 }
1452
1453 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1454 {
1455         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1456
1457         TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1458
1459         if (This->sWorkDir)
1460             HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1461         if (!(This->sWorkDir = HEAP_strdupWtoA(GetProcessHeap(), 0, pszDir)))
1462             return E_OUTOFMEMORY;
1463
1464         return NOERROR;
1465 }
1466
1467 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1468 {
1469         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1470
1471         TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1472
1473         MultiByteToWideChar( CP_ACP, 0, This->sArgs ? This->sArgs : "", -1, pszArgs, cchMaxPath );
1474
1475         return NOERROR;
1476 }
1477
1478 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1479 {
1480         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1481
1482         TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1483
1484         if (This->sArgs)
1485             HeapFree(GetProcessHeap(), 0, This->sArgs);
1486         if (!(This->sArgs = HEAP_strdupWtoA(GetProcessHeap(), 0, pszArgs)))
1487             return E_OUTOFMEMORY;
1488
1489         return NOERROR;
1490 }
1491
1492 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1493 {
1494         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1495
1496         FIXME("(%p)->(%p)\n",This, pwHotkey);
1497         *pwHotkey=0x0;
1498         return NOERROR;
1499 }
1500
1501 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1502 {
1503         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1504
1505         FIXME("(%p)->(hotkey=%x)\n",This, wHotkey);
1506         return NOERROR;
1507 }
1508
1509 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1510 {
1511         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1512
1513         FIXME("(%p)->(%p)\n",This, piShowCmd);
1514         *piShowCmd=0;
1515         return NOERROR;
1516 }
1517
1518 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1519 {
1520         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1521
1522         /* SW_SHOWNORMAL is the default ... The others would have 
1523          * to be somehow passed through the link file ... We can't 
1524          * do that currently.
1525          */
1526         if (iShowCmd != SW_SHOWNORMAL)
1527                 FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
1528         return NOERROR;
1529 }
1530
1531 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1532 {
1533         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1534
1535         TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1536
1537         MultiByteToWideChar( CP_ACP, 0, This->sIcoPath ? This->sIcoPath : "", -1, pszIconPath, cchIconPath );
1538         *piIcon = This->iIcoNdx;
1539
1540         return NOERROR;
1541 }
1542
1543 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1544 {
1545         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1546
1547         TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1548
1549         if (This->sIcoPath)
1550             HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1551         if (!(This->sIcoPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszIconPath)))
1552             return E_OUTOFMEMORY;
1553         This->iIcoNdx = iIcon;
1554
1555         return NOERROR;
1556 }
1557
1558 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1559 {
1560         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1561
1562         FIXME("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1563         return NOERROR;
1564 }
1565
1566 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1567 {
1568         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1569
1570         FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1571         return NOERROR;
1572 }
1573
1574 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1575 {
1576         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1577
1578         TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1579
1580         if (This->sPath)
1581             HeapFree(GetProcessHeap(), 0, This->sPath);
1582         if (!(This->sPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszFile)))
1583             return E_OUTOFMEMORY;
1584
1585         return NOERROR;
1586 }
1587
1588 /**************************************************************************
1589 * IShellLinkW Implementation
1590 */
1591
1592 static ICOM_VTABLE(IShellLinkW) slvtw =
1593 {
1594         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1595         IShellLinkW_fnQueryInterface,
1596         IShellLinkW_fnAddRef,
1597         IShellLinkW_fnRelease,
1598         IShellLinkW_fnGetPath,
1599         IShellLinkW_fnGetIDList,
1600         IShellLinkW_fnSetIDList,
1601         IShellLinkW_fnGetDescription,
1602         IShellLinkW_fnSetDescription,
1603         IShellLinkW_fnGetWorkingDirectory,
1604         IShellLinkW_fnSetWorkingDirectory,
1605         IShellLinkW_fnGetArguments,
1606         IShellLinkW_fnSetArguments,
1607         IShellLinkW_fnGetHotkey,
1608         IShellLinkW_fnSetHotkey,
1609         IShellLinkW_fnGetShowCmd,
1610         IShellLinkW_fnSetShowCmd,
1611         IShellLinkW_fnGetIconLocation,
1612         IShellLinkW_fnSetIconLocation,
1613         IShellLinkW_fnSetRelativePath,
1614         IShellLinkW_fnResolve,
1615         IShellLinkW_fnSetPath
1616 };