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