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