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