Release 1.5.29.
[wine] / dlls / shell32 / tests / shelllink.c
1 /*
2  * Unit tests for shelllinks
3  *
4  * Copyright 2004 Mike McCormack
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21
22 #define COBJMACROS
23
24 #include "initguid.h"
25 #include "windows.h"
26 #include "shlguid.h"
27 #include "shobjidl.h"
28 #include "shlobj.h"
29 #include "wine/test.h"
30
31 #include "shell32_test.h"
32
33 #ifndef SLDF_HAS_LOGO3ID
34 #  define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */
35 #endif
36
37 static void (WINAPI *pILFree)(LPITEMIDLIST);
38 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
39 static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
40 static HRESULT (WINAPI *pSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT);
41 static HRESULT (WINAPI *pSHGetStockIconInfo)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
42 static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD);
43 static DWORD (WINAPI *pGetShortPathNameA)(LPCSTR, LPSTR, DWORD);
44
45 static const GUID _IID_IShellLinkDataList = {
46     0x45e2b4ae, 0xb1c3, 0x11d0,
47     { 0xb9, 0x2f, 0x00, 0xa0, 0xc9, 0x03, 0x12, 0xe1 }
48 };
49
50 static const WCHAR notafile[]= { 'C',':','\\','n','o','n','e','x','i','s','t','e','n','t','\\','f','i','l','e',0 };
51
52
53 /* For some reason SHILCreateFromPath does not work on Win98 and
54  * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
55  * get what we want on all platforms.
56  */
57 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
58
59 static LPITEMIDLIST path_to_pidl(const char* path)
60 {
61     LPITEMIDLIST pidl;
62
63     if (!pSHSimpleIDListFromPathAW)
64     {
65         HMODULE hdll=GetModuleHandleA("shell32.dll");
66         pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
67         if (!pSHSimpleIDListFromPathAW)
68             win_skip("SHSimpleIDListFromPathAW not found in shell32.dll\n");
69     }
70
71     pidl=NULL;
72     /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
73     if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
74         pidl=pSHSimpleIDListFromPathAW(path);
75
76     if (!pidl)
77     {
78         WCHAR* pathW;
79         HRESULT r;
80         int len;
81
82         len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
83         pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
84         MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
85
86         r=pSHILCreateFromPath(pathW, &pidl, NULL);
87         ok(r == S_OK, "SHILCreateFromPath failed (0x%08x)\n", r);
88         HeapFree(GetProcessHeap(), 0, pathW);
89     }
90     return pidl;
91 }
92
93
94 /*
95  * Test manipulation of an IShellLink's properties.
96  */
97
98 static void test_get_set(void)
99 {
100     HRESULT r;
101     IShellLinkA *sl;
102     IShellLinkW *slW = NULL;
103     char mypath[MAX_PATH];
104     char buffer[INFOTIPSIZE];
105     LPITEMIDLIST pidl, tmp_pidl;
106     const char * str;
107     int i;
108     WORD w;
109
110     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
111                          &IID_IShellLinkA, (LPVOID*)&sl);
112     ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
113     if (r != S_OK)
114         return;
115
116     /* Test Getting / Setting the description */
117     strcpy(buffer,"garbage");
118     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
119     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
120     ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
121
122     str="Some description";
123     r = IShellLinkA_SetDescription(sl, str);
124     ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
125
126     strcpy(buffer,"garbage");
127     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
128     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
129     ok(lstrcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
130
131     r = IShellLinkA_SetDescription(sl, NULL);
132     ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
133
134     strcpy(buffer,"garbage");
135     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
136     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
137     ok(*buffer=='\0' || broken(lstrcmp(buffer,str)==0), "GetDescription returned '%s'\n", buffer); /* NT4 */
138
139
140     /* Test Getting / Setting the work directory */
141     strcpy(buffer,"garbage");
142     r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
143     ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
144     ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
145
146     str="c:\\nonexistent\\directory";
147     r = IShellLinkA_SetWorkingDirectory(sl, str);
148     ok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
149
150     strcpy(buffer,"garbage");
151     r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
152     ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
153     ok(lstrcmpi(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
154
155     /* Test Getting / Setting the path */
156     strcpy(buffer,"garbage");
157     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
158     todo_wine ok(r == S_FALSE || broken(r == S_OK) /* NT4/W2K */, "GetPath failed (0x%08x)\n", r);
159     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
160
161     CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
162                      &IID_IShellLinkW, (LPVOID*)&slW);
163     if (!slW /* Win9x */ || !pGetLongPathNameA /* NT4 */)
164         skip("SetPath with NULL parameter crashes on Win9x and some NT4\n");
165     else
166     {
167         IShellLinkW_Release(slW);
168         r = IShellLinkA_SetPath(sl, NULL);
169         ok(r==E_INVALIDARG ||
170            broken(r==S_OK), /* Some Win95 and NT4 */
171            "SetPath returned wrong error (0x%08x)\n", r);
172     }
173
174     r = IShellLinkA_SetPath(sl, "");
175     ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
176
177     strcpy(buffer,"garbage");
178     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
179     todo_wine ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
180     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
181
182     /* Win98 returns S_FALSE, but WinXP returns S_OK */
183     str="c:\\nonexistent\\file";
184     r = IShellLinkA_SetPath(sl, str);
185     ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08x)\n", r);
186
187     strcpy(buffer,"garbage");
188     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
189     ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
190     ok(lstrcmpi(buffer,str)==0, "GetPath returned '%s'\n", buffer);
191
192     /* Get some real path to play with */
193     GetWindowsDirectoryA( mypath, sizeof(mypath)-12 );
194     strcat(mypath, "\\regedit.exe");
195
196     /* Test the interaction of SetPath and SetIDList */
197     tmp_pidl=NULL;
198     r = IShellLinkA_GetIDList(sl, &tmp_pidl);
199     ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
200     if (r == S_OK)
201     {
202         BOOL ret;
203
204         strcpy(buffer,"garbage");
205         ret = SHGetPathFromIDListA(tmp_pidl, buffer);
206         ok(ret, "SHGetPathFromIDListA failed\n");
207         if (ret)
208             ok(lstrcmpi(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
209         pILFree(tmp_pidl);
210     }
211
212     pidl=path_to_pidl(mypath);
213     ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
214
215     if (pidl)
216     {
217         LPITEMIDLIST second_pidl;
218
219         r = IShellLinkA_SetIDList(sl, pidl);
220         ok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
221
222         tmp_pidl=NULL;
223         r = IShellLinkA_GetIDList(sl, &tmp_pidl);
224         ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
225         ok(tmp_pidl && pILIsEqual(pidl, tmp_pidl),
226            "GetIDList returned an incorrect pidl\n");
227
228         r = IShellLinkA_GetIDList(sl, &second_pidl);
229         ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
230         ok(second_pidl && pILIsEqual(pidl, second_pidl),
231            "GetIDList returned an incorrect pidl\n");
232         ok(second_pidl != tmp_pidl, "pidls are the same\n");
233
234         pILFree(second_pidl);
235         pILFree(tmp_pidl);
236         pILFree(pidl);
237
238         strcpy(buffer,"garbage");
239         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
240         ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
241         todo_wine
242         ok(lstrcmpi(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
243
244     }
245
246     /* test path with quotes (IShellLinkA_SetPath returns S_FALSE on W2K and below and S_OK on XP and above */
247     r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
248     ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
249
250     strcpy(buffer,"garbage");
251     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
252     ok(r==S_OK, "GetPath failed (0x%08x)\n", r);
253     ok(!lstrcmp(buffer, "C:\\nonexistent\\file") ||
254        broken(!lstrcmp(buffer, "C:\\\"c:\\nonexistent\\file\"")), /* NT4 */
255        "case doesn't match\n");
256
257     r = IShellLinkA_SetPath(sl, "\"c:\\foo");
258     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
259
260     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
261     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
262
263     r = IShellLinkA_SetPath(sl, "c:\\foo\"");
264     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
265
266     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
267     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
268
269     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
270     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
271
272     /* Test Getting / Setting the arguments */
273     strcpy(buffer,"garbage");
274     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
275     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
276     ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
277
278     str="param1 \"spaced param2\"";
279     r = IShellLinkA_SetArguments(sl, str);
280     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
281
282     strcpy(buffer,"garbage");
283     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
284     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
285     ok(lstrcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
286
287     strcpy(buffer,"garbage");
288     r = IShellLinkA_SetArguments(sl, NULL);
289     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
290     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
291     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
292     ok(!buffer[0] || lstrcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
293
294     strcpy(buffer,"garbage");
295     r = IShellLinkA_SetArguments(sl, "");
296     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
297     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
298     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
299     ok(!buffer[0], "GetArguments returned '%s'\n", buffer);
300
301     /* Test Getting / Setting showcmd */
302     i=0xdeadbeef;
303     r = IShellLinkA_GetShowCmd(sl, &i);
304     ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
305     ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
306
307     r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
308     ok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
309
310     i=0xdeadbeef;
311     r = IShellLinkA_GetShowCmd(sl, &i);
312     ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
313     ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
314
315     /* Test Getting / Setting the icon */
316     i=0xdeadbeef;
317     strcpy(buffer,"garbage");
318     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
319     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
320     ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
321     ok(i==0, "GetIconLocation returned %d\n", i);
322
323     str="c:\\nonexistent\\file";
324     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
325     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
326
327     i=0xdeadbeef;
328     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
329     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
330     ok(lstrcmpi(buffer,str)==0, "GetIconLocation returned '%s'\n", buffer);
331     ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
332
333     /* Test Getting / Setting the hot key */
334     w=0xbeef;
335     r = IShellLinkA_GetHotkey(sl, &w);
336     ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
337     ok(w==0, "GetHotkey returned %d\n", w);
338
339     r = IShellLinkA_SetHotkey(sl, 0x5678);
340     ok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
341
342     w=0xbeef;
343     r = IShellLinkA_GetHotkey(sl, &w);
344     ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
345     ok(w==0x5678, "GetHotkey returned %d'\n", w);
346
347     IShellLinkA_Release(sl);
348 }
349
350
351 /*
352  * Test saving and loading .lnk files
353  */
354
355 #define lok                   ok_(__FILE__, line)
356 #define lok_todo_4(todo_flag,a,b,c,d) \
357     if ((todo & todo_flag) == 0) lok((a), (b), (c), (d)); \
358     else todo_wine lok((a), (b), (c), (d));
359 #define lok_todo_2(todo_flag,a,b) \
360     if ((todo & todo_flag) == 0) lok((a), (b)); \
361     else todo_wine lok((a), (b));
362 #define check_lnk(a,b,c)        check_lnk_(__LINE__, (a), (b), (c))
363
364 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
365 {
366     HRESULT r;
367     IShellLinkA *sl;
368     IPersistFile *pf;
369
370     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
371                          &IID_IShellLinkA, (LPVOID*)&sl);
372     lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
373     if (r != S_OK)
374         return;
375
376     if (desc->description)
377     {
378         r = IShellLinkA_SetDescription(sl, desc->description);
379         lok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
380     }
381     if (desc->workdir)
382     {
383         r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
384         lok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
385     }
386     if (desc->path)
387     {
388         r = IShellLinkA_SetPath(sl, desc->path);
389         lok(SUCCEEDED(r), "SetPath failed (0x%08x)\n", r);
390     }
391     if (desc->pidl)
392     {
393         r = IShellLinkA_SetIDList(sl, desc->pidl);
394         lok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
395     }
396     if (desc->arguments)
397     {
398         r = IShellLinkA_SetArguments(sl, desc->arguments);
399         lok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
400     }
401     if (desc->showcmd)
402     {
403         r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
404         lok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
405     }
406     if (desc->icon)
407     {
408         r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
409         lok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
410     }
411     if (desc->hotkey)
412     {
413         r = IShellLinkA_SetHotkey(sl, desc->hotkey);
414         lok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
415     }
416
417     r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
418     lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
419     if (r == S_OK)
420     {
421         LPOLESTR str;
422
423     if (0)
424     {
425         /* crashes on XP */
426         IPersistFile_GetCurFile(pf, NULL);
427     }
428
429         /* test GetCurFile before ::Save */
430         str = (LPWSTR)0xdeadbeef;
431         r = IPersistFile_GetCurFile(pf, &str);
432         lok(r == S_FALSE ||
433             broken(r == S_OK), /* shell32 < 5.0 */
434             "got 0x%08x\n", r);
435         lok(str == NULL, "got %p\n", str);
436
437         r = IPersistFile_Save(pf, path, TRUE);
438         if (save_fails)
439         {
440             todo_wine {
441             lok(r == S_OK, "save failed (0x%08x)\n", r);
442             }
443         }
444         else
445         {
446             lok(r == S_OK, "save failed (0x%08x)\n", r);
447         }
448
449         /* test GetCurFile after ::Save */
450         r = IPersistFile_GetCurFile(pf, &str);
451         lok(r == S_OK, "got 0x%08x\n", r);
452         lok(str != NULL ||
453             broken(str == NULL), /* shell32 < 5.0 */
454             "Didn't expect NULL\n");
455         if (str != NULL)
456         {
457             IMalloc *pmalloc;
458
459             lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
460                 wine_dbgstr_w(path), wine_dbgstr_w(str));
461
462             SHGetMalloc(&pmalloc);
463             IMalloc_Free(pmalloc, str);
464         }
465         else
466             win_skip("GetCurFile fails on shell32 < 5.0\n");
467
468         IPersistFile_Release(pf);
469     }
470
471     IShellLinkA_Release(sl);
472 }
473
474 static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
475 {
476     HRESULT r;
477     IShellLinkA *sl;
478     IPersistFile *pf;
479     char buffer[INFOTIPSIZE];
480     LPOLESTR str;
481
482     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
483                          &IID_IShellLinkA, (LPVOID*)&sl);
484     lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
485     if (r != S_OK)
486         return;
487
488     r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
489     lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
490     if (r != S_OK)
491     {
492         IShellLinkA_Release(sl);
493         return;
494     }
495
496     /* test GetCurFile before ::Load */
497     str = (LPWSTR)0xdeadbeef;
498     r = IPersistFile_GetCurFile(pf, &str);
499     lok(r == S_FALSE ||
500         broken(r == S_OK), /* shell32 < 5.0 */
501         "got 0x%08x\n", r);
502     lok(str == NULL, "got %p\n", str);
503
504     r = IPersistFile_Load(pf, path, STGM_READ);
505     lok(r == S_OK, "load failed (0x%08x)\n", r);
506
507     /* test GetCurFile after ::Save */
508     r = IPersistFile_GetCurFile(pf, &str);
509     lok(r == S_OK, "got 0x%08x\n", r);
510     lok(str != NULL ||
511         broken(str == NULL), /* shell32 < 5.0 */
512         "Didn't expect NULL\n");
513     if (str != NULL)
514     {
515         IMalloc *pmalloc;
516
517         lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
518             wine_dbgstr_w(path), wine_dbgstr_w(str));
519
520         SHGetMalloc(&pmalloc);
521         IMalloc_Free(pmalloc, str);
522     }
523     else
524         win_skip("GetCurFile fails on shell32 < 5.0\n");
525
526     IPersistFile_Release(pf);
527     if (r != S_OK)
528     {
529         IShellLinkA_Release(sl);
530         return;
531     }
532
533     if (desc->description)
534     {
535         strcpy(buffer,"garbage");
536         r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
537         lok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
538         lok_todo_4(0x1, lstrcmp(buffer, desc->description)==0,
539            "GetDescription returned '%s' instead of '%s'\n",
540            buffer, desc->description);
541     }
542     if (desc->workdir)
543     {
544         strcpy(buffer,"garbage");
545         r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
546         lok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
547         lok_todo_4(0x2, lstrcmpi(buffer, desc->workdir)==0,
548            "GetWorkingDirectory returned '%s' instead of '%s'\n",
549            buffer, desc->workdir);
550     }
551     if (desc->path)
552     {
553         strcpy(buffer,"garbage");
554         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
555         lok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
556         lok_todo_4(0x4, lstrcmpi(buffer, desc->path)==0,
557            "GetPath returned '%s' instead of '%s'\n",
558            buffer, desc->path);
559     }
560     if (desc->pidl)
561     {
562         LPITEMIDLIST pidl=NULL;
563         r = IShellLinkA_GetIDList(sl, &pidl);
564         lok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
565         lok_todo_2(0x8, pILIsEqual(pidl, desc->pidl),
566            "GetIDList returned an incorrect pidl\n");
567     }
568     if (desc->showcmd)
569     {
570         int i=0xdeadbeef;
571         r = IShellLinkA_GetShowCmd(sl, &i);
572         lok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
573         lok_todo_4(0x10, i==desc->showcmd,
574            "GetShowCmd returned 0x%0x instead of 0x%0x\n",
575            i, desc->showcmd);
576     }
577     if (desc->icon)
578     {
579         int i=0xdeadbeef;
580         strcpy(buffer,"garbage");
581         r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
582         lok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
583         lok_todo_4(0x20, lstrcmpi(buffer, desc->icon)==0,
584            "GetIconLocation returned '%s' instead of '%s'\n",
585            buffer, desc->icon);
586         lok_todo_4(0x20, i==desc->icon_id,
587            "GetIconLocation returned 0x%0x instead of 0x%0x\n",
588            i, desc->icon_id);
589     }
590     if (desc->hotkey)
591     {
592         WORD i=0xbeef;
593         r = IShellLinkA_GetHotkey(sl, &i);
594         lok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
595         lok_todo_4(0x40, i==desc->hotkey,
596            "GetHotkey returned 0x%04x instead of 0x%04x\n",
597            i, desc->hotkey);
598     }
599
600     IShellLinkA_Release(sl);
601 }
602
603 static void test_load_save(void)
604 {
605     WCHAR lnkfile[MAX_PATH];
606     char lnkfileA[MAX_PATH];
607     static const char lnkfileA_name[] = "\\test.lnk";
608
609     lnk_desc_t desc;
610     char mypath[MAX_PATH];
611     char mydir[MAX_PATH];
612     char realpath[MAX_PATH];
613     char* p;
614     HANDLE hf;
615     DWORD r;
616
617     if (!pGetLongPathNameA)
618     {
619         win_skip("GetLongPathNameA is not available\n");
620         return;
621     }
622
623     /* Don't used a fixed path for the test.lnk file */
624     GetTempPathA(MAX_PATH, lnkfileA);
625     lstrcatA(lnkfileA, lnkfileA_name);
626     MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
627
628     /* Save an empty .lnk file */
629     memset(&desc, 0, sizeof(desc));
630     create_lnk(lnkfile, &desc, 0);
631
632     /* It should come back as a bunch of empty strings */
633     desc.description="";
634     desc.workdir="";
635     desc.path="";
636     desc.arguments="";
637     desc.icon="";
638     check_lnk(lnkfile, &desc, 0x0);
639
640     /* Point a .lnk file to nonexistent files */
641     desc.description="";
642     desc.workdir="c:\\Nonexitent\\work\\directory";
643     desc.path="c:\\nonexistent\\path";
644     desc.pidl=NULL;
645     desc.arguments="";
646     desc.showcmd=0;
647     desc.icon="c:\\nonexistent\\icon\\file";
648     desc.icon_id=1234;
649     desc.hotkey=0;
650     create_lnk(lnkfile, &desc, 0);
651     check_lnk(lnkfile, &desc, 0x0);
652
653     r=GetModuleFileName(NULL, mypath, sizeof(mypath));
654     ok(r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
655     strcpy(mydir, mypath);
656     p=strrchr(mydir, '\\');
657     if (p)
658         *p='\0';
659
660     /* IShellLink returns path in long form */
661     if (!pGetLongPathNameA(mypath, realpath, MAX_PATH)) strcpy( realpath, mypath );
662
663     /* Overwrite the existing lnk file and point it to existing files */
664     desc.description="test 2";
665     desc.workdir=mydir;
666     desc.path=realpath;
667     desc.pidl=NULL;
668     desc.arguments="/option1 /option2 \"Some string\"";
669     desc.showcmd=SW_SHOWNORMAL;
670     desc.icon=mypath;
671     desc.icon_id=0;
672     desc.hotkey=0x1234;
673     create_lnk(lnkfile, &desc, 0);
674     check_lnk(lnkfile, &desc, 0x0);
675
676     /* Test omitting .exe from an absolute path */
677     p=strrchr(realpath, '.');
678     if (p)
679         *p='\0';
680
681     desc.description="absolute path without .exe";
682     desc.workdir=mydir;
683     desc.path=realpath;
684     desc.pidl=NULL;
685     desc.arguments="/option1 /option2 \"Some string\"";
686     desc.showcmd=SW_SHOWNORMAL;
687     desc.icon=mypath;
688     desc.icon_id=0;
689     desc.hotkey=0x1234;
690     create_lnk(lnkfile, &desc, 0);
691     strcat(realpath, ".exe");
692     check_lnk(lnkfile, &desc, 0x4);
693
694     /* Overwrite the existing lnk file and test link to a command on the path */
695     desc.description="command on path";
696     desc.workdir=mypath;
697     desc.path="rundll32.exe";
698     desc.pidl=NULL;
699     desc.arguments="/option1 /option2 \"Some string\"";
700     desc.showcmd=SW_SHOWNORMAL;
701     desc.icon=mypath;
702     desc.icon_id=0;
703     desc.hotkey=0x1234;
704     create_lnk(lnkfile, &desc, 0);
705     /* Check that link is created to proper location */
706     SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
707     desc.path=realpath;
708     check_lnk(lnkfile, &desc, 0x0);
709
710     /* Test omitting .exe from a command on the path */
711     desc.description="command on path without .exe";
712     desc.workdir=mypath;
713     desc.path="rundll32";
714     desc.pidl=NULL;
715     desc.arguments="/option1 /option2 \"Some string\"";
716     desc.showcmd=SW_SHOWNORMAL;
717     desc.icon=mypath;
718     desc.icon_id=0;
719     desc.hotkey=0x1234;
720     create_lnk(lnkfile, &desc, 0);
721     /* Check that link is created to proper location */
722     SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL);
723     desc.path=realpath;
724     check_lnk(lnkfile, &desc, 0x4);
725
726     /* Create a temporary non-executable file */
727     r=GetTempPath(sizeof(mypath), mypath);
728     ok(r<sizeof(mypath), "GetTempPath failed (%d), err %d\n", r, GetLastError());
729     r=pGetLongPathNameA(mypath, mydir, sizeof(mydir));
730     ok(r<sizeof(mydir), "GetLongPathName failed (%d), err %d\n", r, GetLastError());
731     p=strrchr(mydir, '\\');
732     if (p)
733         *p='\0';
734
735     strcpy(mypath, mydir);
736     strcat(mypath, "\\test.txt");
737     hf = CreateFile(mypath, GENERIC_WRITE, 0, NULL,
738                     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
739     CloseHandle(hf);
740
741     /* Overwrite the existing lnk file and test link to an existing non-executable file */
742     desc.description="non-executable file";
743     desc.workdir=mydir;
744     desc.path=mypath;
745     desc.pidl=NULL;
746     desc.arguments="";
747     desc.showcmd=SW_SHOWNORMAL;
748     desc.icon=mypath;
749     desc.icon_id=0;
750     desc.hotkey=0x1234;
751     create_lnk(lnkfile, &desc, 0);
752     check_lnk(lnkfile, &desc, 0x0);
753
754     r=pGetShortPathNameA(mydir, mypath, sizeof(mypath));
755     ok(r<sizeof(mypath), "GetShortPathName failed (%d), err %d\n", r, GetLastError());
756
757     strcpy(realpath, mypath);
758     strcat(realpath, "\\test.txt");
759     strcat(mypath, "\\\\test.txt");
760
761     /* Overwrite the existing lnk file and test link to a short path with double backslashes */
762     desc.description="non-executable file";
763     desc.workdir=mydir;
764     desc.path=mypath;
765     desc.pidl=NULL;
766     desc.arguments="";
767     desc.showcmd=SW_SHOWNORMAL;
768     desc.icon=mypath;
769     desc.icon_id=0;
770     desc.hotkey=0x1234;
771     create_lnk(lnkfile, &desc, 0);
772     desc.path=realpath;
773     check_lnk(lnkfile, &desc, 0x0);
774
775     r = DeleteFileA(mypath);
776     ok(r, "failed to delete file %s (%d)\n", mypath, GetLastError());
777
778     /* Create a temporary .bat file */
779     strcpy(mypath, mydir);
780     strcat(mypath, "\\test.bat");
781     hf = CreateFile(mypath, GENERIC_WRITE, 0, NULL,
782                     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
783     CloseHandle(hf);
784
785     strcpy(realpath, mypath);
786
787     p=strrchr(mypath, '.');
788     if (p)
789         *p='\0';
790
791     /* Try linking to the .bat file without the extension */
792     desc.description="batch file";
793     desc.workdir=mydir;
794     desc.path=mypath;
795     desc.pidl=NULL;
796     desc.arguments="";
797     desc.showcmd=SW_SHOWNORMAL;
798     desc.icon=mypath;
799     desc.icon_id=0;
800     desc.hotkey=0x1234;
801     create_lnk(lnkfile, &desc, 0);
802     desc.path = realpath;
803     check_lnk(lnkfile, &desc, 0x4);
804
805     r = DeleteFileA(realpath);
806     ok(r, "failed to delete file %s (%d)\n", realpath, GetLastError());
807
808     /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
809      * represented as a path.
810      */
811
812     /* DeleteFileW is not implemented on Win9x */
813     r=DeleteFileA(lnkfileA);
814     ok(r, "failed to delete link '%s' (%d)\n", lnkfileA, GetLastError());
815 }
816
817 static void test_datalink(void)
818 {
819     static const WCHAR lnk[] = {
820       ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
821       '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
822       '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
823       'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
824       '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
825       '1','-',']',':',':',0 };
826     static const WCHAR comp[] = {
827       '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
828       'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
829       'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
830     IShellLinkDataList *dl = NULL;
831     IShellLinkW *sl = NULL;
832     HRESULT r;
833     DWORD flags = 0;
834     EXP_DARWIN_LINK *dar;
835
836     r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
837                             &IID_IShellLinkW, (LPVOID*)&sl );
838     ok( r == S_OK ||
839         broken(r == E_NOINTERFACE), /* Win9x */
840         "CoCreateInstance failed (0x%08x)\n", r);
841     if (!sl)
842     {
843         win_skip("no shelllink\n");
844         return;
845     }
846
847     r = IShellLinkW_QueryInterface( sl, &_IID_IShellLinkDataList, (LPVOID*) &dl );
848     ok( r == S_OK ||
849         broken(r == E_NOINTERFACE), /* NT4 */
850         "IShellLinkW_QueryInterface failed (0x%08x)\n", r);
851
852     if (!dl)
853     {
854         win_skip("no datalink interface\n");
855         IShellLinkW_Release( sl );
856         return;
857     }
858
859     flags = 0;
860     r = IShellLinkDataList_GetFlags( dl, &flags );
861     ok( r == S_OK, "GetFlags failed\n");
862     ok( flags == 0, "GetFlags returned wrong flags\n");
863
864     dar = (void*)-1;
865     r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
866     ok( r == E_FAIL, "CopyDataBlock failed\n");
867     ok( dar == NULL, "should be null\n");
868
869     if (!pGetLongPathNameA /* NT4 */)
870         skip("SetPath with NULL parameter crashes on NT4\n");
871     else
872     {
873         r = IShellLinkW_SetPath(sl, NULL);
874         ok(r == E_INVALIDARG, "SetPath returned wrong error (0x%08x)\n", r);
875     }
876
877     r = IShellLinkW_SetPath(sl, lnk);
878     ok(r == S_OK, "SetPath failed\n");
879
880 if (0)
881 {
882     /* the following crashes */
883     IShellLinkDataList_GetFlags( dl, NULL );
884 }
885
886     flags = 0;
887     r = IShellLinkDataList_GetFlags( dl, &flags );
888     ok( r == S_OK, "GetFlags failed\n");
889     /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
890     ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
891         "GetFlags returned wrong flags\n");
892
893     dar = NULL;
894     r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
895     ok( r == S_OK, "CopyDataBlock failed\n");
896
897     ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
898     ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
899
900     LocalFree( dar );
901
902     IShellLinkDataList_Release( dl );
903     IShellLinkW_Release( sl );
904 }
905
906 static void test_shdefextracticon(void)
907 {
908     HICON hiconlarge=NULL, hiconsmall=NULL;
909     HRESULT res;
910
911     if (!pSHDefExtractIconA)
912     {
913         win_skip("SHDefExtractIconA is unavailable\n");
914         return;
915     }
916
917     res = pSHDefExtractIconA("shell32.dll", 0, 0, &hiconlarge, &hiconsmall, MAKELONG(16,24));
918     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
919     ok(hiconlarge != NULL, "got null hiconlarge\n");
920     ok(hiconsmall != NULL, "got null hiconsmall\n");
921     DestroyIcon(hiconlarge);
922     DestroyIcon(hiconsmall);
923
924     hiconsmall = NULL;
925     res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, &hiconsmall, MAKELONG(16,24));
926     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
927     ok(hiconsmall != NULL, "got null hiconsmall\n");
928     DestroyIcon(hiconsmall);
929
930     res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, NULL, MAKELONG(16,24));
931     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
932 }
933
934 static void test_GetIconLocation(void)
935 {
936     IShellLinkA *sl;
937     const char *str;
938     char buffer[INFOTIPSIZE], mypath[MAX_PATH];
939     int i;
940     HRESULT r;
941     LPITEMIDLIST pidl;
942
943     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
944             &IID_IShellLinkA, (LPVOID*)&sl);
945     ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
946     if(r != S_OK)
947         return;
948
949     i = 0xdeadbeef;
950     strcpy(buffer, "garbage");
951     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
952     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
953     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
954     ok(i == 0, "GetIconLocation returned %d\n", i);
955
956     str = "c:\\some\\path";
957     r = IShellLinkA_SetPath(sl, str);
958     ok(r == S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
959
960     i = 0xdeadbeef;
961     strcpy(buffer, "garbage");
962     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
963     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
964     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
965     ok(i == 0, "GetIconLocation returned %d\n", i);
966
967     GetWindowsDirectoryA(mypath, sizeof(mypath) - 12);
968     strcat(mypath, "\\regedit.exe");
969     pidl = path_to_pidl(mypath);
970     r = IShellLinkA_SetIDList(sl, pidl);
971     ok(r == S_OK, "SetPath failed (0x%08x)\n", r);
972     pILFree(pidl);
973
974     i = 0xdeadbeef;
975     strcpy(buffer, "garbage");
976     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
977     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
978     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
979     ok(i == 0, "GetIconLocation returned %d\n", i);
980
981     str = "c:\\nonexistent\\file";
982     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
983     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
984
985     i = 0xdeadbeef;
986     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
987     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
988     ok(lstrcmpi(buffer,str) == 0, "GetIconLocation returned '%s'\n", buffer);
989     ok(i == 0xbabecafe, "GetIconLocation returned %d'\n", i);
990
991     IShellLinkA_Release(sl);
992 }
993
994 static void test_SHGetStockIconInfo(void)
995 {
996     BYTE buffer[sizeof(SHSTOCKICONINFO) + 16];
997     SHSTOCKICONINFO *sii = (SHSTOCKICONINFO *) buffer;
998     BOOL atleast_win7;
999     HRESULT hr;
1000     INT i;
1001
1002     /* not present before vista */
1003     if (!pSHGetStockIconInfo)
1004     {
1005         win_skip("SHGetStockIconInfo not available\n");
1006         return;
1007     }
1008
1009     /* negative values are handled */
1010     memset(buffer, '#', sizeof(buffer));
1011     sii->cbSize = sizeof(SHSTOCKICONINFO);
1012     hr = pSHGetStockIconInfo(-1, SHGSI_ICONLOCATION, sii);
1013     ok(hr == E_INVALIDARG, "-1: got 0x%x (expected E_INVALIDARG)\n", hr);
1014
1015     /* max. id for vista is 140 (no definition exists for this value) */
1016     for (i = 0; i <= 140; i++)
1017     {
1018         memset(buffer, '#', sizeof(buffer));
1019         sii->cbSize = sizeof(SHSTOCKICONINFO);
1020         hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1021
1022         ok(hr == S_OK,
1023             "%3d: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1024             i, hr, sii->iSysImageIndex, sii->iIcon);
1025
1026         if ((hr == S_OK) && (winetest_debug > 1))
1027             trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1028                   sii->iIcon, wine_dbgstr_w(sii->szPath));
1029     }
1030
1031     /* there are more icons since win7 */
1032     memset(buffer, '#', sizeof(buffer));
1033     sii->cbSize = sizeof(SHSTOCKICONINFO);
1034     hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1035     atleast_win7 = (!hr);
1036
1037     for (; i < (SIID_MAX_ICONS + 25) ; i++)
1038     {
1039         memset(buffer, '#', sizeof(buffer));
1040         sii->cbSize = sizeof(SHSTOCKICONINFO);
1041         hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1042
1043         if (atleast_win7 && (i == (SIID_MAX_ICONS - 1)) && broken(hr == E_INVALIDARG))
1044         {
1045             /* off by one windows bug: there are SIID_MAX_ICONS icons from 0 upto
1046                SIID_MAX_ICONS-1 on win8, but the last one is missing on win7 */
1047             trace("%3d: got E_INVALIDARG (windows bug: off by one)\n", i);
1048         }
1049         else if (atleast_win7 && (i < (SIID_MAX_ICONS)))
1050         {
1051             ok(hr == S_OK,
1052                 "%3d: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1053                 i, hr, sii->iSysImageIndex, sii->iIcon);
1054
1055             if ((hr == S_OK) && (winetest_debug > 1))
1056                 trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1057                       sii->iIcon, wine_dbgstr_w(sii->szPath));
1058         }
1059         else
1060             ok(hr == E_INVALIDARG, "%3d: got 0x%x (expected E_INVALIDARG)\n", i, hr);
1061     }
1062
1063     /* test more returned SHSTOCKICONINFO elements without extra flags */
1064     memset(buffer, '#', sizeof(buffer));
1065     sii->cbSize = sizeof(SHSTOCKICONINFO);
1066     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1067     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
1068     ok(!sii->hIcon, "got %p (expected NULL)\n", sii->hIcon);
1069     ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1070
1071     /* the exact size is required of the struct */
1072     memset(buffer, '#', sizeof(buffer));
1073     sii->cbSize = sizeof(SHSTOCKICONINFO) + 2;
1074     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1075     ok(hr == E_INVALIDARG, "+2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1076
1077     memset(buffer, '#', sizeof(buffer));
1078     sii->cbSize = sizeof(SHSTOCKICONINFO) + 1;
1079     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1080     ok(hr == E_INVALIDARG, "+1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1081
1082     memset(buffer, '#', sizeof(buffer));
1083     sii->cbSize = sizeof(SHSTOCKICONINFO) - 1;
1084     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1085     ok(hr == E_INVALIDARG, "-1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1086
1087     memset(buffer, '#', sizeof(buffer));
1088     sii->cbSize = sizeof(SHSTOCKICONINFO) - 2;
1089     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1090     ok(hr == E_INVALIDARG, "-2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1091
1092     /* there is a NULL check for the struct  */
1093     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
1094     ok(hr == E_INVALIDARG, "NULL: got 0x%x\n", hr);
1095 }
1096
1097 START_TEST(shelllink)
1098 {
1099     HRESULT r;
1100     HMODULE hmod = GetModuleHandleA("shell32.dll");
1101     HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1102
1103     pILFree = (void *)GetProcAddress(hmod, (LPSTR)155);
1104     pILIsEqual = (void *)GetProcAddress(hmod, (LPSTR)21);
1105     pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28);
1106     pSHDefExtractIconA = (void *)GetProcAddress(hmod, "SHDefExtractIconA");
1107     pSHGetStockIconInfo = (void *)GetProcAddress(hmod, "SHGetStockIconInfo");
1108     pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA");
1109     pGetShortPathNameA = (void *)GetProcAddress(hkernel32, "GetShortPathNameA");
1110
1111     r = CoInitialize(NULL);
1112     ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
1113     if (r != S_OK)
1114         return;
1115
1116     test_get_set();
1117     test_load_save();
1118     test_datalink();
1119     test_shdefextracticon();
1120     test_GetIconLocation();
1121     test_SHGetStockIconInfo();
1122
1123     CoUninitialize();
1124 }