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(BindToObject);
59 DEFINE_EXPECT(GetDisplayName);
60 
61 DEFINE_EXPECT(ComposeWith);
62 DEFINE_EXPECT(OnNavigationComplete);
63 DEFINE_EXPECT(Enum);
64 DEFINE_EXPECT(Reduce);
65 
66 DEFINE_EXPECT(GetClassID);
67 DEFINE_EXPECT(Save);
68 
69 DEFINE_EXPECT(HBC_QueryInterface_IHlinkHistory);
70 DEFINE_EXPECT(HBC_QueryInterface_IMarshal);
71 DEFINE_EXPECT(HBC_QueryInterface_IdentityUnmarshal);
72 DEFINE_EXPECT(HBC_QueryInterface_IUnknown);
73 DEFINE_EXPECT(HBC_GetObject);
74 DEFINE_EXPECT(HBC_UpdateHlink);
75 
76 DEFINE_EXPECT(HT_QueryInterface_IHlinkTarget);
77 DEFINE_EXPECT(HT_SetBrowseContext);
78 DEFINE_EXPECT(HT_GetBrowseContext);
79 DEFINE_EXPECT(HT_Navigate);
80 DEFINE_EXPECT(HT_GetFriendlyName);
81 
82 DEFINE_EXPECT(HLF_UpdateHlink);
83 
84 DEFINE_GUID(CLSID_IdentityUnmarshal,0x0000001b,0x0000,0x0000,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
85 DEFINE_GUID(IID_IHlinkHistory,0x79eac9c8,0xbaf9,0x11ce,0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b);
86 
87 static const WCHAR winehq_urlW[] =
88         {'h','t','t','p',':','/','/','t','e','s','t','.','w','i','n','e','h','q','.','o','r','g',
89          '/','t','e','s','t','s','/','h','e','l','l','o','.','h','t','m','l',0};
90 static const WCHAR winehq_404W[] =
91         {'h','t','t','p',':','/','/','t','e','s','t','.','w','i','n','e','h','q','.','o','r','g',
92          '/','t','e','s','t','s','/','f','a','k','e','u','r','l',0};
93 
94 static void test_HlinkIsShortcut(void)
95 {
96     UINT i;
97     HRESULT hres;
98 
99     static const WCHAR file0[] = {'f','i','l','e',0};
100     static const WCHAR file1[] = {'f','i','l','e','.','u','r','l',0};
101     static const WCHAR file2[] = {'f','i','l','e','.','l','n','k',0};
102     static const WCHAR file3[] = {'f','i','l','e','.','u','R','l',0};
103     static const WCHAR file4[] = {'f','i','l','e','u','r','l',0};
104     static const WCHAR file5[] = {'c',':','\\','f','i','l','e','.','u','r','l',0};
105     static const WCHAR file6[] = {'c',':','\\','f','i','l','e','.','l','n','k',0};
106     static const WCHAR file7[] = {'.','u','r','l',0};
107 
108     static struct {
109         LPCWSTR file;
110         HRESULT hres;
111     } shortcut_test[] = {
112         {file0, S_FALSE},
113         {file1, S_OK},
114         {file2, S_FALSE},
115         {file3, S_OK},
116         {file4, S_FALSE},
117         {file5, S_OK},
118         {file6, S_FALSE},
119         {file7, S_OK},
120         {NULL,  E_INVALIDARG}
121     };
122 
123     for(i=0; i<sizeof(shortcut_test)/sizeof(shortcut_test[0]); i++) {
124         hres = HlinkIsShortcut(shortcut_test[i].file);
125         ok(hres == shortcut_test[i].hres, "[%d] HlinkIsShortcut returned %08x, expected %08x\n",
126            i, hres, shortcut_test[i].hres);
127     }
128 }
129 
130 static void test_reference(void)
131 {
132     HRESULT r;
133     IHlink *lnk = NULL;
134     IMoniker *mk = NULL;
135     const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
136     const WCHAR url2[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g','/',0 };
137     LPWSTR str = NULL;
138 
139     r = HlinkCreateFromString(url, NULL, NULL, NULL,
140                               0, NULL, &IID_IHlink, (LPVOID*) &lnk);
141     ok(r == S_OK, "failed to create link\n");
142     if (FAILED(r))
143         return;
144 
145     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
146     ok(r == S_OK, "failed\n");
147 
148     r = IHlink_GetMonikerReference(lnk, HLINKGETREF_DEFAULT, &mk, &str);
149     ok(r == S_OK, "failed\n");
150     ok(mk != NULL, "no moniker\n");
151     ok(str == NULL, "string should be null\n");
152 
153     r = IMoniker_Release(mk);
154     ok( r == 1, "moniker refcount wrong\n");
155 
156     r = IHlink_GetStringReference(lnk, -1, &str, NULL);
157     ok(r == S_OK, "failed\n");
158     CoTaskMemFree(str);
159 
160     r = IHlink_GetStringReference(lnk, -1, NULL, NULL);
161     ok(r == S_OK, "failed, r=%08x\n", r);
162 
163     r = IHlink_GetStringReference(lnk, -1, NULL, &str);
164     ok(r == S_OK, "failed, r=%08x\n", r);
165     ok(str == NULL, "string should be null\n");
166 
167     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, &str, NULL);
168     ok(r == S_OK, "failed\n");
169     ok(!lstrcmpW(str, url2), "url wrong\n");
170     CoTaskMemFree(str);
171 
172     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, NULL);
173     ok(r == S_OK, "failed\n");
174 
175     r = IHlink_GetStringReference(lnk, HLINKGETREF_DEFAULT, NULL, &str);
176     ok(r == S_OK, "failed\n");
177     ok(str == NULL, "string should be null\n");
178 
179     /* Unimplented functions checks */
180     r = IHlink_GetAdditionalParams(lnk, NULL);
181     ok(r == E_NOTIMPL, "failed\n");
182 
183     r = IHlink_SetAdditionalParams(lnk, NULL);
184     ok(r == E_NOTIMPL, "failed\n");
185 
186     IHlink_Release(lnk);
187 }
188 
189 /* url only */
190 static const unsigned char expected_hlink_data[] =
191 {
192     0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
193     0xe0,0xc9,0xea,0x79,0xf9,0xba,0xce,0x11,
194     0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b,
195     0x26,0x00,0x00,0x00,0x68,0x00,0x74,0x00,
196     0x74,0x00,0x70,0x00,0x3a,0x00,0x2f,0x00,
197     0x2f,0x00,0x77,0x00,0x69,0x00,0x6e,0x00,
198     0x65,0x00,0x68,0x00,0x71,0x00,0x2e,0x00,
199     0x6f,0x00,0x72,0x00,0x67,0x00,0x2f,0x00,
200     0x00,0x00,
201 };
202 
203 /* url only (IE7) */
204 static const unsigned char expected_hlink_data_ie7[] =
205 {
206     0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
207     0xe0,0xc9,0xea,0x79,0xf9,0xba,0xce,0x11,
208     0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b,
209     0x3e,0x00,0x00,0x00,0x68,0x00,0x74,0x00,
210     0x74,0x00,0x70,0x00,0x3a,0x00,0x2f,0x00,
211     0x2f,0x00,0x77,0x00,0x69,0x00,0x6e,0x00,
212     0x65,0x00,0x68,0x00,0x71,0x00,0x2e,0x00,
213     0x6f,0x00,0x72,0x00,0x67,0x00,0x2f,0x00,
214     0x00,0x00,0x79,0x58,0x81,0xf4,0x3b,0x1d,
215     0x7f,0x48,0xaf,0x2c,0x82,0x5d,0xc4,0x85,
216     0x27,0x63,0x00,0x00,0x00,0x00,0xa5,0xab,
217     0x00,0x00,
218 };
219 
220 /* url + friendly name */
221 static const unsigned char expected_hlink_data2[] =
222 {
223     0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,
224     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
225     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
226     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
227     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
228     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
229     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
230     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
231     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
232     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
233     0x67,0x00,0x2f,0x00,0x00,0x00,
234 };
235 
236 /* url + friendly name (IE7) */
237 static const unsigned char expected_hlink_data2_ie7[] =
238 {
239     0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,
240     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
241     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
242     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
243     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
244     0x00,0x4b,0xa9,0x0b,0x3e,0x00,0x00,0x00,
245     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
246     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
247     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
248     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
249     0x67,0x00,0x2f,0x00,0x00,0x00,0x79,0x58,
250     0x81,0xf4,0x3b,0x1d,0x7f,0x48,0xaf,0x2c,
251     0x82,0x5d,0xc4,0x85,0x27,0x63,0x00,0x00,
252     0x00,0x00,0xa5,0xab,0x00,0x00,
253 };
254 
255 /* url + friendly name + location */
256 static const unsigned char expected_hlink_data3[] =
257 {
258     0x02,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
259     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
260     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
261     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
262     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
263     0x00,0x4b,0xa9,0x0b,0x26,0x00,0x00,0x00,
264     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
265     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
266     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
267     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
268     0x67,0x00,0x2f,0x00,0x00,0x00,0x07,0x00,
269     0x00,0x00,0x5f,0x00,0x62,0x00,0x6c,0x00,
270     0x61,0x00,0x6e,0x00,0x6b,0x00,0x00,0x00,
271 };
272 
273 /* url + friendly name + location (IE7) */
274 static const unsigned char expected_hlink_data3_ie7[] =
275 {
276     0x02,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
277     0x08,0x00,0x00,0x00,0x57,0x00,0x69,0x00,
278     0x6e,0x00,0x65,0x00,0x20,0x00,0x48,0x00,
279     0x51,0x00,0x00,0x00,0xe0,0xc9,0xea,0x79,
280     0xf9,0xba,0xce,0x11,0x8c,0x82,0x00,0xaa,
281     0x00,0x4b,0xa9,0x0b,0x3e,0x00,0x00,0x00,
282     0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,
283     0x3a,0x00,0x2f,0x00,0x2f,0x00,0x77,0x00,
284     0x69,0x00,0x6e,0x00,0x65,0x00,0x68,0x00,
285     0x71,0x00,0x2e,0x00,0x6f,0x00,0x72,0x00,
286     0x67,0x00,0x2f,0x00,0x00,0x00,0x79,0x58,
287     0x81,0xf4,0x3b,0x1d,0x7f,0x48,0xaf,0x2c,
288     0x82,0x5d,0xc4,0x85,0x27,0x63,0x00,0x00,
289     0x00,0x00,0xa5,0xab,0x00,0x00,0x07,0x00,
290     0x00,0x00,0x5f,0x00,0x62,0x00,0x6c,0x00,
291     0x61,0x00,0x6e,0x00,0x6b,0x00,0x00,0x00,
292 };
293 
294 /* relative url */
295 static const unsigned char expected_hlink_data4[] =
296 {
297     0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
298     0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
299     0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
300     0x00,0x00,0x0b,0x00,0x00,0x00,0x69,0x6e,
301     0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,
302     0x00,0xff,0xff,0xad,0xde,0x00,0x00,0x00,
303     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
304     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
305     0x00,0x00,0x00,0x00,0x00,
306 };
307 
308 /* url + target frame name */
309 static const unsigned char expected_hlink_data5[] =
310 {
311     0x02,0x00,0x00,0x00,0x83,0x00,0x00,0x00,
312     0x07,0x00,0x00,0x00,0x74,0x00,0x67,0x00,
313     0x74,0x00,0x66,0x00,0x72,0x00,0x6d,0x00,
314     0x00,0x00,0xe0,0xc9,0xea,0x79,0xf9,0xba,
315     0xce,0x11,0x8c,0x82,0x00,0xaa,0x00,0x4b,
316     0xa9,0x0b,0x26,0x00,0x00,0x00,0x68,0x00,
317     0x74,0x00,0x74,0x00,0x70,0x00,0x3a,0x00,
318     0x2f,0x00,0x2f,0x00,0x77,0x00,0x69,0x00,
319     0x6e,0x00,0x65,0x00,0x68,0x00,0x71,0x00,
320     0x2e,0x00,0x6f,0x00,0x72,0x00,0x67,0x00,
321     0x2f,0x00,0x00,0x00,
322 };
323 
324 /* url + target frame name (IE7) */
325 static const unsigned char expected_hlink_data5_ie7[] =
326 {
327     0x02,0x00,0x00,0x00,0x83,0x00,0x00,0x00,
328     0x07,0x00,0x00,0x00,0x74,0x00,0x67,0x00,
329     0x74,0x00,0x66,0x00,0x72,0x00,0x6d,0x00,
330     0x00,0x00,0xe0,0xc9,0xea,0x79,0xf9,0xba,
331     0xce,0x11,0x8c,0x82,0x00,0xaa,0x00,0x4b,
332     0xa9,0x0b,0x3e,0x00,0x00,0x00,0x68,0x00,
333     0x74,0x00,0x74,0x00,0x70,0x00,0x3a,0x00,
334     0x2f,0x00,0x2f,0x00,0x77,0x00,0x69,0x00,
335     0x6e,0x00,0x65,0x00,0x68,0x00,0x71,0x00,
336     0x2e,0x00,0x6f,0x00,0x72,0x00,0x67,0x00,
337     0x2f,0x00,0x00,0x00,0x79,0x58,0x81,0xf4,
338     0x3b,0x1d,0x7f,0x48,0xaf,0x2c,0x82,0x5d,
339     0xc4,0x85,0x27,0x63,0x00,0x00,0x00,0x00,
340     0xa5,0xab,0x00,0x00,
341 };
342 
343 /* filename */
344 static const unsigned char expected_hlink_data6[] =
345 {
346      0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
347      0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
348      0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
349      0x00,0x00,0x04,0x00,0x00,0x00,0x63,0x3a,
350      0x5c,0x00,0xff,0xff,0xad,0xde,0x00,0x00,
351      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
352      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
353      0x00,0x00,0x0c,0x00,0x00,0x00,0x06,0x00,
354      0x00,0x00,0x03,0x00,0x63,0x00,0x3a,0x00,
355      0x5c,0x00,
356 };
357 
358 static void test_persist_save_data(const char *testname, IHlink *lnk,
359                                    const unsigned char *expected_data,
360                                    unsigned int expected_data_size,
361                                    const unsigned char *expected_data_alt,
362                                    unsigned int expected_data_alt_size)
363 {
364     HRESULT hr;
365     IStream *stream;
366     IPersistStream *ps;
367     HGLOBAL hglobal;
368     DWORD data_size;
369     const unsigned char *data;
370     DWORD i;
371     BOOL same;
372 
373     hr = IHlink_QueryInterface(lnk, &IID_IPersistStream, (void **)&ps);
374     ok(hr == S_OK, "IHlink_QueryInterface failed with error 0x%08x\n", hr);
375 
376     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
377     ok(hr == S_OK, "CreateStreamOnHGlobal failed with error 0x%08x\n", hr);
378 
379     hr = IPersistStream_Save(ps, stream, TRUE);
380     ok(hr == S_OK, "IPersistStream_Save failed with error 0x%08x\n", hr);
381 
382     hr = GetHGlobalFromStream(stream, &hglobal);
383     ok(hr == S_OK, "GetHGlobalFromStream failed with error 0x%08x\n", hr);
384 
385     data_size = GlobalSize(hglobal);
386 
387     data = GlobalLock(hglobal);
388 
389     /* first check we have the right amount of data */
390     ok((data_size == expected_data_size) ||
391        (data_size == expected_data_alt_size),
392        "%s: Size of saved data differs (expected %d or %d, actual %d)\n",
393        testname, expected_data_size, expected_data_alt_size, data_size);
394 
395     same = TRUE;
396     /* then do a byte-by-byte comparison */
397     for (i = 0; i < min(data_size, expected_data_size); i++)
398     {
399         if ((expected_data[i] != data[i]) &&
400             (((expected_data != expected_hlink_data2) &&
401               (expected_data != expected_hlink_data3)) ||
402              ((i < 52 || i >= 56) && (i < 80 || i >= 84))))
403         {
404             same = FALSE;
405             break;
406         }
407     }
408 
409     if (!same && (expected_data_alt != expected_data))
410     {
411         /* then try the alternate data */
412         same = TRUE;
413         for (i = 0; i < min(data_size, expected_data_alt_size); i++)
414         {
415             if ((expected_data_alt == expected_hlink_data_ie7  && i == 89)  /* Win10 */ ||
416                 (expected_data_alt == expected_hlink_data2_ie7 && i == 109) /* Win10 */ ||
417                 (expected_data_alt == expected_hlink_data3_ie7 && i == 109) /* Win10 */ ||
418                 (expected_data_alt == expected_hlink_data5_ie7 && i == 107) /* Win10 */)
419             {
420                 ok(data[i] == 0 || broken(data[i] == 1) || broken(data[i] == 3),
421                    "Expected 0 or 1, got %d\n", data[i]);
422                 continue;
423             }
424             if ((expected_data_alt[i] != data[i]) &&
425                 (((expected_data_alt != expected_hlink_data2) &&
426                   (expected_data_alt != expected_hlink_data3)) ||
427                  ((i < 52 || i >= 56) && (i < 80 || i >= 84))))
428             {
429                 same = FALSE;
430                 break;
431             }
432         }
433     }
434 
435     ok(same, "%s: Saved data differs\n", testname);
436     if (!same)
437     {
438         for (i = 0; i < data_size; i++)
439         {
440             if (i % 8 == 0) printf("    ");
441             printf("0x%02x,", data[i]);
442             if (i % 8 == 7) printf("\n");
443         }
444         printf("\n");
445     }
446 
447     GlobalUnlock(hglobal);
448 
449     IStream_Release(stream);
450     IPersistStream_Release(ps);
451 }
452 
453 static void test_persist(void)
454 {
455     static const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0 };
456     static const WCHAR rel_url[] = { 'i','n','d','e','x','.','h','t','m','l',0 };
457     static const WCHAR filename[] = { 'c',':','\\',0 };
458     static const WCHAR friendly_name[] = { 'W','i','n','e',' ','H','Q',0 };
459     static const WCHAR location[] = { '_','b','l','a','n','k',0 };
460     static const WCHAR target_frame_name[] = { 't','g','t','f','r','m',0 };
461     HRESULT hr;
462     IHlink *lnk;
463 
464     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
465                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
466     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
467     if (!lnk) {
468         skip("Can't create lnk, skipping test_persist.\n");
469         return;
470     }
471     test_persist_save_data("url only", lnk,
472         expected_hlink_data, sizeof(expected_hlink_data),
473         expected_hlink_data_ie7, sizeof(expected_hlink_data_ie7));
474     IHlink_Release(lnk);
475 
476     hr = HlinkCreateFromString(url, NULL, friendly_name, NULL,
477                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
478     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
479     test_persist_save_data("url + friendly name", lnk,
480         expected_hlink_data2, sizeof(expected_hlink_data2),
481         expected_hlink_data2_ie7, sizeof(expected_hlink_data2_ie7));
482     IHlink_Release(lnk);
483 
484     hr = HlinkCreateFromString(url, location, friendly_name, NULL,
485                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
486     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
487     test_persist_save_data("url + friendly_name + location", lnk,
488         expected_hlink_data3, sizeof(expected_hlink_data3),
489         expected_hlink_data3_ie7, sizeof(expected_hlink_data3_ie7));
490     IHlink_Release(lnk);
491 
492     hr = HlinkCreateFromString(rel_url, NULL, NULL, NULL,
493                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
494     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
495     test_persist_save_data("relative url", lnk,
496         expected_hlink_data4, sizeof(expected_hlink_data4),
497         expected_hlink_data4, sizeof(expected_hlink_data4));
498     IHlink_Release(lnk);
499 
500     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
501                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
502     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
503     hr = IHlink_SetTargetFrameName(lnk, target_frame_name);
504     ok(hr == S_OK, "IHlink_SetTargetFrameName failed with error 0x%08x\n", hr);
505     test_persist_save_data("url + target frame name", lnk,
506         expected_hlink_data5, sizeof(expected_hlink_data5),
507         expected_hlink_data5_ie7, sizeof(expected_hlink_data5_ie7));
508     IHlink_Release(lnk);
509 
510     hr = HlinkCreateFromString(filename, NULL, NULL, NULL,
511                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
512     ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
513     test_persist_save_data("filename", lnk,
514         expected_hlink_data6, sizeof(expected_hlink_data6),
515         expected_hlink_data6, sizeof(expected_hlink_data6));
516     IHlink_Release(lnk);
517 }
518 
519 static void test_special_reference(void)
520 {
521     LPWSTR ref;
522     HRESULT hres;
523 
524     hres = HlinkGetSpecialReference(HLSR_HOME, &ref);
525     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_HOME) failed: %08x\n", hres);
526     ok(ref != NULL, "ref == NULL\n");
527     CoTaskMemFree(ref);
528 
529     hres = HlinkGetSpecialReference(HLSR_SEARCHPAGE, &ref);
530     ok(hres == S_OK, "HlinkGetSpecialReference(HLSR_SEARCHPAGE) failed: %08x\n", hres);
531     ok(ref != NULL, "ref == NULL\n");
532     CoTaskMemFree(ref);
533 
534     ref = (void*)0xdeadbeef;
535     hres = HlinkGetSpecialReference(HLSR_HISTORYFOLDER, &ref);
536     ok(hres == E_NOTIMPL, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
537     ok(ref == NULL, "ref=%p\n", ref);
538 
539     ref = (void*)0xdeadbeef;
540     hres = HlinkGetSpecialReference(4, &ref);
541     ok(hres == E_INVALIDARG, "HlinkGetSpecialReference(HLSR_HISTORYFOLDER) failed: %08x\n", hres);
542     ok(ref == NULL, "ref=%p\n", ref);
543 }
544 
545 static void test_HlinkCreateExtensionServices(void)
546 {
547     IAuthenticate *authenticate;
548     IHttpNegotiate *http_negotiate;
549     LPWSTR password, username, headers;
550     HWND hwnd;
551     HRESULT hres;
552 
553     static const WCHAR usernameW[] = {'u','s','e','r',0};
554     static const WCHAR passwordW[] = {'p','a','s','s',0};
555     static const WCHAR headersW[] = {'h','e','a','d','e','r','s',0};
556     static const WCHAR headersexW[] = {'h','e','a','d','e','r','s','\r','\n',0};
557 
558     hres = HlinkCreateExtensionServices(NULL, NULL, NULL, NULL,
559                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
560     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
561     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
562 
563     password = username = (void*)0xdeadbeef;
564     hwnd = (void*)0xdeadbeef;
565     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
566     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
567     ok(!hwnd, "hwnd != NULL\n");
568     ok(!username, "username != NULL\n");
569     ok(!password, "password != NULL\n");
570 
571     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
572     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
573 
574     headers = (void*)0xdeadbeef;
575     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
576                                                0, &headers);
577     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
578     ok(headers == NULL, "headers != NULL\n");
579 
580     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
581                                                0, NULL);
582     ok(hres == E_INVALIDARG, "BeginningTransaction failed: %08x, expected E_INVALIDARG\n", hres);
583 
584     headers = (void*)0xdeadbeef;
585     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
586     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
587     ok(headers == NULL, "headers != NULL\n");
588 
589     IHttpNegotiate_Release(http_negotiate);
590     IAuthenticate_Release(authenticate);
591 
592 
593     hres = HlinkCreateExtensionServices(headersW, (HWND)0xfefefefe, usernameW, passwordW,
594                                         NULL, &IID_IAuthenticate, (void**)&authenticate);
595     ok(hres == S_OK, "HlinkCreateExtensionServices failed: %08x\n", hres);
596     ok(authenticate != NULL, "HlinkCreateExtensionServices returned NULL\n");
597 
598     password = username = NULL;
599     hwnd = NULL;
600     hres = IAuthenticate_Authenticate(authenticate, &hwnd, &username, &password);
601     ok(hres == S_OK, "Authenticate failed: %08x\n", hres);
602     ok(hwnd == (HWND)0xfefefefe, "hwnd=%p\n", hwnd);
603     ok(!lstrcmpW(username, usernameW), "unexpected username\n");
604     ok(!lstrcmpW(password, passwordW), "unexpected password\n");
605     CoTaskMemFree(username);
606     CoTaskMemFree(password);
607 
608     password = username = (void*)0xdeadbeef;
609     hwnd = (void*)0xdeadbeef;
610     hres = IAuthenticate_Authenticate(authenticate, &hwnd, NULL, &password);
611     ok(hres == E_INVALIDARG, "Authenticate failed: %08x\n", hres);
612     ok(password == (void*)0xdeadbeef, "password = %p\n", password);
613     ok(hwnd == (void*)0xdeadbeef, "hwnd = %p\n", hwnd);
614 
615     hres = IAuthenticate_QueryInterface(authenticate, &IID_IHttpNegotiate, (void**)&http_negotiate);
616     ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres);
617 
618     headers = (void*)0xdeadbeef;
619     hres = IHttpNegotiate_BeginningTransaction(http_negotiate, (void*)0xdeadbeef, (void*)0xdeadbeef,
620                                                0, &headers);
621     ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres);
622     ok(!lstrcmpW(headers, headersexW), "unexpected headers %s\n", wine_dbgstr_w(headers));
623     CoTaskMemFree(headers);
624 
625     headers = (void*)0xdeadbeef;
626     hres = IHttpNegotiate_OnResponse(http_negotiate, 200, (void*)0xdeadbeef, (void*)0xdeadbeef, &headers);
627     ok(hres == S_OK, "OnResponse failed: %08x\n", hres);
628     ok(headers == NULL, "unexpected headers %s\n", wine_dbgstr_w(headers));
629 
630     IHttpNegotiate_Release(http_negotiate);
631     IAuthenticate_Release(authenticate);
632 }
633 
634 static void test_HlinkParseDisplayName(void)
635 {
636     IMoniker *mon = NULL;
637     LPWSTR name;
638     DWORD issys;
639     ULONG eaten = 0;
640     IBindCtx *bctx;
641     HRESULT hres;
642 
643     static const WCHAR invalid_urlW[] = {'t','e','s','t',':','1','2','3','a','b','c',0};
644     static const WCHAR clsid_nameW[] = {'c','l','s','i','d',':',
645             '2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-','A','2','D','8',
646             '-','0','8','0','0','2','B','3','0','3','0','9','D',':',0};
647     static const WCHAR file_urlW[] =
648             {'f','i','l','e',':','/','/','/','c',':','\\','f','i','l','e','.','t','x','t',0};
649 
650     CreateBindCtx(0, &bctx);
651 
652     hres = HlinkParseDisplayName(bctx, winehq_urlW, FALSE, &eaten, &mon);
653     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
654     ok(eaten == sizeof(winehq_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
655     ok(mon != NULL, "mon == NULL\n");
656 
657     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
658     ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
659     ok(!lstrcmpW(name, winehq_urlW), "wrong display name %s\n", wine_dbgstr_w(name));
660     CoTaskMemFree(name);
661 
662     hres = IMoniker_IsSystemMoniker(mon, &issys);
663     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
664     ok(issys == MKSYS_URLMONIKER, "issys=%x\n", issys);
665 
666     IMoniker_Release(mon);
667 
668     hres = HlinkParseDisplayName(bctx, clsid_nameW, FALSE, &eaten, &mon);
669     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
670     ok(eaten == sizeof(clsid_nameW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
671     ok(mon != NULL, "mon == NULL\n");
672 
673     hres = IMoniker_IsSystemMoniker(mon, &issys);
674     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
675     ok(issys == MKSYS_CLASSMONIKER, "issys=%x\n", issys);
676 
677     IMoniker_Release(mon);
678 
679     hres = HlinkParseDisplayName(bctx, invalid_urlW, FALSE, &eaten, &mon);
680     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
681     ok(eaten == sizeof(invalid_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
682     ok(mon != NULL, "mon == NULL\n");
683 
684     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
685     ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
686     ok(!lstrcmpW(name, invalid_urlW), "wrong display name %s\n", wine_dbgstr_w(name));
687     CoTaskMemFree(name);
688 
689     hres = IMoniker_IsSystemMoniker(mon, &issys);
690     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
691     ok(issys == MKSYS_FILEMONIKER, "issys=%x\n", issys);
692 
693     IMoniker_Release(mon);
694 
695     hres = HlinkParseDisplayName(bctx, file_urlW, FALSE, &eaten, &mon);
696     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
697     ok(eaten == sizeof(file_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
698     ok(mon != NULL, "mon == NULL\n");
699 
700     hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
701     ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
702     ok(!lstrcmpW(name, file_urlW+8), "wrong display name %s\n", wine_dbgstr_w(name));
703     CoTaskMemFree(name);
704 
705     hres = IMoniker_IsSystemMoniker(mon, &issys);
706     ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
707     ok(issys == MKSYS_FILEMONIKER, "issys=%x\n", issys);
708 
709     IMoniker_Release(mon);
710     IBindCtx_Release(bctx);
711 }
712 
713 static IBindCtx *_bctx;
714 
715 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
716 {
717     ok(0, "unexpected call\n");
718     return E_NOINTERFACE;
719 }
720 
721 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
722 {
723     return 2;
724 }
725 
726 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
727 {
728     return 1;
729 }
730 
731 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
732         REFGUID guidService, REFIID riid, void **ppv)
733 {
734     ok(0, "unexpected service %s\n", wine_dbgstr_guid(guidService));
735     return E_NOINTERFACE;
736 }
737 
738 static IServiceProviderVtbl ServiceProviderVtbl = {
739     ServiceProvider_QueryInterface,
740     ServiceProvider_AddRef,
741     ServiceProvider_Release,
742     ServiceProvider_QueryService
743 };
744 
745 static IServiceProvider ServiceProvider = { &ServiceProviderVtbl };
746 
747 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
748 {
749     *ppv = NULL;
750 
751     if(IsEqualGUID(riid, &IID_IServiceProvider)) {
752         *ppv = &ServiceProvider;
753 	return S_OK;
754     }
755 
756     ok(0, "unexpected interface %s\n", wine_dbgstr_guid(riid));
757     return E_NOINTERFACE;
758 }
759 
760 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
761 {
762     return 2;
763 }
764 
765 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
766 {
767     return 1;
768 }
769 
770 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
771         IBinding *pib)
772 {
773     ok(0, "unexpected call\n");
774     return E_NOTIMPL;
775 }
776 
777 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
778 {
779     ok(0, "unexpected call\n");
780     return E_NOTIMPL;
781 }
782 
783 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
784 {
785     ok(0, "unexpected call\n");
786     return E_NOTIMPL;
787 }
788 
789 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
790         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
791 {
792     ok(0, "unexpected call\n");
793     return E_NOTIMPL;
794 }
795 
796 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
797 {
798     ok(0, "unexpected call\n");
799     return E_NOTIMPL;
800 }
801 
802 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
803 {
804     ok(0, "unexpected call\n");
805     return E_NOTIMPL;
806 }
807 
808 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
809         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
810 {
811     ok(0, "unexpected call\n");
812     return E_NOTIMPL;
813 }
814 
815 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
816 {
817     ok(0, "unexpected call\n");
818     return E_NOTIMPL;
819 }
820 
821 static IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
822     BindStatusCallback_QueryInterface,
823     BindStatusCallback_AddRef,
824     BindStatusCallback_Release,
825     BindStatusCallback_OnStartBinding,
826     BindStatusCallback_GetPriority,
827     BindStatusCallback_OnLowResource,
828     BindStatusCallback_OnProgress,
829     BindStatusCallback_OnStopBinding,
830     BindStatusCallback_GetBindInfo,
831     BindStatusCallback_OnDataAvailable,
832     BindStatusCallback_OnObjectAvailable
833 };
834 
835 static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl };
836 
837 static HRESULT WINAPI HlinkBrowseContext_QueryInterface(
838         IHlinkBrowseContext *iface, REFIID riid, void **ppv)
839 {
840     *ppv = NULL;
841 
842     if (IsEqualGUID(&IID_IHlinkHistory, riid))
843         CHECK_EXPECT(HBC_QueryInterface_IHlinkHistory);
844     else if (IsEqualGUID(&IID_IMarshal, riid))
845         CHECK_EXPECT2(HBC_QueryInterface_IMarshal);
846     else if (IsEqualGUID(&CLSID_IdentityUnmarshal, riid))
847         CHECK_EXPECT(HBC_QueryInterface_IdentityUnmarshal);
848     else if (IsEqualGUID(&IID_IUnknown, riid))
849         CHECK_EXPECT(HBC_QueryInterface_IUnknown);
850     else
851         ok(0, "unexpected interface: %s\n", wine_dbgstr_guid(riid));
852 
853     return E_NOINTERFACE;
854 }
855 
856 static ULONG WINAPI HlinkBrowseContext_AddRef(IHlinkBrowseContext *iface)
857 {
858     return 2;
859 }
860 
861 static ULONG WINAPI HlinkBrowseContext_Release(IHlinkBrowseContext *iface)
862 {
863     return 1;
864 }
865 
866 static HRESULT WINAPI HlinkBrowseContext_Register(IHlinkBrowseContext *iface,
867         DWORD reserved, IUnknown *piunk, IMoniker *pimk, DWORD *pdwRegister)
868 {
869     ok(0, "unexpected call\n");
870     return E_NOTIMPL;
871 }
872 
873 static IUnknown *HBC_object;
874 
875 static IMoniker Moniker;
876 static HRESULT WINAPI HlinkBrowseContext_GetObject(IHlinkBrowseContext *iface,
877         IMoniker *pimk, BOOL fBindIfRootRegistered, IUnknown **ppiunk)
878 {
879     IBindCtx *bctx;
880     WCHAR *name;
881     HRESULT hr;
882 
883     CHECK_EXPECT(HBC_GetObject);
884 
885     CreateBindCtx(0, &bctx);
886     hr = IMoniker_GetDisplayName(pimk, bctx, NULL, &name);
887     ok(hr == S_OK, "Failed to get display name, hr %#x.\n", hr);
888     ok(!lstrcmpW(winehq_urlW, name) || !lstrcmpW(winehq_404W, name), "got unexpected url\n");
889     CoTaskMemFree(name);
890     IBindCtx_Release(bctx);
891 
892     ok(fBindIfRootRegistered == 1, "fBindIfRootRegistered = %x\n", fBindIfRootRegistered);
893 
894     *ppiunk = HBC_object;
895     return HBC_object ? S_OK : 0xdeadbeef;
896 }
897 
898 static HRESULT WINAPI HlinkBrowseContext_Revoke(IHlinkBrowseContext *iface, DWORD dwRegister)
899 {
900     ok(0, "unexpected call\n");
901     return E_NOTIMPL;
902 }
903 
904 static HRESULT WINAPI HlinkBrowseContext_SetBrowseWindowInfo(
905         IHlinkBrowseContext *iface, HLBWINFO *phlbwi)
906 {
907     ok(0, "unexpected call\n");
908     return E_NOTIMPL;
909 }
910 
911 static HRESULT WINAPI HlinkBrowseContext_GetBrowseWindowInfo(
912         IHlinkBrowseContext *iface, HLBWINFO *phlbwi)
913 {
914     ok(0, "unexpected call\n");
915     return E_NOTIMPL;
916 }
917 
918 static HRESULT WINAPI HlinkBrowseContext_SetInitialHlink(IHlinkBrowseContext *iface,
919         IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName)
920 {
921     ok(0, "unexpected call\n");
922     return E_NOTIMPL;
923 }
924 
925 static HRESULT WINAPI HlinkBrowseContext_OnNavigateHlink(IHlinkBrowseContext *iface, DWORD grfHLNF,
926         IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName, ULONG *puHLID)
927 {
928     ok(0, "unexpected call\n");
929     return E_NOTIMPL;
930 }
931 
932 static HRESULT WINAPI HlinkBrowseContext_UpdateHlink(IHlinkBrowseContext *iface, ULONG uHLID,
933         IMoniker *pimkTarget, LPCWSTR location, LPCWSTR friendly_name)
934 {
935     CHECK_EXPECT(HBC_UpdateHlink);
936     return S_OK;
937 }
938 
939 static HRESULT WINAPI HlinkBrowseContext_EnumNavigationStack(IHlinkBrowseContext *iface,
940         DWORD dwReserved, DWORD grfHLFNAMEF, IEnumHLITEM **ppienumhlitem)
941 {
942     ok(0, "unexpected call\n");
943     return E_NOTIMPL;
944 }
945 
946 static HRESULT WINAPI HlinkBrowseContext_QueryHlink(IHlinkBrowseContext *iface,
947         DWORD grfHLQF, ULONG uHLID)
948 {
949     ok(0, "unexpected call\n");
950     return E_NOTIMPL;
951 }
952 
953 static HRESULT WINAPI HlinkBrowseContext_GetHlink(IHlinkBrowseContext *iface,
954         ULONG uHLID, IHlink **ppihl)
955 {
956     ok(0, "unexpected call\n");
957     return E_NOTIMPL;
958 }
959 
960 static HRESULT WINAPI HlinkBrowseContext_SetCurrentHlink(
961         IHlinkBrowseContext *iface, ULONG uHLID)
962 {
963     ok(0, "unexpected call\n");
964     return E_NOTIMPL;
965 }
966 
967 static HRESULT WINAPI HlinkBrowseContext_Clone(IHlinkBrowseContext *iface,
968         IUnknown *piunkOuter, REFIID riid, IUnknown **ppiunkObj)
969 {
970     ok(0, "unexpected call\n");
971     return E_NOTIMPL;
972 }
973 
974 static HRESULT WINAPI HlinkBrowseContext_Close(IHlinkBrowseContext *iface, DWORD reserved)
975 {
976     ok(0, "unexpected call\n");
977     return E_NOTIMPL;
978 }
979 
980 static IHlinkBrowseContextVtbl HlinkBrowseContextVtbl = {
981     HlinkBrowseContext_QueryInterface,
982     HlinkBrowseContext_AddRef,
983     HlinkBrowseContext_Release,
984     HlinkBrowseContext_Register,
985     HlinkBrowseContext_GetObject,
986     HlinkBrowseContext_Revoke,
987     HlinkBrowseContext_SetBrowseWindowInfo,
988     HlinkBrowseContext_GetBrowseWindowInfo,
989     HlinkBrowseContext_SetInitialHlink,
990     HlinkBrowseContext_OnNavigateHlink,
991     HlinkBrowseContext_UpdateHlink,
992     HlinkBrowseContext_EnumNavigationStack,
993     HlinkBrowseContext_QueryHlink,
994     HlinkBrowseContext_GetHlink,
995     HlinkBrowseContext_SetCurrentHlink,
996     HlinkBrowseContext_Clone,
997     HlinkBrowseContext_Close
998 };
999 
1000 static IHlinkBrowseContext HlinkBrowseContext = { &HlinkBrowseContextVtbl };
1001 
1002 static HRESULT WINAPI HlinkTarget_QueryInterface(IHlinkTarget *iface, REFIID riid, void **ppv)
1003 {
1004     if(IsEqualGUID(&IID_IHlinkTarget, riid)) {
1005         CHECK_EXPECT(HT_QueryInterface_IHlinkTarget);
1006         *ppv = iface;
1007         return S_OK;
1008     }
1009 
1010     ok(0, "unexpected interface: %s\n", wine_dbgstr_guid(riid));
1011     return E_NOINTERFACE;
1012 }
1013 
1014 static ULONG WINAPI HlinkTarget_AddRef(IHlinkTarget *iface)
1015 {
1016     return 2;
1017 }
1018 
1019 static ULONG WINAPI HlinkTarget_Release(IHlinkTarget *iface)
1020 {
1021     return 1;
1022 }
1023 
1024 static HRESULT WINAPI HlinkTarget_SetBrowseContext(IHlinkTarget *iface,
1025         IHlinkBrowseContext *pihlbc)
1026 {
1027     CHECK_EXPECT(HT_SetBrowseContext);
1028 
1029     ok(pihlbc == &HlinkBrowseContext, "pihlbc != &HlinkBrowseContext (%p)\n", pihlbc);
1030     return S_OK;
1031 }
1032 
1033 static HRESULT WINAPI HlinkTarget_GetBrowseContext(IHlinkTarget *iface,
1034         IHlinkBrowseContext **ppihlbc)
1035 {
1036     CHECK_EXPECT(HT_GetBrowseContext);
1037 
1038     *ppihlbc = NULL;
1039     return S_OK;
1040 }
1041 
1042 static HRESULT WINAPI HlinkTarget_Navigate(IHlinkTarget *iface,
1043         DWORD grfHLNF, LPCWSTR pwzJumpLocation)
1044 {
1045     CHECK_EXPECT(HT_Navigate);
1046 
1047     ok(grfHLNF == 0, "grfHLNF = %x\n", grfHLNF);
1048     ok(pwzJumpLocation == NULL, "pwzJumpLocation = %s\n", wine_dbgstr_w(pwzJumpLocation));
1049     return S_OK;
1050 }
1051 
1052 static HRESULT WINAPI HlinkTarget_GetMoniker(IHlinkTarget *iface,
1053         LPCWSTR pwzLocation, DWORD dwAssign, IMoniker **ppimkLocation)
1054 {
1055     ok(0, "unexpected call\n");
1056     return E_NOTIMPL;
1057 }
1058 
1059 static HRESULT WINAPI HlinkTarget_GetFriendlyName(IHlinkTarget *iface,
1060         LPCWSTR pwzLocation, LPWSTR *ppwzFriendlyName)
1061 {
1062     CHECK_EXPECT(HT_GetFriendlyName);
1063     return E_NOTIMPL;
1064 }
1065 
1066 static IHlinkTargetVtbl HlinkTargetVtbl = {
1067     HlinkTarget_QueryInterface,
1068     HlinkTarget_AddRef,
1069     HlinkTarget_Release,
1070     HlinkTarget_SetBrowseContext,
1071     HlinkTarget_GetBrowseContext,
1072     HlinkTarget_Navigate,
1073     HlinkTarget_GetMoniker,
1074     HlinkTarget_GetFriendlyName
1075 };
1076 
1077 static IHlinkTarget HlinkTarget = { &HlinkTargetVtbl };
1078 
1079 static HRESULT WINAPI Moniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
1080 {
1081     *ppv = NULL;
1082 
1083     ok(0, "unexpected riid: %s\n", wine_dbgstr_guid(riid));
1084     return E_NOINTERFACE;
1085 }
1086 
1087 static ULONG WINAPI Moniker_AddRef(IMoniker *iface)
1088 {
1089     return 2;
1090 }
1091 
1092 static ULONG WINAPI Moniker_Release(IMoniker *iface)
1093 {
1094     return 1;
1095 }
1096 
1097 static HRESULT WINAPI Moniker_GetClassID(IMoniker *iface, CLSID *pClassID)
1098 {
1099     CHECK_EXPECT(GetClassID);
1100     *pClassID = IID_IUnknown; /* not a valid CLSID */
1101     return S_OK;
1102 }
1103 
1104 static HRESULT WINAPI Moniker_IsDirty(IMoniker *iface)
1105 {
1106     ok(0, "unexpected call\n");
1107     return E_NOTIMPL;
1108 }
1109 
1110 static HRESULT WINAPI Moniker_Load(IMoniker *iface, IStream *pStm)
1111 {
1112     ok(0, "unexpected call\n");
1113     return E_NOTIMPL;
1114 }
1115 
1116 static HRESULT WINAPI Moniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
1117 {
1118     CHECK_EXPECT(Save);
1119     return S_OK;
1120 }
1121 
1122 static HRESULT WINAPI Moniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
1123 {
1124     ok(0, "unexpected call\n");
1125     return E_NOTIMPL;
1126 }
1127 
1128 static HRESULT WINAPI Moniker_BindToObject(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
1129         REFIID riid, void **ppv)
1130 {
1131     CHECK_EXPECT(BindToObject);
1132 
1133     ok(pbc != _bctx, "pbc != _bctx\n");
1134     ok(pmkToLeft == NULL, "pmkToLeft = %p\n", pmkToLeft);
1135     ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid));
1136     ok(ppv != NULL, "ppv == NULL\n");
1137     ok(*ppv == NULL, "*ppv = %p\n", *ppv);
1138 
1139     *ppv = &HlinkTarget;
1140     return S_OK;
1141 }
1142 
1143 static HRESULT WINAPI Moniker_BindToStorage(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
1144         REFIID riid, void **ppv)
1145 {
1146     IUnknown *unk;
1147     HRESULT hres;
1148 
1149     static OLECHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
1150 
1151     CHECK_EXPECT(BindToStorage);
1152 
1153     ok(pbc == _bctx, "pbc != _bctx\n");
1154     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
1155     ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid));
1156     ok(ppv != NULL, "ppv == NULL\n");
1157     ok(*ppv == NULL, "*ppv=%p\n", *ppv);
1158 
1159     hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
1160     ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres);
1161     ok(unk != NULL, "unk == NULL\n");
1162 
1163     IUnknown_Release(unk);
1164 
1165     return S_OK;
1166 }
1167 
1168 static HRESULT WINAPI Moniker_Reduce(IMoniker *iface, IBindCtx *pbc, DWORD dwReduceHowFar,
1169         IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
1170 {
1171     CHECK_EXPECT(Reduce);
1172     return E_NOTIMPL;
1173 }
1174 
1175 static HRESULT WINAPI Moniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
1176         BOOL fOnlyIfNotGeneric, IMoniker **ppnkComposite)
1177 {
1178     ok(0, "unexpected call\n");
1179     return E_NOTIMPL;
1180 }
1181 
1182 static HRESULT WINAPI Moniker_Enum(IMoniker *iface, BOOL fForwrd, IEnumMoniker **ppenumMoniker)
1183 {
1184     CHECK_EXPECT(Enum);
1185     return E_NOTIMPL;
1186 }
1187 
1188 static HRESULT WINAPI Moniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
1189 {
1190     ok(0, "unexpected call\n");
1191     return E_NOTIMPL;
1192 }
1193 
1194 static HRESULT WINAPI Moniker_Hash(IMoniker *iface, DWORD *pdwHash)
1195 {
1196     ok(0, "unexpected call\n");
1197     return E_NOTIMPL;
1198 }
1199 
1200 static HRESULT WINAPI Moniker_IsRunning(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
1201         IMoniker *pmkNewlyRunning)
1202 {
1203     ok(0, "unexpected call\n");
1204     return E_NOTIMPL;
1205 }
1206 
1207 static HRESULT WINAPI Moniker_GetTimeOfLastChange(IMoniker *iface, IBindCtx *pbc,
1208         IMoniker *pmkToLeft, FILETIME *pFileTime)
1209 {
1210     ok(0, "unexpected call\n");
1211     return E_NOTIMPL;
1212 }
1213 
1214 static HRESULT WINAPI Moniker_Inverse(IMoniker *iface, IMoniker **ppmk)
1215 {
1216     ok(0, "unexpected call\n");
1217     return E_NOTIMPL;
1218 }
1219 
1220 static HRESULT WINAPI Moniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther,
1221         IMoniker **ppmkPrefix)
1222 {
1223     ok(0, "unexpected call\n");
1224     return E_NOTIMPL;
1225 }
1226 
1227 static HRESULT WINAPI Moniker_RelativePathTo(IMoniker *iface, IMoniker *pmkOther,
1228         IMoniker **pmkRelPath)
1229 {
1230     ok(0, "unexpected call\n");
1231     return E_NOTIMPL;
1232 }
1233 
1234 static HRESULT WINAPI Moniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc,
1235         IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
1236 {
1237     CHECK_EXPECT2(GetDisplayName);
1238 
1239     ok(pbc != NULL, "pbc == NULL\n");
1240     ok(pbc != _bctx, "pbc == _bctx\n");
1241     ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
1242 
1243     *ppszDisplayName = CoTaskMemAlloc(sizeof(winehq_urlW));
1244     memcpy(*ppszDisplayName, winehq_urlW, sizeof(winehq_urlW));
1245     return S_OK;
1246 }
1247 
1248 static HRESULT WINAPI Moniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc,
1249         IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
1250 {
1251     ok(0, "unexpected call\n");
1252     return E_NOTIMPL;
1253 }
1254 
1255 static HRESULT WINAPI Moniker_IsSystemMoniker(IMoniker *iface, DWORD *pdwMksys)
1256 {
1257     CHECK_EXPECT2(IsSystemMoniker);
1258 
1259     *pdwMksys = MKSYS_URLMONIKER;
1260     return S_OK;
1261 }
1262 
1263 static IMonikerVtbl MonikerVtbl = {
1264     Moniker_QueryInterface,
1265     Moniker_AddRef,
1266     Moniker_Release,
1267     Moniker_GetClassID,
1268     Moniker_IsDirty,
1269     Moniker_Load,
1270     Moniker_Save,
1271     Moniker_GetSizeMax,
1272     Moniker_BindToObject,
1273     Moniker_BindToStorage,
1274     Moniker_Reduce,
1275     Moniker_ComposeWith,
1276     Moniker_Enum,
1277     Moniker_IsEqual,
1278     Moniker_Hash,
1279     Moniker_IsRunning,
1280     Moniker_GetTimeOfLastChange,
1281     Moniker_Inverse,
1282     Moniker_CommonPrefixWith,
1283     Moniker_RelativePathTo,
1284     Moniker_GetDisplayName,
1285     Moniker_ParseDisplayName,
1286     Moniker_IsSystemMoniker
1287 };
1288 
1289 static IMoniker Moniker = { &MonikerVtbl };
1290 
1291 static void test_HlinkResolveMonikerForData(void)
1292 {
1293     IBindCtx *bctx;
1294     HRESULT hres;
1295 
1296     CreateBindCtx(0, &bctx);
1297     _bctx = bctx;
1298 
1299     SET_EXPECT(IsSystemMoniker);
1300     SET_EXPECT(GetDisplayName);
1301     SET_EXPECT(BindToStorage);
1302 
1303     hres = HlinkResolveMonikerForData(&Moniker, 0, bctx, 0, NULL, &BindStatusCallback, NULL);
1304     ok(hres == S_OK, "HlinkResolveMonikerForData failed: %08x\n", hres);
1305 
1306     CHECK_CALLED(IsSystemMoniker);
1307     CHECK_CALLED(GetDisplayName);
1308     CHECK_CALLED(BindToStorage);
1309 
1310     IBindCtx_Release(bctx);
1311     _bctx = NULL;
1312 }
1313 
1314 static void test_HlinkGetSetMonikerReference(void)
1315 {
1316     IMoniker *found_trgt, *dummy, *dummy2;
1317     IHlink *hlink;
1318     HRESULT hres;
1319     const WCHAR one[] = {'1',0};
1320     const WCHAR two[] = {'2',0};
1321     const WCHAR name[] = {'a',0};
1322     WCHAR *found_loc;
1323 
1324     /* create two dummy monikers to use as targets */
1325     hres = CreateItemMoniker(one, one, &dummy);
1326     ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
1327 
1328     hres = CreateItemMoniker(two, two, &dummy2);
1329     ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
1330 
1331     /* create a new hlink: target => dummy, location => one */
1332     hres = HlinkCreateFromMoniker(dummy, one, name, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
1333     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
1334 
1335     /* validate the target and location */
1336     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1337     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1338     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1339     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1340     IMoniker_Release(found_trgt);
1341     CoTaskMemFree(found_loc);
1342 
1343     /* set location => two */
1344     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_LOCATION, dummy2, two);
1345     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1346 
1347     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1348     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1349     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1350     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
1351     IMoniker_Release(found_trgt);
1352     CoTaskMemFree(found_loc);
1353 
1354     /* set target => dummy2 */
1355     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET, dummy2, one);
1356     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1357 
1358     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1359     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1360     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
1361     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
1362     IMoniker_Release(found_trgt);
1363     CoTaskMemFree(found_loc);
1364 
1365     /* set target => dummy, location => one */
1366     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, dummy, one);
1367     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1368 
1369     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1370     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1371     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1372     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1373     IMoniker_Release(found_trgt);
1374     CoTaskMemFree(found_loc);
1375 
1376     /* no HLINKSETF flags */
1377     hres = IHlink_SetMonikerReference(hlink, 0, dummy2, two);
1378     ok(hres == E_INVALIDARG, "IHlink_SetMonikerReference should've failed with E_INVALIDARG (0x%08x), failed with 0x%08x\n", E_INVALIDARG, hres);
1379 
1380     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1381     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1382     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1383     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1384     IMoniker_Release(found_trgt);
1385     CoTaskMemFree(found_loc);
1386 
1387     /* invalid HLINKSETF flags */
1388     /* Windows returns garbage; on 32-bit it returns the flags probably because the compiler happened to store them in %eax at some point */
1389     if (0)
1390         IHlink_SetMonikerReference(hlink, 12, dummy2, two);
1391 
1392     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1393     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1394     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
1395     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1396     IMoniker_Release(found_trgt);
1397     CoTaskMemFree(found_loc);
1398 
1399     /* valid & invalid HLINKSETF flags */
1400     hres = IHlink_SetMonikerReference(hlink, 12 | HLINKSETF_TARGET, dummy2, two);
1401     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1402 
1403     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1404     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1405     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
1406     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
1407     IMoniker_Release(found_trgt);
1408     CoTaskMemFree(found_loc);
1409 
1410     /* NULL args */
1411     hres = IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, NULL, NULL);
1412     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1413 
1414     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
1415     ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1416     ok(found_trgt == NULL, "Found target should've been %p, was: %p\n", NULL, found_trgt);
1417     ok(found_loc == NULL, "Found location should've been %s, was: %s\n", wine_dbgstr_w(NULL), wine_dbgstr_w(found_loc));
1418     if(found_trgt)
1419         IMoniker_Release(found_trgt);
1420 
1421     IHlink_Release(hlink);
1422     IMoniker_Release(dummy2);
1423     IMoniker_Release(dummy);
1424 
1425     SET_EXPECT(Reduce);
1426     SET_EXPECT(Enum);
1427     SET_EXPECT(IsSystemMoniker);
1428     SET_EXPECT(GetDisplayName);
1429     hres = HlinkCreateFromMoniker(&Moniker, NULL, NULL, NULL, 0, NULL,
1430             &IID_IHlink, (void **)&hlink);
1431     ok(hres == S_OK, "CreateFromMoniker failed: %08x\n", hres);
1432 
1433     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_ABSOLUTE,
1434             &found_trgt, &found_loc);
1435     ok(hres == S_OK, "CreateFromMoniker failed: %08x\n", hres);
1436     ok(found_trgt == &Moniker, "Got unexpected moniker: %p\n", found_trgt);
1437     ok(found_loc == NULL, "Got unexpected location: %p\n", found_loc);
1438     todo_wine CHECK_CALLED(Reduce);
1439     todo_wine CHECK_CALLED(Enum);
1440     CHECK_CALLED(IsSystemMoniker);
1441     CHECK_CALLED(GetDisplayName);
1442 
1443     IHlink_Release(hlink);
1444 }
1445 
1446 static void test_HlinkGetSetStringReference(void)
1447 {
1448     IHlink *link;
1449     static const WCHAR one[] = {'1',0};
1450     static const WCHAR two[] = {'2',0};
1451     static const WCHAR three[] = {'3',0};
1452     static const WCHAR empty[] = {0};
1453     WCHAR *fnd_tgt, *fnd_loc;
1454     HRESULT hres;
1455 
1456     /* create a new hlink: target => NULL, location => one */
1457     hres = HlinkCreateFromMoniker(NULL, one, empty, NULL, 0, NULL, &IID_IHlink, (void**)&link);
1458     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
1459 
1460     /* test setting/getting location */
1461     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1462     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1463     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1464     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1465     CoTaskMemFree(fnd_tgt);
1466     CoTaskMemFree(fnd_loc);
1467 
1468     hres = IHlink_SetStringReference(link, HLINKSETF_LOCATION, one, two);
1469     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1470 
1471     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1472     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1473     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1474     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1475     CoTaskMemFree(fnd_tgt);
1476     CoTaskMemFree(fnd_loc);
1477 
1478     hres = IHlink_SetStringReference(link, -HLINKSETF_LOCATION, two, one);
1479     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1480 
1481     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1482     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1483     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1484     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1485     CoTaskMemFree(fnd_tgt);
1486     CoTaskMemFree(fnd_loc);
1487 
1488     /* test setting/getting target */
1489     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET, two, three);
1490     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1491 
1492     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1493     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1494     ok(!lstrcmpW(fnd_tgt, two), "Found target should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_tgt));
1495     ok(!lstrcmpW(fnd_loc, one), "Found location should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_loc));
1496     CoTaskMemFree(fnd_tgt);
1497     CoTaskMemFree(fnd_loc);
1498 
1499     hres = IHlink_SetStringReference(link, -HLINKSETF_TARGET, three, two);
1500     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1501 
1502     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1503     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1504     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1505     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1506     CoTaskMemFree(fnd_tgt);
1507     CoTaskMemFree(fnd_loc);
1508 
1509     /* test setting/getting both */
1510     hres = IHlink_SetStringReference(link, HLINKSETF_TARGET | HLINKSETF_LOCATION, one, two);
1511     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1512 
1513     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1514     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1515     ok(!lstrcmpW(fnd_tgt, one), "Found target should have been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(fnd_tgt));
1516     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1517     CoTaskMemFree(fnd_tgt);
1518     CoTaskMemFree(fnd_loc);
1519 
1520     hres = IHlink_SetStringReference(link, -(HLINKSETF_TARGET | HLINKSETF_LOCATION), three, one);
1521     ok(hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1522 
1523     hres = IHlink_GetStringReference(link, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1524     ok(hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1525     ok(!lstrcmpW(fnd_tgt, three), "Found target should have been %s, was: %s\n", wine_dbgstr_w(three), wine_dbgstr_w(fnd_tgt));
1526     ok(!lstrcmpW(fnd_loc, two), "Found location should have been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(fnd_loc));
1527     CoTaskMemFree(fnd_tgt);
1528     CoTaskMemFree(fnd_loc);
1529 
1530     /* test invalid flags/params */
1531     hres = IHlink_GetStringReference(link, 4, &fnd_tgt, &fnd_loc);
1532     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1533            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1534     ok(fnd_tgt == NULL, "Found target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1535     ok(fnd_loc == NULL, "Found location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1536     CoTaskMemFree(fnd_tgt);
1537     CoTaskMemFree(fnd_loc);
1538 
1539     hres = IHlink_GetStringReference(link, -1, &fnd_tgt, NULL);
1540     todo_wine ok(hres == E_FAIL, "IHlink_GetStringReference should have failed "
1541            "with E_FAIL (0x%08x), instead: 0x%08x\n", E_FAIL, hres);
1542     CoTaskMemFree(fnd_tgt);
1543 
1544     hres = IHlink_GetStringReference(link, -1, NULL, NULL);
1545     ok(hres == S_OK, "failed, hres=%08x\n", hres);
1546 
1547     hres = IHlink_GetStringReference(link, -1, NULL, &fnd_loc);
1548     ok(hres == S_OK, "failed, hres=%08x\n", hres);
1549     CoTaskMemFree(fnd_loc);
1550 
1551     hres = IHlink_GetStringReference(link, -1, &fnd_tgt, &fnd_loc);
1552     todo_wine ok(hres == E_FAIL, "IHlink_GetStringReference should have failed "
1553            "with E_FAIL (0x%08x), instead: 0x%08x\n", E_FAIL, hres);
1554     CoTaskMemFree(fnd_tgt);
1555     CoTaskMemFree(fnd_loc);
1556 
1557     hres = IHlink_GetStringReference(link, -2, &fnd_tgt, &fnd_loc);
1558     ok(hres == E_INVALIDARG, "IHlink_GetStringReference should have failed "
1559            "with E_INVALIDARG (0x%08x), instead: 0x%08x\n", E_INVALIDARG, hres);
1560     CoTaskMemFree(fnd_tgt);
1561     CoTaskMemFree(fnd_loc);
1562 
1563     if (0)
1564     {
1565         /* Windows returns garbage; on 32-bit it returns the flags probably because the compiler happened to store them in %eax at some point */
1566         IHlink_SetStringReference(link, 4, NULL, NULL);
1567         IHlink_SetStringReference(link, -4, NULL, NULL);
1568     }
1569 
1570     IHlink_Release(link);
1571 }
1572 
1573 #define setStringRef(h,f,t,l) r_setStringRef(__LINE__,h,f,t,l)
1574 static void r_setStringRef(unsigned line, IHlink *hlink, DWORD flags, const WCHAR *tgt, const WCHAR *loc)
1575 {
1576     HRESULT hres;
1577     hres = IHlink_SetStringReference(hlink, flags, tgt, loc);
1578     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetStringReference failed: 0x%08x\n", hres);
1579 }
1580 
1581 #define getStringRef(h,t,l) r_getStringRef(__LINE__,h,t,l)
1582 static void r_getStringRef(unsigned line, IHlink *hlink, const WCHAR *exp_tgt, const WCHAR *exp_loc)
1583 {
1584     HRESULT hres;
1585     WCHAR *fnd_tgt, *fnd_loc;
1586 
1587     hres = IHlink_GetStringReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
1588     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetStringReference failed: 0x%08x\n", hres);
1589 
1590     if(exp_tgt)
1591         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));
1592     else
1593         ok_(__FILE__,line) (fnd_tgt == NULL, "Found string target should have been NULL, was: %s\n", wine_dbgstr_w(fnd_tgt));
1594 
1595     if(exp_loc)
1596         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));
1597     else
1598         ok_(__FILE__,line) (fnd_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1599 
1600     CoTaskMemFree(fnd_tgt);
1601     CoTaskMemFree(fnd_loc);
1602 }
1603 
1604 #define setMonikerRef(h,f,t,l) r_setMonikerRef(__LINE__,h,f,t,l)
1605 static void r_setMonikerRef(unsigned line, IHlink *hlink, DWORD flags, IMoniker *tgt, const WCHAR *loc)
1606 {
1607     HRESULT hres;
1608     hres = IHlink_SetMonikerReference(hlink, flags, tgt, loc);
1609     ok_(__FILE__,line) (hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
1610 }
1611 
1612 /* passing 0xFFFFFFFF as exp_tgt will return the retrieved target & not test it */
1613 #define getMonikerRef(h,t,l,r) r_getMonikerRef(__LINE__,h,t,l,r)
1614 static IMoniker *r_getMonikerRef(unsigned line, IHlink *hlink, IMoniker *exp_tgt, const WCHAR *exp_loc, DWORD ref)
1615 {
1616     HRESULT hres;
1617     IMoniker *fnd_tgt;
1618     WCHAR *fnd_loc;
1619 
1620     hres = IHlink_GetMonikerReference(hlink, ref, &fnd_tgt, &fnd_loc);
1621     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
1622 
1623     if(exp_loc)
1624         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));
1625     else
1626         ok_(__FILE__,line) (fnd_loc == NULL, "Found string location should have been NULL, was: %s\n", wine_dbgstr_w(fnd_loc));
1627 
1628     CoTaskMemFree(fnd_loc);
1629 
1630     if(exp_tgt == (IMoniker*)0xFFFFFFFF)
1631         return fnd_tgt;
1632 
1633     ok_(__FILE__,line) (fnd_tgt == exp_tgt, "Found moniker target should have been %p, was: %p\n", exp_tgt, fnd_tgt);
1634 
1635     if(fnd_tgt)
1636         IMoniker_Release(fnd_tgt);
1637 
1638     return NULL;
1639 }
1640 
1641 static void test_HlinkMoniker(void)
1642 {
1643     IHlink *hlink;
1644     IMoniker *aMon, *file_mon;
1645     static const WCHAR emptyW[] = {0};
1646     static const WCHAR wordsW[] = {'w','o','r','d','s',0};
1647     static const WCHAR aW[] = {'a',0};
1648     static const WCHAR bW[] = {'b',0};
1649     HRESULT hres;
1650 
1651     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
1652     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1653     getStringRef(hlink, NULL, NULL);
1654     getMonikerRef(hlink, NULL, NULL, HLINKGETREF_RELATIVE);
1655 
1656     /* setting a string target creates a moniker reference */
1657     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, aW, wordsW);
1658     getStringRef(hlink, aW, wordsW);
1659     aMon = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, wordsW, HLINKGETREF_RELATIVE);
1660     ok(aMon != NULL, "Moniker from %s target should not be NULL\n", wine_dbgstr_w(aW));
1661     if(aMon)
1662         IMoniker_Release(aMon);
1663 
1664     /* setting target & location to the empty string deletes the moniker
1665      * reference */
1666     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, emptyW, emptyW);
1667     getStringRef(hlink, NULL, NULL);
1668     getMonikerRef(hlink, NULL, NULL, HLINKGETREF_RELATIVE);
1669 
1670     /* setting a moniker target also sets the target string to that moniker's
1671      * display name */
1672     hres = CreateFileMoniker(bW, &file_mon);
1673     ok(hres == S_OK, "CreateFileMoniker failed: 0x%08x\n", hres);
1674 
1675     setMonikerRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, file_mon, wordsW);
1676     getStringRef(hlink, bW, wordsW);
1677     getMonikerRef(hlink, file_mon, wordsW, HLINKGETREF_RELATIVE);
1678 
1679     IMoniker_Release(file_mon);
1680 
1681     IHlink_Release(hlink);
1682 }
1683 
1684 static void test_HashLink(void)
1685 {
1686     IHlink *hlink;
1687     IMoniker *pmk;
1688     const WCHAR hash_targetW[] = {'a','f','i','l','e','#','a','n','a','n','c','h','o','r',0};
1689     const WCHAR two_hash_targetW[] = {'a','f','i','l','e','#','a','n','a','n','c','h','o','r','#','a','n','o','t','h','e','r',0};
1690     const WCHAR hash_no_tgtW[] = {'#','a','n','a','n','c','h','o','r',0};
1691     const WCHAR tgt_partW[] = {'a','f','i','l','e',0};
1692     const WCHAR loc_partW[] = {'a','n','a','n','c','h','o','r',0};
1693     const WCHAR two_hash_loc_partW[] = {'a','n','a','n','c','h','o','r','#','a','n','o','t','h','e','r',0};
1694     const WCHAR test_locW[] = {'t','e','s','t','l','o','c',0};
1695     HRESULT hres;
1696 
1697     /* simple single hash test */
1698     hres = HlinkCreateFromString(hash_targetW, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void*)&hlink);
1699     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1700     ok(hlink != NULL, "Didn't get an hlink\n");
1701 
1702     if(hlink){
1703         getStringRef(hlink, tgt_partW, loc_partW);
1704         pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW, HLINKGETREF_RELATIVE);
1705         ok(pmk != NULL, "Found moniker should not be NULL\n");
1706         if(pmk)
1707             IMoniker_Release(pmk);
1708 
1709         setStringRef(hlink, HLINKSETF_TARGET, hash_targetW, NULL);
1710         getStringRef(hlink, hash_targetW, loc_partW);
1711 
1712         IHlink_Release(hlink);
1713     }
1714 
1715     /* two hashes in the target */
1716     hres = HlinkCreateFromString(two_hash_targetW, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void*)&hlink);
1717     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1718     ok(hlink != NULL, "Didn't get an hlink\n");
1719 
1720     if(hlink){
1721         getStringRef(hlink, tgt_partW, two_hash_loc_partW);
1722         pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, two_hash_loc_partW, HLINKGETREF_RELATIVE);
1723         ok(pmk != NULL, "Found moniker should not be NULL\n");
1724         if(pmk)
1725             IMoniker_Release(pmk);
1726 
1727         IHlink_Release(hlink);
1728     }
1729 
1730     /* target with hash plus a location string */
1731     hres = HlinkCreateFromString(hash_targetW, test_locW, NULL, NULL, 0, NULL, &IID_IHlink, (void*)&hlink);
1732     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1733     ok(hlink != NULL, "Didn't get an hlink\n");
1734 
1735     if(hlink){
1736         getStringRef(hlink, tgt_partW, test_locW);
1737         pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, test_locW, HLINKGETREF_RELATIVE);
1738         ok(pmk != NULL, "Found moniker should not be NULL\n");
1739         if(pmk)
1740             IMoniker_Release(pmk);
1741 
1742         IHlink_Release(hlink);
1743     }
1744 
1745     /* target with hash containing no "target part" */
1746     hres = HlinkCreateFromString(hash_no_tgtW, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void*)&hlink);
1747     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
1748     ok(hlink != NULL, "Didn't get an hlink\n");
1749 
1750     if(hlink){
1751         getStringRef(hlink, NULL, loc_partW);
1752         pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW, HLINKGETREF_RELATIVE);
1753         ok(pmk == NULL, "Found moniker should be NULL\n");
1754         if(pmk)
1755             IMoniker_Release(pmk);
1756 
1757         IHlink_Release(hlink);
1758     }
1759 }
1760 
1761 static const WCHAR site_monikerW[] = {'S','I','T','E','_','M','O','N','I','K','E','R',0};
1762 static const WCHAR ref_monikerW[] = {'R','E','F','_','M','O','N','I','K','E','R',0};
1763 
1764 static HRESULT WINAPI hls_test_Moniker_BindToStorage(IMoniker *iface,
1765         IBindCtx *pbc, IMoniker *toLeft, REFIID riid, void **obj)
1766 {
1767     ok(0, "BTS: %p %p %p %s %p\n", iface, pbc, toLeft, wine_dbgstr_guid(riid), obj);
1768     return E_NOTIMPL;
1769 }
1770 
1771 static HRESULT WINAPI hls_site_Moniker_ComposeWith(IMoniker *iface,
1772         IMoniker *right, BOOL onlyIfNotGeneric, IMoniker **composite)
1773 {
1774     LPOLESTR rightName;
1775     HRESULT hres;
1776 
1777     ok(onlyIfNotGeneric == 0, "Expected onlyIfNotGeneric to be FALSE\n");
1778 
1779     CHECK_EXPECT(ComposeWith);
1780 
1781     hres = IMoniker_GetDisplayName(right, NULL, NULL, &rightName);
1782     ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
1783     ok(!lstrcmpW(rightName, ref_monikerW),
1784             "Expected to get moniker set via SetMonikerReference, instead got: %s\n",
1785             wine_dbgstr_w(rightName));
1786     CoTaskMemFree(rightName);
1787 
1788     *composite = NULL;
1789 
1790     /* unlikely error code to verify this return result is used */
1791     return E_OUTOFMEMORY;
1792 }
1793 
1794 static HRESULT WINAPI hls_site_Moniker_GetDisplayName(IMoniker *iface,
1795         IBindCtx *pbc, IMoniker *toLeft, LPOLESTR *displayName)
1796 {
1797     *displayName = CoTaskMemAlloc(sizeof(site_monikerW));
1798     memcpy(*displayName, site_monikerW, sizeof(site_monikerW));
1799     return S_OK;
1800 }
1801 
1802 static HRESULT WINAPI hls_ref_Moniker_GetDisplayName(IMoniker *iface,
1803         IBindCtx *pbc, IMoniker *toLeft, LPOLESTR *displayName)
1804 {
1805     *displayName = CoTaskMemAlloc(sizeof(ref_monikerW));
1806     memcpy(*displayName, ref_monikerW, sizeof(ref_monikerW));
1807     return S_OK;
1808 }
1809 
1810 static HRESULT WINAPI hls_test_Moniker_IsSystemMoniker(IMoniker *iface,
1811         DWORD *mksys)
1812 {
1813     return S_FALSE;
1814 }
1815 
1816 static IMonikerVtbl hls_site_MonikerVtbl = {
1817     Moniker_QueryInterface,
1818     Moniker_AddRef,
1819     Moniker_Release,
1820     Moniker_GetClassID,
1821     Moniker_IsDirty,
1822     Moniker_Load,
1823     Moniker_Save,
1824     Moniker_GetSizeMax,
1825     Moniker_BindToObject,
1826     hls_test_Moniker_BindToStorage,
1827     Moniker_Reduce,
1828     hls_site_Moniker_ComposeWith,
1829     Moniker_Enum,
1830     Moniker_IsEqual,
1831     Moniker_Hash,
1832     Moniker_IsRunning,
1833     Moniker_GetTimeOfLastChange,
1834     Moniker_Inverse,
1835     Moniker_CommonPrefixWith,
1836     Moniker_RelativePathTo,
1837     hls_site_Moniker_GetDisplayName,
1838     Moniker_ParseDisplayName,
1839     hls_test_Moniker_IsSystemMoniker
1840 };
1841 
1842 static IMonikerVtbl hls_ref_MonikerVtbl = {
1843     Moniker_QueryInterface,
1844     Moniker_AddRef,
1845     Moniker_Release,
1846     Moniker_GetClassID,
1847     Moniker_IsDirty,
1848     Moniker_Load,
1849     Moniker_Save,
1850     Moniker_GetSizeMax,
1851     Moniker_BindToObject,
1852     hls_test_Moniker_BindToStorage,
1853     Moniker_Reduce,
1854     Moniker_ComposeWith,
1855     Moniker_Enum,
1856     Moniker_IsEqual,
1857     Moniker_Hash,
1858     Moniker_IsRunning,
1859     Moniker_GetTimeOfLastChange,
1860     Moniker_Inverse,
1861     Moniker_CommonPrefixWith,
1862     Moniker_RelativePathTo,
1863     hls_ref_Moniker_GetDisplayName,
1864     Moniker_ParseDisplayName,
1865     hls_test_Moniker_IsSystemMoniker
1866 };
1867 
1868 static IMoniker hls_site_Moniker = { &hls_site_MonikerVtbl };
1869 static IMoniker hls_ref_Moniker = { &hls_ref_MonikerVtbl };
1870 
1871 static HRESULT WINAPI hls_QueryInterface(IHlinkSite *iface, REFGUID iid,
1872         void **obj)
1873 {
1874     ok(0, "QI: %p %s %p\n", iface, wine_dbgstr_guid(iid), obj);
1875     return E_NOTIMPL;
1876 }
1877 
1878 static ULONG WINAPI hls_AddRef(IHlinkSite *iface)
1879 {
1880     return 2;
1881 }
1882 
1883 static ULONG WINAPI hls_Release(IHlinkSite *iface)
1884 {
1885     return 1;
1886 }
1887 
1888 static HRESULT WINAPI hls_QueryService(IHlinkSite *iface, DWORD siteData,
1889         REFGUID service, REFIID riid, IUnknown **punk)
1890 {
1891     ok(0, "QS: %p %x %s %s %p\n", iface, siteData, wine_dbgstr_guid(service),
1892             wine_dbgstr_guid(riid), punk);
1893     return E_NOTIMPL;
1894 }
1895 
1896 #define SITEDATA_SUCCESS 1
1897 #define SITEDATA_NOTIMPL 2
1898 
1899 static HRESULT WINAPI hls_GetMoniker(IHlinkSite *iface, DWORD siteData,
1900         DWORD assign, DWORD which, IMoniker **pmk)
1901 {
1902     ok(siteData == SITEDATA_NOTIMPL ||
1903             siteData == SITEDATA_SUCCESS, "Unexpected site data: %u\n", siteData);
1904 
1905     if(siteData == SITEDATA_SUCCESS){
1906         *pmk = &hls_site_Moniker;
1907         return S_OK;
1908     }
1909 
1910     return E_NOTIMPL;
1911 }
1912 
1913 static HRESULT WINAPI hls_ReadyToNavigate(IHlinkSite *iface, DWORD siteData,
1914         DWORD reserved)
1915 {
1916     ok(0, "RTN: %p %x %x\n", iface, siteData, reserved);
1917     return E_NOTIMPL;
1918 }
1919 
1920 static HRESULT WINAPI hls_OnNavigationComplete(IHlinkSite *iface,
1921         DWORD siteData, DWORD reserved, HRESULT error, LPCWSTR errorStr)
1922 {
1923     CHECK_EXPECT(OnNavigationComplete);
1924     ok(siteData == SITEDATA_SUCCESS, "Unexpected site data: %u\n", siteData);
1925     ok(error == E_OUTOFMEMORY, "Expected E_OUTOFMEMORY, got: %08x\n", error);
1926     return E_NOTIMPL;
1927 }
1928 
1929 static IHlinkSiteVtbl HlinkSiteVtbl = {
1930     hls_QueryInterface,
1931     hls_AddRef,
1932     hls_Release,
1933     hls_QueryService,
1934     hls_GetMoniker,
1935     hls_ReadyToNavigate,
1936     hls_OnNavigationComplete
1937 };
1938 
1939 static IHlinkSite HlinkSite = { &HlinkSiteVtbl };
1940 
1941 static void test_HlinkSite(void)
1942 {
1943     IHlink *hl;
1944     IMoniker *mon_ref;
1945     IBindCtx *pbc;
1946     HRESULT hres;
1947 
1948     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL,
1949             &IID_IHlink, (LPVOID*)&hl);
1950     ok(hres == S_OK, "HlinkCreateFromString failed: %08x\n", hres);
1951     getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
1952 
1953     hres = IHlink_SetHlinkSite(hl, &HlinkSite, SITEDATA_SUCCESS);
1954     ok(hres == S_OK, "SetHlinkSite failed: %08x\n", hres);
1955     getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
1956     getStringRef(hl, NULL, NULL);
1957 
1958     hres = IHlink_GetMonikerReference(hl, HLINKGETREF_RELATIVE, &mon_ref, NULL);
1959     ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
1960     ok(mon_ref == NULL, "Didn't get expected moniker, instead: %p\n", mon_ref);
1961 
1962     hres = IHlink_GetMonikerReference(hl, HLINKGETREF_ABSOLUTE, &mon_ref, NULL);
1963     ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
1964     ok(mon_ref == &hls_site_Moniker, "Didn't get expected moniker, instead: %p\n", mon_ref);
1965 
1966     SET_EXPECT(Reduce);
1967     SET_EXPECT(Enum);
1968     hres = IHlink_SetMonikerReference(hl, HLINKSETF_TARGET, &hls_ref_Moniker, NULL);
1969     ok(hres == S_OK, "SetMonikerReference failed: %08x\n", hres);
1970     todo_wine CHECK_CALLED(Reduce);
1971     todo_wine CHECK_CALLED(Enum);
1972 
1973     getMonikerRef(hl, &hls_ref_Moniker, NULL, HLINKGETREF_RELATIVE);
1974 
1975     SET_EXPECT(Enum);
1976     getStringRef(hl, ref_monikerW, NULL);
1977     todo_wine CHECK_CALLED(Enum);
1978 
1979     hres = IHlink_GetMonikerReference(hl, HLINKGETREF_RELATIVE, &mon_ref, NULL);
1980     ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
1981     ok(mon_ref == &hls_ref_Moniker, "Didn't get expected moniker, instead: %p\n", mon_ref);
1982     IMoniker_Release(mon_ref);
1983 
1984     SET_EXPECT(ComposeWith);
1985     hres = IHlink_GetMonikerReference(hl, HLINKGETREF_ABSOLUTE, &mon_ref, NULL);
1986     ok(hres == E_OUTOFMEMORY, "Expected E_OUTOFMEMORY, got: %08x\n", hres);
1987     ok(mon_ref == NULL, "Shouldn't have got a Moniker, got: %p\n", mon_ref);
1988     CHECK_CALLED(ComposeWith);
1989 
1990     hres = CreateBindCtx(0, &pbc);
1991     ok(hres == S_OK, "CreateBindCtx failed: %08x\n", hres);
1992 
1993     SET_EXPECT(ComposeWith);
1994     SET_EXPECT(OnNavigationComplete);
1995     hres = IHlink_Navigate(hl, 0, pbc, NULL, NULL);
1996     ok(hres == E_OUTOFMEMORY, "Navigate should've failed: %08x\n", hres);
1997     CHECK_CALLED(ComposeWith);
1998     CHECK_CALLED(OnNavigationComplete);
1999 
2000     IBindCtx_Release(pbc);
2001     IHlink_Release(hl);
2002 
2003     SET_EXPECT(Reduce);
2004     SET_EXPECT(Enum);
2005     hres = HlinkCreateFromMoniker(&hls_ref_Moniker, NULL, NULL, &HlinkSite, SITEDATA_SUCCESS,
2006             NULL, &IID_IHlink, (LPVOID*)&hl);
2007     ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
2008     todo_wine CHECK_CALLED(Reduce);
2009     todo_wine CHECK_CALLED(Enum);
2010     getMonikerRef(hl, &hls_ref_Moniker, NULL, HLINKGETREF_RELATIVE);
2011     IHlink_Release(hl);
2012 
2013     hres = HlinkCreateFromMoniker(NULL, NULL, NULL, &HlinkSite, SITEDATA_SUCCESS,
2014             NULL, &IID_IHlink, (LPVOID*)&hl);
2015     ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
2016     getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
2017     IHlink_Release(hl);
2018 
2019     SET_EXPECT(Reduce);
2020     SET_EXPECT(Enum);
2021     SET_EXPECT(IsSystemMoniker);
2022     SET_EXPECT(GetDisplayName);
2023     hres = HlinkCreateFromMoniker(&Moniker, NULL, NULL, &HlinkSite, SITEDATA_NOTIMPL,
2024             NULL, &IID_IHlink, (LPVOID*)&hl);
2025     ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
2026     getMonikerRef(hl, &Moniker, NULL, HLINKGETREF_ABSOLUTE);
2027     IHlink_Release(hl);
2028     todo_wine CHECK_CALLED(Reduce);
2029     todo_wine CHECK_CALLED(Enum);
2030     CHECK_CALLED(IsSystemMoniker);
2031     CHECK_CALLED(GetDisplayName);
2032 }
2033 
2034 static void test_HlinkClone(void)
2035 {
2036     HRESULT hres;
2037     IHlink *hl, *cloned = NULL;
2038     IMoniker *dummy, *fnd_mk;
2039     IHlinkSite *fnd_site;
2040     WCHAR *fnd_name;
2041     DWORD fnd_data;
2042     const WCHAR one[] = {'1',0};
2043     const WCHAR two[] = {'2',0};
2044     const WCHAR name[] = {'a',0};
2045 
2046     hres = HlinkClone(NULL, NULL, NULL, 0, NULL);
2047     ok(hres == E_INVALIDARG, "Got wrong failure code: %08x\n", hres);
2048 
2049     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL,
2050             &IID_IHlink, (void**)&hl);
2051     ok(hres == S_OK, "HlinkCreateFromString failed: %08x\n", hres);
2052 
2053     hres = HlinkClone(hl, &IID_IHlink, NULL, 0, NULL);
2054     ok(hres == E_INVALIDARG, "Got wrong failure code: %08x\n", hres);
2055 
2056     if (0)
2057     {
2058         /* crash on Windows XP */
2059         HlinkClone(hl, NULL, NULL, 0, NULL);
2060 
2061         HlinkClone(hl, NULL, NULL, 0, (void**)&cloned);
2062     }
2063 
2064     hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
2065     ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
2066     ok(cloned != NULL, "Didn't get a clone\n");
2067     getMonikerRef(cloned, NULL, NULL, HLINKGETREF_RELATIVE);
2068     IHlink_Release(cloned);
2069 
2070     IHlink_Release(hl);
2071 
2072     SET_EXPECT(Reduce);
2073     SET_EXPECT(Enum);
2074     hres = HlinkCreateFromMoniker(&hls_ref_Moniker, two, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hl);
2075     todo_wine CHECK_CALLED(Reduce);
2076     todo_wine CHECK_CALLED(Enum);
2077     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
2078     getMonikerRef(hl, &hls_ref_Moniker, two, HLINKGETREF_RELATIVE);
2079 
2080     SET_EXPECT(Save);
2081     SET_EXPECT(GetClassID);
2082     cloned = (IHlink*)0xdeadbeef;
2083     hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
2084     /* fails because of invalid CLSID given by Moniker_GetClassID */
2085     ok(hres == REGDB_E_CLASSNOTREG, "Wrong error code: %08x\n", hres);
2086     ok(cloned == NULL, "Shouldn't have gotten a clone\n");
2087     CHECK_CALLED(Save);
2088     CHECK_CALLED(GetClassID);
2089 
2090     IHlink_Release(hl);
2091 
2092     hres = CreateItemMoniker(one, one, &dummy);
2093     ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
2094 
2095     hres = HlinkCreateFromMoniker(dummy, two, name, &HlinkSite, SITEDATA_SUCCESS, NULL, &IID_IHlink, (void**)&hl);
2096     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
2097     getMonikerRef(hl, dummy, two, HLINKGETREF_RELATIVE);
2098 
2099     cloned = NULL;
2100     hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
2101     ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
2102     ok(cloned != NULL, "Should have gotten a clone\n");
2103 
2104     fnd_mk = getMonikerRef(cloned, (IMoniker*)0xFFFFFFFF, two, HLINKGETREF_RELATIVE);
2105     ok(fnd_mk != NULL, "Expected non-null Moniker\n");
2106     ok(fnd_mk != dummy, "Expected a new Moniker to be created\n");
2107 
2108     fnd_name = NULL;
2109     hres = IHlink_GetFriendlyName(cloned, HLFNAMEF_DEFAULT, &fnd_name);
2110     ok(hres == S_OK, "GetFriendlyName failed: %08x\n", hres);
2111     ok(fnd_name != NULL, "Expected friendly name to be non-NULL\n");
2112     ok(lstrcmpW(fnd_name, name) == 0, "Expected friendly name to be %s, was %s\n",
2113             wine_dbgstr_w(name), wine_dbgstr_w(fnd_name));
2114     CoTaskMemFree(fnd_name);
2115 
2116     fnd_site = (IHlinkSite*)0xdeadbeef;
2117     fnd_data = 4;
2118     hres = IHlink_GetHlinkSite(cloned, &fnd_site, &fnd_data);
2119     ok(hres == S_OK, "GetHlinkSite failed: %08x\n", hres);
2120     ok(fnd_site == NULL, "Expected NULL site\n");
2121     ok(fnd_data == 4, "Expected site data to be 4, was: %d\n", fnd_data);
2122 
2123     IHlink_Release(cloned);
2124     IHlink_Release(hl);
2125 
2126     hres = HlinkCreateFromMoniker(dummy, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hl);
2127     ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
2128     getMonikerRef(hl, dummy, NULL, HLINKGETREF_RELATIVE);
2129 
2130     cloned = NULL;
2131     hres = HlinkClone(hl, &IID_IHlink, &HlinkSite, SITEDATA_SUCCESS, (void**)&cloned);
2132     ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
2133     ok(cloned != NULL, "Should have gotten a clone\n");
2134 
2135     fnd_mk = getMonikerRef(cloned, (IMoniker*)0xFFFFFFFF, NULL, HLINKGETREF_RELATIVE);
2136     ok(fnd_mk != NULL, "Expected non-null Moniker\n");
2137     ok(fnd_mk != dummy, "Expected a new Moniker to be created\n");
2138 
2139     fnd_site = (IHlinkSite*)0xdeadbeef;
2140     fnd_data = 4;
2141     hres = IHlink_GetHlinkSite(cloned, &fnd_site, &fnd_data);
2142     ok(hres == S_OK, "GetHlinkSite failed: %08x\n", hres);
2143     ok(fnd_site == &HlinkSite, "Expected found site to be HlinkSite, was: %p\n", fnd_site);
2144     ok(fnd_data == SITEDATA_SUCCESS, "Unexpected site data: %u\n", fnd_data);
2145 
2146     IHlink_Release(cloned);
2147     IHlink_Release(hl);
2148 
2149     IMoniker_Release(dummy);
2150 }
2151 
2152 static void test_StdHlink(void)
2153 {
2154     IHlink *hlink;
2155     WCHAR *str;
2156     HRESULT hres;
2157 
2158     static const WCHAR testW[] = {'t','e','s','t',0};
2159 
2160     hres = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
2161             &IID_IHlink, (void**)&hlink);
2162     ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres);
2163 
2164     str = (void*)0xdeadbeef;
2165     hres = IHlink_GetTargetFrameName(hlink, &str);
2166     ok(hres == S_FALSE, "GetTargetFrameName failed: %08x\n", hres);
2167     ok(!str, "str = %s\n", wine_dbgstr_w(str));
2168 
2169     hres = IHlink_SetTargetFrameName(hlink, testW);
2170     ok(hres == S_OK, "SetTargetFrameName failed: %08x\n", hres);
2171 
2172     str = (void*)0xdeadbeef;
2173     hres = IHlink_GetTargetFrameName(hlink, &str);
2174     ok(hres == S_OK, "GetTargetFrameName failed: %08x\n", hres);
2175     ok(!lstrcmpW(str, testW), "str = %s\n", wine_dbgstr_w(str));
2176     CoTaskMemFree(str);
2177 
2178     hres = IHlink_SetTargetFrameName(hlink, NULL);
2179     ok(hres == S_OK, "SetTargetFrameName failed: %08x\n", hres);
2180 
2181     str = (void*)0xdeadbeef;
2182     hres = IHlink_GetTargetFrameName(hlink, &str);
2183     ok(hres == S_FALSE, "GetTargetFrameName failed: %08x\n", hres);
2184     ok(!str, "str = %s\n", wine_dbgstr_w(str));
2185 
2186     IHlink_Release(hlink);
2187 }
2188 
2189 static void test_Hlink_Navigate(void)
2190 {
2191     IHlink *hlink;
2192     IBindCtx *pbc;
2193     HRESULT hres;
2194 
2195     hres = CreateBindCtx(0, &pbc);
2196     ok(hres == S_OK, "CreateBindCtx failed: %08x\n", hres);
2197     _bctx = pbc;
2198 
2199     HBC_object = NULL;
2200 
2201     SET_EXPECT(Reduce);
2202     SET_EXPECT(Enum);
2203     SET_EXPECT(IsSystemMoniker);
2204     SET_EXPECT(GetDisplayName);
2205     hres = HlinkCreateFromMoniker(&Moniker, NULL, NULL, NULL,
2206             0, NULL, &IID_IHlink, (void**)&hlink);
2207     ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
2208     todo_wine CHECK_CALLED(Reduce);
2209     todo_wine CHECK_CALLED(Enum);
2210     todo_wine CHECK_CALLED(IsSystemMoniker);
2211     CHECK_CALLED(GetDisplayName);
2212 
2213     SET_EXPECT(IsSystemMoniker);
2214     SET_EXPECT(GetDisplayName);
2215     SET_EXPECT(HBC_GetObject);
2216     SET_EXPECT(Reduce);
2217     SET_EXPECT(BindToObject);
2218     SET_EXPECT(HT_QueryInterface_IHlinkTarget);
2219     SET_EXPECT(HT_GetBrowseContext);
2220     SET_EXPECT(HT_SetBrowseContext);
2221     SET_EXPECT(HBC_QueryInterface_IHlinkHistory);
2222     SET_EXPECT(HT_Navigate);
2223     SET_EXPECT(HT_GetFriendlyName);
2224     hres = IHlink_Navigate(hlink, 0, pbc, NULL, &HlinkBrowseContext);
2225     ok(hres == S_OK, "Navigate failed: %08x\n", hres);
2226     CHECK_CALLED(IsSystemMoniker);
2227     CHECK_CALLED(GetDisplayName);
2228     CHECK_CALLED(HBC_GetObject);
2229     todo_wine CHECK_CALLED(Reduce);
2230     CHECK_CALLED(BindToObject);
2231     CHECK_CALLED(HT_QueryInterface_IHlinkTarget);
2232     todo_wine CHECK_CALLED(HT_GetBrowseContext);
2233     CHECK_CALLED(HT_SetBrowseContext);
2234     todo_wine CHECK_CALLED(HBC_QueryInterface_IHlinkHistory);
2235     CHECK_CALLED(HT_Navigate);
2236     todo_wine CHECK_CALLED(HT_GetFriendlyName);
2237 
2238     /* Test with valid return from HlinkBrowseContext::GetObject */
2239     HBC_object = (IUnknown *)&HlinkTarget;
2240 
2241     SET_EXPECT(IsSystemMoniker);
2242     SET_EXPECT(GetDisplayName);
2243     SET_EXPECT(HBC_GetObject);
2244     SET_EXPECT(HT_QueryInterface_IHlinkTarget);
2245     SET_EXPECT(HT_Navigate);
2246     SET_EXPECT(HT_GetFriendlyName);
2247     hres = IHlink_Navigate(hlink, 0, pbc, NULL, &HlinkBrowseContext);
2248     ok(hres == S_OK, "Navigate failed: %08x\n", hres);
2249     CHECK_CALLED(IsSystemMoniker);
2250     CHECK_CALLED(GetDisplayName);
2251     CHECK_CALLED(HBC_GetObject);
2252     CHECK_CALLED(HT_QueryInterface_IHlinkTarget);
2253     CHECK_CALLED(HT_Navigate);
2254     todo_wine CHECK_CALLED(HT_GetFriendlyName);
2255 
2256     HBC_object = NULL;
2257 
2258 if (0) {    /* these currently open a browser window on wine */
2259     /* Test from string */
2260     SET_EXPECT(HBC_GetObject);
2261     hres = HlinkNavigateToStringReference(winehq_404W, NULL, NULL, 0, NULL, 0, pbc, NULL, &HlinkBrowseContext);
2262     todo_wine ok(hres == INET_E_OBJECT_NOT_FOUND, "Expected INET_E_OBJECT_NOT_FOUND, got %08x\n", hres);
2263     CHECK_CALLED(HBC_GetObject);
2264 
2265     /* MSDN claims browse context and bind context can't be null, but they can */
2266     SET_EXPECT(HBC_GetObject);
2267     hres = HlinkNavigateToStringReference(winehq_404W, NULL, NULL, 0, NULL, 0, NULL, NULL, &HlinkBrowseContext);
2268     todo_wine ok(hres == INET_E_OBJECT_NOT_FOUND, "Expected INET_E_OBJECT_NOT_FOUND, got %08x\n", hres);
2269     CHECK_CALLED(HBC_GetObject);
2270 }
2271 
2272     /* these open a browser window, so mark them interactive only */
2273     if (winetest_interactive)
2274     {
2275         /* both parameters null */
2276         SET_EXPECT(IsSystemMoniker);
2277         SET_EXPECT(GetDisplayName);
2278         hres = IHlink_Navigate(hlink, 0, NULL, NULL, NULL);
2279         ok(hres == DRAGDROP_S_DROP, "Expected DRAGDROP_S_DROP, got %08x\n", hres);
2280         CHECK_CALLED(IsSystemMoniker);
2281         CHECK_CALLED(GetDisplayName);
2282 
2283         /* same, from string */
2284         hres = HlinkNavigateToStringReference(winehq_404W, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL);
2285         ok(hres == DRAGDROP_S_DROP, "Expected DRAGDROP_S_DROP, got %08x\n", hres);
2286 
2287         /* try basic test with valid URL */
2288         SET_EXPECT(HBC_GetObject);
2289         SET_EXPECT(HBC_QueryInterface_IHlinkHistory);
2290         SET_EXPECT(HBC_QueryInterface_IMarshal);
2291         SET_EXPECT(HBC_QueryInterface_IdentityUnmarshal);
2292         SET_EXPECT(HBC_QueryInterface_IUnknown);
2293         hres = HlinkNavigateToStringReference(winehq_urlW, NULL, NULL, 0, NULL, 0, pbc, NULL, &HlinkBrowseContext);
2294         ok(hres == S_OK, "Expected S_OK, got %08x\n", hres);
2295         CHECK_CALLED(HBC_GetObject);
2296         todo_wine CHECK_CALLED(HBC_QueryInterface_IHlinkHistory);
2297         todo_wine CHECK_CALLED(HBC_QueryInterface_IMarshal);
2298         todo_wine CHECK_CALLED(HBC_QueryInterface_IdentityUnmarshal);
2299         todo_wine CHECK_CALLED(HBC_QueryInterface_IUnknown);
2300     }
2301     else
2302         skip("interactive IHlink_Navigate tests\n");
2303 
2304     IHlink_Release(hlink);
2305     IBindCtx_Release(pbc);
2306     _bctx = NULL;
2307 }
2308 
2309 static HRESULT WINAPI hlinkframe_QueryInterface(IHlinkFrame *iface, REFIID riid, void **obj)
2310 {
2311     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IHlinkFrame))
2312     {
2313         *obj = iface;
2314         return S_OK;
2315     }
2316 
2317     *obj = NULL;
2318     return E_NOINTERFACE;
2319 }
2320 
2321 static ULONG WINAPI hlinkframe_AddRef(IHlinkFrame *iface)
2322 {
2323     return 2;
2324 }
2325 
2326 static ULONG WINAPI hlinkframe_Release(IHlinkFrame *iface)
2327 {
2328     return 1;
2329 }
2330 
2331 static HRESULT WINAPI hlinkframe_SetBrowseContext(IHlinkFrame *iface, IHlinkBrowseContext *bc)
2332 {
2333     ok(0, "unexpected %p\n", bc);
2334     return E_NOTIMPL;
2335 }
2336 
2337 static HRESULT WINAPI hlinkframe_GetBrowseContext(IHlinkFrame *iface, IHlinkBrowseContext **bc)
2338 {
2339     *bc = NULL;
2340     ok(0, "unexpected %p\n", bc);
2341     return E_NOTIMPL;
2342 }
2343 
2344 static HRESULT WINAPI hlinkframe_Navigate(IHlinkFrame *iface, DWORD grfHLNF, LPBC pbc, IBindStatusCallback *bsc, IHlink *navigate)
2345 {
2346     ok(0, "unexpected\n");
2347     return E_NOTIMPL;
2348 }
2349 
2350 static HRESULT WINAPI hlinkframe_OnNavigate(IHlinkFrame *iface, DWORD grfHLNF, IMoniker *target, LPCWSTR location, LPCWSTR friendly_name,
2351     DWORD reserved)
2352 {
2353     ok(0, "unexpected\n");
2354     return E_NOTIMPL;
2355 }
2356 
2357 static HRESULT WINAPI hlinkframe_UpdateHlink(IHlinkFrame *iface, ULONG uHLID, IMoniker *target, LPCWSTR location, LPCWSTR friendly_name)
2358 {
2359     CHECK_EXPECT(HLF_UpdateHlink);
2360     return S_OK;
2361 }
2362 
2363 static IHlinkFrameVtbl hlinkframevtbl = {
2364     hlinkframe_QueryInterface,
2365     hlinkframe_AddRef,
2366     hlinkframe_Release,
2367     hlinkframe_SetBrowseContext,
2368     hlinkframe_GetBrowseContext,
2369     hlinkframe_Navigate,
2370     hlinkframe_OnNavigate,
2371     hlinkframe_UpdateHlink
2372 };
2373 
2374 static IHlinkFrame testframe = { &hlinkframevtbl };
2375 
2376 static void test_HlinkUpdateStackItem(void)
2377 {
2378     static const WCHAR location[] = {'l','o','c','a','t','i','o','n',0};
2379     HRESULT hr;
2380 
2381     hr = HlinkUpdateStackItem(NULL, NULL, HLID_CURRENT, &Moniker, location, NULL);
2382     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2383 
2384     SET_EXPECT(HBC_UpdateHlink);
2385     hr = HlinkUpdateStackItem(NULL, &HlinkBrowseContext, HLID_CURRENT, &Moniker, location, NULL);
2386     ok(hr == S_OK, "got 0x%08x\n", hr);
2387     CHECK_CALLED(HBC_UpdateHlink);
2388 
2389     SET_EXPECT(HLF_UpdateHlink);
2390     hr = HlinkUpdateStackItem(&testframe, &HlinkBrowseContext, HLID_CURRENT, &Moniker, location, NULL);
2391     ok(hr == S_OK, "got 0x%08x\n", hr);
2392     CHECK_CALLED(HLF_UpdateHlink);
2393 }
2394 
2395 START_TEST(hlink)
2396 {
2397     CoInitialize(NULL);
2398 
2399     test_HlinkIsShortcut();
2400     test_reference();
2401     test_persist();
2402     test_special_reference();
2403     test_HlinkCreateExtensionServices();
2404     test_HlinkParseDisplayName();
2405     test_HlinkResolveMonikerForData();
2406     test_HlinkGetSetMonikerReference();
2407     test_HlinkGetSetStringReference();
2408     test_HlinkMoniker();
2409     test_HashLink();
2410     test_HlinkSite();
2411     test_HlinkClone();
2412     test_StdHlink();
2413     test_Hlink_Navigate();
2414     test_HlinkUpdateStackItem();
2415 
2416     CoUninitialize();
2417 }
2418