xref: /reactos/dll/win32/hlink/link.c (revision d2c71d76)
1 /*
2  * Implementation of hyperlinking (hlink.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "hlink_private.h"
22 
23 #include "shellapi.h"
24 #include "hlguids.h"
25 
26 #include "wine/debug.h"
27 
28 WINE_DEFAULT_DEBUG_CHANNEL(hlink);
29 
30 #define HLINK_SAVE_MAGIC    0x00000002
31 #define HLINK_SAVE_MONIKER_PRESENT      0x01
32 #define HLINK_SAVE_MONIKER_IS_ABSOLUTE  0x02
33 #define HLINK_SAVE_LOCATION_PRESENT     0x08
34 #define HLINK_SAVE_FRIENDLY_PRESENT     0x10
35 /* 0x20, 0x40 unknown */
36 #define HLINK_SAVE_TARGET_FRAME_PRESENT 0x80
37 /* known flags */
38 #define HLINK_SAVE_ALL (HLINK_SAVE_TARGET_FRAME_PRESENT|HLINK_SAVE_FRIENDLY_PRESENT|HLINK_SAVE_LOCATION_PRESENT|0x04|HLINK_SAVE_MONIKER_IS_ABSOLUTE|HLINK_SAVE_MONIKER_PRESENT)
39 
40 typedef struct
41 {
42     IHlink              IHlink_iface;
43     LONG                ref;
44 
45     IPersistStream      IPersistStream_iface;
46     IDataObject         IDataObject_iface;
47 
48     LPWSTR              FriendlyName;
49     LPWSTR              Location;
50     LPWSTR              TargetFrameName;
51     IMoniker            *Moniker;
52     IHlinkSite          *Site;
53     DWORD               SiteData;
54     BOOL                absolute;
55 
56     IBindStatusCallback IBindStatusCallback_iface;
57     IBindStatusCallback *bind_callback;
58     IBindCtx *async_bind_ctx;
59     DWORD async_flags;
60     IHlinkBrowseContext *async_browse_ctx;
61 } HlinkImpl;
62 
63 static inline HlinkImpl *impl_from_IHlink(IHlink *iface)
64 {
65     return CONTAINING_RECORD(iface, HlinkImpl, IHlink_iface);
66 }
67 
68 
69 static inline HlinkImpl* impl_from_IPersistStream( IPersistStream* iface)
70 {
71     return CONTAINING_RECORD(iface, HlinkImpl, IPersistStream_iface);
72 }
73 
74 static inline HlinkImpl* impl_from_IDataObject( IDataObject* iface)
75 {
76     return CONTAINING_RECORD(iface, HlinkImpl, IDataObject_iface);
77 }
78 
79 static HRESULT __GetMoniker(HlinkImpl* This, IMoniker** moniker,
80         DWORD ref_type)
81 {
82     HRESULT hres;
83 
84     if (ref_type == HLINKGETREF_DEFAULT)
85         ref_type = HLINKGETREF_RELATIVE;
86 
87     if (This->Moniker)
88     {
89         DWORD mktype = MKSYS_NONE;
90 
91         hres = IMoniker_IsSystemMoniker(This->Moniker, &mktype);
92         if (hres == S_OK && mktype != MKSYS_NONE)
93         {
94             *moniker = This->Moniker;
95             IMoniker_AddRef(*moniker);
96             return S_OK;
97         }
98     }
99 
100     if (ref_type == HLINKGETREF_ABSOLUTE && This->Site)
101     {
102         IMoniker *hls_moniker;
103 
104         hres = IHlinkSite_GetMoniker(This->Site, This->SiteData,
105                 OLEGETMONIKER_FORCEASSIGN, OLEWHICHMK_CONTAINER, &hls_moniker);
106         if (FAILED(hres))
107             return hres;
108 
109         if (This->Moniker)
110         {
111             hres = IMoniker_ComposeWith(hls_moniker, This->Moniker, FALSE,
112                     moniker);
113             IMoniker_Release(hls_moniker);
114             return hres;
115         }
116 
117         *moniker = hls_moniker;
118         return S_OK;
119     }
120 
121     *moniker = This->Moniker;
122     if (*moniker)
123         IMoniker_AddRef(*moniker);
124 
125     return S_OK;
126 }
127 
128 static HRESULT WINAPI IHlink_fnQueryInterface(IHlink* iface, REFIID riid,
129         LPVOID *ppvObj)
130 {
131     HlinkImpl  *This = impl_from_IHlink(iface);
132 
133     TRACE ("(%p)->(%s,%p)\n", This, debugstr_guid (riid), ppvObj);
134 
135     *ppvObj = NULL;
136 
137     if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IHlink)))
138         *ppvObj = &This->IHlink_iface;
139     else if (IsEqualIID(riid, &IID_IPersistStream))
140         *ppvObj = &This->IPersistStream_iface;
141     else if (IsEqualIID(riid, &IID_IDataObject))
142         *ppvObj = &This->IDataObject_iface;
143 
144     if (*ppvObj)
145     {
146         IUnknown_AddRef((IUnknown*)(*ppvObj));
147         return S_OK;
148     }
149     return E_NOINTERFACE;
150 }
151 
152 static ULONG WINAPI IHlink_fnAddRef (IHlink* iface)
153 {
154     HlinkImpl  *This = impl_from_IHlink(iface);
155     ULONG refCount = InterlockedIncrement(&This->ref);
156 
157     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
158 
159     return refCount;
160 }
161 
162 static ULONG WINAPI IHlink_fnRelease (IHlink* iface)
163 {
164     HlinkImpl  *This = impl_from_IHlink(iface);
165     ULONG refCount = InterlockedDecrement(&This->ref);
166 
167     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
168     if (refCount)
169         return refCount;
170 
171     TRACE("-- destroying IHlink (%p)\n", This);
172     heap_free(This->FriendlyName);
173     heap_free(This->TargetFrameName);
174     heap_free(This->Location);
175     if (This->Moniker)
176         IMoniker_Release(This->Moniker);
177     if (This->Site)
178         IHlinkSite_Release(This->Site);
179     heap_free(This);
180     return 0;
181 }
182 
183 static HRESULT WINAPI IHlink_fnSetHlinkSite( IHlink* iface,
184         IHlinkSite* pihlSite, DWORD dwSiteData)
185 {
186     HlinkImpl  *This = impl_from_IHlink(iface);
187 
188     TRACE("(%p)->(%p %i)\n", This, pihlSite, dwSiteData);
189 
190     if (This->Site)
191         IHlinkSite_Release(This->Site);
192 
193     This->Site = pihlSite;
194     if (This->Site)
195         IHlinkSite_AddRef(This->Site);
196 
197     This->SiteData = dwSiteData;
198 
199     return S_OK;
200 }
201 
202 static HRESULT WINAPI IHlink_fnGetHlinkSite( IHlink* iface,
203         IHlinkSite** ppihlSite, DWORD *pdwSiteData)
204 {
205     HlinkImpl  *This = impl_from_IHlink(iface);
206 
207     TRACE("(%p)->(%p %p)\n", This, ppihlSite, pdwSiteData);
208 
209     *ppihlSite = This->Site;
210 
211     if (This->Site) {
212         IHlinkSite_AddRef(This->Site);
213         *pdwSiteData = This->SiteData;
214     }
215 
216     return S_OK;
217 }
218 
219 static HRESULT WINAPI IHlink_fnSetMonikerReference( IHlink* iface,
220         DWORD rfHLSETF, IMoniker *pmkTarget, LPCWSTR pwzLocation)
221 {
222     HlinkImpl  *This = impl_from_IHlink(iface);
223 
224     TRACE("(%p)->(%i %p %s)\n", This, rfHLSETF, pmkTarget,
225             debugstr_w(pwzLocation));
226 
227     if(rfHLSETF == 0)
228         return E_INVALIDARG;
229     if(!(rfHLSETF & (HLINKSETF_TARGET | HLINKSETF_LOCATION)))
230         return rfHLSETF;
231 
232     if(rfHLSETF & HLINKSETF_TARGET){
233         if (This->Moniker)
234             IMoniker_Release(This->Moniker);
235 
236         This->Moniker = pmkTarget;
237         if (This->Moniker)
238         {
239             IBindCtx *pbc;
240             LPOLESTR display_name;
241             IMoniker_AddRef(This->Moniker);
242             CreateBindCtx( 0, &pbc);
243             IMoniker_GetDisplayName(This->Moniker, pbc, NULL, &display_name);
244             IBindCtx_Release(pbc);
245             This->absolute = display_name && strchrW(display_name, ':');
246             CoTaskMemFree(display_name);
247         }
248     }
249 
250     if(rfHLSETF & HLINKSETF_LOCATION){
251         heap_free(This->Location);
252         This->Location = hlink_strdupW( pwzLocation );
253     }
254 
255     return S_OK;
256 }
257 
258 static HRESULT WINAPI IHlink_fnSetStringReference(IHlink* iface,
259         DWORD grfHLSETF, LPCWSTR pwzTarget, LPCWSTR pwzLocation)
260 {
261     HlinkImpl  *This = impl_from_IHlink(iface);
262 
263     TRACE("(%p)->(%i %s %s)\n", This, grfHLSETF, debugstr_w(pwzTarget),
264             debugstr_w(pwzLocation));
265 
266     if(grfHLSETF > (HLINKSETF_TARGET | HLINKSETF_LOCATION) &&
267             grfHLSETF < -(HLINKSETF_TARGET | HLINKSETF_LOCATION))
268         return grfHLSETF;
269 
270     if (grfHLSETF & HLINKSETF_TARGET)
271     {
272         if (This->Moniker)
273         {
274             IMoniker_Release(This->Moniker);
275             This->Moniker = NULL;
276         }
277         if (pwzTarget && *pwzTarget)
278         {
279             IMoniker *pMon;
280             IBindCtx *pbc = NULL;
281             ULONG eaten;
282             HRESULT r;
283 
284             r = CreateBindCtx(0, &pbc);
285             if (FAILED(r))
286                 return E_OUTOFMEMORY;
287 
288             r = MkParseDisplayName(pbc, pwzTarget, &eaten, &pMon);
289             IBindCtx_Release(pbc);
290 
291             if (FAILED(r))
292             {
293                 LPCWSTR p = strchrW(pwzTarget, ':');
294                 if (p && (p - pwzTarget > 1))
295                     r = CreateURLMoniker(NULL, pwzTarget, &pMon);
296                 else
297                     r = CreateFileMoniker(pwzTarget, &pMon);
298                 if (FAILED(r))
299                 {
300                     ERR("couldn't create moniker for %s, failed with error 0x%08x\n",
301                         debugstr_w(pwzTarget), r);
302                     return r;
303                 }
304             }
305 
306             IHlink_SetMonikerReference(iface, HLINKSETF_TARGET, pMon, NULL);
307             IMoniker_Release(pMon);
308         }
309     }
310 
311     if (grfHLSETF & HLINKSETF_LOCATION)
312     {
313         heap_free(This->Location);
314         This->Location = NULL;
315         if (pwzLocation && *pwzLocation)
316             This->Location = hlink_strdupW( pwzLocation );
317     }
318 
319     return S_OK;
320 }
321 
322 static HRESULT WINAPI IHlink_fnGetMonikerReference(IHlink* iface,
323         DWORD dwWhichRef, IMoniker **ppimkTarget, LPWSTR *ppwzLocation)
324 {
325     HlinkImpl  *This = impl_from_IHlink(iface);
326 
327     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppimkTarget,
328             ppwzLocation);
329 
330     if (ppimkTarget)
331     {
332         HRESULT hres = __GetMoniker(This, ppimkTarget, dwWhichRef);
333         if (FAILED(hres))
334         {
335             if (ppwzLocation)
336                 *ppwzLocation = NULL;
337             return hres;
338         }
339     }
340 
341     if (ppwzLocation)
342         IHlink_GetStringReference(iface, dwWhichRef, NULL, ppwzLocation);
343 
344     return S_OK;
345 }
346 
347 static HRESULT WINAPI IHlink_fnGetStringReference (IHlink* iface,
348         DWORD dwWhichRef, LPWSTR *ppwzTarget, LPWSTR *ppwzLocation)
349 {
350     HlinkImpl  *This = impl_from_IHlink(iface);
351 
352     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppwzTarget, ppwzLocation);
353 
354     if(dwWhichRef != -1 && dwWhichRef & ~(HLINKGETREF_DEFAULT | HLINKGETREF_ABSOLUTE | HLINKGETREF_RELATIVE))
355     {
356         if(ppwzTarget)
357             *ppwzTarget = NULL;
358         if(ppwzLocation)
359             *ppwzLocation = NULL;
360         return E_INVALIDARG;
361     }
362 
363     if (ppwzTarget)
364     {
365         IMoniker* mon;
366         HRESULT hres = __GetMoniker(This, &mon, dwWhichRef);
367         if (FAILED(hres))
368         {
369             if (ppwzLocation)
370                 *ppwzLocation = NULL;
371             return hres;
372         }
373         if (mon)
374         {
375             IBindCtx *pbc;
376 
377             CreateBindCtx( 0, &pbc);
378             IMoniker_GetDisplayName(mon, pbc, NULL, ppwzTarget);
379             IBindCtx_Release(pbc);
380             IMoniker_Release(mon);
381         }
382         else
383             *ppwzTarget = NULL;
384     }
385     if (ppwzLocation)
386         *ppwzLocation = hlink_co_strdupW( This->Location );
387 
388     TRACE("(Target: %s Location: %s)\n",
389             (ppwzTarget)?debugstr_w(*ppwzTarget):"<NULL>",
390             (ppwzLocation)?debugstr_w(*ppwzLocation):"<NULL>");
391 
392     return S_OK;
393 }
394 
395 static HRESULT WINAPI IHlink_fnSetFriendlyName (IHlink *iface,
396         LPCWSTR pwzFriendlyName)
397 {
398     HlinkImpl  *This = impl_from_IHlink(iface);
399 
400     TRACE("(%p) -> (%s)\n", This, debugstr_w(pwzFriendlyName));
401 
402     heap_free(This->FriendlyName);
403     This->FriendlyName = hlink_strdupW( pwzFriendlyName );
404 
405     return S_OK;
406 }
407 
408 static HRESULT WINAPI IHlink_fnGetFriendlyName (IHlink* iface,
409         DWORD grfHLFNAMEF, LPWSTR* ppwzFriendlyName)
410 {
411     HlinkImpl  *This = impl_from_IHlink(iface);
412 
413     TRACE("(%p) -> (%i %p)\n", This, grfHLFNAMEF, ppwzFriendlyName);
414 
415     /* FIXME: Only using explicitly set and cached friendly names */
416 
417     if (This->FriendlyName)
418         *ppwzFriendlyName = hlink_co_strdupW( This->FriendlyName );
419     else
420     {
421         IMoniker *moniker;
422         HRESULT hres = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
423         if (FAILED(hres))
424         {
425             *ppwzFriendlyName = NULL;
426             return hres;
427         }
428         if (moniker)
429         {
430             IBindCtx *bcxt;
431             CreateBindCtx(0, &bcxt);
432 
433             IMoniker_GetDisplayName(moniker, bcxt, NULL, ppwzFriendlyName);
434             IBindCtx_Release(bcxt);
435             IMoniker_Release(moniker);
436         }
437         else
438             *ppwzFriendlyName = NULL;
439     }
440 
441     return S_OK;
442 }
443 
444 static HRESULT WINAPI IHlink_fnSetTargetFrameName(IHlink* iface,
445         LPCWSTR pwzTargetFramename)
446 {
447     HlinkImpl  *This = impl_from_IHlink(iface);
448     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzTargetFramename));
449 
450     heap_free(This->TargetFrameName);
451     This->TargetFrameName = hlink_strdupW( pwzTargetFramename );
452 
453     return S_OK;
454 }
455 
456 static HRESULT WINAPI IHlink_fnGetTargetFrameName(IHlink* iface,
457         LPWSTR *ppwzTargetFrameName)
458 {
459     HlinkImpl  *This = impl_from_IHlink(iface);
460 
461     TRACE("(%p)->(%p)\n", This, ppwzTargetFrameName);
462 
463     if(!This->TargetFrameName) {
464         *ppwzTargetFrameName = NULL;
465         return S_FALSE;
466     }
467 
468     *ppwzTargetFrameName = hlink_co_strdupW( This->TargetFrameName );
469     if(!*ppwzTargetFrameName)
470         return E_OUTOFMEMORY;
471 
472     return S_OK;
473 }
474 
475 static HRESULT WINAPI IHlink_fnGetMiscStatus(IHlink* iface, DWORD* pdwStatus)
476 {
477     FIXME("\n");
478     return E_NOTIMPL;
479 }
480 
481 static HRESULT WINAPI IHlink_fnNavigate(IHlink *iface, DWORD flags, IBindCtx *user_bind_ctx,
482         IBindStatusCallback *bind_callback, IHlinkBrowseContext *browse_ctx)
483 {
484     HlinkImpl *This = impl_from_IHlink(iface);
485     IMoniker *mon = NULL;
486     HRESULT r;
487 
488     TRACE("hlink %p, flags %#x, user_bind_ctx %p, bind_callback %p, browse_ctx %p.\n",
489             This, flags, user_bind_ctx, bind_callback, browse_ctx);
490 
491     if (This->async_bind_ctx)
492         return E_UNEXPECTED;
493 
494     r = __GetMoniker(This, &mon, HLINKGETREF_ABSOLUTE);
495     TRACE("Moniker %p\n", mon);
496 
497     if (SUCCEEDED(r))
498     {
499         IBindCtx *bind_ctx = NULL;
500         IUnknown *unk = NULL;
501         IHlinkTarget *target;
502 
503         if (browse_ctx)
504         {
505             r = IHlinkBrowseContext_GetObject(browse_ctx, mon, TRUE, &unk);
506             if (r != S_OK)
507             {
508                 CreateBindCtx(0, &bind_ctx);
509                 RegisterBindStatusCallback(bind_ctx, &This->IBindStatusCallback_iface, NULL, 0);
510                 This->bind_callback = bind_callback;
511                 r = IMoniker_BindToObject(mon, bind_ctx, NULL, &IID_IUnknown, (void**)&unk);
512                 if (r == MK_S_ASYNCHRONOUS)
513                 {
514                     This->async_bind_ctx = bind_ctx;
515                     This->async_flags = flags;
516                     if (bind_callback)
517                         IBindStatusCallback_AddRef(bind_callback);
518                     IHlinkBrowseContext_AddRef(This->async_browse_ctx = browse_ctx);
519                     IMoniker_Release(mon);
520                     return r;
521                 }
522             }
523             if (r == S_OK)
524             {
525                 r = IUnknown_QueryInterface(unk, &IID_IHlinkTarget, (void **)&target);
526                 IUnknown_Release(unk);
527             }
528             if (r == S_OK)
529             {
530                 if (bind_ctx) IHlinkTarget_SetBrowseContext(target, browse_ctx);
531                 r = IHlinkTarget_Navigate(target, flags, This->Location);
532                 IHlinkTarget_Release(target);
533             }
534 
535             RevokeBindStatusCallback(bind_ctx, &This->IBindStatusCallback_iface);
536             if (bind_ctx) IBindCtx_Release(bind_ctx);
537         }
538         else
539         {
540             static const WCHAR szOpen[] = {'o','p','e','n',0};
541             LPWSTR target = NULL;
542 
543             r = IHlink_GetStringReference(iface, HLINKGETREF_DEFAULT, &target, NULL);
544             if (SUCCEEDED(r) && target)
545             {
546                 ShellExecuteW(NULL, szOpen, target, NULL, NULL, SW_SHOW);
547                 CoTaskMemFree(target);
548                 r = DRAGDROP_S_DROP;
549             }
550         }
551         IMoniker_Release(mon);
552     }
553 
554     if (This->Site)
555         IHlinkSite_OnNavigationComplete(This->Site, This->SiteData, 0, r, NULL);
556 
557     TRACE("Finished Navigation\n");
558     return r;
559 }
560 
561 static HRESULT WINAPI IHlink_fnSetAdditionalParams(IHlink* iface,
562         LPCWSTR pwzAdditionalParams)
563 {
564     TRACE("Not implemented in native IHlink\n");
565     return E_NOTIMPL;
566 }
567 
568 static HRESULT WINAPI IHlink_fnGetAdditionalParams(IHlink* iface,
569         LPWSTR* ppwzAdditionalParams)
570 {
571     TRACE("Not implemented in native IHlink\n");
572     return E_NOTIMPL;
573 }
574 
575 static const IHlinkVtbl hlvt =
576 {
577     IHlink_fnQueryInterface,
578     IHlink_fnAddRef,
579     IHlink_fnRelease,
580     IHlink_fnSetHlinkSite,
581     IHlink_fnGetHlinkSite,
582     IHlink_fnSetMonikerReference,
583     IHlink_fnGetMonikerReference,
584     IHlink_fnSetStringReference,
585     IHlink_fnGetStringReference,
586     IHlink_fnSetFriendlyName,
587     IHlink_fnGetFriendlyName,
588     IHlink_fnSetTargetFrameName,
589     IHlink_fnGetTargetFrameName,
590     IHlink_fnGetMiscStatus,
591     IHlink_fnNavigate,
592     IHlink_fnSetAdditionalParams,
593     IHlink_fnGetAdditionalParams
594 };
595 
596 static HRESULT WINAPI IDataObject_fnQueryInterface(IDataObject* iface,
597         REFIID riid, LPVOID *ppvObj)
598 {
599     HlinkImpl *This = impl_from_IDataObject(iface);
600     TRACE("%p\n", This);
601     return IHlink_QueryInterface(&This->IHlink_iface, riid, ppvObj);
602 }
603 
604 static ULONG WINAPI IDataObject_fnAddRef (IDataObject* iface)
605 {
606     HlinkImpl *This = impl_from_IDataObject(iface);
607     TRACE("%p\n", This);
608     return IHlink_AddRef(&This->IHlink_iface);
609 }
610 
611 static ULONG WINAPI IDataObject_fnRelease (IDataObject* iface)
612 {
613     HlinkImpl *This = impl_from_IDataObject(iface);
614     TRACE("%p\n", This);
615     return IHlink_Release(&This->IHlink_iface);
616 }
617 
618 static HRESULT WINAPI IDataObject_fnGetData(IDataObject* iface,
619         FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
620 {
621     FIXME("\n");
622     return E_NOTIMPL;
623 }
624 
625 static HRESULT WINAPI IDataObject_fnGetDataHere(IDataObject* iface,
626         FORMATETC* pformatetc, STGMEDIUM* pmedium)
627 {
628     FIXME("\n");
629     return E_NOTIMPL;
630 }
631 
632 static HRESULT WINAPI IDataObject_fnQueryGetData(IDataObject* iface,
633         FORMATETC* pformatetc)
634 {
635     FIXME("\n");
636     return E_NOTIMPL;
637 }
638 
639 static HRESULT WINAPI IDataObject_fnGetConicalFormatEtc(IDataObject* iface,
640         FORMATETC* pformatetcIn, FORMATETC* pformatetcOut)
641 {
642     FIXME("\n");
643     return E_NOTIMPL;
644 }
645 
646 static HRESULT WINAPI IDataObject_fnSetData(IDataObject* iface,
647         FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
648 {
649     FIXME("\n");
650     return E_NOTIMPL;
651 }
652 
653 static HRESULT WINAPI IDataObject_fnEnumFormatEtc(IDataObject* iface,
654         DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
655 {
656     FIXME("\n");
657     return E_NOTIMPL;
658 }
659 
660 static HRESULT WINAPI IDataObject_fnDAdvise(IDataObject* iface,
661         FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink,
662         DWORD* pdwConnection)
663 {
664     FIXME("\n");
665     return E_NOTIMPL;
666 }
667 
668 static HRESULT WINAPI IDataObject_fnDUnadvise(IDataObject* iface,
669         DWORD dwConnection)
670 {
671     FIXME("\n");
672     return E_NOTIMPL;
673 }
674 
675 static HRESULT WINAPI IDataObject_fnEnumDAdvise(IDataObject* iface,
676         IEnumSTATDATA** ppenumAdvise)
677 {
678     FIXME("\n");
679     return E_NOTIMPL;
680 }
681 
682 static const IDataObjectVtbl dovt =
683 {
684     IDataObject_fnQueryInterface,
685     IDataObject_fnAddRef,
686     IDataObject_fnRelease,
687     IDataObject_fnGetData,
688     IDataObject_fnGetDataHere,
689     IDataObject_fnQueryGetData,
690     IDataObject_fnGetConicalFormatEtc,
691     IDataObject_fnSetData,
692     IDataObject_fnEnumFormatEtc,
693     IDataObject_fnDAdvise,
694     IDataObject_fnDUnadvise,
695     IDataObject_fnEnumDAdvise
696 };
697 
698 static HRESULT WINAPI IPersistStream_fnQueryInterface(IPersistStream* iface,
699         REFIID riid, LPVOID *ppvObj)
700 {
701     HlinkImpl *This = impl_from_IPersistStream(iface);
702     TRACE("(%p)\n", This);
703     return IHlink_QueryInterface(&This->IHlink_iface, riid, ppvObj);
704 }
705 
706 static ULONG WINAPI IPersistStream_fnAddRef (IPersistStream* iface)
707 {
708     HlinkImpl *This = impl_from_IPersistStream(iface);
709     TRACE("(%p)\n", This);
710     return IHlink_AddRef(&This->IHlink_iface);
711 }
712 
713 static ULONG WINAPI IPersistStream_fnRelease (IPersistStream* iface)
714 {
715     HlinkImpl *This = impl_from_IPersistStream(iface);
716     TRACE("(%p)\n", This);
717     return IHlink_Release(&This->IHlink_iface);
718 }
719 
720 static HRESULT WINAPI IPersistStream_fnGetClassID(IPersistStream* iface,
721         CLSID* pClassID)
722 {
723     HlinkImpl *This = impl_from_IPersistStream(iface);
724     TRACE("(%p)\n", This);
725     *pClassID = CLSID_StdHlink;
726     return S_OK;
727 }
728 
729 static HRESULT WINAPI IPersistStream_fnIsDirty(IPersistStream* iface)
730 {
731     FIXME("\n");
732     return E_NOTIMPL;
733 }
734 
735 static HRESULT write_hlink_string(IStream *pStm, LPCWSTR str)
736 {
737     DWORD len;
738     HRESULT hr;
739 
740     TRACE("(%p, %s)\n", pStm, debugstr_w(str));
741 
742     len = strlenW(str) + 1;
743 
744     hr = IStream_Write(pStm, &len, sizeof(len), NULL);
745     if (FAILED(hr)) return hr;
746 
747     hr = IStream_Write(pStm, str, len * sizeof(WCHAR), NULL);
748     if (FAILED(hr)) return hr;
749 
750     return S_OK;
751 }
752 
753 static inline ULONG size_hlink_string(LPCWSTR str)
754 {
755     return sizeof(DWORD) + (strlenW(str) + 1) * sizeof(WCHAR);
756 }
757 
758 static HRESULT read_hlink_string(IStream *pStm, LPWSTR *out_str)
759 {
760     LPWSTR str;
761     DWORD len;
762     ULONG read;
763     HRESULT hr;
764 
765     hr = IStream_Read(pStm, &len, sizeof(len), &read);
766     if (FAILED(hr)) return hr;
767     if (read != sizeof(len)) return STG_E_READFAULT;
768 
769     TRACE("read len %d\n", len);
770 
771     str = heap_alloc(len * sizeof(WCHAR));
772     if (!str) return E_OUTOFMEMORY;
773 
774     hr = IStream_Read(pStm, str, len * sizeof(WCHAR), &read);
775     if (FAILED(hr))
776     {
777         heap_free(str);
778         return hr;
779     }
780     if (read != len * sizeof(WCHAR))
781     {
782         heap_free(str);
783         return STG_E_READFAULT;
784     }
785     TRACE("read string %s\n", debugstr_w(str));
786 
787     *out_str = str;
788     return S_OK;
789 }
790 
791 static HRESULT WINAPI IPersistStream_fnLoad(IPersistStream* iface,
792         IStream* pStm)
793 {
794     HRESULT r;
795     DWORD hdr[2];
796     DWORD read;
797     HlinkImpl *This = impl_from_IPersistStream(iface);
798 
799     r = IStream_Read(pStm, hdr, sizeof(hdr), &read);
800     if (read != sizeof(hdr) || (hdr[0] != HLINK_SAVE_MAGIC))
801     {
802         r = E_FAIL;
803         goto end;
804     }
805     if (hdr[1] & ~HLINK_SAVE_ALL)
806         FIXME("unknown flag(s) 0x%x\n", hdr[1] & ~HLINK_SAVE_ALL);
807 
808     if (hdr[1] & HLINK_SAVE_TARGET_FRAME_PRESENT)
809     {
810         TRACE("loading target frame name\n");
811         r = read_hlink_string(pStm, &This->TargetFrameName);
812         if (FAILED(r)) goto end;
813     }
814 
815     if (hdr[1] & HLINK_SAVE_FRIENDLY_PRESENT)
816     {
817         TRACE("loading target friendly name\n");
818         if (!(hdr[1] & 0x4))
819             FIXME("0x4 flag not present with friendly name flag - not sure what this means\n");
820         r = read_hlink_string(pStm, &This->FriendlyName);
821         if (FAILED(r)) goto end;
822     }
823 
824     if (hdr[1] & HLINK_SAVE_MONIKER_PRESENT)
825     {
826         TRACE("loading moniker\n");
827         r = OleLoadFromStream(pStm, &IID_IMoniker, (LPVOID*)&(This->Moniker));
828         if (FAILED(r))
829             goto end;
830         This->absolute = (hdr[1] & HLINK_SAVE_MONIKER_IS_ABSOLUTE) != 0;
831     }
832 
833     if (hdr[1] & HLINK_SAVE_LOCATION_PRESENT)
834     {
835         TRACE("loading location\n");
836         r = read_hlink_string(pStm, &This->Location);
837         if (FAILED(r)) goto end;
838     }
839 
840 end:
841     TRACE("Load Result 0x%x (%p)\n", r, This->Moniker);
842 
843     return r;
844 }
845 
846 static HRESULT WINAPI IPersistStream_fnSave(IPersistStream* iface,
847         IStream* pStm, BOOL fClearDirty)
848 {
849     HRESULT r;
850     HlinkImpl *This = impl_from_IPersistStream(iface);
851     DWORD hdr[2];
852     IMoniker *moniker;
853 
854     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
855 
856     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
857     if (FAILED(r))
858         return r;
859     r = E_FAIL;
860 
861     hdr[0] = HLINK_SAVE_MAGIC;
862     hdr[1] = 0;
863 
864     if (moniker)
865         hdr[1] |= HLINK_SAVE_MONIKER_PRESENT;
866     if (This->absolute)
867         hdr[1] |= HLINK_SAVE_MONIKER_IS_ABSOLUTE;
868     if (This->Location)
869         hdr[1] |= HLINK_SAVE_LOCATION_PRESENT;
870     if (This->FriendlyName)
871         hdr[1] |= HLINK_SAVE_FRIENDLY_PRESENT | 4 /* FIXME */;
872     if (This->TargetFrameName)
873         hdr[1] |= HLINK_SAVE_TARGET_FRAME_PRESENT;
874 
875     IStream_Write(pStm, hdr, sizeof(hdr), NULL);
876 
877     if (This->TargetFrameName)
878     {
879         r = write_hlink_string(pStm, This->TargetFrameName);
880         if (FAILED(r)) goto end;
881     }
882 
883     if (This->FriendlyName)
884     {
885         r = write_hlink_string(pStm, This->FriendlyName);
886         if (FAILED(r)) goto end;
887     }
888 
889     if (moniker)
890     {
891         IPersistStream* monstream;
892 
893         monstream = NULL;
894         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
895                 (LPVOID*)&monstream);
896         if (monstream)
897         {
898             r = OleSaveToStream(monstream, pStm);
899             IPersistStream_Release(monstream);
900         }
901         if (FAILED(r)) goto end;
902     }
903 
904     if (This->Location)
905     {
906         r = write_hlink_string(pStm, This->Location);
907         if (FAILED(r)) goto end;
908     }
909 
910 end:
911     if (moniker) IMoniker_Release(moniker);
912     TRACE("Save Result 0x%x\n", r);
913 
914     return r;
915 }
916 
917 static HRESULT WINAPI IPersistStream_fnGetSizeMax(IPersistStream* iface,
918         ULARGE_INTEGER* pcbSize)
919 {
920     HRESULT r;
921     HlinkImpl *This = impl_from_IPersistStream(iface);
922     IMoniker *moniker;
923 
924     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
925 
926     pcbSize->QuadPart = sizeof(DWORD)*2;
927 
928     if (This->TargetFrameName)
929         pcbSize->QuadPart += size_hlink_string(This->TargetFrameName);
930 
931     if (This->FriendlyName)
932         pcbSize->QuadPart += size_hlink_string(This->FriendlyName);
933 
934     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
935     if (FAILED(r))
936         return r;
937     r = E_FAIL;
938 
939     if (moniker)
940     {
941         IPersistStream* monstream = NULL;
942         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
943                 (LPVOID*)&monstream);
944         if (monstream)
945         {
946             ULARGE_INTEGER mon_size;
947             r = IPersistStream_GetSizeMax(monstream, &mon_size);
948             pcbSize->QuadPart += mon_size.QuadPart;
949             IPersistStream_Release(monstream);
950         }
951         IMoniker_Release(moniker);
952     }
953 
954     if (This->Location)
955         pcbSize->QuadPart += size_hlink_string(This->Location);
956 
957     return r;
958 }
959 
960 static const IPersistStreamVtbl psvt =
961 {
962     IPersistStream_fnQueryInterface,
963     IPersistStream_fnAddRef,
964     IPersistStream_fnRelease,
965     IPersistStream_fnGetClassID,
966     IPersistStream_fnIsDirty,
967     IPersistStream_fnLoad,
968     IPersistStream_fnSave,
969     IPersistStream_fnGetSizeMax,
970 };
971 
972 static HlinkImpl *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
973 {
974     return CONTAINING_RECORD(iface, HlinkImpl, IBindStatusCallback_iface);
975 }
976 
977 static HRESULT WINAPI bind_callback_QueryInterface(IBindStatusCallback *iface, REFIID iid, void **out)
978 {
979     if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IBindStatusCallback))
980     {
981         IBindStatusCallback_AddRef(*out = iface);
982         return S_OK;
983     }
984 
985     WARN("No interface for %s.\n", debugstr_guid(iid));
986     return E_NOINTERFACE;
987 }
988 
989 static ULONG WINAPI bind_callback_AddRef(IBindStatusCallback *iface)
990 {
991     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
992     return IHlink_AddRef(&hlink->IHlink_iface);
993 }
994 
995 static ULONG WINAPI bind_callback_Release(IBindStatusCallback *iface)
996 {
997     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
998     return IHlink_Release(&hlink->IHlink_iface);
999 }
1000 
1001 static HRESULT WINAPI bind_callback_OnStartBinding(IBindStatusCallback *iface,
1002         DWORD reserved, IBinding *binding)
1003 {
1004     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1005 
1006     TRACE("hlink %p, reserved %#x, binding %p.\n", hlink, reserved, binding);
1007 
1008     if (hlink->bind_callback)
1009         return IBindStatusCallback_OnStartBinding(hlink->bind_callback, reserved, binding);
1010     return S_OK;
1011 }
1012 
1013 static HRESULT WINAPI bind_callback_GetPriority(IBindStatusCallback *iface, LONG *priority)
1014 {
1015     FIXME("iface %p, priority %p, stub!\n", iface, priority);
1016     return E_NOTIMPL;
1017 }
1018 
1019 static HRESULT WINAPI bind_callback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
1020 {
1021     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1022 
1023     TRACE("hlink %p, reserved %#x.\n", hlink, reserved);
1024 
1025     if (hlink->bind_callback)
1026         return IBindStatusCallback_OnLowResource(hlink->bind_callback, reserved);
1027     return S_OK;
1028 }
1029 
1030 static HRESULT WINAPI bind_callback_OnProgress(IBindStatusCallback *iface,
1031         ULONG progress, ULONG max, ULONG status, const WCHAR *text)
1032 {
1033     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1034 
1035     TRACE("hlink %p, progress %u, max %u, status %u, text %s.\n",
1036             hlink, progress, max, status, debugstr_w(text));
1037 
1038     if (hlink->bind_callback)
1039         return IBindStatusCallback_OnProgress(hlink->bind_callback, progress, max, status, text);
1040     return S_OK;
1041 }
1042 
1043 static HRESULT WINAPI bind_callback_OnStopBinding(IBindStatusCallback *iface,
1044         HRESULT hr, const WCHAR *error)
1045 {
1046     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1047 
1048     TRACE("hlink %p, hr %#x, error %s.\n", hlink, hr, debugstr_w(error));
1049 
1050     if (hlink->bind_callback)
1051         IBindStatusCallback_OnStopBinding(hlink->bind_callback, hr, error);
1052 
1053     if (hlink->async_bind_ctx)
1054     {
1055         if (hlink->bind_callback)
1056             IBindStatusCallback_Release(hlink->bind_callback);
1057         RevokeBindStatusCallback(hlink->async_bind_ctx, iface);
1058         IBindCtx_Release(hlink->async_bind_ctx);
1059         IHlinkBrowseContext_Release(hlink->async_browse_ctx);
1060         hlink->async_bind_ctx = NULL;
1061     }
1062     return S_OK;
1063 }
1064 
1065 static HRESULT WINAPI bind_callback_GetBindInfo(IBindStatusCallback *iface,
1066         DWORD *bind_flags, BINDINFO *bind_info)
1067 {
1068     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1069 
1070     TRACE("hlink %p, bind_flags %p, bind_info %p.\n", hlink, bind_flags, bind_info);
1071 
1072     if (hlink->bind_callback)
1073         return IBindStatusCallback_GetBindInfo(hlink->bind_callback, bind_flags, bind_info);
1074     return S_OK;
1075 }
1076 
1077 static HRESULT WINAPI bind_callback_OnDataAvailable(IBindStatusCallback *iface,
1078         DWORD flags, DWORD size, FORMATETC *formatetc, STGMEDIUM *stgmed)
1079 {
1080     FIXME("iface %p, flags %#x, size %d, formatetc %p, stgmed %p, stub!\n",
1081             iface, flags, size, formatetc, stgmed);
1082     return E_NOTIMPL;
1083 }
1084 
1085 static HRESULT WINAPI bind_callback_OnObjectAvailable(IBindStatusCallback *iface,
1086         REFIID iid, IUnknown *unk)
1087 {
1088     HlinkImpl *hlink = impl_from_IBindStatusCallback(iface);
1089     IHlinkTarget *target;
1090     HRESULT hr;
1091 
1092     TRACE("hlink %p, iid %s, unk %p.\n", hlink, debugstr_guid(iid), unk);
1093 
1094     if (hlink->bind_callback)
1095         IBindStatusCallback_OnObjectAvailable(hlink->bind_callback, iid, unk);
1096 
1097     if (hlink->async_bind_ctx)
1098     {
1099         hr = IUnknown_QueryInterface(unk, &IID_IHlinkTarget, (void **)&target);
1100         if (FAILED(hr))
1101             return hr;
1102 
1103         IHlinkTarget_SetBrowseContext(target, hlink->async_browse_ctx);
1104         hr = IHlinkTarget_Navigate(target, hlink->async_flags, hlink->Location);
1105         IHlinkTarget_Release(target);
1106 
1107         if (hlink->Site)
1108             IHlinkSite_OnNavigationComplete(hlink->Site, hlink->SiteData, 0, hr, NULL);
1109 
1110         return hr;
1111     }
1112 
1113     return S_OK;
1114 }
1115 
1116 static const IBindStatusCallbackVtbl bind_callback_vtbl =
1117 {
1118     bind_callback_QueryInterface,
1119     bind_callback_AddRef,
1120     bind_callback_Release,
1121     bind_callback_OnStartBinding,
1122     bind_callback_GetPriority,
1123     bind_callback_OnLowResource,
1124     bind_callback_OnProgress,
1125     bind_callback_OnStopBinding,
1126     bind_callback_GetBindInfo,
1127     bind_callback_OnDataAvailable,
1128     bind_callback_OnObjectAvailable,
1129 };
1130 
1131 HRESULT HLink_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
1132 {
1133     HlinkImpl * hl;
1134 
1135     TRACE("unkOut=%p riid=%s\n", pUnkOuter, debugstr_guid(riid));
1136     *ppv = NULL;
1137 
1138     if (pUnkOuter)
1139         return CLASS_E_NOAGGREGATION;
1140 
1141     hl = heap_alloc_zero(sizeof(HlinkImpl));
1142     if (!hl)
1143         return E_OUTOFMEMORY;
1144 
1145     hl->ref = 1;
1146     hl->IHlink_iface.lpVtbl = &hlvt;
1147     hl->IPersistStream_iface.lpVtbl = &psvt;
1148     hl->IDataObject_iface.lpVtbl = &dovt;
1149     hl->IBindStatusCallback_iface.lpVtbl = &bind_callback_vtbl;
1150 
1151     *ppv = hl;
1152     return S_OK;
1153 }
1154