hlink: String target reference is actually moniker target's display name.
[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_guid(REFIID riid)
61 {
62     static char buf[50];
63
64     sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
65             riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
66             riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
67             riid->Data4[5], riid->Data4[6], riid->Data4[7]);
68
69     return buf;
70 }
71
72 static void test_HlinkIsShortcut(void)
73 {
74     UINT i;
75     HRESULT hres;
76
77     static const WCHAR file0[] = {'f','i','l','e',0};
78     static const WCHAR file1[] = {'f','i','l','e','.','u','r','l',0};
79     static const WCHAR file2[] = {'f','i','l','e','.','l','n','k',0};
80     static const WCHAR file3[] = {'f','i','l','e','.','u','R','l',0};
81     static const WCHAR file4[] = {'f','i','l','e','u','r','l',0};
82     static const WCHAR file5[] = {'c',':','\\','f','i','l','e','.','u','r','l',0};
83     static const WCHAR file6[] = {'c',':','\\','f','i','l','e','.','l','n','k',0};
84     static const WCHAR file7[] = {'.','u','r','l',0};
85
86     static struct {
87         LPCWSTR file;
88         HRESULT hres;
89     } shortcut_test[] = {
90         {file0, S_FALSE},
91         {file1, S_OK},
92         {file2, S_FALSE},
93         {file3, S_OK},
94         {file4, S_FALSE},
95         {file5, S_OK},
96         {file6, S_FALSE},
97         {file7, S_OK},
98         {NULL,  E_INVALIDARG}
99     };
100
101     for(i=0; i<sizeof(shortcut_test)/sizeof(shortcut_test[0]); i++) {
102         hres = HlinkIsShortcut(shortcut_test[i].file);
103         ok(hres == shortcut_test[i].hres, "[%d] HlinkIsShortcut returned %08x, expected %08x\n",
104            i, hres, shortcut_test[i].hres);
105     }
106 }
107
108 static void test_reference(void)
109 {
110     HRESULT r;
111     IHlink *lnk = NULL;
112     IMoniker *mk = NULL;
113     const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
114     const WCHAR url2[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g','/',0 };
115     LPWSTR str = NULL;
116
117     r = HlinkCreateFromString(url, NULL, NULL, NULL,
118                               0, NULL, &IID_IHlink, (LPVOID*) &lnk);
119     ok(r == S_OK, "failed to create link\n");
120     if (FAILED(r))
121         return;
122
123     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
124     ok(r == S_OK, "failed\n");
125
126     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, &mk, &str);
127     ok(r == S_OK, "failed\n");
128     ok(mk != NULL, "no moniker\n");
129     ok(str == NULL, "string should be null\n");
130
131     r = IMoniker_Release(mk);
132     ok( r == 1, "moniker refcount wrong\n");
133
134     r = IHlink_GetStringReference(lnk, -1, &str, NULL);
135     ok(r == S_OK, "failed\n");
136     CoTaskMemFree(str);
137
138     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, &str, NULL);
139     ok(r == S_OK, "failed\n");
140     ok(!lstrcmpW(str, url2), "url wrong\n");
141     CoTaskMemFree(str);
142
143     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
144     ok(r == S_OK, "failed\n");
145
146     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, &str);
147     ok(r == S_OK, "failed\n");
148     ok(str == NULL, "string should be null\n");
149
150     /* Unimplented functions checks */
151     r = IHlink_GetAdditionalParams(lnk, NULL);
152     ok(r == E_NOTIMPL, "failed\n");
153
154     r = IHlink_SetAdditionalParams(lnk, NULL);
155     ok(r == E_NOTIMPL, "failed\n");
156
157     IHlink_Release(lnk);
158 }
159
160 /* url only */
161 static const unsigned char expected_hlink_data[] =
162 {
163     0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
164     0xe0,0xc9,0xea,0x79,0xf9,0xba,0xce,0x11,
165     0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b,
166     0x26,0x00,0x00,0x00,0x68,0x00,0x74,0x00,
167     0x74,0x00,0x70,0x00,0x3a,0x00,0x2f,0x00,
168     0x2f,0x00,0x77,0x00,0x69,0x00,0x6e,0x00,
169     0x65,0x00,0x68,0x00,0x71,0x00,0x2e,0x00,
170     0x6f,0x00,0x72,0x00,0x67,0x00,0x2f,0x00,
171     0x00,0x00,
172 };
173
174 /* url only (IE7) */
175 static const unsigned char expected_hlink_data_ie7[] =
176 {
177     0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
178     0xe0,0xc9,0xea,0x79,0xf9,0xba,0xce,0x11,
179     0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b,
180     0x3e,0x00,0x00,0x00,0x68,0x00,0x74,0x00,
181     0x74,0x00,0x70,0x00,0x3a,0x00,0x2f,0x00,
182     0x2f,0x00,0x77,0x00,0x69,0x00,0x6e,0x00,
183     0x65,0x00,0x68,0x00,0x71,0x00,0x2e,0x00,
184     0x6f,0x00,0x72,0x00,0x67,0x00,0x2f,0x00,
185     0x00,0x00,0x79,0x58,0x81,0xf4,0x3b,0x1d,
186     0x7f,0x48,0xaf,0x2c,0x82,0x5d,0xc4,0x85,
187     0x27,0x63,0x00,0x00,0x00,0x00,0xa5,0xab,
188     0x00,0x00,
189 };
190
191 /* url + friendly name */
192 static const unsigned char expected_hlink_data2[] =
193 {
194     0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,
195     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
196     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
197     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
198     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
199     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
200     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
201     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
202     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
203     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
204     0x67,0x00,0x2f,0x00,0x00,0x00,
205 };
206
207 /* url + friendly name (IE7) */
208 static const unsigned char expected_hlink_data2_ie7[] =
209 {
210     0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,
211     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
212     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
213     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
214     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
215     0x00,0x4b,0xa9,0x0b,0x3e,0x00,0x00,0x00,
216     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
217     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
218     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
219     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
220     0x67,0x00,0x2f,0x00,0x00,0x00,0x79,0x58,
221     0x81,0xf4,0x3b,0x1d,0x7f,0x48,0xaf,0x2c,
222     0x82,0x5d,0xc4,0x85,0x27,0x63,0x00,0x00,
223     0x00,0x00,0xa5,0xab,0x00,0x00,
224 };
225
226 /* url + friendly name + location */
227 static const unsigned char expected_hlink_data3[] =
228 {
229     0x02,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
230     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
231     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
232     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
233     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
234     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
235     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
236     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
237     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
238     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
239     0x67,0x00,0x2f,0x00,0x00,0x00,0x07,0x00,
240     0x00,0x00,0x5f,0x00,0x62,0x00,0x6c,0x00,
241     0x61,0x00,0x6e,0x00,0x6b,0x00,0x00,0x00,
242 };
243
244 /* url + friendly name + location (IE7) */
245 static const unsigned char expected_hlink_data3_ie7[] =
246 {
247     0x02,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
248     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
249     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
250     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
251     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
252     0x00,0x4b,0xa9,0x0b,0x3e,0x00,0x00,0x00,
253     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
254     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
255     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
256     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
257     0x67,0x00,0x2f,0x00,0x00,0x00,0x79,0x58,
258     0x81,0xf4,0x3b,0x1d,0x7f,0x48,0xaf,0x2c,
259     0x82,0x5d,0xc4,0x85,0x27,0x63,0x00,0x00,
260     0x00,0x00,0xa5,0xab,0x00,0x00,0x07,0x00,
261     0x00,0x00,0x5f,0x00,0x62,0x00,0x6c,0x00,
262     0x61,0x00,0x6e,0x00,0x6b,0x00,0x00,0x00,
263 };
264
265 /* relative url */
266 static const unsigned char expected_hlink_data4[] =
267 {
268     0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
269     0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
270     0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
271     0x00,0x00,0x0b,0x00,0x00,0x00,0x69,0x6e,
272     0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,
273     0x00,0xff,0xff,0xad,0xde,0x00,0x00,0x00,
274     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
275     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
276     0x00,0x00,0x00,0x00,0x00,
277 };
278
279 /* url + target frame name */
280 static const unsigned char expected_hlink_data5[] =
281 {
282     0x02,0x00,0x00,0x00,0x83,0x00,0x00,0x00,
283     0x07,0x00,0x00,0x00,0x74,0x00,0x67,0x00,
284     0x74,0x00,0x66,0x00,0x72,0x00,0x6d,0x00,
285     0x00,0x00,0xe0,0xc9,0xea,0x79,0xf9,0xba,
286     0xce,0x11,0x8c,0x82,0x00,0xaa,0x00,0x4b,
287     0xa9,0x0b,0x26,0x00,0x00,0x00,0x68,0x00,
288     0x74,0x00,0x74,0x00,0x70,0x00,0x3a,0x00,
289     0x2f,0x00,0x2f,0x00,0x77,0x00,0x69,0x00,
290     0x6e,0x00,0x65,0x00,0x68,0x00,0x71,0x00,
291     0x2e,0x00,0x6f,0x00,0x72,0x00,0x67,0x00,
292     0x2f,0x00,0x00,0x00,
293 };
294
295 /* url + target frame name (IE7) */
296 static const unsigned char expected_hlink_data5_ie7[] =
297 {
298     0x02,0x00,0x00,0x00,0x83,0x00,0x00,0x00,
299     0x07,0x00,0x00,0x00,0x74,0x00,0x67,0x00,
300     0x74,0x00,0x66,0x00,0x72,0x00,0x6d,0x00,
301     0x00,0x00,0xe0,0xc9,0xea,0x79,0xf9,0xba,
302     0xce,0x11,0x8c,0x82,0x00,0xaa,0x00,0x4b,
303     0xa9,0x0b,0x3e,0x00,0x00,0x00,0x68,0x00,
304     0x74,0x00,0x74,0x00,0x70,0x00,0x3a,0x00,
305     0x2f,0x00,0x2f,0x00,0x77,0x00,0x69,0x00,
306     0x6e,0x00,0x65,0x00,0x68,0x00,0x71,0x00,
307     0x2e,0x00,0x6f,0x00,0x72,0x00,0x67,0x00,
308     0x2f,0x00,0x00,0x00,0x79,0x58,0x81,0xf4,
309     0x3b,0x1d,0x7f,0x48,0xaf,0x2c,0x82,0x5d,
310     0xc4,0x85,0x27,0x63,0x00,0x00,0x00,0x00,
311     0xa5,0xab,0x00,0x00,
312 };
313
314 /* filename */
315 static const unsigned char expected_hlink_data6[] =
316 {
317      0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
318      0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
319      0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
320      0x00,0x00,0x04,0x00,0x00,0x00,0x63,0x3a,
321      0x5c,0x00,0xff,0xff,0xad,0xde,0x00,0x00,
322      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
323      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
324      0x00,0x00,0x0c,0x00,0x00,0x00,0x06,0x00,
325      0x00,0x00,0x03,0x00,0x63,0x00,0x3a,0x00,
326      0x5c,0x00,
327 };
328
329 static void test_persist_save_data(const char *testname, IHlink *lnk,
330                                    const unsigned char *expected_data,
331                                    unsigned int expected_data_size,
332                                    const unsigned char *expected_data_alt,
333                                    unsigned int expected_data_alt_size)
334 {
335     HRESULT hr;
336     IStream *stream;
337     IPersistStream *ps;
338     HGLOBAL hglobal;
339     DWORD data_size;
340     const unsigned char *data;
341     DWORD i;
342     BOOL same;
343     unsigned int expected_data_win9x_size = 0;
344
345     hr = IHlink_QueryInterface(lnk, &IID_IPersistStream, (void **)&ps);
346     ok(hr == S_OK, "IHlink_QueryInterface failed with error 0x%08x\n", hr);
347
348     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
349     ok(hr == S_OK, "CreateStreamOnHGlobal failed with error 0x%08x\n", hr);
350
351     hr = IPersistStream_Save(ps, stream, TRUE);
352     ok(hr == S_OK, "IPersistStream_Save failed with error 0x%08x\n", hr);
353
354     hr = GetHGlobalFromStream(stream, &hglobal);
355     ok(hr == S_OK, "GetHGlobalFromStream failed with error 0x%08x\n", hr);
356
357     data_size = GlobalSize(hglobal);
358
359     data = GlobalLock(hglobal);
360
361     if (expected_data_size % 4)
362         expected_data_win9x_size =  4 * ((expected_data_size / 4) + 1);
363
364     /* first check we have the right amount of data */
365     ok((data_size == expected_data_size) ||
366        (data_size == expected_data_alt_size) ||
367        broken(data_size == expected_data_win9x_size), /* Win9x and WinMe */
368        "%s: Size of saved data differs (expected %d or %d, actual %d)\n",
369        testname, expected_data_size, expected_data_alt_size, data_size);
370
371     same = TRUE;
372     /* then do a byte-by-byte comparison */
373     for (i = 0; i < min(data_size, expected_data_size); i++)
374     {
375         if ((expected_data[i] != data[i]) &&
376             (((expected_data != expected_hlink_data2) &&
377               (expected_data != expected_hlink_data3)) ||
378              ((i < 52 || i >= 56) && (i < 80 || i >= 84))))
379         {
380             same = FALSE;
381             break;
382         }
383     }
384
385     if (!same && (expected_data_alt != expected_data))
386     {
387         /* then try the alternate data */
388         same = TRUE;
389         for (i = 0; i < min(data_size, expected_data_alt_size); i++)
390         {
391             if ((expected_data_alt[i] != data[i]) &&
392                 (((expected_data_alt != expected_hlink_data2) &&
393                   (expected_data_alt != expected_hlink_data3)) ||
394                  ((i < 52 || i >= 56) && (i < 80 || i >= 84))))
395             {
396                 same = FALSE;
397                 break;
398             }
399         }
400     }
401
402     ok(same, "%s: Saved data differs\n", testname);
403     if (!same)
404     {
405         for (i = 0; i < data_size; i++)
406         {
407             if (i % 8 == 0) printf("    ");
408             printf("0x%02x,", data[i]);
409             if (i % 8 == 7) printf("\n");
410         }
411         printf("\n");
412     }
413
414     GlobalUnlock(hglobal);
415
416     IStream_Release(stream);
417     IPersistStream_Release(ps);
418 }
419
420 static void test_persist(void)
421 {
422     static const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
423     static const WCHAR rel_url[] = { 'i','n','d','e','x','.','h','t','m','l',0 };
424     static const WCHAR filename[] = { 'c',':','\\',0 };
425     static const WCHAR friendly_name[] = { 'W','i','n','e',' ','H','Q',0 };
426     static const WCHAR location[] = { '_','b','l','a','n','k',0 };
427     static const WCHAR target_frame_name[] = { 't','g','t','f','r','m',0 };
428     HRESULT hr;
429     IHlink *lnk;
430
431     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
432                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
433     ok(hr == S_OK, "IHlinkCreateFromString failed with error 0x%08x\n", hr);
434     if (!lnk) {
435         skip("Can't create lnk, skipping test_persist.  Was wineprefixcreate run properly?\n");
436         return;
437     }
438     test_persist_save_data("url only", lnk,
439         expected_hlink_data, sizeof(expected_hlink_data),
440         expected_hlink_data_ie7, sizeof(expected_hlink_data_ie7));
441     IHlink_Release(lnk);
442
443     hr = HlinkCreateFromString(url, NULL, friendly_name, NULL,
444                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
445     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
446     test_persist_save_data("url + friendly name", lnk,
447         expected_hlink_data2, sizeof(expected_hlink_data2),
448         expected_hlink_data2_ie7, sizeof(expected_hlink_data2_ie7));
449     IHlink_Release(lnk);
450
451     hr = HlinkCreateFromString(url, location, friendly_name, NULL,
452                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
453     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
454     test_persist_save_data("url + friendly_name + location", lnk,
455         expected_hlink_data3, sizeof(expected_hlink_data3),
456         expected_hlink_data3_ie7, sizeof(expected_hlink_data3_ie7));
457     IHlink_Release(lnk);
458
459     hr = HlinkCreateFromString(rel_url, NULL, NULL, NULL,
460                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
461     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
462     test_persist_save_data("relative url", lnk,
463         expected_hlink_data4, sizeof(expected_hlink_data4),
464         expected_hlink_data4, sizeof(expected_hlink_data4));
465     IHlink_Release(lnk);
466
467     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
468                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
469     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
470     hr = IHlink_SetTargetFrameName(lnk, target_frame_name);
471     ok(hr == S_OK, "IHlink_SetTargetFrameName failed with error 0x%08x\n", hr);
472     test_persist_save_data("url + target frame name", lnk,
473         expected_hlink_data5, sizeof(expected_hlink_data5),
474         expected_hlink_data5_ie7, sizeof(expected_hlink_data5_ie7));
475     IHlink_Release(lnk);
476
477     hr = HlinkCreateFromString(filename, NULL, NULL, NULL,
478                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
479     ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
480     test_persist_save_data("filename", lnk,
481         expected_hlink_data6, sizeof(expected_hlink_data6),
482         expected_hlink_data6, sizeof(expected_hlink_data6));
483     IHlink_Release(lnk);
484 }
485
486 static void test_special_reference(void)
487 {
488     LPWSTR ref;
489     HRESULT hres;
490
491     hres = HlinkGetSpecialReference(HLSR_HOME, &ref);
492     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_HOME) failed: %08x\n", hres);
493     ok(ref != NULL, "ref == NULL\n");
494     CoTaskMemFree(ref);
495
496     hres = HlinkGetSpecialReference(HLSR_SEARCHPAGE, &ref);
497     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_SEARCHPAGE) failed: %08x\n", hres);
498     ok(ref != NULL, "ref == NULL\n");
499     CoTaskMemFree(ref);
500
501     ref = (void*)0xdeadbeef;
502     hres = HlinkGetSpecialReference(HLSR_HISTORYFOLDER, &ref);
503     ok(hres == E_NOTIMPL, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
504     ok(ref == NULL, "ref=%p\n", ref);
505
506     ref = (void*)0xdeadbeef;
507     hres = HlinkGetSpecialReference(4, &ref);
508     ok(hres == E_INVALIDARG, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
509     ok(ref == NULL, "ref=%p\n", ref);
510 }
511
512 static void test_HlinkCreateExtensionServices(void)
513 {
514     IAuthenticate *authenticate;
515     IHttpNegotiate *http_negotiate;
516     LPWSTR password, username, headers;
517     HWND hwnd;
518     HRESULT hres;
519
520     static const WCHAR usernameW[] = {'u','s','e','r',0};
521     static const WCHAR passwordW[] = {'p','a','s','s',0};
522     static const WCHAR headersW[] = {'h','e','a','d','e','r','s',0};
523     static const WCHAR headersexW[] = {'h','e','a','d','e','r','s','\r','\n',0};
524
525     hres = HlinkCreateExtensionServices(NULL, NULL, NULL, NULL,
526                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
527     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
528     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
529
530     password = username = (void*)0xdeadbeef;
531     hwnd = (void*)0xdeadbeef;
532     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
533     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
534     ok(!hwnd, "hwnd != NULL\n");
535     ok(!username, "username != NULL\n");
536     ok(!password, "password != NULL\n");
537
538     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
539     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
540
541     headers = (void*)0xdeadbeef;
542     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
543                                                0, &headers);
544     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
545     ok(headers == NULL, "headers != NULL\n");
546
547     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
548                                                0, NULL);
549     ok(hres == E_INVALIDARG, "BeginningTransaction failed: %08x, expected E_INVALIDARG\n", hres);
550
551     headers = (void*)0xdeadbeef;
552     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
553     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
554     ok(headers == NULL, "headers != NULL\n");
555
556     IHttpNegotiate_Release(http_negotiate);
557     IAuthenticate_Release(authenticate);
558
559
560     hres = HlinkCreateExtensionServices(headersW, (HWND)0xfefefefe, usernameW, passwordW,
561                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
562     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
563     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
564
565     password = username = NULL;
566     hwnd = NULL;
567     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
568     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
569     ok(hwnd == (HWND)0xfefefefe, "hwnd=%p\n", hwnd);
570     ok(!lstrcmpW(username, usernameW), "unexpected username\n");
571     ok(!lstrcmpW(password, passwordW), "unexpected password\n");
572     CoTaskMemFree(username);
573     CoTaskMemFree(password);
574
575     password = username = (void*)0xdeadbeef;
576     hwnd = (void*)0xdeadbeef;
577     hres = IAuthenticate_Authenticate(authenticate, &hwnd, NULL, &password);
578     ok(hres == E_INVALIDARG, "Authenticate failed: %08x\n", hres);
579     ok(password == (void*)0xdeadbeef, "password = %p\n", password);
580     ok(hwnd == (void*)0xdeadbeef, "hwnd = %p\n", hwnd);
581
582     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
583     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
584
585     headers = (void*)0xdeadbeef;
586     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
587                                                0, &headers);
588     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
589     ok(!lstrcmpW(headers, headersexW), "unexpected headers %s\n", wine_dbgstr_w(headers));
590     CoTaskMemFree(headers);
591
592     headers = (void*)0xdeadbeef;
593     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
594     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
595     ok(headers == NULL, "unexpected headers %s\n", wine_dbgstr_w(headers));
596
597     IHttpNegotiate_Release(http_negotiate);
598     IAuthenticate_Release(authenticate);
599 }
600
601 static void test_HlinkParseDisplayName(void)
602 {
603     IMoniker *mon = NULL;
604     LPWSTR name;
605     DWORD issys;
606     ULONG eaten = 0;
607     IBindCtx *bctx;
608     HRESULT hres;
609
610     static const WCHAR winehq_urlW[] =
611             {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
612              '/','s','i','t','e','/','a','b','o','u','t',0};
613     static const WCHAR invalid_urlW[] = {'t','e','s','t',':','1','2','3','a','b','c',0};
614     static const WCHAR clsid_nameW[] = {'c','l','s','i','d',':',
615             '2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-','A','2','D','8',
616             '-','0','8','0','0','2','B','3','0','3','0','9','D',':',0};
617
618     CreateBindCtx(0, &bctx);
619
620     hres = HlinkParseDisplayName(bctx, winehq_urlW, FALSE, &eaten, &mon);
621     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
622     ok(eaten == sizeof(winehq_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
623     ok(mon != NULL, "mon == NULL\n");
624
625     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
626     ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
627     ok(!lstrcmpW(name, winehq_urlW), "wrong display name %s\n", wine_dbgstr_w(name));
628     CoTaskMemFree(name);
629
630     hres = IMoniker_IsSystemMoniker(mon, &issys);
631     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
632     ok(issys == MKSYS_URLMONIKER, "issys=%x\n", issys);
633
634     IMoniker_Release(mon);
635
636     hres = HlinkParseDisplayName(bctx, clsid_nameW, FALSE, &eaten, &mon);
637     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
638     ok(eaten == sizeof(clsid_nameW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
639     ok(mon != NULL, "mon == NULL\n");
640
641     hres = IMoniker_IsSystemMoniker(mon, &issys);
642     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
643     ok(issys == MKSYS_CLASSMONIKER, "issys=%x\n", issys);
644
645     IMoniker_Release(mon);
646
647     hres = HlinkParseDisplayName(bctx, invalid_urlW, FALSE, &eaten, &mon);
648      ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
649     ok(eaten == sizeof(invalid_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
650     ok(mon != NULL, "mon == NULL\n");
651
652     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
653     ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
654     ok(!lstrcmpW(name, invalid_urlW), "wrong display name %s\n", wine_dbgstr_w(name));
655     CoTaskMemFree(name);
656
657     hres = IMoniker_IsSystemMoniker(mon, &issys);
658     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
659     ok(issys == MKSYS_FILEMONIKER, "issys=%x\n", issys);
660
661     IMoniker_Release(mon);
662     IBindCtx_Release(bctx);
663 }
664
665 static IBindCtx *_bctx;
666
667 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
668 {
669     ok(0, "unexpected call\n");
670     return E_NOINTERFACE;
671 }
672
673 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
674 {
675     return 2;
676 }
677
678 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
679 {
680     return 1;
681 }
682
683 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
684         REFGUID guidService, REFIID riid, void **ppv)
685 {
686     ok(0, "unexpected service %s\n", debugstr_guid(guidService));
687     return E_NOINTERFACE;
688 }
689
690 static IServiceProviderVtbl ServiceProviderVtbl = {
691     ServiceProvider_QueryInterface,
692     ServiceProvider_AddRef,
693     ServiceProvider_Release,
694     ServiceProvider_QueryService
695 };
696
697 static IServiceProvider ServiceProvider = { &ServiceProviderVtbl };
698
699 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
700 {
701     *ppv = NULL;
702
703     if(IsEqualGUID(riid, &IID_IServiceProvider)) {
704         *ppv = &ServiceProvider;
705         return S_OK;
706     }
707
708     ok(0, "unexpected interface %s\n", debugstr_guid(riid));
709     return E_NOINTERFACE;
710 }
711
712 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
713 {
714     return 2;
715 }
716
717 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
718 {
719     return 1;
720 }
721
722 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
723         IBinding *pib)
724 {
725     ok(0, "unexpected call\n");
726     return E_NOTIMPL;
727 }
728
729 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
730 {
731     ok(0, "unexpected call\n");
732     return E_NOTIMPL;
733 }
734
735 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
736 {
737     ok(0, "unexpected call\n");
738     return E_NOTIMPL;
739 }
740
741 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
742         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
743 {
744     ok(0, "unexpected call\n");
745     return E_NOTIMPL;
746 }
747
748 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
749 {
750     ok(0, "unexpected call\n");
751     return E_NOTIMPL;
752 }
753
754 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
755 {
756     ok(0, "unexpected call\n");
757     return E_NOTIMPL;
758 }
759
760 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
761         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
762 {
763     ok(0, "unexpected call\n");
764     return E_NOTIMPL;
765 }
766
767 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
768 {
769     ok(0, "unexpected call\n");
770     return E_NOTIMPL;
771 }
772
773 static IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
774     BindStatusCallback_QueryInterface,
775     BindStatusCallback_AddRef,
776     BindStatusCallback_Release,
777     BindStatusCallback_OnStartBinding,
778     BindStatusCallback_GetPriority,
779     BindStatusCallback_OnLowResource,
780     BindStatusCallback_OnProgress,
781     BindStatusCallback_OnStopBinding,
782     BindStatusCallback_GetBindInfo,
783     BindStatusCallback_OnDataAvailable,
784     BindStatusCallback_OnObjectAvailable
785 };
786
787 static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl };
788
789 static HRESULT WINAPI Moniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
790 {
791     *ppv = NULL;
792
793     ok(0, "unexpected riid: %s\n", debugstr_guid(riid));
794     return E_NOINTERFACE;
795 }
796
797 static ULONG WINAPI Moniker_AddRef(IMoniker *iface)
798 {
799     return 2;
800 }
801
802 static ULONG WINAPI Moniker_Release(IMoniker *iface)
803 {
804     return 1;
805 }
806
807 static HRESULT WINAPI Moniker_GetClassID(IMoniker *iface, CLSID *pClassID)
808 {
809     ok(0, "unexpected call\n");
810     return E_NOTIMPL;
811 }
812
813 static HRESULT WINAPI Moniker_IsDirty(IMoniker *iface)
814 {
815     ok(0, "unexpected call\n");
816     return E_NOTIMPL;
817 }
818
819 static HRESULT WINAPI Moniker_Load(IMoniker *iface, IStream *pStm)
820 {
821     ok(0, "unexpected call\n");
822     return E_NOTIMPL;
823 }
824
825 static HRESULT WINAPI Moniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
826 {
827     ok(0, "unexpected call\n");
828     return E_NOTIMPL;
829 }
830
831 static HRESULT WINAPI Moniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
832 {
833     ok(0, "unexpected call\n");
834     return E_NOTIMPL;
835 }
836
837 static HRESULT WINAPI Moniker_BindToObject(IMoniker *iface, IBindCtx *pcb, IMoniker *pmkToLeft,
838         REFIID riidResult, void **ppvResult)
839 {
840     ok(0, "unexpected call\n");
841     return E_NOTIMPL;
842 }
843
844 static HRESULT WINAPI Moniker_BindToStorage(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
845         REFIID riid, void **ppv)
846 {
847     IUnknown *unk;
848     HRESULT hres;
849
850     static OLECHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
851
852     CHECK_EXPECT(BindToStorage);
853
854     ok(pbc == _bctx, "pbc != _bctx\n");
855     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
856     ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", debugstr_guid(riid));
857     ok(ppv != NULL, "ppv == NULL\n");
858     ok(*ppv == NULL, "*ppv=%p\n", *ppv);
859
860     hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
861     ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres);
862     ok(unk != NULL, "unk == NULL\n");
863
864     IUnknown_Release(unk);
865
866     return S_OK;
867 }
868
869 static HRESULT WINAPI Moniker_Reduce(IMoniker *iface, IBindCtx *pbc, DWORD dwReduceHowFar,
870         IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
871 {
872     ok(0, "unexpected call\n");
873     return E_NOTIMPL;
874 }
875
876 static HRESULT WINAPI Moniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
877         BOOL fOnlyIfNotGeneric, IMoniker **ppnkComposite)
878 {
879     ok(0, "unexpected call\n");
880     return E_NOTIMPL;
881 }
882
883 static HRESULT WINAPI Moniker_Enum(IMoniker *iface, BOOL fForwrd, IEnumMoniker **ppenumMoniker)
884 {
885     ok(0, "unexpected call\n");
886     return E_NOTIMPL;
887 }
888
889 static HRESULT WINAPI Moniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
890 {
891     ok(0, "unexpected call\n");
892     return E_NOTIMPL;
893 }
894
895 static HRESULT WINAPI Moniker_Hash(IMoniker *iface, DWORD *pdwHash)
896 {
897     ok(0, "unexpected call\n");
898     return E_NOTIMPL;
899 }
900
901 static HRESULT WINAPI Moniker_IsRunning(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
902         IMoniker *pmkNewlyRunning)
903 {
904     ok(0, "unexpected call\n");
905     return E_NOTIMPL;
906 }
907
908 static HRESULT WINAPI Moniker_GetTimeOfLastChange(IMoniker *iface, IBindCtx *pbc,
909         IMoniker *pmkToLeft, FILETIME *pFileTime)
910 {
911     ok(0, "unexpected call\n");
912     return E_NOTIMPL;
913 }
914
915 static HRESULT WINAPI Moniker_Inverse(IMoniker *iface, IMoniker **ppmk)
916 {
917     ok(0, "unexpected call\n");
918     return E_NOTIMPL;
919 }
920
921 static HRESULT WINAPI Moniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther,
922         IMoniker **ppmkPrefix)
923 {
924     ok(0, "unexpected call\n");
925     return E_NOTIMPL;
926 }
927
928 static HRESULT WINAPI Moniker_RelativePathTo(IMoniker *iface, IMoniker *pmkOther,
929         IMoniker **pmkRelPath)
930 {
931     ok(0, "unexpected call\n");
932     return E_NOTIMPL;
933 }
934
935 static HRESULT WINAPI Moniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc,
936         IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
937 {
938     static const WCHAR winehq_urlW[] =
939             {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
940              '/','s','i','t','e','/','a','b','o','u','t',0};
941
942     CHECK_EXPECT(GetDisplayName);
943
944     ok(pbc != NULL, "pbc == NULL\n");
945     ok(pbc != _bctx, "pbc == _bctx\n");
946     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
947
948     *ppszDisplayName = CoTaskMemAlloc(sizeof(winehq_urlW));
949     memcpy(*ppszDisplayName, winehq_urlW, sizeof(winehq_urlW));
950     return S_OK;
951 }
952
953 static HRESULT WINAPI Moniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc,
954         IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
955 {
956     ok(0, "unexpected call\n");
957     return E_NOTIMPL;
958 }
959
960 static HRESULT WINAPI Moniker_IsSystemMoniker(IMoniker *iface, DWORD *pdwMksys)
961 {
962     CHECK_EXPECT2(IsSystemMoniker);
963
964     *pdwMksys = MKSYS_URLMONIKER;
965     return S_OK;
966 }
967
968 static IMonikerVtbl MonikerVtbl = {
969     Moniker_QueryInterface,
970     Moniker_AddRef,
971     Moniker_Release,
972     Moniker_GetClassID,
973     Moniker_IsDirty,
974     Moniker_Load,
975     Moniker_Save,
976     Moniker_GetSizeMax,
977     Moniker_BindToObject,
978     Moniker_BindToStorage,
979     Moniker_Reduce,
980     Moniker_ComposeWith,
981     Moniker_Enum,
982     Moniker_IsEqual,
983     Moniker_Hash,
984     Moniker_IsRunning,
985     Moniker_GetTimeOfLastChange,
986     Moniker_Inverse,
987     Moniker_CommonPrefixWith,
988     Moniker_RelativePathTo,
989     Moniker_GetDisplayName,
990     Moniker_ParseDisplayName,
991     Moniker_IsSystemMoniker
992 };
993
994 static IMoniker Moniker = { &MonikerVtbl };
995
996 static void test_HlinkResolveMonikerForData(void)
997 {
998     IBindCtx *bctx;
999     HRESULT hres;
1000
1001     CreateBindCtx(0, &bctx);
1002     _bctx = bctx;
1003
1004     SET_EXPECT(IsSystemMoniker);
1005     SET_EXPECT(GetDisplayName);
1006     SET_EXPECT(BindToStorage);
1007
1008     hres = HlinkResolveMonikerForData(&Moniker, 0, bctx, 0, NULL, &BindStatusCallback, NULL);
1009     ok(hres == S_OK, "HlinkResolveMonikerForData failed: %08x\n", hres);
1010
1011     CHECK_CALLED(IsSystemMoniker);
1012     CHECK_CALLED(GetDisplayName);
1013     CHECK_CALLED(BindToStorage);
1014
1015     IBindCtx_Release(bctx);
1016 }
1017
1018 static void test_HlinkGetSetMonikerReference(void)
1019 {
1020     IMoniker *found_trgt, *dummy, *dummy2;
1021     IHlink *hlink;
1022     HRESULT hres;
1023     const WCHAR one[] = {'1',0};
1024     const WCHAR two[] = {'2',0};
1025     const WCHAR name[] = {'a',0};
1026     WCHAR *found_loc;
1027
1028     /* create two dummy monikers to use as targets */
1029     hres = CreateItemMoniker(one, one, &dummy);
1030     ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
1031
1032     hres = CreateItemMoniker(two, two, &dummy2);
1033     ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
1034
1035     /* create a new hlink: target => dummy, location => one */
1036     hres = HlinkCreateFromMoniker(dummy, one, name, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
1037     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
1038
1039     /* validate the target and location */
1040     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1041     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1042     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1043     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1044     IMoniker_Release(found_trgt);
1045     CoTaskMemFree(found_loc);
1046
1047     /* set location => two */
1048     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_LOCATION, dummy2, two);
1049     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1050
1051     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1052     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1053     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
1054     IMoniker_Release(found_trgt);
1055     CoTaskMemFree(found_loc);
1056
1057     /* set target => dummy2 */
1058     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET, dummy2, one);
1059     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1060
1061     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1062     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
1063     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
1064     IMoniker_Release(found_trgt);
1065     CoTaskMemFree(found_loc);
1066
1067     /* set target => dummy, location => one */
1068     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, dummy, one);
1069     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1070
1071     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1072     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1073     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1074     IMoniker_Release(found_trgt);
1075     CoTaskMemFree(found_loc);
1076
1077     /* no HLINKSETF flags */
1078     hres = IHlink_SetMonikerReference(hlink, 0, dummy2, two);
1079     ok(hres == E_INVALIDARG, "IHlink_SetMonikerReference should've failed with E_INVALIDARG (0x%08x), failed with 0x%08x\n", E_INVALIDARG, hres);
1080
1081     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1082     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1083     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1084     IMoniker_Release(found_trgt);
1085     CoTaskMemFree(found_loc);
1086
1087     /* invalid HLINKSETF flags */
1088     hres = IHlink_SetMonikerReference(hlink, 12, dummy2, two);
1089     ok(hres == 12, "IHlink_SetMonikerReference should've failed with 0x%08x, failed with 0x%08x\n", 12, hres);
1090
1091     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1092     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1093     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1094     IMoniker_Release(found_trgt);
1095     CoTaskMemFree(found_loc);
1096
1097     /* valid & invalid HLINKSETF flags */
1098     hres = IHlink_SetMonikerReference(hlink, 12 | HLINKSETF_TARGET, dummy2, two);
1099     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1100
1101     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1102     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
1103     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1104     IMoniker_Release(found_trgt);
1105     CoTaskMemFree(found_loc);
1106
1107     /* NULL args */
1108     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, NULL, NULL);
1109     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1110
1111     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1112     ok(found_trgt == NULL, "Found target should've been %p, was: %p\n", NULL, found_trgt);
1113     ok(found_loc == NULL, "Found location should've been %s, was: %s\n", wine_dbgstr_w(NULL), wine_dbgstr_w(found_loc));
1114     if(found_trgt)
1115         IMoniker_Release(found_trgt);
1116
1117     IHlink_Release(hlink);
1118     IMoniker_Release(dummy2);
1119     IMoniker_Release(dummy);
1120 }
1121
1122 static void test_HlinkGetSetStringReference(void)
1123 {
1124     IHlink *link;
1125     static const WCHAR one[] = {'1',0};
1126     static const WCHAR two[] = {'2',0};
1127     static const WCHAR three[] = {'3',0};
1128     static const WCHAR empty[] = {0};
1129     WCHAR *fnd_tgt, *fnd_loc;
1130     HRESULT hres;
1131
1132     /* create a new hlink: target => NULL, location => one */
1133     hres = HlinkCreateFromMoniker(NULL, one, empty, NULL, 0, NULL, &IID_IHlink, (void**)&link);
1134     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
1135
1136     /* test setting/getting location */
1137     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1138     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1139     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1140     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1141
1142     hres = IHlink_SetStringReference(link, HLINKSETF_LOCATION, one, two);
1143     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1144
1145     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1146     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1147     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1148     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1149
1150     hres = IHlink_SetStringReference(link, -HLINKSETF_LOCATION, two, one);
1151     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1152
1153     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1154     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1155     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1156     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1157
1158     /* test setting/getting target */
1159     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET, two, three);
1160     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1161
1162     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1163     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1164     ok(!lstrcmpW(fnd_tgt, two), "Found target should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_tgt));
1165     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1166
1167     hres = IHlink_SetStringReference(link, -HLINKSETF_TARGET, three, two);
1168     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1169
1170     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1171     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1172     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1173     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1174
1175     /* test setting/getting both */
1176     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET | HLINKSETF_LOCATION, one, two);
1177     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1178
1179     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1180     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1181     ok(!lstrcmpW(fnd_tgt, one), "Found target should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_tgt));
1182     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1183
1184     hres = IHlink_SetStringReference(link, -(HLINKSETF_TARGET | HLINKSETF_LOCATION), three, one);
1185     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1186
1187     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1188     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1189     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1190     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1191
1192     /* test invalid flags/params */
1193     hres = IHlink_GetStringReference(link, 4, &fnd_tgt, &fnd_loc);
1194     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1195            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1196     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1197     ok(fnd_loc == NULL, "Found location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1198
1199     hres = IHlink_GetStringReference(link, -1, &fnd_tgt, &fnd_loc);
1200     todo_wine ok(hres == E_FAIL, "IHlink_GetStringReference should have failed "
1201            "with E_FAIL (0x%08x), instead: 0x%08x\n", E_FAIL, hres);
1202
1203     hres = IHlink_GetStringReference(link, -2, &fnd_tgt, &fnd_loc);
1204     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1205            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1206
1207     hres = IHlink_SetStringReference(link, 4, NULL, NULL);
1208     ok(hres == 4, "IHlink_SetStringReference should have failed with 0x4, instead: 0x%08x\n", hres);
1209
1210     hres = IHlink_SetStringReference(link, -4, NULL, NULL);
1211     ok(hres == -4, "IHlink_SetStringReference should have failed with 0xFFFFFFFC, instead: 0x%08x\n", hres);
1212
1213     IHlink_Release(link);
1214 }
1215
1216 #define setStringRef(h,f,t,l) r_setStringRef(__LINE__,h,f,t,l)
1217 static void r_setStringRef(unsigned line, IHlink *hlink, DWORD flags, const WCHAR *tgt, const WCHAR *loc)
1218 {
1219     HRESULT hres;
1220     hres = IHlink_SetStringReference(hlink, flags, tgt, loc);
1221     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1222 }
1223
1224 #define getStringRef(h,t,l) r_getStringRef(__LINE__,h,t,l)
1225 static void r_getStringRef(unsigned line, IHlink *hlink, const WCHAR *exp_tgt, const WCHAR *exp_loc)
1226 {
1227     HRESULT hres;
1228     WCHAR *fnd_tgt, *fnd_loc;
1229
1230     hres = IHlink_GetStringReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1231     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1232
1233     if(exp_tgt)
1234         ok_(__FILE__,line) (!lstrcmpW(fnd_tgt, exp_tgt), "Found string target should have been %s, was: %s\n", wine_dbgstr_w(exp_tgt), wine_dbgstr_w(fnd_tgt));
1235     else
1236         ok_(__FILE__,line) (exp_tgt == NULL, "Found string target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1237
1238     if(exp_loc)
1239         ok_(__FILE__,line) (!lstrcmpW(fnd_loc, exp_loc), "Found string location should have been %s, was: %s\n", wine_dbgstr_w(exp_loc), wine_dbgstr_w(fnd_loc));
1240     else
1241         ok_(__FILE__,line) (exp_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1242
1243     CoTaskMemFree(fnd_tgt);
1244     CoTaskMemFree(fnd_loc);
1245 }
1246
1247 #define setMonikerRef(h,f,t,l) r_setMonikerRef(__LINE__,h,f,t,l)
1248 static void r_setMonikerRef(unsigned line, IHlink *hlink, DWORD flags, IMoniker *tgt, const WCHAR *loc)
1249 {
1250     HRESULT hres;
1251     hres = IHlink_SetMonikerReference(hlink, flags, tgt, loc);
1252     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1253 }
1254
1255 /* passing 0xFFFFFFFF as exp_tgt will return the retrieved target & not test it */
1256 #define getMonikerRef(h,t,l) r_getMonikerRef(__LINE__,h,t,l)
1257 static IMoniker *r_getMonikerRef(unsigned line, IHlink *hlink, IMoniker *exp_tgt, const WCHAR *exp_loc)
1258 {
1259     HRESULT hres;
1260     IMoniker *fnd_tgt;
1261     WCHAR *fnd_loc;
1262
1263     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1264     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1265
1266     if(exp_loc)
1267         ok_(__FILE__,line) (!lstrcmpW(fnd_loc, exp_loc), "Found string location should have been %s, was: %s\n", wine_dbgstr_w(exp_loc), wine_dbgstr_w(fnd_loc));
1268     else
1269         ok_(__FILE__,line) (exp_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1270
1271     CoTaskMemFree(fnd_loc);
1272
1273     if(exp_tgt == (IMoniker*)0xFFFFFFFF)
1274         return fnd_tgt;
1275
1276     ok_(__FILE__,line) (fnd_tgt == exp_tgt, "Found moniker target should have been %p, was: %p\n", exp_tgt, fnd_tgt);
1277
1278     if(fnd_tgt)
1279         IMoniker_Release(fnd_tgt);
1280
1281     return NULL;
1282 }
1283
1284 static void test_HlinkMoniker(void)
1285 {
1286     IHlink *hlink;
1287     IMoniker *aMon, *file_mon;
1288     static const WCHAR emptyW[] = {0};
1289     static const WCHAR wordsW[] = {'w','o','r','d','s',0};
1290     static const WCHAR aW[] = {'a',0};
1291     static const WCHAR bW[] = {'b',0};
1292     HRESULT hres;
1293
1294     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
1295     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1296     getStringRef(hlink, NULL, NULL);
1297     getMonikerRef(hlink, NULL, NULL);
1298
1299     /* setting a string target creates a moniker reference */
1300     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, aW, wordsW);
1301     getStringRef(hlink, aW, wordsW);
1302     aMon = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, wordsW);
1303     ok(aMon != NULL, "Moniker from %s target should not be NULL\n", wine_dbgstr_w(aW));
1304     if(aMon)
1305         IMoniker_Release(aMon);
1306
1307     /* setting target & location to the empty string deletes the moniker
1308      * reference */
1309     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, emptyW, emptyW);
1310     getStringRef(hlink, NULL, NULL);
1311     getMonikerRef(hlink, NULL, NULL);
1312
1313     /* setting a moniker target also sets the target string to that moniker's
1314      * display name */
1315     hres = CreateFileMoniker(bW, &file_mon);
1316     ok(hres == S_OK, "CreateFileMoniker failed: 0x%08x\n", hres);
1317
1318     setMonikerRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, file_mon, wordsW);
1319     getStringRef(hlink, bW, wordsW);
1320     getMonikerRef(hlink, file_mon, wordsW);
1321
1322     IMoniker_Release(file_mon);
1323
1324     IHlink_Release(hlink);
1325 }
1326
1327 START_TEST(hlink)
1328 {
1329     CoInitialize(NULL);
1330
1331     test_HlinkIsShortcut();
1332     test_reference();
1333     test_persist();
1334     test_special_reference();
1335     test_HlinkCreateExtensionServices();
1336     test_HlinkParseDisplayName();
1337     test_HlinkResolveMonikerForData();
1338     test_HlinkGetSetMonikerReference();
1339     test_HlinkGetSetStringReference();
1340     test_HlinkMoniker();
1341
1342     CoUninitialize();
1343 }