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