1 /* Unit test suite for SHLWAPI IQueryAssociations functions
2  *
3  * Copyright 2008 Google (Lei Zhang)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 
22 #include "wine/test.h"
23 #include "shlwapi.h"
24 #include "shlguid.h"
25 
26 #define expect(expected, got) ok( (expected) == (got), "Expected %d, got %d\n", (expected), (got))
27 #define expect_hr(expected, got) ok( (expected) == (got), "Expected %08x, got %08x\n", (expected), (got))
28 
29 static HRESULT (WINAPI *pAssocQueryStringA)(ASSOCF,ASSOCSTR,LPCSTR,LPCSTR,LPSTR,LPDWORD) = NULL;
30 static HRESULT (WINAPI *pAssocQueryStringW)(ASSOCF,ASSOCSTR,LPCWSTR,LPCWSTR,LPWSTR,LPDWORD) = NULL;
31 static HRESULT (WINAPI *pAssocCreate)(CLSID, REFIID, void **) = NULL;
32 static HRESULT (WINAPI *pAssocGetPerceivedType)(LPCWSTR, PERCEIVED *, INT *, LPWSTR *) = NULL;
33 
34 /* Every version of Windows with IE should have this association? */
35 static const WCHAR dotHtml[] = { '.','h','t','m','l',0 };
36 static const WCHAR badBad[] = { 'b','a','d','b','a','d',0 };
37 static const WCHAR dotBad[] = { '.','b','a','d',0 };
38 static const WCHAR open[] = { 'o','p','e','n',0 };
39 static const WCHAR invalid[] = { 'i','n','v','a','l','i','d',0 };
40 
41 static void test_getstring_bad(void)
42 {
43     static const WCHAR openwith[] = {'O','p','e','n','W','i','t','h','.','e','x','e',0};
44     WCHAR buf[MAX_PATH];
45     HRESULT hr;
46     DWORD len;
47 
48     if (!pAssocQueryStringW)
49     {
50         win_skip("AssocQueryStringW() is missing\n");
51         return;
52     }
53 
54     len = 0xdeadbeef;
55     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, NULL, open, NULL, &len);
56     expect_hr(E_INVALIDARG, hr);
57     ok(len == 0xdeadbeef, "got %u\n", len);
58 
59     len = 0xdeadbeef;
60     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, badBad, open, NULL, &len);
61     ok(hr == E_FAIL ||
62        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION), /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */
63        "Unexpected result : %08x\n", hr);
64     ok(len == 0xdeadbeef, "got %u\n", len);
65 
66     len = ARRAY_SIZE(buf);
67     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, dotBad, open, buf, &len);
68     ok(hr == E_FAIL ||
69        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */ ||
70        hr == S_OK /* Win8 */,
71        "Unexpected result : %08x\n", hr);
72     if (hr == S_OK)
73     {
74         ok(len < ARRAY_SIZE(buf), "got %u\n", len);
75         ok(!lstrcmpiW(buf + len - ARRAY_SIZE(openwith), openwith), "wrong data\n");
76     }
77 
78     len = 0xdeadbeef;
79     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, dotHtml, invalid, NULL, &len);
80     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
81        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION), /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */
82        "Unexpected result : %08x\n", hr);
83     ok(len == 0xdeadbeef, "got %u\n", len);
84 
85     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, dotHtml, open, NULL, NULL);
86     ok(hr == E_UNEXPECTED ||
87        hr == E_INVALIDARG, /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */
88        "Unexpected result : %08x\n", hr);
89 
90     len = 0xdeadbeef;
91     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, NULL, open, NULL, &len);
92     expect_hr(E_INVALIDARG, hr);
93     ok(len == 0xdeadbeef, "got %u\n", len);
94 
95     len = 0xdeadbeef;
96     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, badBad, open, NULL, &len);
97     ok(hr == E_FAIL ||
98        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION), /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */
99        "Unexpected result : %08x\n", hr);
100     ok(len == 0xdeadbeef, "got %u\n", len);
101 
102     len = 0xdeadbeef;
103     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, dotBad, open, NULL, &len);
104     ok(hr == E_FAIL ||
105        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */ ||
106        hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) /* Win8 */ ||
107        hr == S_FALSE, /* Win10 */
108        "Unexpected result : %08x\n", hr);
109     ok((hr == S_FALSE && len < ARRAY_SIZE(buf)) || len == 0xdeadbeef,
110        "got hr=%08x and len=%u\n", hr, len);
111 
112     len = 0xdeadbeef;
113     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, dotHtml, invalid, NULL, &len);
114     ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
115        hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) || /* W2K/Vista/W2K8 */
116        hr == E_FAIL, /* Win9x/WinMe/NT4 */
117        "Unexpected result : %08x\n", hr);
118     ok(len == 0xdeadbeef, "got %u\n", len);
119 
120     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, dotHtml, open, NULL, NULL);
121     ok(hr == E_UNEXPECTED ||
122        hr == E_INVALIDARG, /* Win9x/WinMe/NT4/W2K/Vista/W2K8 */
123        "Unexpected result : %08x\n", hr);
124 }
125 
126 static void test_getstring_basic(void)
127 {
128     HRESULT hr;
129     WCHAR * friendlyName;
130     WCHAR * executableName;
131     DWORD len, len2, slen;
132 
133     if (!pAssocQueryStringW)
134     {
135         win_skip("AssocQueryStringW() is missing\n");
136         return;
137     }
138 
139     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, dotHtml, open, NULL, &len);
140     expect_hr(S_FALSE, hr);
141     if (hr != S_FALSE)
142     {
143         skip("failed to get initial len\n");
144         return;
145     }
146 
147     executableName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
148                                len * sizeof(WCHAR));
149     if (!executableName)
150     {
151         skip("failed to allocate memory\n");
152         return;
153     }
154 
155     len2 = len;
156     hr = pAssocQueryStringW(0, ASSOCSTR_EXECUTABLE, dotHtml, open,
157                            executableName, &len2);
158     expect_hr(S_OK, hr);
159     slen = lstrlenW(executableName) + 1;
160     expect(len, len2);
161     expect(len, slen);
162 
163     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, dotHtml, open, NULL,
164                            &len);
165     ok(hr == S_FALSE ||
166        hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* Win9x/NT4 */ ||
167        hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND), /* Win8 */
168        "Unexpected result : %08x\n", hr);
169     if (hr != S_FALSE)
170     {
171         HeapFree(GetProcessHeap(), 0, executableName);
172         skip("failed to get initial len\n");
173         return;
174     }
175 
176     friendlyName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
177                                len * sizeof(WCHAR));
178     if (!friendlyName)
179     {
180         HeapFree(GetProcessHeap(), 0, executableName);
181         skip("failed to allocate memory\n");
182         return;
183     }
184 
185     len2 = len;
186     hr = pAssocQueryStringW(0, ASSOCSTR_FRIENDLYAPPNAME, dotHtml, open,
187                            friendlyName, &len2);
188     expect_hr(S_OK, hr);
189     slen = lstrlenW(friendlyName) + 1;
190     expect(len, len2);
191     expect(len, slen);
192 
193     HeapFree(GetProcessHeap(), 0, executableName);
194     HeapFree(GetProcessHeap(), 0, friendlyName);
195 }
196 
197 static void test_getstring_no_extra(void)
198 {
199     LONG ret;
200     HKEY hkey;
201     HRESULT hr;
202     static const CHAR dotWinetest[] = {
203         '.','w','i','n','e','t','e','s','t',0
204     };
205     static const CHAR winetestfile[] = {
206         'w','i','n','e','t','e','s','t', 'f','i','l','e',0
207     };
208     static const CHAR winetestfileAction[] = {
209         'w','i','n','e','t','e','s','t','f','i','l','e',
210         '\\','s','h','e','l','l',
211         '\\','f','o','o',
212         '\\','c','o','m','m','a','n','d',0
213     };
214     static const CHAR action[] = {
215         'n','o','t','e','p','a','d','.','e','x','e',0
216     };
217     CHAR buf[MAX_PATH];
218     DWORD len = MAX_PATH;
219 
220     if (!pAssocQueryStringA)
221     {
222         win_skip("AssocQueryStringA() is missing\n");
223         return;
224     }
225 
226     buf[0] = '\0';
227     ret = RegCreateKeyA(HKEY_CLASSES_ROOT, dotWinetest, &hkey);
228     if (ret != ERROR_SUCCESS) {
229         skip("failed to create dotWinetest key\n");
230         return;
231     }
232 
233     ret = RegSetValueA(hkey, NULL, REG_SZ, winetestfile, lstrlenA(winetestfile));
234     RegCloseKey(hkey);
235     if (ret != ERROR_SUCCESS)
236     {
237         skip("failed to set dotWinetest key\n");
238         goto cleanup;
239     }
240 
241     ret = RegCreateKeyA(HKEY_CLASSES_ROOT, winetestfileAction, &hkey);
242     if (ret != ERROR_SUCCESS)
243     {
244         skip("failed to create winetestfileAction key\n");
245         goto cleanup;
246     }
247 
248     ret = RegSetValueA(hkey, NULL, REG_SZ, action, lstrlenA(action));
249     RegCloseKey(hkey);
250     if (ret != ERROR_SUCCESS)
251     {
252         skip("failed to set winetestfileAction key\n");
253         goto cleanup;
254     }
255 
256     hr = pAssocQueryStringA(0, ASSOCSTR_EXECUTABLE, dotWinetest, NULL, buf, &len);
257     ok(hr == S_OK ||
258        hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP and W2K3 */
259        "Unexpected result : %08x\n", hr);
260     hr = pAssocQueryStringA(0, ASSOCSTR_EXECUTABLE, dotWinetest, "foo", buf, &len);
261     expect_hr(S_OK, hr);
262     ok(strstr(buf, action) != NULL,
263         "got '%s' (Expected result to include 'notepad.exe')\n", buf);
264 
265 cleanup:
266     SHDeleteKeyA(HKEY_CLASSES_ROOT, dotWinetest);
267     SHDeleteKeyA(HKEY_CLASSES_ROOT, winetestfile);
268 
269 }
270 
271 static void test_assoc_create(void)
272 {
273     HRESULT hr;
274     IQueryAssociations *pqa;
275 
276     if (!pAssocCreate)
277     {
278         win_skip("AssocCreate() is missing\n");
279         return;
280     }
281 
282     hr = pAssocCreate(IID_NULL, &IID_NULL, NULL);
283     ok(hr == E_INVALIDARG, "Unexpected result : %08x\n", hr);
284 
285     hr = pAssocCreate(CLSID_QueryAssociations, &IID_NULL, (LPVOID*)&pqa);
286     ok(hr == CLASS_E_CLASSNOTAVAILABLE || hr == E_NOTIMPL || hr == E_NOINTERFACE
287         , "Unexpected result : %08x\n", hr);
288 
289     hr = pAssocCreate(IID_NULL, &IID_IQueryAssociations, (LPVOID*)&pqa);
290     ok(hr == CLASS_E_CLASSNOTAVAILABLE || hr == E_NOTIMPL || hr == E_INVALIDARG
291         , "Unexpected result : %08x\n", hr);
292 
293     hr = pAssocCreate(CLSID_QueryAssociations, &IID_IQueryAssociations, (LPVOID*)&pqa);
294     ok(hr == S_OK  || hr == E_NOTIMPL /* win98 */
295         , "Unexpected result : %08x\n", hr);
296     if(hr == S_OK)
297     {
298         IQueryAssociations_Release(pqa);
299     }
300 
301     hr = pAssocCreate(CLSID_QueryAssociations, &IID_IUnknown, (LPVOID*)&pqa);
302     ok(hr == S_OK  || hr == E_NOTIMPL /* win98 */
303         , "Unexpected result : %08x\n", hr);
304     if(hr == S_OK)
305     {
306         IQueryAssociations_Release(pqa);
307     }
308 }
309 
310 /* Based on http://www.geoffchappell.com/studies/windows/shell/shlwapi/api/assocapi/getperceivedtype.htm */
311 struct assoc_test_struct
312 {
313     PCSTR     extension;
314     PERCEIVED perceived;
315     INT       flags;
316     PCSTR     type;
317     DWORD     minversion;
318     HRESULT   hr;
319 };
320 
321 #define HARDCODED_NATIVE_WMSDK      (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_WMSDK)
322 #define HARDCODED_NATIVE_GDIPLUS    (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_GDIPLUS)
323 #define HARDCODED_NATIVE_ZIPFLDR    (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_ZIPFOLDER)
324 #define SOFTCODED_NATIVESUPPORT     (PERCEIVEDFLAG_SOFTCODED | PERCEIVEDFLAG_NATIVESUPPORT)
325 
326 static const struct assoc_test_struct assoc_perceived_types[] =
327 {
328     /* builtins */
329     { ".aif",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
330     { ".aifc",          PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
331     { ".aiff",          PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
332     { ".asf",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
333     { ".asx",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
334     { ".au",            PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
335     { ".avi",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
336     { ".bas",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
337     { ".bat",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
338     { ".bmp",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
339     { ".cmd",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
340     { ".com",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
341     { ".cpl",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_HARDCODED,    "system",       0x600 },
342     { ".dib",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
343     { ".dvr-ms",        PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
344     { ".emf",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
345     { ".exe",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
346     { ".gif",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
347     { ".hta",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
348     /* htm & html are PERCEIVED_TYPE_TEXT, PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_SOFTCODED in w2k3 */
349     { ".htm",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_HARDCODED,    "document",     0x600 },
350     { ".html",          PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_HARDCODED,    "document",     0x600 },
351     { ".ico",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
352     { ".IVF",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
353     { ".jfif",          PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
354     { ".jpe",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
355     { ".jpeg",          PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
356     { ".jpg",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
357     { ".lnk",           PERCEIVED_TYPE_UNSPECIFIED, PERCEIVEDFLAG_HARDCODED,    NULL,           0x600, E_FAIL },
358     { ".m1v",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
359     { ".m3u",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
360     { ".mht",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_HARDCODED,    "document",     0x600 },
361     { ".mid",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
362     { ".midi",          PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
363     { ".msi",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
364     /* below win8 this is defined to be video */
365     { ".mp2",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio",        0x602 },
366     { ".mp2v",          PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
367     { ".mp3",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
368     { ".mpa",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
369     { ".mpe",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
370     { ".mpeg",          PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
371     { ".mpg",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
372     { ".mpv2",          PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
373     { ".pif",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
374     { ".png",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
375     { ".reg",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
376     { ".rle",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
377     { ".rmi",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
378     { ".scr",           PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
379     { ".search-ms",     PERCEIVED_TYPE_UNSPECIFIED, PERCEIVEDFLAG_HARDCODED,    NULL,           0x600, E_FAIL },
380     { ".snd",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
381     { ".tif",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
382     { ".tiff",          PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
383     { ".vb",            PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,    "application" },
384     { ".wav",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
385     { ".wax",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
386     { ".wm",            PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
387     { ".wma",           PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,     "audio" },
388     { ".wmf",           PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS,   "image" },
389     { ".wmv",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
390     { ".wmx",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
391     { ".wvx",           PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,     "video" },
392     { ".zip",           PERCEIVED_TYPE_COMPRESSED,  HARDCODED_NATIVE_ZIPFLDR,   "compressed" },
393     /* found in the registry under HKEY_CLASSES_ROOT on a new Win7 VM */
394     { ".386",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
395     { ".3g2",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
396     { ".3gp",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
397     { ".3gp2",          PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
398     { ".3gpp",          PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
399     { ".AAC",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
400     { ".ADT",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
401     { ".ADTS",          PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
402     { ".asm",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
403     { ".asmx",          PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
404     { ".aspx",          PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
405     { ".c",             PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
406     { ".cab",           PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed",   0x600 },
407     { ".chk",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
408     { ".cpp",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
409     { ".css",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
410     { ".cxx",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
411     { ".def",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
412     { ".diz",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
413     { ".docx",          PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x601 },
414     { ".drv",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system",       0x600 },
415     { ".gz",            PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
416     { ".h",             PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
417     { ".hpp",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
418     { ".hxx",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
419     { ".inc",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
420     { ".ini",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text",         0x600 },
421     { ".java",          PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
422     { ".local",         PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
423     { ".M2T",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
424     { ".M2TS",          PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
425     { ".M2V",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
426     { ".m4a",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
427     { ".m4b",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
428     { ".m4p",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
429     { ".m4v",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
430     { ".manifest",      PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
431     { ".MOD",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
432     { ".mov",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
433     { ".mp4",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
434     { ".mp4v",          PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
435     { ".MTS",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
436     { ".nvr",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
437     { ".ocx",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
438     { ".odt",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x601 },
439     { ".php3",          PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
440     { ".pl",            PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
441     { ".plg",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
442     { ".ps1xml",        PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "Text" },
443     { ".rtf",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
444     { ".sed",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
445     { ".shtml",         PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
446     { ".sql",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
447     { ".sys",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system",       0x600 },
448     { ".tar",           PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
449     { ".text",          PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
450     { ".tgz",           PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
451     { ".TS",            PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
452     { ".tsv",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
453     { ".TTS",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
454     { ".txt",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
455     { ".vob",           PERCEIVED_TYPE_VIDEO,       PERCEIVEDFLAG_SOFTCODED,    "video",        0x601 },
456     { ".vxd",           PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_SOFTCODED,    "system" },
457     { ".wdp",           PERCEIVED_TYPE_IMAGE,       PERCEIVEDFLAG_SOFTCODED,    "image" },
458     { ".wmz",           PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
459     { ".wpl",           PERCEIVED_TYPE_AUDIO,       PERCEIVEDFLAG_SOFTCODED,    "audio",        0x601 },
460     { ".wsz",           PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
461     { ".x",             PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text" },
462     { ".xml",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text",         0x601 },
463     { ".xsl",           PERCEIVED_TYPE_TEXT,        SOFTCODED_NATIVESUPPORT,    "text",         0x601 },
464     { ".z",             PERCEIVED_TYPE_COMPRESSED,  PERCEIVEDFLAG_SOFTCODED,    "compressed" },
465     /* found in the registry under HKEY_CLASSES_ROOT\PerceivedType */
466     { ".doc",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
467     { ".dot",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
468     { ".mhtml",         PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
469     { ".pot",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
470     { ".ppt",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
471     { ".rtf",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
472     { ".wri",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
473     { ".xls",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
474     { ".xlt",           PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_SOFTCODED,    "document",     0x600 },
475 
476 };
477 
478 static void test_assoc_one(const struct assoc_test_struct* test)
479 {
480     LPWSTR extension, type_expected, type_returned;
481     PERCEIVED perceived;
482     HRESULT hr;
483     INT flags;
484 
485     /* if SHStrDupA receives a nullptr as input, it will null the output */
486     SHStrDupA(test->extension, &extension);
487     SHStrDupA(test->type, &type_expected);
488 
489     perceived = 0xdeadbeef;
490     flags = 0xdeadbeef;
491 
492     hr = pAssocGetPerceivedType(extension, &perceived, &flags, NULL);
493     expect_hr(type_expected ? S_OK : test->hr, hr);
494     ok(perceived == test->perceived, "%s: got perceived 0x%x, expected 0x%x\n",
495        test->extension, perceived, test->perceived);
496     ok(flags == test->flags, "%s: got flags 0x%x, expected 0x%x\n",
497        test->extension, flags, test->flags);
498 
499     type_returned = (void *)0xdeadbeef;
500     perceived = 0xdeadbeef;
501     flags = 0xdeadbeef;
502 
503     hr = pAssocGetPerceivedType(extension, &perceived, &flags, &type_returned);
504     expect_hr(type_expected ? S_OK : test->hr, hr);
505     ok(perceived == test->perceived, "%s: got perceived 0x%x, expected 0x%x\n",
506        test->extension, perceived, test->perceived);
507     ok(flags == test->flags, "%s: got flags 0x%x, expected 0x%x\n",
508        test->extension, flags, test->flags);
509 
510     if (!type_expected)
511     {
512         ok(type_returned == (void *)0xdeadbeef || broken(type_returned == NULL) /* Win 8 */,
513            "%s: got type %p, expected 0xdeadbeef\n", test->extension, type_returned);
514     }
515     else if (type_returned == (void *)0xdeadbeef)
516     {
517         ok(type_returned != (void *)0xdeadbeef, "%s: got type %p, expected '%s'\n",
518            test->extension, type_returned, test->type);
519     }
520     else
521     {
522         ok(StrCmpIW(type_expected, type_returned) == 0, "%s: got type %s, expected '%s'\n",
523            test->extension, wine_dbgstr_w(type_returned), test->type);
524     }
525 
526     CoTaskMemFree(type_returned);
527     CoTaskMemFree(extension);
528     CoTaskMemFree(type_expected);
529 }
530 
531 static void test_assoc_perceived(void)
532 {
533     static const struct assoc_test_struct should_not_exist =
534         { ".should_not_exist", PERCEIVED_TYPE_UNSPECIFIED, PERCEIVEDFLAG_UNDEFINED, NULL, 0, 0x80070002 };
535     static const struct assoc_test_struct htm[] =
536     {
537         { ".htm",  PERCEIVED_TYPE_TEXT, SOFTCODED_NATIVESUPPORT, "text", 0x600 },
538         { ".html", PERCEIVED_TYPE_TEXT, SOFTCODED_NATIVESUPPORT, "text", 0x600 },
539     };
540     static const struct assoc_test_struct mp2 =
541         { ".mp2", PERCEIVED_TYPE_VIDEO, HARDCODED_NATIVE_WMSDK, "video" };
542 
543     OSVERSIONINFOEXW osvi;
544     DWORD version;
545     size_t i;
546 
547     if (!pAssocGetPerceivedType)
548     {
549         win_skip("AssocGetPerceivedType() is missing\n");
550         return;
551     }
552 
553     memset(&osvi, 0, sizeof(osvi));
554     osvi.dwOSVersionInfoSize = sizeof(osvi);
555     GetVersionExW((LPOSVERSIONINFOW)&osvi);
556     version = (osvi.dwMajorVersion << 8) | osvi.dwMinorVersion;
557 
558     /* invalid entry results in HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) */
559     test_assoc_one(&should_not_exist);
560 
561     for (i = 0; i < sizeof(assoc_perceived_types) / sizeof(assoc_perceived_types[0]); ++i)
562     {
563         if (assoc_perceived_types[i].minversion && assoc_perceived_types[i].minversion > version)
564             continue;
565         if (!(assoc_perceived_types[i].flags & PERCEIVEDFLAG_HARDCODED))
566             todo_wine test_assoc_one(&assoc_perceived_types[i]);
567         else
568             test_assoc_one(&assoc_perceived_types[i]);
569     }
570 
571     /* below Vista */
572     if (version < 0x600)
573     {
574         todo_wine
575         test_assoc_one(&htm[0]);
576         todo_wine
577         test_assoc_one(&htm[1]);
578     }
579 
580     /* below Win8 */
581     if (version < 0x602)
582     {
583         test_assoc_one(&mp2);
584     }
585 }
586 
587 START_TEST(assoc)
588 {
589     HMODULE hshlwapi;
590     hshlwapi = GetModuleHandleA("shlwapi.dll");
591     pAssocQueryStringA = (void*)GetProcAddress(hshlwapi, "AssocQueryStringA");
592     pAssocQueryStringW = (void*)GetProcAddress(hshlwapi, "AssocQueryStringW");
593     pAssocCreate       = (void*)GetProcAddress(hshlwapi, "AssocCreate");
594     pAssocGetPerceivedType = (void*)GetProcAddress(hshlwapi, "AssocGetPerceivedType");
595 
596     test_getstring_bad();
597     test_getstring_basic();
598     test_getstring_no_extra();
599     test_assoc_create();
600     test_assoc_perceived();
601 }
602