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 "shellapi.h"
30 #include "commoncontrols.h"
31 
32 #include "wine/heap.h"
33 #include "wine/test.h"
34 
35 #include "shell32_test.h"
36 
37 #ifdef __REACTOS__
38 #include <reactos/undocshell.h>
39 #endif
40 
41 #ifndef SLDF_HAS_LOGO3ID
42 #  define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */
43 #endif
44 
45 static void (WINAPI *pILFree)(LPITEMIDLIST);
46 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
47 static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
48 static HRESULT (WINAPI *pSHGetFolderLocation)(HWND,INT,HANDLE,DWORD,PIDLIST_ABSOLUTE*);
49 static HRESULT (WINAPI *pSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT);
50 static HRESULT (WINAPI *pSHGetStockIconInfo)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
51 static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD);
52 static DWORD (WINAPI *pGetShortPathNameA)(LPCSTR, LPSTR, DWORD);
53 static UINT (WINAPI *pSHExtractIconsW)(LPCWSTR, int, int, int, HICON *, UINT *, UINT, UINT);
54 static BOOL (WINAPI *pIsProcessDPIAware)(void);
55 
56 static const GUID _IID_IShellLinkDataList = {
57     0x45e2b4ae, 0xb1c3, 0x11d0,
58     { 0xb9, 0x2f, 0x00, 0xa0, 0xc9, 0x03, 0x12, 0xe1 }
59 };
60 
61 
62 /* For some reason SHILCreateFromPath does not work on Win98 and
63  * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
64  * get what we want on all platforms.
65  */
66 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
67 
68 static LPITEMIDLIST path_to_pidl(const char* path)
69 {
70     LPITEMIDLIST pidl;
71 
72     if (!pSHSimpleIDListFromPathAW)
73     {
74         HMODULE hdll=GetModuleHandleA("shell32.dll");
75         pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
76         if (!pSHSimpleIDListFromPathAW)
77             win_skip("SHSimpleIDListFromPathAW not found in shell32.dll\n");
78     }
79 
80     pidl=NULL;
81     /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
82     if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
83         pidl=pSHSimpleIDListFromPathAW(path);
84 
85     if (!pidl)
86     {
87         WCHAR* pathW;
88         HRESULT r;
89         int len;
90 
91         len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
92         pathW = heap_alloc(len * sizeof(WCHAR));
93         MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
94 
95         r=pSHILCreateFromPath(pathW, &pidl, NULL);
96         ok(r == S_OK, "SHILCreateFromPath failed (0x%08x)\n", r);
97         heap_free(pathW);
98     }
99     return pidl;
100 }
101 
102 
103 /*
104  * Test manipulation of an IShellLink's properties.
105  */
106 
107 static void test_get_set(void)
108 {
109     HRESULT r;
110     IShellLinkA *sl;
111     IShellLinkW *slW = NULL;
112     char mypath[MAX_PATH];
113     char buffer[INFOTIPSIZE];
114     WIN32_FIND_DATAA finddata;
115     LPITEMIDLIST pidl, tmp_pidl;
116     const char * str;
117     int i;
118     WORD w;
119 
120     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
121                          &IID_IShellLinkA, (LPVOID*)&sl);
122     ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
123     if (r != S_OK)
124         return;
125 
126     /* Test Getting / Setting the description */
127     strcpy(buffer,"garbage");
128     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
129     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
130     ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
131 
132     str="Some description";
133     r = IShellLinkA_SetDescription(sl, str);
134     ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
135 
136     strcpy(buffer,"garbage");
137     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
138     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
139     ok(strcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
140 
141     r = IShellLinkA_SetDescription(sl, NULL);
142     ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
143 
144     strcpy(buffer,"garbage");
145     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
146     ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
147     ok(*buffer=='\0' || broken(strcmp(buffer,str)==0), "GetDescription returned '%s'\n", buffer); /* NT4 */
148 
149     /* Test Getting / Setting the work directory */
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(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
154 
155     str="c:\\nonexistent\\directory";
156     r = IShellLinkA_SetWorkingDirectory(sl, str);
157     ok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
158 
159     strcpy(buffer,"garbage");
160     r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
161     ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
162     ok(lstrcmpiA(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
163 
164     /* Test Getting / Setting the path */
165     strcpy(buffer,"garbage");
166     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
167     ok(r == S_FALSE || broken(r == S_OK) /* NT4/W2K */, "GetPath failed (0x%08x)\n", r);
168     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
169 
170     strcpy(buffer,"garbage");
171     memset(&finddata, 0xaa, sizeof(finddata));
172     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
173     ok(r == S_FALSE || broken(r == S_OK) /* NT4/W2K */, "GetPath failed (0x%08x)\n", r);
174     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
175     ok(finddata.dwFileAttributes == 0, "unexpected attributes %x\n", finddata.dwFileAttributes);
176     ok(finddata.cFileName[0] == 0, "unexpected filename '%s'\n", finddata.cFileName);
177 
178     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
179                          &IID_IShellLinkW, (LPVOID*)&slW);
180     ok(r == S_OK, "CoCreateInstance failed (0x%08x)\n", r);
181     if (!slW /* Win9x */ || !pGetLongPathNameA /* NT4 */)
182         skip("SetPath with NULL parameter crashes on Win9x and some NT4\n");
183     else
184     {
185         IShellLinkW_Release(slW);
186         r = IShellLinkA_SetPath(sl, NULL);
187         ok(r==E_INVALIDARG ||
188            broken(r==S_OK), /* Some Win95 and NT4 */
189            "SetPath returned wrong error (0x%08x)\n", r);
190     }
191 
192     r = IShellLinkA_SetPath(sl, "");
193     ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
194 
195     strcpy(buffer,"garbage");
196     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
197     ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
198     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
199 
200     /* Win98 returns S_FALSE, but WinXP returns S_OK */
201     str="c:\\nonexistent\\file";
202     r = IShellLinkA_SetPath(sl, str);
203     ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08x)\n", r);
204 
205     strcpy(buffer,"garbage");
206     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
207     ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
208     ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
209 
210     strcpy(buffer,"garbage");
211     memset(&finddata, 0xaa, sizeof(finddata));
212     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
213     ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
214     ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
215     ok(finddata.dwFileAttributes == 0, "unexpected attributes %x\n", finddata.dwFileAttributes);
216     ok(lstrcmpiA(finddata.cFileName, "file") == 0, "unexpected filename '%s'\n", finddata.cFileName);
217 
218     /* Get some real path to play with */
219     GetWindowsDirectoryA( mypath, sizeof(mypath)-12 );
220     strcat(mypath, "\\regedit.exe");
221 
222     /* Test the interaction of SetPath and SetIDList */
223     tmp_pidl=NULL;
224     r = IShellLinkA_GetIDList(sl, &tmp_pidl);
225     ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
226     if (r == S_OK)
227     {
228         BOOL ret;
229 
230         strcpy(buffer,"garbage");
231         ret = SHGetPathFromIDListA(tmp_pidl, buffer);
232         ok(ret, "SHGetPathFromIDListA failed\n");
233         if (ret)
234             ok(lstrcmpiA(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
235         pILFree(tmp_pidl);
236     }
237 
238     pidl=path_to_pidl(mypath);
239     ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
240 
241     if (pidl)
242     {
243         LPITEMIDLIST second_pidl;
244 
245         r = IShellLinkA_SetIDList(sl, pidl);
246         ok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
247 
248         tmp_pidl=NULL;
249         r = IShellLinkA_GetIDList(sl, &tmp_pidl);
250         ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
251         ok(tmp_pidl && pILIsEqual(pidl, tmp_pidl),
252            "GetIDList returned an incorrect pidl\n");
253 
254         r = IShellLinkA_GetIDList(sl, &second_pidl);
255         ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
256         ok(second_pidl && pILIsEqual(pidl, second_pidl),
257            "GetIDList returned an incorrect pidl\n");
258         ok(second_pidl != tmp_pidl, "pidls are the same\n");
259 
260         pILFree(second_pidl);
261         pILFree(tmp_pidl);
262         pILFree(pidl);
263 
264         strcpy(buffer,"garbage");
265         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
266         ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
267         ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
268 
269         strcpy(buffer,"garbage");
270         memset(&finddata, 0xaa, sizeof(finddata));
271         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
272         ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
273         ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
274         ok(finddata.dwFileAttributes != 0, "unexpected attributes %x\n", finddata.dwFileAttributes);
275         ok(lstrcmpiA(finddata.cFileName, "regedit.exe") == 0, "unexpected filename '%s'\n", finddata.cFileName);
276     }
277 
278     if (pSHGetFolderLocation)
279     {
280         LPITEMIDLIST pidl_controls;
281 
282         r = pSHGetFolderLocation(NULL, CSIDL_CONTROLS, NULL, 0, &pidl_controls);
283         ok(r == S_OK, "SHGetFolderLocation failed (0x%08x)\n", r);
284 
285         r = IShellLinkA_SetIDList(sl, pidl_controls);
286         ok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
287 
288         strcpy(buffer,"garbage");
289         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
290         ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
291         ok(buffer[0] == 0, "GetPath returned '%s'\n", buffer);
292 
293         strcpy(buffer,"garbage");
294         memset(&finddata, 0xaa, sizeof(finddata));
295         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
296         ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
297         ok(buffer[0] == 0, "GetPath returned '%s'\n", buffer);
298         ok(finddata.dwFileAttributes == 0, "unexpected attributes %x\n", finddata.dwFileAttributes);
299         ok(finddata.cFileName[0] == 0, "unexpected filename '%s'\n", finddata.cFileName);
300 
301         pILFree(pidl_controls);
302     }
303 
304     /* test path with quotes (IShellLinkA_SetPath returns S_FALSE on W2K and below and S_OK on XP and above */
305     r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
306     ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
307 
308     strcpy(buffer,"garbage");
309     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
310     ok(r==S_OK, "GetPath failed (0x%08x)\n", r);
311     todo_wine ok(!strcmp(buffer, "C:\\nonexistent\\file") ||
312        broken(!strcmp(buffer, "C:\\\"c:\\nonexistent\\file\"")), /* NT4 */
313        "case doesn't match\n");
314 
315     r = IShellLinkA_SetPath(sl, "\"c:\\foo");
316     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
317 
318     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
319     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
320 
321     r = IShellLinkA_SetPath(sl, "c:\\foo\"");
322     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
323 
324     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
325     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
326 
327     r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
328     ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
329 
330     /* Test Getting / Setting the arguments */
331     strcpy(buffer,"garbage");
332     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
333     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
334     ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
335 
336     str="param1 \"spaced param2\"";
337     r = IShellLinkA_SetArguments(sl, str);
338     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
339 
340     strcpy(buffer,"garbage");
341     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
342     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
343     ok(strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
344 
345     strcpy(buffer,"garbage");
346     r = IShellLinkA_SetArguments(sl, NULL);
347     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
348     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
349     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
350     ok(!buffer[0] || strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
351 
352     strcpy(buffer,"garbage");
353     r = IShellLinkA_SetArguments(sl, "");
354     ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
355     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
356     ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
357     ok(!buffer[0], "GetArguments returned '%s'\n", buffer);
358 
359     /* Test Getting / Setting showcmd */
360     i=0xdeadbeef;
361     r = IShellLinkA_GetShowCmd(sl, &i);
362     ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
363     ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
364 
365     r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
366     ok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
367 
368     i=0xdeadbeef;
369     r = IShellLinkA_GetShowCmd(sl, &i);
370     ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
371     ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
372 
373     /* Test Getting / Setting the icon */
374     i=0xdeadbeef;
375     strcpy(buffer,"garbage");
376     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
377     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
378     ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
379     ok(i==0, "GetIconLocation returned %d\n", i);
380 
381     str="c:\\nonexistent\\file";
382     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
383     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
384 
385     i=0xdeadbeef;
386     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
387     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
388     ok(lstrcmpiA(buffer,str)==0, "GetIconLocation returned '%s'\n", buffer);
389     ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
390 
391     /* Test Getting / Setting the hot key */
392     w=0xbeef;
393     r = IShellLinkA_GetHotkey(sl, &w);
394     ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
395     ok(w==0, "GetHotkey returned %d\n", w);
396 
397     r = IShellLinkA_SetHotkey(sl, 0x5678);
398     ok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
399 
400     w=0xbeef;
401     r = IShellLinkA_GetHotkey(sl, &w);
402     ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
403     ok(w==0x5678, "GetHotkey returned %d'\n", w);
404 
405     IShellLinkA_Release(sl);
406 }
407 
408 
409 /*
410  * Test saving and loading .lnk files
411  */
412 
413 #define lok                   ok_(__FILE__, line)
414 #define check_lnk(a,b,c)        check_lnk_(__LINE__, (a), (b), (c))
415 
416 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
417 {
418     HRESULT r;
419     IShellLinkA *sl;
420     IPersistFile *pf;
421 
422     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
423                          &IID_IShellLinkA, (LPVOID*)&sl);
424     lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
425     if (r != S_OK)
426         return;
427 
428     if (desc->description)
429     {
430         r = IShellLinkA_SetDescription(sl, desc->description);
431         lok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
432     }
433     if (desc->workdir)
434     {
435         r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
436         lok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
437     }
438     if (desc->path)
439     {
440         r = IShellLinkA_SetPath(sl, desc->path);
441         lok(SUCCEEDED(r), "SetPath failed (0x%08x)\n", r);
442     }
443     if (desc->pidl)
444     {
445         r = IShellLinkA_SetIDList(sl, desc->pidl);
446         lok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
447     }
448     if (desc->arguments)
449     {
450         r = IShellLinkA_SetArguments(sl, desc->arguments);
451         lok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
452     }
453     if (desc->showcmd)
454     {
455         r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
456         lok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
457     }
458     if (desc->icon)
459     {
460         r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
461         lok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
462     }
463     if (desc->hotkey)
464     {
465         r = IShellLinkA_SetHotkey(sl, desc->hotkey);
466         lok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
467     }
468 
469     r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
470     lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
471     if (r == S_OK)
472     {
473         LPOLESTR str;
474 
475     if (0)
476     {
477         /* crashes on XP */
478         IPersistFile_GetCurFile(pf, NULL);
479     }
480 
481         /* test GetCurFile before ::Save */
482         str = (LPWSTR)0xdeadbeef;
483         r = IPersistFile_GetCurFile(pf, &str);
484         lok(r == S_FALSE ||
485             broken(r == S_OK), /* shell32 < 5.0 */
486             "got 0x%08x\n", r);
487         lok(str == NULL, "got %p\n", str);
488 
489         r = IPersistFile_Save(pf, path, TRUE);
490         todo_wine_if (save_fails)
491             lok(r == S_OK, "save failed (0x%08x)\n", r);
492 
493         /* test GetCurFile after ::Save */
494         r = IPersistFile_GetCurFile(pf, &str);
495         lok(r == S_OK, "got 0x%08x\n", r);
496         lok(str != NULL ||
497             broken(str == NULL), /* shell32 < 5.0 */
498             "Didn't expect NULL\n");
499         if (str != NULL)
500         {
501             IMalloc *pmalloc;
502 
503             lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
504                 wine_dbgstr_w(path), wine_dbgstr_w(str));
505 
506             SHGetMalloc(&pmalloc);
507             IMalloc_Free(pmalloc, str);
508         }
509         else
510             win_skip("GetCurFile fails on shell32 < 5.0\n");
511 
512         IPersistFile_Release(pf);
513     }
514 
515     IShellLinkA_Release(sl);
516 }
517 
518 static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
519 {
520     HRESULT r;
521     IShellLinkA *sl;
522     IPersistFile *pf;
523     char buffer[INFOTIPSIZE];
524     LPOLESTR str;
525 
526     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
527                          &IID_IShellLinkA, (LPVOID*)&sl);
528     lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
529     if (r != S_OK)
530         return;
531 
532     r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
533     lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
534     if (r != S_OK)
535     {
536         IShellLinkA_Release(sl);
537         return;
538     }
539 
540     /* test GetCurFile before ::Load */
541     str = (LPWSTR)0xdeadbeef;
542     r = IPersistFile_GetCurFile(pf, &str);
543     lok(r == S_FALSE ||
544         broken(r == S_OK), /* shell32 < 5.0 */
545         "got 0x%08x\n", r);
546     lok(str == NULL, "got %p\n", str);
547 
548     r = IPersistFile_Load(pf, path, STGM_READ);
549     lok(r == S_OK, "load failed (0x%08x)\n", r);
550 
551     /* test GetCurFile after ::Save */
552     r = IPersistFile_GetCurFile(pf, &str);
553     lok(r == S_OK, "got 0x%08x\n", r);
554     lok(str != NULL ||
555         broken(str == NULL), /* shell32 < 5.0 */
556         "Didn't expect NULL\n");
557     if (str != NULL)
558     {
559         IMalloc *pmalloc;
560 
561         lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
562             wine_dbgstr_w(path), wine_dbgstr_w(str));
563 
564         SHGetMalloc(&pmalloc);
565         IMalloc_Free(pmalloc, str);
566     }
567     else
568         win_skip("GetCurFile fails on shell32 < 5.0\n");
569 
570     IPersistFile_Release(pf);
571     if (r != S_OK)
572     {
573         IShellLinkA_Release(sl);
574         return;
575     }
576 
577     if (desc->description)
578     {
579         strcpy(buffer,"garbage");
580         r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
581         lok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
582         todo_wine_if ((todo & 0x1) != 0)
583             lok(strcmp(buffer, desc->description)==0, "GetDescription returned '%s' instead of '%s'\n",
584                 buffer, desc->description);
585     }
586     if (desc->workdir)
587     {
588         strcpy(buffer,"garbage");
589         r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
590         lok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
591         todo_wine_if ((todo & 0x2) != 0)
592             lok(lstrcmpiA(buffer, desc->workdir)==0, "GetWorkingDirectory returned '%s' instead of '%s'\n",
593                 buffer, desc->workdir);
594     }
595     if (desc->path)
596     {
597         strcpy(buffer,"garbage");
598         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
599         lok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
600         todo_wine_if ((todo & 0x4) != 0)
601             lok(lstrcmpiA(buffer, desc->path)==0, "GetPath returned '%s' instead of '%s'\n",
602                 buffer, desc->path);
603     }
604     if (desc->pidl)
605     {
606         LPITEMIDLIST pidl=NULL;
607         r = IShellLinkA_GetIDList(sl, &pidl);
608         lok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
609         todo_wine_if ((todo & 0x8) != 0)
610             lok(pILIsEqual(pidl, desc->pidl), "GetIDList returned an incorrect pidl\n");
611     }
612     if (desc->showcmd)
613     {
614         int i=0xdeadbeef;
615         r = IShellLinkA_GetShowCmd(sl, &i);
616         lok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
617         todo_wine_if ((todo & 0x10) != 0)
618             lok(i==desc->showcmd, "GetShowCmd returned 0x%0x instead of 0x%0x\n",
619                 i, desc->showcmd);
620     }
621     if (desc->icon)
622     {
623         int i=0xdeadbeef;
624         strcpy(buffer,"garbage");
625         r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
626         lok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
627         todo_wine_if ((todo & 0x20) != 0) {
628             lok(lstrcmpiA(buffer, desc->icon)==0, "GetIconLocation returned '%s' instead of '%s'\n",
629                 buffer, desc->icon);
630             lok(i==desc->icon_id, "GetIconLocation returned 0x%0x instead of 0x%0x\n",
631                 i, desc->icon_id);
632         }
633     }
634     if (desc->hotkey)
635     {
636         WORD i=0xbeef;
637         r = IShellLinkA_GetHotkey(sl, &i);
638         lok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
639         todo_wine_if ((todo & 0x40) != 0)
640             lok(i==desc->hotkey, "GetHotkey returned 0x%04x instead of 0x%04x\n",
641                 i, desc->hotkey);
642     }
643 
644     IShellLinkA_Release(sl);
645 }
646 
647 static void test_load_save(void)
648 {
649     WCHAR lnkfile[MAX_PATH];
650     char lnkfileA[MAX_PATH];
651     static const char lnkfileA_name[] = "\\test.lnk";
652 
653     lnk_desc_t desc;
654     char mypath[MAX_PATH];
655     char mydir[MAX_PATH];
656     char realpath[MAX_PATH];
657     char* p;
658     HANDLE hf;
659     DWORD r;
660 
661     if (!pGetLongPathNameA)
662     {
663         win_skip("GetLongPathNameA is not available\n");
664         return;
665     }
666 
667     /* Don't used a fixed path for the test.lnk file */
668     GetTempPathA(MAX_PATH, lnkfileA);
669     lstrcatA(lnkfileA, lnkfileA_name);
670     MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
671 
672     /* Save an empty .lnk file */
673     memset(&desc, 0, sizeof(desc));
674     create_lnk(lnkfile, &desc, 0);
675 
676     /* It should come back as a bunch of empty strings */
677     desc.description="";
678     desc.workdir="";
679     desc.path="";
680     desc.arguments="";
681     desc.icon="";
682     check_lnk(lnkfile, &desc, 0x0);
683 
684     /* Point a .lnk file to nonexistent files */
685     desc.description="";
686     desc.workdir="c:\\Nonexitent\\work\\directory";
687     desc.path="c:\\nonexistent\\path";
688     desc.pidl=NULL;
689     desc.arguments="";
690     desc.showcmd=0;
691     desc.icon="c:\\nonexistent\\icon\\file";
692     desc.icon_id=1234;
693     desc.hotkey=0;
694     create_lnk(lnkfile, &desc, 0);
695     check_lnk(lnkfile, &desc, 0x0);
696 
697     r=GetModuleFileNameA(NULL, mypath, sizeof(mypath));
698     ok(r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
699     strcpy(mydir, mypath);
700     p=strrchr(mydir, '\\');
701     if (p)
702         *p='\0';
703 
704     /* IShellLink returns path in long form */
705     if (!pGetLongPathNameA(mypath, realpath, MAX_PATH)) strcpy( realpath, mypath );
706 
707     /* Overwrite the existing lnk file and point it to existing files */
708     desc.description="test 2";
709     desc.workdir=mydir;
710     desc.path=realpath;
711     desc.pidl=NULL;
712     desc.arguments="/option1 /option2 \"Some string\"";
713     desc.showcmd=SW_SHOWNORMAL;
714     desc.icon=mypath;
715     desc.icon_id=0;
716     desc.hotkey=0x1234;
717     create_lnk(lnkfile, &desc, 0);
718     check_lnk(lnkfile, &desc, 0x0);
719 
720     /* Test omitting .exe from an absolute path */
721     p=strrchr(realpath, '.');
722     if (p)
723         *p='\0';
724 
725     desc.description="absolute path without .exe";
726     desc.workdir=mydir;
727     desc.path=realpath;
728     desc.pidl=NULL;
729     desc.arguments="/option1 /option2 \"Some string\"";
730     desc.showcmd=SW_SHOWNORMAL;
731     desc.icon=mypath;
732     desc.icon_id=0;
733     desc.hotkey=0x1234;
734     create_lnk(lnkfile, &desc, 0);
735     strcat(realpath, ".exe");
736     check_lnk(lnkfile, &desc, 0x4);
737 
738     /* Overwrite the existing lnk file and test link to a command on the path */
739     desc.description="command on path";
740     desc.workdir=mypath;
741     desc.path="rundll32.exe";
742     desc.pidl=NULL;
743     desc.arguments="/option1 /option2 \"Some string\"";
744     desc.showcmd=SW_SHOWNORMAL;
745     desc.icon=mypath;
746     desc.icon_id=0;
747     desc.hotkey=0x1234;
748     create_lnk(lnkfile, &desc, 0);
749     /* Check that link is created to proper location */
750     SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
751     desc.path=realpath;
752     check_lnk(lnkfile, &desc, 0x0);
753 
754     /* Test omitting .exe from a command on the path */
755     desc.description="command on path without .exe";
756     desc.workdir=mypath;
757     desc.path="rundll32";
758     desc.pidl=NULL;
759     desc.arguments="/option1 /option2 \"Some string\"";
760     desc.showcmd=SW_SHOWNORMAL;
761     desc.icon=mypath;
762     desc.icon_id=0;
763     desc.hotkey=0x1234;
764     create_lnk(lnkfile, &desc, 0);
765     /* Check that link is created to proper location */
766     SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL);
767     desc.path=realpath;
768     check_lnk(lnkfile, &desc, 0x4);
769 
770     /* Create a temporary non-executable file */
771     r=GetTempPathA(sizeof(mypath), mypath);
772     ok(r<sizeof(mypath), "GetTempPath failed (%d), err %d\n", r, GetLastError());
773     r=pGetLongPathNameA(mypath, mydir, sizeof(mydir));
774     ok(r<sizeof(mydir), "GetLongPathName failed (%d), err %d\n", r, GetLastError());
775     p=strrchr(mydir, '\\');
776     if (p)
777         *p='\0';
778 
779     strcpy(mypath, mydir);
780     strcat(mypath, "\\test.txt");
781     hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
782                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
783     CloseHandle(hf);
784 
785     /* Overwrite the existing lnk file and test link to an existing non-executable file */
786     desc.description="non-executable file";
787     desc.workdir=mydir;
788     desc.path=mypath;
789     desc.pidl=NULL;
790     desc.arguments="";
791     desc.showcmd=SW_SHOWNORMAL;
792     desc.icon=mypath;
793     desc.icon_id=0;
794     desc.hotkey=0x1234;
795     create_lnk(lnkfile, &desc, 0);
796     check_lnk(lnkfile, &desc, 0x0);
797 
798     r=pGetShortPathNameA(mydir, mypath, sizeof(mypath));
799     ok(r<sizeof(mypath), "GetShortPathName failed (%d), err %d\n", r, GetLastError());
800 
801     strcpy(realpath, mypath);
802     strcat(realpath, "\\test.txt");
803     strcat(mypath, "\\\\test.txt");
804 
805     /* Overwrite the existing lnk file and test link to a short path with double backslashes */
806     desc.description="non-executable file";
807     desc.workdir=mydir;
808     desc.path=mypath;
809     desc.pidl=NULL;
810     desc.arguments="";
811     desc.showcmd=SW_SHOWNORMAL;
812     desc.icon=mypath;
813     desc.icon_id=0;
814     desc.hotkey=0x1234;
815     create_lnk(lnkfile, &desc, 0);
816     desc.path=realpath;
817     check_lnk(lnkfile, &desc, 0x0);
818 
819     r = DeleteFileA(mypath);
820     ok(r, "failed to delete file %s (%d)\n", mypath, GetLastError());
821 
822     /* Create a temporary .bat file */
823     strcpy(mypath, mydir);
824     strcat(mypath, "\\test.bat");
825     hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
826                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
827     CloseHandle(hf);
828 
829     strcpy(realpath, mypath);
830 
831     p=strrchr(mypath, '.');
832     if (p)
833         *p='\0';
834 
835     /* Try linking to the .bat file without the extension */
836     desc.description="batch file";
837     desc.workdir=mydir;
838     desc.path=mypath;
839     desc.pidl=NULL;
840     desc.arguments="";
841     desc.showcmd=SW_SHOWNORMAL;
842     desc.icon=mypath;
843     desc.icon_id=0;
844     desc.hotkey=0x1234;
845     create_lnk(lnkfile, &desc, 0);
846     desc.path = realpath;
847     check_lnk(lnkfile, &desc, 0x4);
848 
849     r = DeleteFileA(realpath);
850     ok(r, "failed to delete file %s (%d)\n", realpath, GetLastError());
851 
852     /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
853      * represented as a path.
854      */
855 
856     /* DeleteFileW is not implemented on Win9x */
857     r=DeleteFileA(lnkfileA);
858     ok(r, "failed to delete link '%s' (%d)\n", lnkfileA, GetLastError());
859 }
860 
861 static void test_datalink(void)
862 {
863     static const WCHAR lnk[] = {
864       ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
865       '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
866       '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
867       'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
868       '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
869       '1','-',']',':',':',0 };
870     static const WCHAR comp[] = {
871       '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
872       'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
873       'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
874     IShellLinkDataList *dl = NULL;
875     IShellLinkW *sl = NULL;
876     HRESULT r;
877     DWORD flags = 0;
878     EXP_DARWIN_LINK *dar;
879 
880     r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
881                             &IID_IShellLinkW, (LPVOID*)&sl );
882     ok( r == S_OK ||
883         broken(r == E_NOINTERFACE), /* Win9x */
884         "CoCreateInstance failed (0x%08x)\n", r);
885     if (!sl)
886     {
887         win_skip("no shelllink\n");
888         return;
889     }
890 
891     r = IShellLinkW_QueryInterface( sl, &_IID_IShellLinkDataList, (LPVOID*) &dl );
892     ok( r == S_OK ||
893         broken(r == E_NOINTERFACE), /* NT4 */
894         "IShellLinkW_QueryInterface failed (0x%08x)\n", r);
895 
896     if (!dl)
897     {
898         win_skip("no datalink interface\n");
899         IShellLinkW_Release( sl );
900         return;
901     }
902 
903     flags = 0;
904     r = IShellLinkDataList_GetFlags( dl, &flags );
905     ok( r == S_OK, "GetFlags failed\n");
906     ok( flags == 0, "GetFlags returned wrong flags\n");
907 
908     dar = (void*)-1;
909     r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
910     ok( r == E_FAIL, "CopyDataBlock failed\n");
911     ok( dar == NULL, "should be null\n");
912 
913     if (!pGetLongPathNameA /* NT4 */)
914         skip("SetPath with NULL parameter crashes on NT4\n");
915     else
916     {
917         r = IShellLinkW_SetPath(sl, NULL);
918         ok(r == E_INVALIDARG, "SetPath returned wrong error (0x%08x)\n", r);
919     }
920 
921     r = IShellLinkW_SetPath(sl, lnk);
922     ok(r == S_OK, "SetPath failed\n");
923 
924 if (0)
925 {
926     /* the following crashes */
927     IShellLinkDataList_GetFlags( dl, NULL );
928 }
929 
930     flags = 0;
931     r = IShellLinkDataList_GetFlags( dl, &flags );
932     ok( r == S_OK, "GetFlags failed\n");
933     /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
934     ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
935         "GetFlags returned wrong flags\n");
936 
937     dar = NULL;
938     r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
939     ok( r == S_OK, "CopyDataBlock failed\n");
940 
941     ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
942     ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
943 
944     LocalFree( dar );
945 
946     IShellLinkDataList_Release( dl );
947     IShellLinkW_Release( sl );
948 }
949 
950 static void test_shdefextracticon(void)
951 {
952     HICON hiconlarge=NULL, hiconsmall=NULL;
953     HRESULT res;
954 
955     if (!pSHDefExtractIconA)
956     {
957         win_skip("SHDefExtractIconA is unavailable\n");
958         return;
959     }
960 
961     res = pSHDefExtractIconA("shell32.dll", 0, 0, &hiconlarge, &hiconsmall, MAKELONG(16,24));
962     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
963     ok(hiconlarge != NULL, "got null hiconlarge\n");
964     ok(hiconsmall != NULL, "got null hiconsmall\n");
965     DestroyIcon(hiconlarge);
966     DestroyIcon(hiconsmall);
967 
968     hiconsmall = NULL;
969     res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, &hiconsmall, MAKELONG(16,24));
970     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
971     ok(hiconsmall != NULL, "got null hiconsmall\n");
972     DestroyIcon(hiconsmall);
973 
974     res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, NULL, MAKELONG(16,24));
975     ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
976 }
977 
978 static void test_GetIconLocation(void)
979 {
980     IShellLinkW *slW;
981     IShellLinkA *sl;
982     const char *str;
983     char buffer[INFOTIPSIZE], mypath[MAX_PATH];
984     int i;
985     HRESULT r;
986     LPITEMIDLIST pidl;
987 
988     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
989             &IID_IShellLinkA, (LPVOID*)&sl);
990     ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
991     if(r != S_OK)
992         return;
993 
994     i = 0xdeadbeef;
995     strcpy(buffer, "garbage");
996     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
997     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
998     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
999     ok(i == 0, "GetIconLocation returned %d\n", i);
1000 
1001     str = "c:\\some\\path";
1002     r = IShellLinkA_SetPath(sl, str);
1003     ok(r == S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
1004 
1005     i = 0xdeadbeef;
1006     strcpy(buffer, "garbage");
1007     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1008     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
1009     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
1010     ok(i == 0, "GetIconLocation returned %d\n", i);
1011 
1012     GetWindowsDirectoryA(mypath, sizeof(mypath) - 12);
1013     strcat(mypath, "\\regedit.exe");
1014     pidl = path_to_pidl(mypath);
1015     r = IShellLinkA_SetIDList(sl, pidl);
1016     ok(r == S_OK, "SetPath failed (0x%08x)\n", r);
1017     pILFree(pidl);
1018 
1019     i = 0xdeadbeef;
1020     strcpy(buffer, "garbage");
1021     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1022     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
1023     ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
1024     ok(i == 0, "GetIconLocation returned %d\n", i);
1025 
1026     str = "c:\\nonexistent\\file";
1027     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
1028     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
1029 
1030     i = 0xdeadbeef;
1031     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1032     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
1033     ok(lstrcmpiA(buffer,str) == 0, "GetIconLocation returned '%s'\n", buffer);
1034     ok(i == 0xbabecafe, "GetIconLocation returned %#x.\n", i);
1035 
1036     r = IShellLinkA_SetIconLocation(sl, NULL, 0xcafefe);
1037     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
1038 
1039     i = 0xdeadbeef;
1040     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1041     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
1042     ok(!*buffer, "GetIconLocation returned '%s'\n", buffer);
1043     ok(i == 0xcafefe, "GetIconLocation returned %#x.\n", i);
1044 
1045     r = IShellLinkA_QueryInterface(sl, &IID_IShellLinkW, (void **)&slW);
1046     ok(SUCCEEDED(r), "Failed to get IShellLinkW, hr %#x.\n", r);
1047 
1048     str = "c:\\nonexistent\\file";
1049     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
1050     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
1051 
1052     r = IShellLinkA_SetIconLocation(sl, NULL, 0xcafefe);
1053     ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
1054 
1055     i = 0xdeadbeef;
1056     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1057     ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
1058     ok(!*buffer, "GetIconLocation returned '%s'\n", buffer);
1059     ok(i == 0xcafefe, "GetIconLocation returned %#x.\n", i);
1060 
1061     IShellLinkW_Release(slW);
1062     IShellLinkA_Release(sl);
1063 }
1064 
1065 static void test_SHGetStockIconInfo(void)
1066 {
1067     BYTE buffer[sizeof(SHSTOCKICONINFO) + 16];
1068     SHSTOCKICONINFO *sii = (SHSTOCKICONINFO *) buffer;
1069     HRESULT hr;
1070     INT i;
1071 
1072     /* not present before vista */
1073     if (!pSHGetStockIconInfo)
1074     {
1075         win_skip("SHGetStockIconInfo not available\n");
1076         return;
1077     }
1078 
1079     /* negative values are handled */
1080     memset(buffer, '#', sizeof(buffer));
1081     sii->cbSize = sizeof(SHSTOCKICONINFO);
1082     hr = pSHGetStockIconInfo(SIID_INVALID, SHGSI_ICONLOCATION, sii);
1083     ok(hr == E_INVALIDARG, "-1: got 0x%x (expected E_INVALIDARG)\n", hr);
1084 
1085     /* max. id for vista is 140 (no definition exists for this value) */
1086     for (i = SIID_DOCNOASSOC; i <= SIID_CLUSTEREDDRIVE; i++)
1087     {
1088         memset(buffer, '#', sizeof(buffer));
1089         sii->cbSize = sizeof(SHSTOCKICONINFO);
1090         hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1091 
1092         ok(hr == S_OK,
1093             "%3d: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1094             i, hr, sii->iSysImageIndex, sii->iIcon);
1095 
1096         if ((hr == S_OK) && (winetest_debug > 1))
1097             trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1098                   sii->iIcon, wine_dbgstr_w(sii->szPath));
1099     }
1100 
1101     /* test invalid icons indices that are invalid for all platforms */
1102     for (i = SIID_MAX_ICONS; i < (SIID_MAX_ICONS + 25) ; i++)
1103     {
1104         memset(buffer, '#', sizeof(buffer));
1105         sii->cbSize = sizeof(SHSTOCKICONINFO);
1106         hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1107         ok(hr == E_INVALIDARG, "%3d: got 0x%x (expected E_INVALIDARG)\n", i, hr);
1108     todo_wine {
1109         ok(sii->iSysImageIndex == -1, "%d: got iSysImageIndex %d\n", i, sii->iSysImageIndex);
1110         ok(sii->iIcon == -1, "%d: got iIcon %d\n", i, sii->iIcon);
1111     }
1112     }
1113 
1114     /* test more returned SHSTOCKICONINFO elements without extra flags */
1115     memset(buffer, '#', sizeof(buffer));
1116     sii->cbSize = sizeof(SHSTOCKICONINFO);
1117     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1118     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
1119     ok(!sii->hIcon, "got %p (expected NULL)\n", sii->hIcon);
1120     ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1121 
1122     /* the exact size is required of the struct */
1123     memset(buffer, '#', sizeof(buffer));
1124     sii->cbSize = sizeof(SHSTOCKICONINFO) + 2;
1125     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1126     ok(hr == E_INVALIDARG, "+2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1127 
1128     memset(buffer, '#', sizeof(buffer));
1129     sii->cbSize = sizeof(SHSTOCKICONINFO) + 1;
1130     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1131     ok(hr == E_INVALIDARG, "+1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1132 
1133     memset(buffer, '#', sizeof(buffer));
1134     sii->cbSize = sizeof(SHSTOCKICONINFO) - 1;
1135     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1136     ok(hr == E_INVALIDARG, "-1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1137 
1138     memset(buffer, '#', sizeof(buffer));
1139     sii->cbSize = sizeof(SHSTOCKICONINFO) - 2;
1140     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1141     ok(hr == E_INVALIDARG, "-2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1142 
1143     /* there is a NULL check for the struct  */
1144     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
1145     ok(hr == E_INVALIDARG, "NULL: got 0x%x\n", hr);
1146 
1147     for(i = 0; i < 140; i++)  /* highest on wvista, i > 140 gives E_INVALIDARG, win7 can go higher */
1148     {
1149         memset(buffer, 0, sizeof(buffer));
1150         sii->cbSize = sizeof(SHSTOCKICONINFO);
1151         hr = pSHGetStockIconInfo(i, SHGSI_ICON | SHGSI_SMALLICON, sii);
1152         ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
1153         ok(sii->hIcon != NULL, "got NULL, expected an icon handle\n");
1154         ok(sii->iIcon != 0, "got unexpected 0 for SIID %d\n", i); /* howto find out exact sii->iIcon value??? */
1155         ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1156         ok(DestroyIcon(sii->hIcon), "DestroyIcon failed\n");
1157         if (winetest_debug > 1)
1158             trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1159             sii->iIcon, wine_dbgstr_w(sii->szPath));
1160     }
1161 }
1162 
1163 static void test_SHExtractIcons(void)
1164 {
1165     static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
1166     static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1167     static const WCHAR emptyW[] = {0};
1168     UINT ret, ret2;
1169     HICON icons[256];
1170     UINT ids[256], i;
1171 
1172     if (!pSHExtractIconsW)
1173     {
1174         win_skip("SHExtractIconsW not available\n");
1175         return;
1176     }
1177 
1178     ret = pSHExtractIconsW(emptyW, 0, 16, 16, icons, ids, 1, 0);
1179     ok(ret == ~0u, "got %u\n", ret);
1180 
1181     ret = pSHExtractIconsW(notepadW, 0, 16, 16, NULL, NULL, 1, 0);
1182     ok(ret == 1 || broken(ret == 2) /* win2k */, "got %u\n", ret);
1183 
1184     icons[0] = (HICON)0xdeadbeef;
1185     ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, NULL, 1, 0);
1186     ok(ret == 1, "got %u\n", ret);
1187     ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1188     DestroyIcon(icons[0]);
1189 
1190     icons[0] = (HICON)0xdeadbeef;
1191     ids[0] = 0xdeadbeef;
1192     ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, ids, 1, 0);
1193     ok(ret == 1, "got %u\n", ret);
1194     ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1195     ok(ids[0] != 0xdeadbeef, "id not set\n");
1196     DestroyIcon(icons[0]);
1197 
1198     ret = pSHExtractIconsW(shell32W, 0, 16, 16, NULL, NULL, 0, 0);
1199     ret2 = pSHExtractIconsW(shell32W, 4, MAKELONG(32,16), MAKELONG(32,16), NULL, NULL, 256, 0);
1200     ok(ret && ret == ret2,
1201        "icon count should be independent of requested icon sizes and base icon index\n");
1202 
1203     ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 0, 0);
1204     ok(ret == ~0u || !ret /* < vista */, "got %u\n", ret);
1205 
1206     ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 3, 0);
1207     ok(ret == 3, "got %u\n", ret);
1208     for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1209 
1210     /* count must be a multiple of two when getting two sizes */
1211     ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 3, 0);
1212     ok(!ret /* vista */ || ret == 4, "got %u\n", ret);
1213     for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1214 
1215     ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 4, 0);
1216     ok(ret == 4, "got %u\n", ret);
1217     for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1218 }
1219 
1220 static void test_propertystore(void)
1221 {
1222     IShellLinkA *linkA;
1223     IShellLinkW *linkW;
1224     IPropertyStore *ps;
1225     HRESULT hr;
1226 
1227     hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1228                          &IID_IShellLinkA, (void**)&linkA);
1229     ok(hr == S_OK, "got 0x%08x\n", hr);
1230 
1231     hr = IShellLinkA_QueryInterface(linkA, &IID_IShellLinkW, (void**)&linkW);
1232     ok(hr == S_OK, "got 0x%08x\n", hr);
1233 
1234     hr = IShellLinkA_QueryInterface(linkA, &IID_IPropertyStore, (void**)&ps);
1235     if (hr == S_OK) {
1236         IPropertyStoreCache *pscache;
1237 
1238         IPropertyStore_Release(ps);
1239 
1240         hr = IShellLinkW_QueryInterface(linkW, &IID_IPropertyStore, (void**)&ps);
1241         ok(hr == S_OK, "got 0x%08x\n", hr);
1242 
1243         hr = IPropertyStore_QueryInterface(ps, &IID_IPropertyStoreCache, (void**)&pscache);
1244         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1245 
1246         IPropertyStore_Release(ps);
1247     }
1248     else
1249         win_skip("IShellLink doesn't support IPropertyStore.\n");
1250 
1251     IShellLinkA_Release(linkA);
1252     IShellLinkW_Release(linkW);
1253 }
1254 
1255 static void test_ExtractIcon(void)
1256 {
1257     static const WCHAR nameW[] = {'\\','e','x','t','r','a','c','t','i','c','o','n','_','t','e','s','t','.','t','x','t',0};
1258     static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1259     WCHAR pathW[MAX_PATH];
1260     HICON hicon, hicon2;
1261     char path[MAX_PATH];
1262     HANDLE file;
1263     int r;
1264     ICONINFO info;
1265     BITMAP bm;
1266 
1267     /* specified instance handle */
1268     hicon = ExtractIconA(GetModuleHandleA("shell32.dll"), NULL, 0);
1269 todo_wine
1270     ok(hicon == NULL, "Got icon %p\n", hicon);
1271     hicon2 = ExtractIconA(GetModuleHandleA("shell32.dll"), "shell32.dll", -1);
1272     ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1273 
1274     /* existing index */
1275     hicon = ExtractIconA(NULL, "shell32.dll", 0);
1276     ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1277     DestroyIcon(hicon);
1278 
1279     /* returns number of resources */
1280     hicon = ExtractIconA(NULL, "shell32.dll", -1);
1281     ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1282 
1283     /* invalid index, valid dll name */
1284     hicon = ExtractIconA(NULL, "shell32.dll", 3000);
1285     ok(hicon == NULL, "Got icon %p\n", hicon);
1286 
1287     /* Create a temporary non-executable file */
1288     GetTempPathA(sizeof(path), path);
1289     strcat(path, "\\extracticon_test.txt");
1290     file = CreateFileA(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1291     ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1292     CloseHandle(file);
1293 
1294     hicon = ExtractIconA(NULL, path, 0);
1295 todo_wine
1296     ok(hicon == NULL, "Got icon %p\n", hicon);
1297 
1298     hicon = ExtractIconA(NULL, path, -1);
1299     ok(hicon == NULL, "Got icon %p\n", hicon);
1300 
1301     hicon = ExtractIconA(NULL, path, 1);
1302 todo_wine
1303     ok(hicon == NULL, "Got icon %p\n", hicon);
1304 
1305     r = DeleteFileA(path);
1306     ok(r, "failed to delete file %s (%d)\n", path, GetLastError());
1307 
1308     /* same for W variant */
1309 if (0)
1310 {
1311     /* specified instance handle, crashes on XP, 2k3 */
1312     hicon = ExtractIconW(GetModuleHandleA("shell32.dll"), NULL, 0);
1313     ok(hicon == NULL, "Got icon %p\n", hicon);
1314 }
1315     hicon2 = ExtractIconW(GetModuleHandleA("shell32.dll"), shell32W, -1);
1316     ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1317 
1318     /* existing index */
1319     hicon = ExtractIconW(NULL, shell32W, 0);
1320     ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1321     GetIconInfo(hicon, &info);
1322     GetObjectW(info.hbmColor, sizeof(bm), &bm);
1323     ok(bm.bmWidth == GetSystemMetrics(SM_CXICON), "got %d\n", bm.bmWidth);
1324     ok(bm.bmHeight == GetSystemMetrics(SM_CYICON), "got %d\n", bm.bmHeight);
1325     DestroyIcon(hicon);
1326 
1327     /* returns number of resources */
1328     hicon = ExtractIconW(NULL, shell32W, -1);
1329     ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1330 
1331     /* invalid index, valid dll name */
1332     hicon = ExtractIconW(NULL, shell32W, 3000);
1333     ok(hicon == NULL, "Got icon %p\n", hicon);
1334 
1335     /* Create a temporary non-executable file */
1336     GetTempPathW(ARRAY_SIZE(pathW), pathW);
1337     lstrcatW(pathW, nameW);
1338     file = CreateFileW(pathW, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1339     ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1340     CloseHandle(file);
1341 
1342     hicon = ExtractIconW(NULL, pathW, 0);
1343 todo_wine
1344     ok(hicon == NULL, "Got icon %p\n", hicon);
1345 
1346     hicon = ExtractIconW(NULL, pathW, -1);
1347     ok(hicon == NULL, "Got icon %p\n", hicon);
1348 
1349     hicon = ExtractIconW(NULL, pathW, 1);
1350 todo_wine
1351     ok(hicon == NULL, "Got icon %p\n", hicon);
1352 
1353     r = DeleteFileW(pathW);
1354     ok(r, "failed to delete file %s (%d)\n", path, GetLastError());
1355 }
1356 
1357 static void test_ExtractAssociatedIcon(void)
1358 {
1359     char pathA[MAX_PATH];
1360     HICON hicon;
1361     WORD index;
1362 
1363     /* empty path */
1364     index = 0;
1365     *pathA = 0;
1366     hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1367 todo_wine {
1368     ok(hicon != NULL, "Got icon %p\n", hicon);
1369     ok(!*pathA, "Unexpected path %s\n", pathA);
1370     ok(index == 0, "Unexpected index %u\n", index);
1371 }
1372     DestroyIcon(hicon);
1373 
1374     /* by index */
1375     index = 0;
1376     strcpy(pathA, "shell32.dll");
1377     hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1378     ok(hicon != NULL, "Got icon %p\n", hicon);
1379     ok(!strcmp(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1380     ok(index == 0, "Unexpected index %u\n", index);
1381     DestroyIcon(hicon);
1382 
1383     /* valid dll name, invalid index */
1384     index = 5000;
1385     strcpy(pathA, "user32.dll");
1386     hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1387     CharLowerBuffA(pathA, strlen(pathA));
1388 todo_wine {
1389     ok(hicon != NULL, "Got icon %p\n", hicon);
1390     ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1391 }
1392     ok(index != 5000, "Unexpected index %u\n", index);
1393     DestroyIcon(hicon);
1394 
1395     /* associated icon */
1396     index = 0xcaca;
1397     strcpy(pathA, "dummy.exe");
1398     hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1399     CharLowerBuffA(pathA, strlen(pathA));
1400 todo_wine {
1401     ok(hicon != NULL, "Got icon %p\n", hicon);
1402     ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1403 }
1404     ok(index != 0xcaca, "Unexpected index %u\n", index);
1405     DestroyIcon(hicon);
1406 }
1407 
1408 static int get_shell_icon_size(void)
1409 {
1410     char buf[10];
1411     DWORD value = 32, size = sizeof(buf), type;
1412     HKEY key;
1413 
1414     if (!RegOpenKeyA( HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", &key ))
1415     {
1416         if (!RegQueryValueExA( key, "Shell Icon Size", NULL, &type, (BYTE *)buf, &size ) && type == REG_SZ)
1417             value = atoi( buf );
1418         RegCloseKey( key );
1419     }
1420     return value;
1421 }
1422 
1423 static void test_SHGetImageList(void)
1424 {
1425     HRESULT hr;
1426     IImageList *list, *list2;
1427     BOOL ret;
1428     HIMAGELIST lg, sm;
1429     ULONG start_refs, refs;
1430     int i, width, height, expect;
1431     BOOL dpi_aware = pIsProcessDPIAware && pIsProcessDPIAware();
1432 
1433     hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list );
1434     ok( hr == S_OK, "got %08x\n", hr );
1435     start_refs = IImageList_AddRef( list );
1436     IImageList_Release( list );
1437 
1438     hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list2 );
1439     ok( hr == S_OK, "got %08x\n", hr );
1440     ok( list == list2, "lists differ\n" );
1441     refs = IImageList_AddRef( list );
1442     IImageList_Release( list );
1443     ok( refs == start_refs + 1, "got %d, start_refs %d\n", refs, start_refs );
1444     IImageList_Release( list2 );
1445 
1446     hr = SHGetImageList( SHIL_SMALL, &IID_IImageList, (void **)&list2 );
1447     ok( hr == S_OK, "got %08x\n", hr );
1448 
1449     ret = Shell_GetImageLists( &lg, &sm );
1450     ok( ret, "got %d\n", ret );
1451     ok( lg == (HIMAGELIST)list, "mismatch\n" );
1452     ok( sm == (HIMAGELIST)list2, "mismatch\n" );
1453 
1454     /* Shell_GetImageLists doesn't take a reference */
1455     refs = IImageList_AddRef( list );
1456     IImageList_Release( list );
1457     ok( refs == start_refs, "got %d, start_refs %d\n", refs, start_refs );
1458 
1459     IImageList_Release( list2 );
1460     IImageList_Release( list );
1461 
1462     /* Test the icon sizes */
1463     for (i = 0; i <= SHIL_LAST; i++)
1464     {
1465         hr = SHGetImageList( i, &IID_IImageList, (void **)&list );
1466         ok( hr == S_OK || broken( i == SHIL_JUMBO && hr == E_INVALIDARG ), /* XP and 2003 */
1467                 "%d: got %08x\n", i, hr );
1468         if (FAILED(hr)) continue;
1469         IImageList_GetIconSize( list, &width, &height );
1470         switch (i)
1471         {
1472         case SHIL_LARGE:
1473             if (dpi_aware) expect = GetSystemMetrics( SM_CXICON );
1474             else expect = get_shell_icon_size();
1475             break;
1476         case SHIL_SMALL:
1477             if (dpi_aware) expect = GetSystemMetrics( SM_CXICON ) / 2;
1478             else expect = GetSystemMetrics( SM_CXSMICON );
1479             break;
1480         case SHIL_EXTRALARGE:
1481             expect = (GetSystemMetrics( SM_CXICON ) * 3) / 2;
1482             break;
1483         case SHIL_SYSSMALL:
1484             expect = GetSystemMetrics( SM_CXSMICON );
1485             break;
1486         case SHIL_JUMBO:
1487             expect = 256;
1488             break;
1489         }
1490         todo_wine_if(i == SHIL_SYSSMALL && dpi_aware && expect != GetSystemMetrics( SM_CXICON ) / 2)
1491         {
1492             ok( width == expect, "%d: got %d expect %d\n", i, width, expect );
1493             ok( height == expect, "%d: got %d expect %d\n", i, height, expect );
1494         }
1495         IImageList_Release( list );
1496     }
1497 }
1498 
1499 START_TEST(shelllink)
1500 {
1501     HRESULT r;
1502     HMODULE hmod = GetModuleHandleA("shell32.dll");
1503     HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1504     HMODULE huser32 = GetModuleHandleA("user32.dll");
1505 
1506     pILFree = (void *)GetProcAddress(hmod, (LPSTR)155);
1507     pILIsEqual = (void *)GetProcAddress(hmod, (LPSTR)21);
1508     pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28);
1509     pSHGetFolderLocation = (void *)GetProcAddress(hmod, "SHGetFolderLocation");
1510     pSHDefExtractIconA = (void *)GetProcAddress(hmod, "SHDefExtractIconA");
1511     pSHGetStockIconInfo = (void *)GetProcAddress(hmod, "SHGetStockIconInfo");
1512     pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA");
1513     pGetShortPathNameA = (void *)GetProcAddress(hkernel32, "GetShortPathNameA");
1514     pSHExtractIconsW = (void *)GetProcAddress(hmod, "SHExtractIconsW");
1515     pIsProcessDPIAware = (void *)GetProcAddress(huser32, "IsProcessDPIAware");
1516 
1517     r = CoInitialize(NULL);
1518     ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
1519     if (r != S_OK)
1520         return;
1521 
1522     test_get_set();
1523     test_load_save();
1524     test_datalink();
1525     test_shdefextracticon();
1526     test_GetIconLocation();
1527     test_SHGetStockIconInfo();
1528     test_SHExtractIcons();
1529     test_propertystore();
1530     test_ExtractIcon();
1531     test_ExtractAssociatedIcon();
1532     test_SHGetImageList();
1533 
1534     CoUninitialize();
1535 }
1536