msi: Set all folders' source paths to the root directory if the source type is compre...
[wine] / dlls / hlink / tests / hlink.c
1 /*
2  * Implementation of hyperlinking (hlink.dll)
3  *
4  * Copyright 2006 Mike McCormack
5  * Copyright 2007-2008 Jacek Caban for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include <stdio.h>
25
26 #include <initguid.h>
27 #include <hlink.h>
28 #include <hlguids.h>
29
30 #include "wine/test.h"
31
32 #define DEFINE_EXPECT(func) \
33     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
34
35 #define SET_EXPECT(func) \
36     expect_ ## func = TRUE
37
38 #define CHECK_EXPECT2(func) \
39     do { \
40         ok(expect_ ##func, "unexpected call " #func "\n"); \
41         called_ ## func = TRUE; \
42     }while(0)
43
44 #define CHECK_EXPECT(func) \
45     do { \
46         CHECK_EXPECT2(func); \
47         expect_ ## func = FALSE; \
48     }while(0)
49
50 #define CHECK_CALLED(func) \
51     do { \
52         ok(called_ ## func, "expected " #func "\n"); \
53         expect_ ## func = called_ ## func = FALSE; \
54     }while(0)
55
56 DEFINE_EXPECT(IsSystemMoniker);
57 DEFINE_EXPECT(BindToStorage);
58 DEFINE_EXPECT(GetDisplayName);
59
60 static const char *debugstr_w(LPCWSTR str)
61 {
62     static char buf[1024];
63     if(!str)
64         return "(null)";
65     WideCharToMultiByte(CP_ACP, 0, str, -1, buf, sizeof(buf), NULL, NULL);
66     return buf;
67 }
68
69 static const char *debugstr_guid(REFIID riid)
70 {
71     static char buf[50];
72
73     sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
74             riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
75             riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
76             riid->Data4[5], riid->Data4[6], riid->Data4[7]);
77
78     return buf;
79 }
80
81 static void test_HlinkIsShortcut(void)
82 {
83     int i;
84     HRESULT hres;
85
86     static const WCHAR file0[] = {'f','i','l','e',0};
87     static const WCHAR file1[] = {'f','i','l','e','.','u','r','l',0};
88     static const WCHAR file2[] = {'f','i','l','e','.','l','n','k',0};
89     static const WCHAR file3[] = {'f','i','l','e','.','u','R','l',0};
90     static const WCHAR file4[] = {'f','i','l','e','u','r','l',0};
91     static const WCHAR file5[] = {'c',':','\\','f','i','l','e','.','u','r','l',0};
92     static const WCHAR file6[] = {'c',':','\\','f','i','l','e','.','l','n','k',0};
93     static const WCHAR file7[] = {'.','u','r','l',0};
94
95     static struct {
96         LPCWSTR file;
97         HRESULT hres;
98     } shortcut_test[] = {
99         {file0, S_FALSE},
100         {file1, S_OK},
101         {file2, S_FALSE},
102         {file3, S_OK},
103         {file4, S_FALSE},
104         {file5, S_OK},
105         {file6, S_FALSE},
106         {file7, S_OK},
107         {NULL,  E_INVALIDARG}
108     };
109
110     for(i=0; i<sizeof(shortcut_test)/sizeof(shortcut_test[0]); i++) {
111         hres = HlinkIsShortcut(shortcut_test[i].file);
112         ok(hres == shortcut_test[i].hres, "[%d] HlinkIsShortcut returned %08x, expected %08x\n",
113            i, hres, shortcut_test[i].hres);
114     }
115 }
116
117 static void test_reference(void)
118 {
119     HRESULT r;
120     IHlink *lnk = NULL;
121     IMoniker *mk = NULL;
122     const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
123     const WCHAR url2[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g','/',0 };
124     LPWSTR str = NULL;
125
126     r = HlinkCreateFromString(url, NULL, NULL, NULL,
127                               0, NULL, &IID_IHlink, (LPVOID*) &lnk);
128     ok(r == S_OK, "failed to create link\n");
129     if (FAILED(r))
130         return;
131
132     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
133     ok(r == S_OK, "failed\n");
134
135     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, &mk, &str);
136     ok(r == S_OK, "failed\n");
137     ok(mk != NULL, "no moniker\n");
138     ok(str == NULL, "string should be null\n");
139
140     r = IMoniker_Release(mk);
141     ok( r == 1, "moniker refcount wrong\n");
142
143     r = IHlink_GetStringReference(lnk, -1, &str, NULL);
144     ok(r == S_OK, "failed\n");
145     CoTaskMemFree(str);
146
147     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, &str, NULL);
148     ok(r == S_OK, "failed\n");
149     todo_wine {
150     ok(!lstrcmpW(str, url2), "url wrong\n");
151     }
152     CoTaskMemFree(str);
153
154     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
155     ok(r == S_OK, "failed\n");
156
157     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, &str);
158     ok(r == S_OK, "failed\n");
159     ok(str == NULL, "string should be null\n");
160
161     IHlink_Release(lnk);
162 }
163
164 /* url only */
165 static const unsigned char expected_hlink_data[] =
166 {
167     0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
168     0xe0,0xc9,0xea,0x79,0xf9,0xba,0xce,0x11,
169     0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b,
170     0x26,0x00,0x00,0x00,0x68,0x00,0x74,0x00,
171     0x74,0x00,0x70,0x00,0x3a,0x00,0x2f,0x00,
172     0x2f,0x00,0x77,0x00,0x69,0x00,0x6e,0x00,
173     0x65,0x00,0x68,0x00,0x71,0x00,0x2e,0x00,
174     0x6f,0x00,0x72,0x00,0x67,0x00,0x2f,0x00,
175     0x00,0x00,
176 };
177
178 /* url + friendly name */
179 static const unsigned char expected_hlink_data2[] =
180 {
181     0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,
182     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
183     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
184     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
185     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
186     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
187     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
188     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
189     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
190     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
191     0x67,0x00,0x2f,0x00,0x00,0x00,
192 };
193
194 /* url + friendly name + location */
195 static const unsigned char expected_hlink_data3[] =
196 {
197     0x02,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
198     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
199     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
200     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
201     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
202     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
203     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
204     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
205     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
206     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
207     0x67,0x00,0x2f,0x00,0x00,0x00,0x07,0x00,
208     0x00,0x00,0x5f,0x00,0x62,0x00,0x6c,0x00,
209     0x61,0x00,0x6e,0x00,0x6b,0x00,0x00,0x00,
210 };
211
212 /* relative url */
213 static const unsigned char expected_hlink_data4[] =
214 {
215     0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
216     0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
217     0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
218     0x00,0x00,0x0b,0x00,0x00,0x00,0x69,0x6e,
219     0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,
220     0x00,0xff,0xff,0xad,0xde,0x00,0x00,0x00,
221     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
222     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
223     0x00,0x00,0x00,0x00,0x00,
224 };
225
226 /* url + target frame name */
227 static const unsigned char expected_hlink_data5[] =
228 {
229     0x02,0x00,0x00,0x00,0x83,0x00,0x00,0x00,
230     0x07,0x00,0x00,0x00,0x74,0x00,0x67,0x00,
231     0x74,0x00,0x66,0x00,0x72,0x00,0x6d,0x00,
232     0x00,0x00,0xe0,0xc9,0xea,0x79,0xf9,0xba,
233     0xce,0x11,0x8c,0x82,0x00,0xaa,0x00,0x4b,
234     0xa9,0x0b,0x26,0x00,0x00,0x00,0x68,0x00,
235     0x74,0x00,0x74,0x00,0x70,0x00,0x3a,0x00,
236     0x2f,0x00,0x2f,0x00,0x77,0x00,0x69,0x00,
237     0x6e,0x00,0x65,0x00,0x68,0x00,0x71,0x00,
238     0x2e,0x00,0x6f,0x00,0x72,0x00,0x67,0x00,
239     0x2f,0x00,0x00,0x00,
240 };
241
242 /* filename */
243 static const unsigned char expected_hlink_data6[] =
244 {
245      0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
246      0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
247      0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
248      0x00,0x00,0x04,0x00,0x00,0x00,0x63,0x3a,
249      0x5c,0x00,0xff,0xff,0xad,0xde,0x00,0x00,
250      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
251      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
252      0x00,0x00,0x0c,0x00,0x00,0x00,0x06,0x00,
253      0x00,0x00,0x03,0x00,0x63,0x00,0x3a,0x00,
254      0x5c,0x00,
255 };
256
257 static void test_persist_save_data(const char *testname, IHlink *lnk,
258                                    const unsigned char *expected_data,
259                                    unsigned int expected_data_size)
260 {
261     HRESULT hr;
262     IStream *stream;
263     IPersistStream *ps;
264     HGLOBAL hglobal;
265     DWORD data_size;
266     const unsigned char *data;
267     DWORD i;
268     BOOL same;
269
270     hr = IHlink_QueryInterface(lnk, &IID_IPersistStream, (void **)&ps);
271     ok(hr == S_OK, "IHlink_QueryInterface failed with error 0x%08x\n", hr);
272
273     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
274     ok(hr == S_OK, "CreateStreamOnHGlobal failed with error 0x%08x\n", hr);
275
276     hr = IPersistStream_Save(ps, stream, TRUE);
277     ok(hr == S_OK, "IPersistStream_Save failed with error 0x%08x\n", hr);
278
279     hr = GetHGlobalFromStream(stream, &hglobal);
280     ok(hr == S_OK, "GetHGlobalFromStream failed with error 0x%08x\n", hr);
281
282     data_size = GlobalSize(hglobal);
283
284     data = GlobalLock(hglobal);
285
286     /* first check we have the right amount of data */
287     ok(data_size == expected_data_size,
288        "%s: Size of saved data differs (expected %d, actual %d)\n",
289        testname, expected_data_size, data_size);
290
291     same = TRUE;
292     /* then do a byte-by-byte comparison */
293     for (i = 0; i < min(data_size, expected_data_size); i++)
294     {
295         if ((expected_data[i] != data[i]) &&
296             (((expected_data != expected_hlink_data2) &&
297               (expected_data != expected_hlink_data3)) ||
298              ((i < 52 || i >= 56) && (i < 80 || i >= 84))))
299         {
300             same = FALSE;
301             break;
302         }
303     }
304
305     ok(same, "%s: Saved data differs\n", testname);
306     if (!same)
307     {
308         for (i = 0; i < data_size; i++)
309         {
310             if (i % 8 == 0) printf("    ");
311             printf("0x%02x,", data[i]);
312             if (i % 8 == 7) printf("\n");
313         }
314         printf("\n");
315     }
316
317     GlobalUnlock(hglobal);
318
319     IStream_Release(stream);
320     IPersistStream_Release(ps);
321 }
322
323 static void test_persist(void)
324 {
325     static const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
326     static const WCHAR rel_url[] = { 'i','n','d','e','x','.','h','t','m','l',0 };
327     static const WCHAR filename[] = { 'c',':','\\',0 };
328     static const WCHAR friendly_name[] = { 'W','i','n','e',' ','H','Q',0 };
329     static const WCHAR location[] = { '_','b','l','a','n','k',0 };
330     static const WCHAR target_frame_name[] = { 't','g','t','f','r','m',0 };
331     HRESULT hr;
332     IHlink *lnk;
333
334     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
335                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
336     ok(hr == S_OK, "IHlinkCreateFromString failed with error 0x%08x\n", hr);
337     if (!lnk) {
338         skip("Can't create lnk, skipping test_persist.  Was wineprefixcreate run properly?\n");
339         return;
340     }
341     test_persist_save_data("url only", lnk, expected_hlink_data, sizeof(expected_hlink_data));
342     IHlink_Release(lnk);
343
344     hr = HlinkCreateFromString(url, NULL, friendly_name, NULL,
345                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
346     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
347     test_persist_save_data("url + friendly name", lnk, expected_hlink_data2, sizeof(expected_hlink_data2));
348     IHlink_Release(lnk);
349
350     hr = HlinkCreateFromString(url, location, friendly_name, NULL,
351                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
352     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
353     test_persist_save_data("url + friendly_name + location", lnk, expected_hlink_data3, sizeof(expected_hlink_data3));
354     IHlink_Release(lnk);
355
356     hr = HlinkCreateFromString(rel_url, NULL, NULL, NULL,
357                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
358     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
359     test_persist_save_data("relative url", lnk, expected_hlink_data4, sizeof(expected_hlink_data4));
360     IHlink_Release(lnk);
361
362     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
363                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
364     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
365     hr = IHlink_SetTargetFrameName(lnk, target_frame_name);
366     ok(hr == S_OK, "IHlink_SetTargetFrameName failed with error 0x%08x\n", hr);
367     test_persist_save_data("url + target frame name", lnk, expected_hlink_data5, sizeof(expected_hlink_data5));
368     IHlink_Release(lnk);
369
370     hr = HlinkCreateFromString(filename, NULL, NULL, NULL,
371                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
372     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
373     test_persist_save_data("filename", lnk, expected_hlink_data6, sizeof(expected_hlink_data6));
374     IHlink_Release(lnk);
375 }
376
377 static void test_special_reference(void)
378 {
379     LPWSTR ref;
380     HRESULT hres;
381
382     hres = HlinkGetSpecialReference(HLSR_HOME, &ref);
383     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_HOME) failed: %08x\n", hres);
384     ok(ref != NULL, "ref == NULL\n");
385     CoTaskMemFree(ref);
386
387     hres = HlinkGetSpecialReference(HLSR_SEARCHPAGE, &ref);
388     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_SEARCHPAGE) failed: %08x\n", hres);
389     ok(ref != NULL, "ref == NULL\n");
390     CoTaskMemFree(ref);
391
392     ref = (void*)0xdeadbeef;
393     hres = HlinkGetSpecialReference(HLSR_HISTORYFOLDER, &ref);
394     ok(hres == E_NOTIMPL, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
395     ok(ref == NULL, "ref=%p\n", ref);
396
397     ref = (void*)0xdeadbeef;
398     hres = HlinkGetSpecialReference(4, &ref);
399     ok(hres == E_INVALIDARG, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
400     ok(ref == NULL, "ref=%p\n", ref);
401 }
402
403 static void test_HlinkCreateExtensionServices(void)
404 {
405     IAuthenticate *authenticate;
406     IHttpNegotiate *http_negotiate;
407     LPWSTR password, username, headers;
408     HWND hwnd;
409     HRESULT hres;
410
411     static const WCHAR usernameW[] = {'u','s','e','r',0};
412     static const WCHAR passwordW[] = {'p','a','s','s',0};
413     static const WCHAR headersW[] = {'h','e','a','d','e','r','s',0};
414     static const WCHAR headersexW[] = {'h','e','a','d','e','r','s','\r','\n',0};
415
416     hres = HlinkCreateExtensionServices(NULL, NULL, NULL, NULL,
417                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
418     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
419     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
420
421     password = username = (void*)0xdeadbeef;
422     hwnd = (void*)0xdeadbeef;
423     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
424     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
425     ok(!hwnd, "hwnd != NULL\n");
426     ok(!username, "username != NULL\n");
427     ok(!password, "password != NULL\n");
428
429     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
430     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
431
432     headers = (void*)0xdeadbeef;
433     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
434                                                0, &headers);
435     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
436     ok(headers == NULL, "headers != NULL\n");
437
438     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
439                                                0, NULL);
440     ok(hres == E_INVALIDARG, "BeginningTransaction failed: %08x, expected E_INVALIDARG\n", hres);
441
442     headers = (void*)0xdeadbeef;
443     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
444     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
445     ok(headers == NULL, "headers != NULL\n");
446
447     IHttpNegotiate_Release(http_negotiate);
448     IAuthenticate_Release(authenticate);
449
450
451     hres = HlinkCreateExtensionServices(headersW, (HWND)0xfefefefe, usernameW, passwordW,
452                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
453     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
454     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
455
456     password = username = NULL;
457     hwnd = NULL;
458     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
459     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
460     ok(hwnd == (HWND)0xfefefefe, "hwnd=%p\n", hwnd);
461     ok(!lstrcmpW(username, usernameW), "unexpected username\n");
462     ok(!lstrcmpW(password, passwordW), "unexpected password\n");
463     CoTaskMemFree(username);
464     CoTaskMemFree(password);
465
466     password = username = (void*)0xdeadbeef;
467     hwnd = (void*)0xdeadbeef;
468     hres = IAuthenticate_Authenticate(authenticate, &hwnd, NULL, &password);
469     ok(hres == E_INVALIDARG, "Authenticate failed: %08x\n", hres);
470     ok(password == (void*)0xdeadbeef, "password = %p\n", password);
471     ok(hwnd == (void*)0xdeadbeef, "hwnd = %p\n", hwnd);
472
473     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
474     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
475
476     headers = (void*)0xdeadbeef;
477     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
478                                                0, &headers);
479     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
480     ok(!lstrcmpW(headers, headersexW), "unexpected headers \"%s\"\n", debugstr_w(headers));
481     CoTaskMemFree(headers);
482
483     headers = (void*)0xdeadbeef;
484     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
485     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
486     ok(headers == NULL, "unexpected headers \"%s\"\n", debugstr_w(headers));
487
488     IHttpNegotiate_Release(http_negotiate);
489     IAuthenticate_Release(authenticate);
490 }
491
492 static void test_HlinkParseDisplayName(void)
493 {
494     IMoniker *mon = NULL;
495     LPWSTR name;
496     DWORD issys;
497     ULONG eaten = 0;
498     IBindCtx *bctx;
499     HRESULT hres;
500
501     static const WCHAR winehq_urlW[] =
502             {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
503              '/','s','i','t','e','/','a','b','o','u','t',0};
504     static const WCHAR invalid_urlW[] = {'t','e','s','t',':','1','2','3','a','b','c',0};
505     static const WCHAR clsid_nameW[] = {'c','l','s','i','d',':',
506             '2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-','A','2','D','8',
507             '-','0','8','0','0','2','B','3','0','3','0','9','D',':',0};
508
509     CreateBindCtx(0, &bctx);
510
511     hres = HlinkParseDisplayName(bctx, winehq_urlW, FALSE, &eaten, &mon);
512     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
513     ok(eaten == sizeof(winehq_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
514     ok(mon != NULL, "mon == NULL\n");
515
516     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
517     ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
518     ok(!lstrcmpW(name, winehq_urlW), "wrong display name %s\n", debugstr_w(name));
519     CoTaskMemFree(name);
520
521     hres = IMoniker_IsSystemMoniker(mon, &issys);
522     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
523     ok(issys == MKSYS_URLMONIKER, "issys=%x\n", issys);
524
525     IMoniker_Release(mon);
526
527     hres = HlinkParseDisplayName(bctx, clsid_nameW, FALSE, &eaten, &mon);
528     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
529     ok(eaten == sizeof(clsid_nameW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
530     ok(mon != NULL, "mon == NULL\n");
531
532     hres = IMoniker_IsSystemMoniker(mon, &issys);
533     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
534     ok(issys == MKSYS_CLASSMONIKER, "issys=%x\n", issys);
535
536     IMoniker_Release(mon);
537
538     hres = HlinkParseDisplayName(bctx, invalid_urlW, FALSE, &eaten, &mon);
539      ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
540     ok(eaten == sizeof(invalid_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
541     ok(mon != NULL, "mon == NULL\n");
542
543     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
544     ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
545     ok(!lstrcmpW(name, invalid_urlW), "wrong display name %s\n", debugstr_w(name));
546     CoTaskMemFree(name);
547
548     hres = IMoniker_IsSystemMoniker(mon, &issys);
549     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
550     ok(issys == MKSYS_FILEMONIKER, "issys=%x\n", issys);
551
552     IBindCtx_Release(bctx);
553 }
554
555 static IBindCtx *_bctx;
556
557 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
558 {
559     ok(0, "unexpected call\n");
560     return E_NOINTERFACE;
561 }
562
563 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
564 {
565     return 2;
566 }
567
568 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
569 {
570     return 1;
571 }
572
573 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
574         REFGUID guidService, REFIID riid, void **ppv)
575 {
576     ok(0, "unexpected service %s\n", debugstr_guid(guidService));
577     return E_NOINTERFACE;
578 }
579
580 static IServiceProviderVtbl ServiceProviderVtbl = {
581     ServiceProvider_QueryInterface,
582     ServiceProvider_AddRef,
583     ServiceProvider_Release,
584     ServiceProvider_QueryService
585 };
586
587 static IServiceProvider ServiceProvider = { &ServiceProviderVtbl };
588
589 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
590 {
591     *ppv = NULL;
592
593     if(IsEqualGUID(riid, &IID_IServiceProvider)) {
594         *ppv = &ServiceProvider;
595         return S_OK;
596     }
597
598     ok(0, "unexpected interface %s\n", debugstr_guid(riid));
599     return E_NOINTERFACE;
600 }
601
602 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
603 {
604     return 2;
605 }
606
607 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
608 {
609     return 1;
610 }
611
612 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
613         IBinding *pib)
614 {
615     ok(0, "unexpected call\n");
616     return E_NOTIMPL;
617 }
618
619 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
620 {
621     ok(0, "unexpected call\n");
622     return E_NOTIMPL;
623 }
624
625 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
626 {
627     ok(0, "unexpected call\n");
628     return E_NOTIMPL;
629 }
630
631 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
632         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
633 {
634     ok(0, "unexpected call\n");
635     return E_NOTIMPL;
636 }
637
638 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
639 {
640     ok(0, "unexpected call\n");
641     return E_NOTIMPL;
642 }
643
644 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
645 {
646     ok(0, "unexpected call\n");
647     return E_NOTIMPL;
648 }
649
650 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
651         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
652 {
653     ok(0, "unexpected call\n");
654     return E_NOTIMPL;
655 }
656
657 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
658 {
659     ok(0, "unexpected call\n");
660     return E_NOTIMPL;
661 }
662
663 static IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
664     BindStatusCallback_QueryInterface,
665     BindStatusCallback_AddRef,
666     BindStatusCallback_Release,
667     BindStatusCallback_OnStartBinding,
668     BindStatusCallback_GetPriority,
669     BindStatusCallback_OnLowResource,
670     BindStatusCallback_OnProgress,
671     BindStatusCallback_OnStopBinding,
672     BindStatusCallback_GetBindInfo,
673     BindStatusCallback_OnDataAvailable,
674     BindStatusCallback_OnObjectAvailable
675 };
676
677 static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl };
678
679 static HRESULT WINAPI Moniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
680 {
681     *ppv = NULL;
682
683     ok(0, "unexpected riid: %s\n", debugstr_guid(riid));
684     return E_NOINTERFACE;
685 }
686
687 static ULONG WINAPI Moniker_AddRef(IMoniker *iface)
688 {
689     return 2;
690 }
691
692 static ULONG WINAPI Moniker_Release(IMoniker *iface)
693 {
694     return 1;
695 }
696
697 static HRESULT WINAPI Moniker_GetClassID(IMoniker *iface, CLSID *pClassID)
698 {
699     ok(0, "unexpected call\n");
700     return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI Moniker_IsDirty(IMoniker *iface)
704 {
705     ok(0, "unexpected call\n");
706     return E_NOTIMPL;
707 }
708
709 static HRESULT WINAPI Moniker_Load(IMoniker *iface, IStream *pStm)
710 {
711     ok(0, "unexpected call\n");
712     return E_NOTIMPL;
713 }
714
715 static HRESULT WINAPI Moniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
716 {
717     ok(0, "unexpected call\n");
718     return E_NOTIMPL;
719 }
720
721 static HRESULT WINAPI Moniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
722 {
723     ok(0, "unexpected call\n");
724     return E_NOTIMPL;
725 }
726
727 static HRESULT WINAPI Moniker_BindToObject(IMoniker *iface, IBindCtx *pcb, IMoniker *pmkToLeft,
728         REFIID riidResult, void **ppvResult)
729 {
730     ok(0, "unexpected call\n");
731     return E_NOTIMPL;
732 }
733
734 static HRESULT WINAPI Moniker_BindToStorage(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
735         REFIID riid, void **ppv)
736 {
737     IUnknown *unk;
738     HRESULT hres;
739
740     static OLECHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
741
742     CHECK_EXPECT(BindToStorage);
743
744     ok(pbc == _bctx, "pbc != _bctx\n");
745     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
746     ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", debugstr_guid(riid));
747     ok(ppv != NULL, "ppv == NULL\n");
748     ok(*ppv == NULL, "*ppv=%p\n", *ppv);
749
750     hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
751     ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres);
752     ok(unk != NULL, "unk == NULL\n");
753
754     IUnknown_Release(unk);
755
756     return S_OK;
757 }
758
759 static HRESULT WINAPI Moniker_Reduce(IMoniker *iface, IBindCtx *pbc, DWORD dwReduceHowFar,
760         IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
761 {
762     ok(0, "unexpected call\n");
763     return E_NOTIMPL;
764 }
765
766 static HRESULT WINAPI Moniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
767         BOOL fOnlyIfNotGeneric, IMoniker **ppnkComposite)
768 {
769     ok(0, "unexpected call\n");
770     return E_NOTIMPL;
771 }
772
773 static HRESULT WINAPI Moniker_Enum(IMoniker *iface, BOOL fForwrd, IEnumMoniker **ppenumMoniker)
774 {
775     ok(0, "unexpected call\n");
776     return E_NOTIMPL;
777 }
778
779 static HRESULT WINAPI Moniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
780 {
781     ok(0, "unexpected call\n");
782     return E_NOTIMPL;
783 }
784
785 static HRESULT WINAPI Moniker_Hash(IMoniker *iface, DWORD *pdwHash)
786 {
787     ok(0, "unexpected call\n");
788     return E_NOTIMPL;
789 }
790
791 static HRESULT WINAPI Moniker_IsRunning(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
792         IMoniker *pmkNewlyRunning)
793 {
794     ok(0, "unexpected call\n");
795     return E_NOTIMPL;
796 }
797
798 static HRESULT WINAPI Moniker_GetTimeOfLastChange(IMoniker *iface, IBindCtx *pbc,
799         IMoniker *pmkToLeft, FILETIME *pFileTime)
800 {
801     ok(0, "unexpected call\n");
802     return E_NOTIMPL;
803 }
804
805 static HRESULT WINAPI Moniker_Inverse(IMoniker *iface, IMoniker **ppmk)
806 {
807     ok(0, "unexpected call\n");
808     return E_NOTIMPL;
809 }
810
811 static HRESULT WINAPI Moniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther,
812         IMoniker **ppmkPrefix)
813 {
814     ok(0, "unexpected call\n");
815     return E_NOTIMPL;
816 }
817
818 static HRESULT WINAPI Moniker_RelativePathTo(IMoniker *iface, IMoniker *pmkOther,
819         IMoniker **pmkRelPath)
820 {
821     ok(0, "unexpected call\n");
822     return E_NOTIMPL;
823 }
824
825 static HRESULT WINAPI Moniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc,
826         IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
827 {
828     static const WCHAR winehq_urlW[] =
829             {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
830              '/','s','i','t','e','/','a','b','o','u','t',0};
831
832     CHECK_EXPECT(GetDisplayName);
833
834     ok(pbc != NULL, "pbc == NULL\n");
835     ok(pbc != _bctx, "pbc == _bctx\n");
836     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
837
838     *ppszDisplayName = CoTaskMemAlloc(sizeof(winehq_urlW));
839     memcpy(*ppszDisplayName, winehq_urlW, sizeof(winehq_urlW));
840     return S_OK;
841 }
842
843 static HRESULT WINAPI Moniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc,
844         IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
845 {
846     ok(0, "unexpected call\n");
847     return E_NOTIMPL;
848 }
849
850 static HRESULT WINAPI Moniker_IsSystemMoniker(IMoniker *iface, DWORD *pdwMksys)
851 {
852     CHECK_EXPECT2(IsSystemMoniker);
853
854     *pdwMksys = MKSYS_URLMONIKER;
855     return S_OK;
856 }
857
858 static IMonikerVtbl MonikerVtbl = {
859     Moniker_QueryInterface,
860     Moniker_AddRef,
861     Moniker_Release,
862     Moniker_GetClassID,
863     Moniker_IsDirty,
864     Moniker_Load,
865     Moniker_Save,
866     Moniker_GetSizeMax,
867     Moniker_BindToObject,
868     Moniker_BindToStorage,
869     Moniker_Reduce,
870     Moniker_ComposeWith,
871     Moniker_Enum,
872     Moniker_IsEqual,
873     Moniker_Hash,
874     Moniker_IsRunning,
875     Moniker_GetTimeOfLastChange,
876     Moniker_Inverse,
877     Moniker_CommonPrefixWith,
878     Moniker_RelativePathTo,
879     Moniker_GetDisplayName,
880     Moniker_ParseDisplayName,
881     Moniker_IsSystemMoniker
882 };
883
884 static IMoniker Moniker = { &MonikerVtbl };
885
886 static void test_HlinkResolveMonikerForData(void)
887 {
888     IBindCtx *bctx;
889     HRESULT hres;
890
891     CreateBindCtx(0, &bctx);
892     _bctx = bctx;
893
894     SET_EXPECT(IsSystemMoniker);
895     SET_EXPECT(GetDisplayName);
896     SET_EXPECT(BindToStorage);
897
898     hres = HlinkResolveMonikerForData(&Moniker, 0, bctx, 0, NULL, &BindStatusCallback, NULL);
899     ok(hres == S_OK, "HlinkResolveMonikerForData failed: %08x\n", hres);
900
901     CHECK_CALLED(IsSystemMoniker);
902     CHECK_CALLED(GetDisplayName);
903     CHECK_CALLED(BindToStorage);
904
905     IBindCtx_Release(bctx);
906 }
907
908 START_TEST(hlink)
909 {
910     CoInitialize(NULL);
911
912     test_HlinkIsShortcut();
913     test_reference();
914     test_persist();
915     test_special_reference();
916     test_HlinkCreateExtensionServices();
917     test_HlinkParseDisplayName();
918     test_HlinkResolveMonikerForData();
919
920     CoUninitialize();
921 }