oleaut32: Rewrite RollUdate to be easier to change and to support more conversions.
[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     CoTaskMemFree(fnd_tgt);
1142     CoTaskMemFree(fnd_loc);
1143
1144     hres = IHlink_SetStringReference(link, HLINKSETF_LOCATION, one, two);
1145     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1146
1147     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1148     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1149     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1150     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1151     CoTaskMemFree(fnd_tgt);
1152     CoTaskMemFree(fnd_loc);
1153
1154     hres = IHlink_SetStringReference(link, -HLINKSETF_LOCATION, two, one);
1155     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1156
1157     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1158     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1159     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1160     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1161     CoTaskMemFree(fnd_tgt);
1162     CoTaskMemFree(fnd_loc);
1163
1164     /* test setting/getting target */
1165     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET, two, three);
1166     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1167
1168     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1169     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1170     ok(!lstrcmpW(fnd_tgt, two), "Found target should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_tgt));
1171     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1172     CoTaskMemFree(fnd_tgt);
1173     CoTaskMemFree(fnd_loc);
1174
1175     hres = IHlink_SetStringReference(link, -HLINKSETF_TARGET, three, two);
1176     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1177
1178     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1179     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1180     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1181     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1182     CoTaskMemFree(fnd_tgt);
1183     CoTaskMemFree(fnd_loc);
1184
1185     /* test setting/getting both */
1186     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET | HLINKSETF_LOCATION, one, two);
1187     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1188
1189     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1190     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1191     ok(!lstrcmpW(fnd_tgt, one), "Found target should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_tgt));
1192     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1193     CoTaskMemFree(fnd_tgt);
1194     CoTaskMemFree(fnd_loc);
1195
1196     hres = IHlink_SetStringReference(link, -(HLINKSETF_TARGET | HLINKSETF_LOCATION), three, one);
1197     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1198
1199     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1200     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1201     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1202     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1203     CoTaskMemFree(fnd_tgt);
1204     CoTaskMemFree(fnd_loc);
1205
1206     /* test invalid flags/params */
1207     hres = IHlink_GetStringReference(link, 4, &fnd_tgt, &fnd_loc);
1208     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1209            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1210     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1211     ok(fnd_loc == NULL, "Found location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1212     CoTaskMemFree(fnd_tgt);
1213     CoTaskMemFree(fnd_loc);
1214
1215     hres = IHlink_GetStringReference(link, -1, &fnd_tgt, &fnd_loc);
1216     todo_wine ok(hres == E_FAIL, "IHlink_GetStringReference should have failed "
1217            "with E_FAIL (0x%08x), instead: 0x%08x\n", E_FAIL, hres);
1218     CoTaskMemFree(fnd_tgt);
1219     CoTaskMemFree(fnd_loc);
1220
1221     hres = IHlink_GetStringReference(link, -2, &fnd_tgt, &fnd_loc);
1222     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1223            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1224     CoTaskMemFree(fnd_tgt);
1225     CoTaskMemFree(fnd_loc);
1226
1227     hres = IHlink_SetStringReference(link, 4, NULL, NULL);
1228     ok(hres == 4, "IHlink_SetStringReference should have failed with 0x4, instead: 0x%08x\n", hres);
1229
1230     hres = IHlink_SetStringReference(link, -4, NULL, NULL);
1231     ok(hres == -4, "IHlink_SetStringReference should have failed with 0xFFFFFFFC, instead: 0x%08x\n", hres);
1232
1233     IHlink_Release(link);
1234 }
1235
1236 #define setStringRef(h,f,t,l) r_setStringRef(__LINE__,h,f,t,l)
1237 static void r_setStringRef(unsigned line, IHlink *hlink, DWORD flags, const WCHAR *tgt, const WCHAR *loc)
1238 {
1239     HRESULT hres;
1240     hres = IHlink_SetStringReference(hlink, flags, tgt, loc);
1241     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1242 }
1243
1244 #define getStringRef(h,t,l) r_getStringRef(__LINE__,h,t,l)
1245 static void r_getStringRef(unsigned line, IHlink *hlink, const WCHAR *exp_tgt, const WCHAR *exp_loc)
1246 {
1247     HRESULT hres;
1248     WCHAR *fnd_tgt, *fnd_loc;
1249
1250     hres = IHlink_GetStringReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1251     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1252
1253     if(exp_tgt)
1254         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));
1255     else
1256         ok_(__FILE__,line) (exp_tgt == NULL, "Found string target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1257
1258     if(exp_loc)
1259         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));
1260     else
1261         ok_(__FILE__,line) (exp_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1262
1263     CoTaskMemFree(fnd_tgt);
1264     CoTaskMemFree(fnd_loc);
1265 }
1266
1267 #define setMonikerRef(h,f,t,l) r_setMonikerRef(__LINE__,h,f,t,l)
1268 static void r_setMonikerRef(unsigned line, IHlink *hlink, DWORD flags, IMoniker *tgt, const WCHAR *loc)
1269 {
1270     HRESULT hres;
1271     hres = IHlink_SetMonikerReference(hlink, flags, tgt, loc);
1272     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1273 }
1274
1275 /* passing 0xFFFFFFFF as exp_tgt will return the retrieved target & not test it */
1276 #define getMonikerRef(h,t,l) r_getMonikerRef(__LINE__,h,t,l)
1277 static IMoniker *r_getMonikerRef(unsigned line, IHlink *hlink, IMoniker *exp_tgt, const WCHAR *exp_loc)
1278 {
1279     HRESULT hres;
1280     IMoniker *fnd_tgt;
1281     WCHAR *fnd_loc;
1282
1283     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1284     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1285
1286     if(exp_loc)
1287         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));
1288     else
1289         ok_(__FILE__,line) (exp_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1290
1291     CoTaskMemFree(fnd_loc);
1292
1293     if(exp_tgt == (IMoniker*)0xFFFFFFFF)
1294         return fnd_tgt;
1295
1296     ok_(__FILE__,line) (fnd_tgt == exp_tgt, "Found moniker target should have been %p, was: %p\n", exp_tgt, fnd_tgt);
1297
1298     if(fnd_tgt)
1299         IMoniker_Release(fnd_tgt);
1300
1301     return NULL;
1302 }
1303
1304 static void test_HlinkMoniker(void)
1305 {
1306     IHlink *hlink;
1307     IMoniker *aMon, *file_mon;
1308     static const WCHAR emptyW[] = {0};
1309     static const WCHAR wordsW[] = {'w','o','r','d','s',0};
1310     static const WCHAR aW[] = {'a',0};
1311     static const WCHAR bW[] = {'b',0};
1312     HRESULT hres;
1313
1314     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
1315     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1316     getStringRef(hlink, NULL, NULL);
1317     getMonikerRef(hlink, NULL, NULL);
1318
1319     /* setting a string target creates a moniker reference */
1320     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, aW, wordsW);
1321     getStringRef(hlink, aW, wordsW);
1322     aMon = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, wordsW);
1323     ok(aMon != NULL, "Moniker from %s target should not be NULL\n", wine_dbgstr_w(aW));
1324     if(aMon)
1325         IMoniker_Release(aMon);
1326
1327     /* setting target & location to the empty string deletes the moniker
1328      * reference */
1329     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, emptyW, emptyW);
1330     getStringRef(hlink, NULL, NULL);
1331     getMonikerRef(hlink, NULL, NULL);
1332
1333     /* setting a moniker target also sets the target string to that moniker's
1334      * display name */
1335     hres = CreateFileMoniker(bW, &file_mon);
1336     ok(hres == S_OK, "CreateFileMoniker failed: 0x%08x\n", hres);
1337
1338     setMonikerRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, file_mon, wordsW);
1339     getStringRef(hlink, bW, wordsW);
1340     getMonikerRef(hlink, file_mon, wordsW);
1341
1342     IMoniker_Release(file_mon);
1343
1344     IHlink_Release(hlink);
1345 }
1346
1347 START_TEST(hlink)
1348 {
1349     CoInitialize(NULL);
1350
1351     test_HlinkIsShortcut();
1352     test_reference();
1353     test_persist();
1354     test_special_reference();
1355     test_HlinkCreateExtensionServices();
1356     test_HlinkParseDisplayName();
1357     test_HlinkResolveMonikerForData();
1358     test_HlinkGetSetMonikerReference();
1359     test_HlinkGetSetStringReference();
1360     test_HlinkMoniker();
1361
1362     CoUninitialize();
1363 }