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