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