gameux: IGameStatistics::GetMaxNameLength implementation.
[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              TargetFrameName;
55     IMoniker            *Moniker;
56     IHlinkSite          *Site;
57     DWORD               SiteData;
58     BOOL                absolute;
59 } HlinkImpl;
60
61
62 static inline HlinkImpl* HlinkImpl_from_IPersistStream( IPersistStream* iface)
63 {
64     return (HlinkImpl*) ((CHAR*)iface - FIELD_OFFSET(HlinkImpl, lpPSVtbl));
65 }
66
67 static inline HlinkImpl* HlinkImpl_from_IDataObject( IDataObject* iface)
68 {
69     return (HlinkImpl*) ((CHAR*)iface - FIELD_OFFSET(HlinkImpl, lpDOVtbl));
70 }
71
72 static HRESULT __GetMoniker(HlinkImpl* This, IMoniker** moniker,
73         DWORD ref_type)
74 {
75     HRESULT hres;
76
77     if (ref_type == HLINKGETREF_DEFAULT)
78         ref_type = HLINKGETREF_RELATIVE;
79
80     if (ref_type == HLINKGETREF_ABSOLUTE && This->Site)
81     {
82         IMoniker *hls_moniker;
83
84         hres = IHlinkSite_GetMoniker(This->Site, This->SiteData,
85                 OLEGETMONIKER_FORCEASSIGN, OLEWHICHMK_CONTAINER, &hls_moniker);
86         if (FAILED(hres))
87             return hres;
88
89         if (This->Moniker)
90         {
91             hres = IMoniker_ComposeWith(hls_moniker, This->Moniker, FALSE,
92                     moniker);
93             IMoniker_Release(hls_moniker);
94             return hres;
95         }
96
97         *moniker = hls_moniker;
98         return S_OK;
99     }
100
101     *moniker = This->Moniker;
102     if (*moniker)
103         IMoniker_AddRef(*moniker);
104
105     return S_OK;
106 }
107
108 HRESULT WINAPI HLink_Constructor(IUnknown *pUnkOuter, REFIID riid,
109         LPVOID *ppv)
110 {
111     HlinkImpl * hl;
112
113     TRACE("unkOut=%p riid=%s\n", pUnkOuter, debugstr_guid(riid));
114     *ppv = NULL;
115
116     if (pUnkOuter)
117         return CLASS_E_NOAGGREGATION;
118
119     hl = heap_alloc_zero(sizeof(HlinkImpl));
120     if (!hl)
121         return E_OUTOFMEMORY;
122
123     hl->ref = 1;
124     hl->lpVtbl = &hlvt;
125     hl->lpPSVtbl = &psvt;
126     hl->lpDOVtbl = &dovt;
127
128     *ppv = hl;
129     return S_OK;
130 }
131
132 static HRESULT WINAPI IHlink_fnQueryInterface(IHlink* iface, REFIID riid,
133         LPVOID *ppvObj)
134 {
135     HlinkImpl  *This = (HlinkImpl*)iface;
136
137     TRACE ("(%p)->(%s,%p)\n", This, debugstr_guid (riid), ppvObj);
138
139     *ppvObj = NULL;
140
141     if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IHlink)))
142         *ppvObj = This;
143     else if (IsEqualIID(riid, &IID_IPersistStream))
144         *ppvObj = &(This->lpPSVtbl);
145     else if (IsEqualIID(riid, &IID_IDataObject))
146         *ppvObj = &(This->lpDOVtbl);
147
148     if (*ppvObj)
149     {
150         IUnknown_AddRef((IUnknown*)(*ppvObj));
151         return S_OK;
152     }
153     return E_NOINTERFACE;
154 }
155
156 static ULONG WINAPI IHlink_fnAddRef (IHlink* iface)
157 {
158     HlinkImpl  *This = (HlinkImpl*)iface;
159     ULONG refCount = InterlockedIncrement(&This->ref);
160
161     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
162
163     return refCount;
164 }
165
166 static ULONG WINAPI IHlink_fnRelease (IHlink* iface)
167 {
168     HlinkImpl  *This = (HlinkImpl*)iface;
169     ULONG refCount = InterlockedDecrement(&This->ref);
170
171     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
172     if (refCount)
173         return refCount;
174
175     TRACE("-- destroying IHlink (%p)\n", This);
176     heap_free(This->FriendlyName);
177     heap_free(This->TargetFrameName);
178     heap_free(This->Location);
179     if (This->Moniker)
180         IMoniker_Release(This->Moniker);
181     if (This->Site)
182         IHlinkSite_Release(This->Site);
183     heap_free(This);
184     return 0;
185 }
186
187 static HRESULT WINAPI IHlink_fnSetHlinkSite( IHlink* iface,
188         IHlinkSite* pihlSite, DWORD dwSiteData)
189 {
190     HlinkImpl  *This = (HlinkImpl*)iface;
191
192     TRACE("(%p)->(%p %i)\n", This, pihlSite, dwSiteData);
193
194     if (This->Site)
195         IHlinkSite_Release(This->Site);
196
197     This->Site = pihlSite;
198     if (This->Site)
199         IHlinkSite_AddRef(This->Site);
200
201     This->SiteData = dwSiteData;
202
203     return S_OK;
204 }
205
206 static HRESULT WINAPI IHlink_fnGetHlinkSite( IHlink* iface,
207         IHlinkSite** ppihlSite, DWORD *pdwSiteData)
208 {
209     HlinkImpl  *This = (HlinkImpl*)iface;
210
211     TRACE("(%p)->(%p %p)\n", This, ppihlSite, pdwSiteData);
212
213     *ppihlSite = This->Site;
214     *pdwSiteData = This->SiteData;
215
216     if (This->Site)
217         IHlinkSite_AddRef(This->Site);
218
219     return S_OK;
220 }
221
222 static HRESULT WINAPI IHlink_fnSetMonikerReference( IHlink* iface,
223         DWORD rfHLSETF, IMoniker *pmkTarget, LPCWSTR pwzLocation)
224 {
225     HlinkImpl  *This = (HlinkImpl*)iface;
226
227     TRACE("(%p)->(%i %p %s)\n", This, rfHLSETF, pmkTarget,
228             debugstr_w(pwzLocation));
229
230     if(rfHLSETF == 0)
231         return E_INVALIDARG;
232     if(!(rfHLSETF & (HLINKSETF_TARGET | HLINKSETF_LOCATION)))
233         return rfHLSETF;
234
235     if(rfHLSETF & HLINKSETF_TARGET){
236         if (This->Moniker)
237             IMoniker_Release(This->Moniker);
238
239         This->Moniker = pmkTarget;
240         if (This->Moniker)
241         {
242             LPOLESTR display_name;
243             IMoniker_AddRef(This->Moniker);
244             IMoniker_GetDisplayName(This->Moniker, NULL, NULL, &display_name);
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 = (HlinkImpl*)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 = (HlinkImpl*)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 = (HlinkImpl*)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 = (HlinkImpl*)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 = (HlinkImpl*)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 = (HlinkImpl*)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 = (HlinkImpl*)iface;
460
461     TRACE("(%p)->(%p)\n", This, ppwzTargetFrameName);
462     *ppwzTargetFrameName = hlink_co_strdupW( This->TargetFrameName );
463
464     return S_OK;
465 }
466
467 static HRESULT WINAPI IHlink_fnGetMiscStatus(IHlink* iface, DWORD* pdwStatus)
468 {
469     FIXME("\n");
470     return E_NOTIMPL;
471 }
472
473 static HRESULT WINAPI IHlink_fnNavigate(IHlink* iface, DWORD grfHLNF, LPBC pbc,
474         IBindStatusCallback *pbsc, IHlinkBrowseContext *phbc)
475 {
476     HlinkImpl  *This = (HlinkImpl*)iface;
477     IMoniker *mon = NULL;
478     HRESULT r;
479
480     FIXME("Semi-Stub:(%p)->(%i %p %p %p)\n", This, grfHLNF, pbc, pbsc, phbc);
481
482     r = __GetMoniker(This, &mon, HLINKGETREF_ABSOLUTE);
483     TRACE("Moniker %p\n", mon);
484
485     if (SUCCEEDED(r))
486     {
487         IBindCtx *bcxt;
488         IHlinkTarget *target = NULL;
489
490         CreateBindCtx(0, &bcxt);
491
492         RegisterBindStatusCallback(bcxt, pbsc, NULL, 0);
493
494         r = IMoniker_BindToObject(mon, bcxt, NULL, &IID_IHlinkTarget,
495                 (LPVOID*)&target);
496         TRACE("IHlinkTarget returned 0x%x\n", r);
497         if (r == S_OK)
498         {
499             IHlinkTarget_SetBrowseContext(target, phbc);
500             IHlinkTarget_Navigate(target, grfHLNF, This->Location);
501             IHlinkTarget_Release(target);
502         }
503         else
504         {
505             static const WCHAR szOpen[] = {'o','p','e','n',0};
506             LPWSTR target = NULL;
507
508             r = IHlink_GetStringReference(iface, HLINKGETREF_DEFAULT, &target, NULL);
509             if (SUCCEEDED(r) && target)
510             {
511                 ShellExecuteW(NULL, szOpen, target, NULL, NULL, SW_SHOW);
512                 CoTaskMemFree(target);
513             }
514         }
515
516         RevokeBindStatusCallback(bcxt, pbsc);
517
518         IBindCtx_Release(bcxt);
519         IMoniker_Release(mon);
520     }
521
522     if (This->Site)
523         IHlinkSite_OnNavigationComplete(This->Site, This->SiteData, 0, r, NULL);
524
525     TRACE("Finished Navigation\n");
526     return r;
527 }
528
529 static HRESULT WINAPI IHlink_fnSetAdditonalParams(IHlink* iface,
530         LPCWSTR pwzAdditionalParams)
531 {
532     TRACE("Not implemented in native IHlink\n");
533     return E_NOTIMPL;
534 }
535
536 static HRESULT WINAPI IHlink_fnGetAdditionalParams(IHlink* iface,
537         LPWSTR* ppwzAdditionalParams)
538 {
539     TRACE("Not implemented in native IHlink\n");
540     return E_NOTIMPL;
541 }
542
543 static const IHlinkVtbl hlvt =
544 {
545     IHlink_fnQueryInterface,
546     IHlink_fnAddRef,
547     IHlink_fnRelease,
548     IHlink_fnSetHlinkSite,
549     IHlink_fnGetHlinkSite,
550     IHlink_fnSetMonikerReference,
551     IHlink_fnGetMonikerReference,
552     IHlink_fnSetStringReference,
553     IHlink_fnGetStringReference,
554     IHlink_fnSetFriendlyName,
555     IHlink_fnGetFriendlyName,
556     IHlink_fnSetTargetFrameName,
557     IHlink_fnGetTargetFrameName,
558     IHlink_fnGetMiscStatus,
559     IHlink_fnNavigate,
560     IHlink_fnSetAdditonalParams,
561     IHlink_fnGetAdditionalParams
562 };
563
564 static HRESULT WINAPI IDataObject_fnQueryInterface(IDataObject* iface,
565         REFIID riid, LPVOID *ppvObj)
566 {
567     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
568     TRACE("%p\n", This);
569     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
570 }
571
572 static ULONG WINAPI IDataObject_fnAddRef (IDataObject* iface)
573 {
574     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
575     TRACE("%p\n", This);
576     return IHlink_AddRef((IHlink*)This);
577 }
578
579 static ULONG WINAPI IDataObject_fnRelease (IDataObject* iface)
580 {
581     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
582     TRACE("%p\n", This);
583     return IHlink_Release((IHlink*)This);
584 }
585
586 static HRESULT WINAPI IDataObject_fnGetData(IDataObject* iface,
587         FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
588 {
589     FIXME("\n");
590     return E_NOTIMPL;
591 }
592
593 static HRESULT WINAPI IDataObject_fnGetDataHere(IDataObject* iface,
594         FORMATETC* pformatetc, STGMEDIUM* pmedium)
595 {
596     FIXME("\n");
597     return E_NOTIMPL;
598 }
599
600 static HRESULT WINAPI IDataObject_fnQueryGetData(IDataObject* iface,
601         FORMATETC* pformatetc)
602 {
603     FIXME("\n");
604     return E_NOTIMPL;
605 }
606
607 static HRESULT WINAPI IDataObject_fnGetConicalFormatEtc(IDataObject* iface,
608         FORMATETC* pformatetcIn, FORMATETC* pformatetcOut)
609 {
610     FIXME("\n");
611     return E_NOTIMPL;
612 }
613
614 static HRESULT WINAPI IDataObject_fnSetData(IDataObject* iface,
615         FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
616 {
617     FIXME("\n");
618     return E_NOTIMPL;
619 }
620
621 static HRESULT WINAPI IDataObject_fnEnumFormatEtc(IDataObject* iface,
622         DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
623 {
624     FIXME("\n");
625     return E_NOTIMPL;
626 }
627
628 static HRESULT WINAPI IDataObject_fnDAdvise(IDataObject* iface,
629         FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink,
630         DWORD* pdwConnection)
631 {
632     FIXME("\n");
633     return E_NOTIMPL;
634 }
635
636 static HRESULT WINAPI IDataObject_fnDUnadvise(IDataObject* iface,
637         DWORD dwConnection)
638 {
639     FIXME("\n");
640     return E_NOTIMPL;
641 }
642
643 static HRESULT WINAPI IDataObject_fnEnumDAdvise(IDataObject* iface,
644         IEnumSTATDATA** ppenumAdvise)
645 {
646     FIXME("\n");
647     return E_NOTIMPL;
648 }
649
650 static const IDataObjectVtbl dovt =
651 {
652     IDataObject_fnQueryInterface,
653     IDataObject_fnAddRef,
654     IDataObject_fnRelease,
655     IDataObject_fnGetData,
656     IDataObject_fnGetDataHere,
657     IDataObject_fnQueryGetData,
658     IDataObject_fnGetConicalFormatEtc,
659     IDataObject_fnSetData,
660     IDataObject_fnEnumFormatEtc,
661     IDataObject_fnDAdvise,
662     IDataObject_fnDUnadvise,
663     IDataObject_fnEnumDAdvise
664 };
665
666 static HRESULT WINAPI IPersistStream_fnQueryInterface(IPersistStream* iface,
667         REFIID riid, LPVOID *ppvObj)
668 {
669     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
670     TRACE("(%p)\n", This);
671     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
672 }
673
674 static ULONG WINAPI IPersistStream_fnAddRef (IPersistStream* iface)
675 {
676     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
677     TRACE("(%p)\n", This);
678     return IHlink_AddRef((IHlink*)This);
679 }
680
681 static ULONG WINAPI IPersistStream_fnRelease (IPersistStream* iface)
682 {
683     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
684     TRACE("(%p)\n", This);
685     return IHlink_Release((IHlink*)This);
686 }
687
688 static HRESULT WINAPI IPersistStream_fnGetClassID(IPersistStream* iface,
689         CLSID* pClassID)
690 {
691     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
692     TRACE("(%p)\n", This);
693     *pClassID = CLSID_StdHlink;
694     return S_OK;
695 }
696
697 static HRESULT WINAPI IPersistStream_fnIsDirty(IPersistStream* iface)
698 {
699     FIXME("\n");
700     return E_NOTIMPL;
701 }
702
703 static HRESULT write_hlink_string(IStream *pStm, LPCWSTR str)
704 {
705     DWORD len;
706     HRESULT hr;
707
708     TRACE("(%p, %s)\n", pStm, debugstr_w(str));
709
710     len = strlenW(str) + 1;
711
712     hr = IStream_Write(pStm, &len, sizeof(len), NULL);
713     if (FAILED(hr)) return hr;
714
715     hr = IStream_Write(pStm, str, len * sizeof(WCHAR), NULL);
716     if (FAILED(hr)) return hr;
717
718     return S_OK;
719 }
720
721 static inline ULONG size_hlink_string(LPCWSTR str)
722 {
723     return sizeof(DWORD) + (strlenW(str) + 1) * sizeof(WCHAR);
724 }
725
726 static HRESULT read_hlink_string(IStream *pStm, LPWSTR *out_str)
727 {
728     LPWSTR str;
729     DWORD len;
730     ULONG read;
731     HRESULT hr;
732
733     hr = IStream_Read(pStm, &len, sizeof(len), &read);
734     if (FAILED(hr)) return hr;
735     if (read != sizeof(len)) return STG_E_READFAULT;
736
737     TRACE("read len %d\n", len);
738
739     str = heap_alloc(len * sizeof(WCHAR));
740     if (!str) return E_OUTOFMEMORY;
741
742     hr = IStream_Read(pStm, str, len * sizeof(WCHAR), &read);
743     if (FAILED(hr))
744     {
745         heap_free(str);
746         return hr;
747     }
748     if (read != len * sizeof(WCHAR))
749     {
750         heap_free(str);
751         return STG_E_READFAULT;
752     }
753     TRACE("read string %s\n", debugstr_w(str));
754
755     *out_str = str;
756     return S_OK;
757 }
758
759 static HRESULT WINAPI IPersistStream_fnLoad(IPersistStream* iface,
760         IStream* pStm)
761 {
762     HRESULT r;
763     DWORD hdr[2];
764     DWORD read;
765     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
766
767     r = IStream_Read(pStm, hdr, sizeof(hdr), &read);
768     if (read != sizeof(hdr) || (hdr[0] != HLINK_SAVE_MAGIC))
769     {
770         r = E_FAIL;
771         goto end;
772     }
773     if (hdr[1] & ~HLINK_SAVE_ALL)
774         FIXME("unknown flag(s) 0x%x\n", hdr[1] & ~HLINK_SAVE_ALL);
775
776     if (hdr[1] & HLINK_SAVE_TARGET_FRAME_PRESENT)
777     {
778         TRACE("loading target frame name\n");
779         r = read_hlink_string(pStm, &This->TargetFrameName);
780         if (FAILED(r)) goto end;
781     }
782
783     if (hdr[1] & HLINK_SAVE_FRIENDLY_PRESENT)
784     {
785         TRACE("loading target friendly name\n");
786         if (!(hdr[1] & 0x4))
787             FIXME("0x4 flag not present with friendly name flag - not sure what this means\n");
788         r = read_hlink_string(pStm, &This->FriendlyName);
789         if (FAILED(r)) goto end;
790     }
791
792     if (hdr[1] & HLINK_SAVE_MONIKER_PRESENT)
793     {
794         TRACE("loading moniker\n");
795         r = OleLoadFromStream(pStm, &IID_IMoniker, (LPVOID*)&(This->Moniker));
796         if (FAILED(r))
797             goto end;
798         This->absolute = hdr[1] & HLINK_SAVE_MONIKER_IS_ABSOLUTE ? TRUE : FALSE;
799     }
800
801     if (hdr[1] & HLINK_SAVE_LOCATION_PRESENT)
802     {
803         TRACE("loading location\n");
804         r = read_hlink_string(pStm, &This->Location);
805         if (FAILED(r)) goto end;
806     }
807
808 end:
809     TRACE("Load Result 0x%x (%p)\n", r, This->Moniker);
810
811     return r;
812 }
813
814 static HRESULT WINAPI IPersistStream_fnSave(IPersistStream* iface,
815         IStream* pStm, BOOL fClearDirty)
816 {
817     HRESULT r;
818     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
819     DWORD hdr[2];
820     IMoniker *moniker;
821
822     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
823
824     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
825     if (FAILED(r))
826         return r;
827     r = E_FAIL;
828
829     hdr[0] = HLINK_SAVE_MAGIC;
830     hdr[1] = 0;
831
832     if (moniker)
833         hdr[1] |= HLINK_SAVE_MONIKER_PRESENT;
834     if (This->absolute)
835         hdr[1] |= HLINK_SAVE_MONIKER_IS_ABSOLUTE;
836     if (This->Location)
837         hdr[1] |= HLINK_SAVE_LOCATION_PRESENT;
838     if (This->FriendlyName)
839         hdr[1] |= HLINK_SAVE_FRIENDLY_PRESENT | 4 /* FIXME */;
840     if (This->TargetFrameName)
841         hdr[1] |= HLINK_SAVE_TARGET_FRAME_PRESENT;
842
843     IStream_Write(pStm, hdr, sizeof(hdr), NULL);
844
845     if (This->TargetFrameName)
846     {
847         r = write_hlink_string(pStm, This->TargetFrameName);
848         if (FAILED(r)) goto end;
849     }
850
851     if (This->FriendlyName)
852     {
853         r = write_hlink_string(pStm, This->FriendlyName);
854         if (FAILED(r)) goto end;
855     }
856
857     if (moniker)
858     {
859         IPersistStream* monstream;
860
861         monstream = NULL;
862         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
863                 (LPVOID*)&monstream);
864         if (monstream)
865         {
866             r = OleSaveToStream(monstream, pStm);
867             IPersistStream_Release(monstream);
868         }
869         if (FAILED(r)) goto end;
870     }
871
872     if (This->Location)
873     {
874         r = write_hlink_string(pStm, This->Location);
875         if (FAILED(r)) goto end;
876     }
877
878 end:
879     if (moniker) IMoniker_Release(moniker);
880     TRACE("Save Result 0x%x\n", r);
881
882     return r;
883 }
884
885 static HRESULT WINAPI IPersistStream_fnGetSizeMax(IPersistStream* iface,
886         ULARGE_INTEGER* pcbSize)
887 {
888     HRESULT r;
889     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
890     IMoniker *moniker;
891
892     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
893
894     pcbSize->QuadPart = sizeof(DWORD)*2;
895
896     if (This->TargetFrameName)
897         pcbSize->QuadPart += size_hlink_string(This->TargetFrameName);
898
899     if (This->FriendlyName)
900         pcbSize->QuadPart += size_hlink_string(This->FriendlyName);
901
902     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
903     if (FAILED(r))
904         return r;
905     r = E_FAIL;
906
907     if (moniker)
908     {
909         IPersistStream* monstream = NULL;
910         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
911                 (LPVOID*)&monstream);
912         if (monstream)
913         {
914             ULARGE_INTEGER mon_size;
915             r = IPersistStream_GetSizeMax(monstream, &mon_size);
916             pcbSize->QuadPart += mon_size.QuadPart;
917             IPersistStream_Release(monstream);
918         }
919         IMoniker_Release(moniker);
920     }
921
922     if (This->Location)
923         pcbSize->QuadPart += size_hlink_string(This->Location);
924
925     return r;
926 }
927
928 static const IPersistStreamVtbl psvt =
929 {
930     IPersistStream_fnQueryInterface,
931     IPersistStream_fnAddRef,
932     IPersistStream_fnRelease,
933     IPersistStream_fnGetClassID,
934     IPersistStream_fnIsDirty,
935     IPersistStream_fnLoad,
936     IPersistStream_fnSave,
937     IPersistStream_fnGetSizeMax,
938 };