msxml3: IXMLDOMSchemaCollection::get() is a stub for version 6.
[wine] / dlls / hlink / link.c
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 } HlinkImpl;
56
57 static inline HlinkImpl *impl_from_IHlink(IHlink *iface)
58 {
59     return CONTAINING_RECORD(iface, HlinkImpl, IHlink_iface);
60 }
61
62
63 static inline HlinkImpl* impl_from_IPersistStream( IPersistStream* iface)
64 {
65     return CONTAINING_RECORD(iface, HlinkImpl, IPersistStream_iface);
66 }
67
68 static inline HlinkImpl* impl_from_IDataObject( IDataObject* iface)
69 {
70     return CONTAINING_RECORD(iface, HlinkImpl, IDataObject_iface);
71 }
72
73 static HRESULT __GetMoniker(HlinkImpl* This, IMoniker** moniker,
74         DWORD ref_type)
75 {
76     HRESULT hres;
77
78     if (ref_type == HLINKGETREF_DEFAULT)
79         ref_type = HLINKGETREF_RELATIVE;
80
81     if (ref_type == HLINKGETREF_ABSOLUTE && This->Site)
82     {
83         IMoniker *hls_moniker;
84
85         hres = IHlinkSite_GetMoniker(This->Site, This->SiteData,
86                 OLEGETMONIKER_FORCEASSIGN, OLEWHICHMK_CONTAINER, &hls_moniker);
87         if (FAILED(hres))
88             return hres;
89
90         if (This->Moniker)
91         {
92             hres = IMoniker_ComposeWith(hls_moniker, This->Moniker, FALSE,
93                     moniker);
94             IMoniker_Release(hls_moniker);
95             return hres;
96         }
97
98         *moniker = hls_moniker;
99         return S_OK;
100     }
101
102     *moniker = This->Moniker;
103     if (*moniker)
104         IMoniker_AddRef(*moniker);
105
106     return S_OK;
107 }
108
109 static HRESULT WINAPI IHlink_fnQueryInterface(IHlink* iface, REFIID riid,
110         LPVOID *ppvObj)
111 {
112     HlinkImpl  *This = impl_from_IHlink(iface);
113
114     TRACE ("(%p)->(%s,%p)\n", This, debugstr_guid (riid), ppvObj);
115
116     *ppvObj = NULL;
117
118     if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IHlink)))
119         *ppvObj = This;
120     else if (IsEqualIID(riid, &IID_IPersistStream))
121         *ppvObj = &This->IPersistStream_iface;
122     else if (IsEqualIID(riid, &IID_IDataObject))
123         *ppvObj = &This->IDataObject_iface;
124
125     if (*ppvObj)
126     {
127         IUnknown_AddRef((IUnknown*)(*ppvObj));
128         return S_OK;
129     }
130     return E_NOINTERFACE;
131 }
132
133 static ULONG WINAPI IHlink_fnAddRef (IHlink* iface)
134 {
135     HlinkImpl  *This = impl_from_IHlink(iface);
136     ULONG refCount = InterlockedIncrement(&This->ref);
137
138     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
139
140     return refCount;
141 }
142
143 static ULONG WINAPI IHlink_fnRelease (IHlink* iface)
144 {
145     HlinkImpl  *This = impl_from_IHlink(iface);
146     ULONG refCount = InterlockedDecrement(&This->ref);
147
148     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
149     if (refCount)
150         return refCount;
151
152     TRACE("-- destroying IHlink (%p)\n", This);
153     heap_free(This->FriendlyName);
154     heap_free(This->TargetFrameName);
155     heap_free(This->Location);
156     if (This->Moniker)
157         IMoniker_Release(This->Moniker);
158     if (This->Site)
159         IHlinkSite_Release(This->Site);
160     heap_free(This);
161     return 0;
162 }
163
164 static HRESULT WINAPI IHlink_fnSetHlinkSite( IHlink* iface,
165         IHlinkSite* pihlSite, DWORD dwSiteData)
166 {
167     HlinkImpl  *This = impl_from_IHlink(iface);
168
169     TRACE("(%p)->(%p %i)\n", This, pihlSite, dwSiteData);
170
171     if (This->Site)
172         IHlinkSite_Release(This->Site);
173
174     This->Site = pihlSite;
175     if (This->Site)
176         IHlinkSite_AddRef(This->Site);
177
178     This->SiteData = dwSiteData;
179
180     return S_OK;
181 }
182
183 static HRESULT WINAPI IHlink_fnGetHlinkSite( IHlink* iface,
184         IHlinkSite** ppihlSite, DWORD *pdwSiteData)
185 {
186     HlinkImpl  *This = impl_from_IHlink(iface);
187
188     TRACE("(%p)->(%p %p)\n", This, ppihlSite, pdwSiteData);
189
190     *ppihlSite = This->Site;
191
192     if (This->Site) {
193         IHlinkSite_AddRef(This->Site);
194         *pdwSiteData = This->SiteData;
195     }
196
197     return S_OK;
198 }
199
200 static HRESULT WINAPI IHlink_fnSetMonikerReference( IHlink* iface,
201         DWORD rfHLSETF, IMoniker *pmkTarget, LPCWSTR pwzLocation)
202 {
203     HlinkImpl  *This = impl_from_IHlink(iface);
204
205     TRACE("(%p)->(%i %p %s)\n", This, rfHLSETF, pmkTarget,
206             debugstr_w(pwzLocation));
207
208     if(rfHLSETF == 0)
209         return E_INVALIDARG;
210     if(!(rfHLSETF & (HLINKSETF_TARGET | HLINKSETF_LOCATION)))
211         return rfHLSETF;
212
213     if(rfHLSETF & HLINKSETF_TARGET){
214         if (This->Moniker)
215             IMoniker_Release(This->Moniker);
216
217         This->Moniker = pmkTarget;
218         if (This->Moniker)
219         {
220             LPOLESTR display_name;
221             IMoniker_AddRef(This->Moniker);
222             IMoniker_GetDisplayName(This->Moniker, NULL, NULL, &display_name);
223             This->absolute = display_name && strchrW(display_name, ':');
224             CoTaskMemFree(display_name);
225         }
226     }
227
228     if(rfHLSETF & HLINKSETF_LOCATION){
229         heap_free(This->Location);
230         This->Location = hlink_strdupW( pwzLocation );
231     }
232
233     return S_OK;
234 }
235
236 static HRESULT WINAPI IHlink_fnSetStringReference(IHlink* iface,
237         DWORD grfHLSETF, LPCWSTR pwzTarget, LPCWSTR pwzLocation)
238 {
239     HlinkImpl  *This = impl_from_IHlink(iface);
240
241     TRACE("(%p)->(%i %s %s)\n", This, grfHLSETF, debugstr_w(pwzTarget),
242             debugstr_w(pwzLocation));
243
244     if(grfHLSETF > (HLINKSETF_TARGET | HLINKSETF_LOCATION) &&
245             grfHLSETF < -(HLINKSETF_TARGET | HLINKSETF_LOCATION))
246         return grfHLSETF;
247
248     if (grfHLSETF & HLINKSETF_TARGET)
249     {
250         if (This->Moniker)
251         {
252             IMoniker_Release(This->Moniker);
253             This->Moniker = NULL;
254         }
255         if (pwzTarget && *pwzTarget)
256         {
257             IMoniker *pMon;
258             IBindCtx *pbc = NULL;
259             ULONG eaten;
260             HRESULT r;
261
262             r = CreateBindCtx(0, &pbc);
263             if (FAILED(r))
264                 return E_OUTOFMEMORY;
265
266             r = MkParseDisplayName(pbc, pwzTarget, &eaten, &pMon);
267             IBindCtx_Release(pbc);
268
269             if (FAILED(r))
270             {
271                 LPCWSTR p = strchrW(pwzTarget, ':');
272                 if (p && (p - pwzTarget > 1))
273                     r = CreateURLMoniker(NULL, pwzTarget, &pMon);
274                 else
275                     r = CreateFileMoniker(pwzTarget, &pMon);
276                 if (FAILED(r))
277                 {
278                     ERR("couldn't create moniker for %s, failed with error 0x%08x\n",
279                         debugstr_w(pwzTarget), r);
280                     return r;
281                 }
282             }
283
284             IHlink_SetMonikerReference(iface, HLINKSETF_TARGET, pMon, NULL);
285             IMoniker_Release(pMon);
286         }
287     }
288
289     if (grfHLSETF & HLINKSETF_LOCATION)
290     {
291         heap_free(This->Location);
292         This->Location = NULL;
293         if (pwzLocation && *pwzLocation)
294             This->Location = hlink_strdupW( pwzLocation );
295     }
296
297     return S_OK;
298 }
299
300 static HRESULT WINAPI IHlink_fnGetMonikerReference(IHlink* iface,
301         DWORD dwWhichRef, IMoniker **ppimkTarget, LPWSTR *ppwzLocation)
302 {
303     HlinkImpl  *This = impl_from_IHlink(iface);
304
305     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppimkTarget,
306             ppwzLocation);
307
308     if (ppimkTarget)
309     {
310         HRESULT hres = __GetMoniker(This, ppimkTarget, dwWhichRef);
311         if (FAILED(hres))
312         {
313             if (ppwzLocation)
314                 *ppwzLocation = NULL;
315             return hres;
316         }
317     }
318
319     if (ppwzLocation)
320         IHlink_GetStringReference(iface, dwWhichRef, NULL, ppwzLocation);
321
322     return S_OK;
323 }
324
325 static HRESULT WINAPI IHlink_fnGetStringReference (IHlink* iface,
326         DWORD dwWhichRef, LPWSTR *ppwzTarget, LPWSTR *ppwzLocation)
327 {
328     HlinkImpl  *This = impl_from_IHlink(iface);
329
330     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppwzTarget, ppwzLocation);
331
332     if(dwWhichRef != -1 && dwWhichRef & ~(HLINKGETREF_DEFAULT | HLINKGETREF_ABSOLUTE | HLINKGETREF_RELATIVE))
333     {
334         if(ppwzTarget)
335             *ppwzTarget = NULL;
336         if(ppwzLocation)
337             *ppwzLocation = NULL;
338         return E_INVALIDARG;
339     }
340
341     if (ppwzTarget)
342     {
343         IMoniker* mon;
344         HRESULT hres = __GetMoniker(This, &mon, dwWhichRef);
345         if (FAILED(hres))
346         {
347             if (ppwzLocation)
348                 *ppwzLocation = NULL;
349             return hres;
350         }
351         if (mon)
352         {
353             IBindCtx *pbc;
354
355             CreateBindCtx( 0, &pbc);
356             IMoniker_GetDisplayName(mon, pbc, NULL, ppwzTarget);
357             IBindCtx_Release(pbc);
358             IMoniker_Release(mon);
359         }
360         else
361             *ppwzTarget = NULL;
362     }
363     if (ppwzLocation)
364         *ppwzLocation = hlink_co_strdupW( This->Location );
365
366     TRACE("(Target: %s Location: %s)\n",
367             (ppwzTarget)?debugstr_w(*ppwzTarget):"<NULL>",
368             (ppwzLocation)?debugstr_w(*ppwzLocation):"<NULL>");
369
370     return S_OK;
371 }
372
373 static HRESULT WINAPI IHlink_fnSetFriendlyName (IHlink *iface,
374         LPCWSTR pwzFriendlyName)
375 {
376     HlinkImpl  *This = impl_from_IHlink(iface);
377
378     TRACE("(%p) -> (%s)\n", This, debugstr_w(pwzFriendlyName));
379
380     heap_free(This->FriendlyName);
381     This->FriendlyName = hlink_strdupW( pwzFriendlyName );
382
383     return S_OK;
384 }
385
386 static HRESULT WINAPI IHlink_fnGetFriendlyName (IHlink* iface,
387         DWORD grfHLFNAMEF, LPWSTR* ppwzFriendlyName)
388 {
389     HlinkImpl  *This = impl_from_IHlink(iface);
390
391     TRACE("(%p) -> (%i %p)\n", This, grfHLFNAMEF, ppwzFriendlyName);
392
393     /* FIXME: Only using explicitly set and cached friendly names */
394
395     if (This->FriendlyName)
396         *ppwzFriendlyName = hlink_co_strdupW( This->FriendlyName );
397     else
398     {
399         IMoniker *moniker;
400         HRESULT hres = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
401         if (FAILED(hres))
402         {
403             *ppwzFriendlyName = NULL;
404             return hres;
405         }
406         if (moniker)
407         {
408             IBindCtx *bcxt;
409             CreateBindCtx(0, &bcxt);
410
411             IMoniker_GetDisplayName(moniker, bcxt, NULL, ppwzFriendlyName);
412             IBindCtx_Release(bcxt);
413             IMoniker_Release(moniker);
414         }
415         else
416             *ppwzFriendlyName = NULL;
417     }
418
419     return S_OK;
420 }
421
422 static HRESULT WINAPI IHlink_fnSetTargetFrameName(IHlink* iface,
423         LPCWSTR pwzTargetFramename)
424 {
425     HlinkImpl  *This = impl_from_IHlink(iface);
426     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzTargetFramename));
427
428     heap_free(This->TargetFrameName);
429     This->TargetFrameName = hlink_strdupW( pwzTargetFramename );
430
431     return S_OK;
432 }
433
434 static HRESULT WINAPI IHlink_fnGetTargetFrameName(IHlink* iface,
435         LPWSTR *ppwzTargetFrameName)
436 {
437     HlinkImpl  *This = impl_from_IHlink(iface);
438
439     TRACE("(%p)->(%p)\n", This, ppwzTargetFrameName);
440
441     if(!This->TargetFrameName) {
442         *ppwzTargetFrameName = NULL;
443         return S_FALSE;
444     }
445
446     *ppwzTargetFrameName = hlink_co_strdupW( This->TargetFrameName );
447     if(!*ppwzTargetFrameName)
448         return E_OUTOFMEMORY;
449
450     return S_OK;
451 }
452
453 static HRESULT WINAPI IHlink_fnGetMiscStatus(IHlink* iface, DWORD* pdwStatus)
454 {
455     FIXME("\n");
456     return E_NOTIMPL;
457 }
458
459 static HRESULT WINAPI IHlink_fnNavigate(IHlink* iface, DWORD grfHLNF, LPBC pbc,
460         IBindStatusCallback *pbsc, IHlinkBrowseContext *phbc)
461 {
462     HlinkImpl  *This = impl_from_IHlink(iface);
463     IMoniker *mon = NULL;
464     HRESULT r;
465
466     FIXME("Semi-Stub:(%p)->(%i %p %p %p)\n", This, grfHLNF, pbc, pbsc, phbc);
467
468     r = __GetMoniker(This, &mon, HLINKGETREF_ABSOLUTE);
469     TRACE("Moniker %p\n", mon);
470
471     if (SUCCEEDED(r))
472     {
473         IBindCtx *bcxt;
474         IHlinkTarget *target = NULL;
475
476         CreateBindCtx(0, &bcxt);
477
478         RegisterBindStatusCallback(bcxt, pbsc, NULL, 0);
479
480         r = IMoniker_BindToObject(mon, bcxt, NULL, &IID_IHlinkTarget,
481                 (LPVOID*)&target);
482         TRACE("IHlinkTarget returned 0x%x\n", r);
483         if (r == S_OK)
484         {
485             IHlinkTarget_SetBrowseContext(target, phbc);
486             IHlinkTarget_Navigate(target, grfHLNF, This->Location);
487             IHlinkTarget_Release(target);
488         }
489         else
490         {
491             static const WCHAR szOpen[] = {'o','p','e','n',0};
492             LPWSTR target = NULL;
493
494             r = IHlink_GetStringReference(iface, HLINKGETREF_DEFAULT, &target, NULL);
495             if (SUCCEEDED(r) && target)
496             {
497                 ShellExecuteW(NULL, szOpen, target, NULL, NULL, SW_SHOW);
498                 CoTaskMemFree(target);
499             }
500         }
501
502         RevokeBindStatusCallback(bcxt, pbsc);
503
504         IBindCtx_Release(bcxt);
505         IMoniker_Release(mon);
506     }
507
508     if (This->Site)
509         IHlinkSite_OnNavigationComplete(This->Site, This->SiteData, 0, r, NULL);
510
511     TRACE("Finished Navigation\n");
512     return r;
513 }
514
515 static HRESULT WINAPI IHlink_fnSetAdditonalParams(IHlink* iface,
516         LPCWSTR pwzAdditionalParams)
517 {
518     TRACE("Not implemented in native IHlink\n");
519     return E_NOTIMPL;
520 }
521
522 static HRESULT WINAPI IHlink_fnGetAdditionalParams(IHlink* iface,
523         LPWSTR* ppwzAdditionalParams)
524 {
525     TRACE("Not implemented in native IHlink\n");
526     return E_NOTIMPL;
527 }
528
529 static const IHlinkVtbl hlvt =
530 {
531     IHlink_fnQueryInterface,
532     IHlink_fnAddRef,
533     IHlink_fnRelease,
534     IHlink_fnSetHlinkSite,
535     IHlink_fnGetHlinkSite,
536     IHlink_fnSetMonikerReference,
537     IHlink_fnGetMonikerReference,
538     IHlink_fnSetStringReference,
539     IHlink_fnGetStringReference,
540     IHlink_fnSetFriendlyName,
541     IHlink_fnGetFriendlyName,
542     IHlink_fnSetTargetFrameName,
543     IHlink_fnGetTargetFrameName,
544     IHlink_fnGetMiscStatus,
545     IHlink_fnNavigate,
546     IHlink_fnSetAdditonalParams,
547     IHlink_fnGetAdditionalParams
548 };
549
550 static HRESULT WINAPI IDataObject_fnQueryInterface(IDataObject* iface,
551         REFIID riid, LPVOID *ppvObj)
552 {
553     HlinkImpl *This = impl_from_IDataObject(iface);
554     TRACE("%p\n", This);
555     return IHlink_QueryInterface(&This->IHlink_iface, riid, ppvObj);
556 }
557
558 static ULONG WINAPI IDataObject_fnAddRef (IDataObject* iface)
559 {
560     HlinkImpl *This = impl_from_IDataObject(iface);
561     TRACE("%p\n", This);
562     return IHlink_AddRef(&This->IHlink_iface);
563 }
564
565 static ULONG WINAPI IDataObject_fnRelease (IDataObject* iface)
566 {
567     HlinkImpl *This = impl_from_IDataObject(iface);
568     TRACE("%p\n", This);
569     return IHlink_Release(&This->IHlink_iface);
570 }
571
572 static HRESULT WINAPI IDataObject_fnGetData(IDataObject* iface,
573         FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
574 {
575     FIXME("\n");
576     return E_NOTIMPL;
577 }
578
579 static HRESULT WINAPI IDataObject_fnGetDataHere(IDataObject* iface,
580         FORMATETC* pformatetc, STGMEDIUM* pmedium)
581 {
582     FIXME("\n");
583     return E_NOTIMPL;
584 }
585
586 static HRESULT WINAPI IDataObject_fnQueryGetData(IDataObject* iface,
587         FORMATETC* pformatetc)
588 {
589     FIXME("\n");
590     return E_NOTIMPL;
591 }
592
593 static HRESULT WINAPI IDataObject_fnGetConicalFormatEtc(IDataObject* iface,
594         FORMATETC* pformatetcIn, FORMATETC* pformatetcOut)
595 {
596     FIXME("\n");
597     return E_NOTIMPL;
598 }
599
600 static HRESULT WINAPI IDataObject_fnSetData(IDataObject* iface,
601         FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
602 {
603     FIXME("\n");
604     return E_NOTIMPL;
605 }
606
607 static HRESULT WINAPI IDataObject_fnEnumFormatEtc(IDataObject* iface,
608         DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
609 {
610     FIXME("\n");
611     return E_NOTIMPL;
612 }
613
614 static HRESULT WINAPI IDataObject_fnDAdvise(IDataObject* iface,
615         FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink,
616         DWORD* pdwConnection)
617 {
618     FIXME("\n");
619     return E_NOTIMPL;
620 }
621
622 static HRESULT WINAPI IDataObject_fnDUnadvise(IDataObject* iface,
623         DWORD dwConnection)
624 {
625     FIXME("\n");
626     return E_NOTIMPL;
627 }
628
629 static HRESULT WINAPI IDataObject_fnEnumDAdvise(IDataObject* iface,
630         IEnumSTATDATA** ppenumAdvise)
631 {
632     FIXME("\n");
633     return E_NOTIMPL;
634 }
635
636 static const IDataObjectVtbl dovt =
637 {
638     IDataObject_fnQueryInterface,
639     IDataObject_fnAddRef,
640     IDataObject_fnRelease,
641     IDataObject_fnGetData,
642     IDataObject_fnGetDataHere,
643     IDataObject_fnQueryGetData,
644     IDataObject_fnGetConicalFormatEtc,
645     IDataObject_fnSetData,
646     IDataObject_fnEnumFormatEtc,
647     IDataObject_fnDAdvise,
648     IDataObject_fnDUnadvise,
649     IDataObject_fnEnumDAdvise
650 };
651
652 static HRESULT WINAPI IPersistStream_fnQueryInterface(IPersistStream* iface,
653         REFIID riid, LPVOID *ppvObj)
654 {
655     HlinkImpl *This = impl_from_IPersistStream(iface);
656     TRACE("(%p)\n", This);
657     return IHlink_QueryInterface(&This->IHlink_iface, riid, ppvObj);
658 }
659
660 static ULONG WINAPI IPersistStream_fnAddRef (IPersistStream* iface)
661 {
662     HlinkImpl *This = impl_from_IPersistStream(iface);
663     TRACE("(%p)\n", This);
664     return IHlink_AddRef(&This->IHlink_iface);
665 }
666
667 static ULONG WINAPI IPersistStream_fnRelease (IPersistStream* iface)
668 {
669     HlinkImpl *This = impl_from_IPersistStream(iface);
670     TRACE("(%p)\n", This);
671     return IHlink_Release(&This->IHlink_iface);
672 }
673
674 static HRESULT WINAPI IPersistStream_fnGetClassID(IPersistStream* iface,
675         CLSID* pClassID)
676 {
677     HlinkImpl *This = impl_from_IPersistStream(iface);
678     TRACE("(%p)\n", This);
679     *pClassID = CLSID_StdHlink;
680     return S_OK;
681 }
682
683 static HRESULT WINAPI IPersistStream_fnIsDirty(IPersistStream* iface)
684 {
685     FIXME("\n");
686     return E_NOTIMPL;
687 }
688
689 static HRESULT write_hlink_string(IStream *pStm, LPCWSTR str)
690 {
691     DWORD len;
692     HRESULT hr;
693
694     TRACE("(%p, %s)\n", pStm, debugstr_w(str));
695
696     len = strlenW(str) + 1;
697
698     hr = IStream_Write(pStm, &len, sizeof(len), NULL);
699     if (FAILED(hr)) return hr;
700
701     hr = IStream_Write(pStm, str, len * sizeof(WCHAR), NULL);
702     if (FAILED(hr)) return hr;
703
704     return S_OK;
705 }
706
707 static inline ULONG size_hlink_string(LPCWSTR str)
708 {
709     return sizeof(DWORD) + (strlenW(str) + 1) * sizeof(WCHAR);
710 }
711
712 static HRESULT read_hlink_string(IStream *pStm, LPWSTR *out_str)
713 {
714     LPWSTR str;
715     DWORD len;
716     ULONG read;
717     HRESULT hr;
718
719     hr = IStream_Read(pStm, &len, sizeof(len), &read);
720     if (FAILED(hr)) return hr;
721     if (read != sizeof(len)) return STG_E_READFAULT;
722
723     TRACE("read len %d\n", len);
724
725     str = heap_alloc(len * sizeof(WCHAR));
726     if (!str) return E_OUTOFMEMORY;
727
728     hr = IStream_Read(pStm, str, len * sizeof(WCHAR), &read);
729     if (FAILED(hr))
730     {
731         heap_free(str);
732         return hr;
733     }
734     if (read != len * sizeof(WCHAR))
735     {
736         heap_free(str);
737         return STG_E_READFAULT;
738     }
739     TRACE("read string %s\n", debugstr_w(str));
740
741     *out_str = str;
742     return S_OK;
743 }
744
745 static HRESULT WINAPI IPersistStream_fnLoad(IPersistStream* iface,
746         IStream* pStm)
747 {
748     HRESULT r;
749     DWORD hdr[2];
750     DWORD read;
751     HlinkImpl *This = impl_from_IPersistStream(iface);
752
753     r = IStream_Read(pStm, hdr, sizeof(hdr), &read);
754     if (read != sizeof(hdr) || (hdr[0] != HLINK_SAVE_MAGIC))
755     {
756         r = E_FAIL;
757         goto end;
758     }
759     if (hdr[1] & ~HLINK_SAVE_ALL)
760         FIXME("unknown flag(s) 0x%x\n", hdr[1] & ~HLINK_SAVE_ALL);
761
762     if (hdr[1] & HLINK_SAVE_TARGET_FRAME_PRESENT)
763     {
764         TRACE("loading target frame name\n");
765         r = read_hlink_string(pStm, &This->TargetFrameName);
766         if (FAILED(r)) goto end;
767     }
768
769     if (hdr[1] & HLINK_SAVE_FRIENDLY_PRESENT)
770     {
771         TRACE("loading target friendly name\n");
772         if (!(hdr[1] & 0x4))
773             FIXME("0x4 flag not present with friendly name flag - not sure what this means\n");
774         r = read_hlink_string(pStm, &This->FriendlyName);
775         if (FAILED(r)) goto end;
776     }
777
778     if (hdr[1] & HLINK_SAVE_MONIKER_PRESENT)
779     {
780         TRACE("loading moniker\n");
781         r = OleLoadFromStream(pStm, &IID_IMoniker, (LPVOID*)&(This->Moniker));
782         if (FAILED(r))
783             goto end;
784         This->absolute = hdr[1] & HLINK_SAVE_MONIKER_IS_ABSOLUTE ? TRUE : FALSE;
785     }
786
787     if (hdr[1] & HLINK_SAVE_LOCATION_PRESENT)
788     {
789         TRACE("loading location\n");
790         r = read_hlink_string(pStm, &This->Location);
791         if (FAILED(r)) goto end;
792     }
793
794 end:
795     TRACE("Load Result 0x%x (%p)\n", r, This->Moniker);
796
797     return r;
798 }
799
800 static HRESULT WINAPI IPersistStream_fnSave(IPersistStream* iface,
801         IStream* pStm, BOOL fClearDirty)
802 {
803     HRESULT r;
804     HlinkImpl *This = impl_from_IPersistStream(iface);
805     DWORD hdr[2];
806     IMoniker *moniker;
807
808     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
809
810     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
811     if (FAILED(r))
812         return r;
813     r = E_FAIL;
814
815     hdr[0] = HLINK_SAVE_MAGIC;
816     hdr[1] = 0;
817
818     if (moniker)
819         hdr[1] |= HLINK_SAVE_MONIKER_PRESENT;
820     if (This->absolute)
821         hdr[1] |= HLINK_SAVE_MONIKER_IS_ABSOLUTE;
822     if (This->Location)
823         hdr[1] |= HLINK_SAVE_LOCATION_PRESENT;
824     if (This->FriendlyName)
825         hdr[1] |= HLINK_SAVE_FRIENDLY_PRESENT | 4 /* FIXME */;
826     if (This->TargetFrameName)
827         hdr[1] |= HLINK_SAVE_TARGET_FRAME_PRESENT;
828
829     IStream_Write(pStm, hdr, sizeof(hdr), NULL);
830
831     if (This->TargetFrameName)
832     {
833         r = write_hlink_string(pStm, This->TargetFrameName);
834         if (FAILED(r)) goto end;
835     }
836
837     if (This->FriendlyName)
838     {
839         r = write_hlink_string(pStm, This->FriendlyName);
840         if (FAILED(r)) goto end;
841     }
842
843     if (moniker)
844     {
845         IPersistStream* monstream;
846
847         monstream = NULL;
848         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
849                 (LPVOID*)&monstream);
850         if (monstream)
851         {
852             r = OleSaveToStream(monstream, pStm);
853             IPersistStream_Release(monstream);
854         }
855         if (FAILED(r)) goto end;
856     }
857
858     if (This->Location)
859     {
860         r = write_hlink_string(pStm, This->Location);
861         if (FAILED(r)) goto end;
862     }
863
864 end:
865     if (moniker) IMoniker_Release(moniker);
866     TRACE("Save Result 0x%x\n", r);
867
868     return r;
869 }
870
871 static HRESULT WINAPI IPersistStream_fnGetSizeMax(IPersistStream* iface,
872         ULARGE_INTEGER* pcbSize)
873 {
874     HRESULT r;
875     HlinkImpl *This = impl_from_IPersistStream(iface);
876     IMoniker *moniker;
877
878     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
879
880     pcbSize->QuadPart = sizeof(DWORD)*2;
881
882     if (This->TargetFrameName)
883         pcbSize->QuadPart += size_hlink_string(This->TargetFrameName);
884
885     if (This->FriendlyName)
886         pcbSize->QuadPart += size_hlink_string(This->FriendlyName);
887
888     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
889     if (FAILED(r))
890         return r;
891     r = E_FAIL;
892
893     if (moniker)
894     {
895         IPersistStream* monstream = NULL;
896         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
897                 (LPVOID*)&monstream);
898         if (monstream)
899         {
900             ULARGE_INTEGER mon_size;
901             r = IPersistStream_GetSizeMax(monstream, &mon_size);
902             pcbSize->QuadPart += mon_size.QuadPart;
903             IPersistStream_Release(monstream);
904         }
905         IMoniker_Release(moniker);
906     }
907
908     if (This->Location)
909         pcbSize->QuadPart += size_hlink_string(This->Location);
910
911     return r;
912 }
913
914 static const IPersistStreamVtbl psvt =
915 {
916     IPersistStream_fnQueryInterface,
917     IPersistStream_fnAddRef,
918     IPersistStream_fnRelease,
919     IPersistStream_fnGetClassID,
920     IPersistStream_fnIsDirty,
921     IPersistStream_fnLoad,
922     IPersistStream_fnSave,
923     IPersistStream_fnGetSizeMax,
924 };
925
926 HRESULT HLink_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
927 {
928     HlinkImpl * hl;
929
930     TRACE("unkOut=%p riid=%s\n", pUnkOuter, debugstr_guid(riid));
931     *ppv = NULL;
932
933     if (pUnkOuter)
934         return CLASS_E_NOAGGREGATION;
935
936     hl = heap_alloc_zero(sizeof(HlinkImpl));
937     if (!hl)
938         return E_OUTOFMEMORY;
939
940     hl->ref = 1;
941     hl->IHlink_iface.lpVtbl = &hlvt;
942     hl->IPersistStream_iface.lpVtbl = &psvt;
943     hl->IDataObject_iface.lpVtbl = &dovt;
944
945     *ppv = hl;
946     return S_OK;
947 }