Don't generate FIXME warnings for DllCanUnloadNow.
[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 = hlink_strdupW( pwzLocation );
295     }
296
297     return S_OK;
298 }
299
300 static HRESULT WINAPI IHlink_fnGetMonikerReference(IHlink* iface,
301         DWORD dwWhichRef, IMoniker **ppimkTarget, LPWSTR *ppwzLocation)
302 {
303     HlinkImpl  *This = (HlinkImpl*)iface;
304
305     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppimkTarget,
306             ppwzLocation);
307
308     if(ppimkTarget)
309         __GetMoniker(This, ppimkTarget);
310
311     if (ppwzLocation)
312         IHlink_GetStringReference(iface, dwWhichRef, NULL, ppwzLocation);
313
314     return S_OK;
315 }
316
317 static HRESULT WINAPI IHlink_fnGetStringReference (IHlink* iface,
318         DWORD dwWhichRef, LPWSTR *ppwzTarget, LPWSTR *ppwzLocation)
319 {
320     HlinkImpl  *This = (HlinkImpl*)iface;
321
322     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppwzTarget, ppwzLocation);
323
324     /* note: undocumented behavior with dwWhichRef == -1 */
325     if(dwWhichRef != -1 && dwWhichRef & ~(HLINKGETREF_DEFAULT | HLINKGETREF_ABSOLUTE | HLINKGETREF_RELATIVE))
326     {
327         if(ppwzTarget)
328             *ppwzTarget = NULL;
329         if(ppwzLocation)
330             *ppwzLocation = NULL;
331         return E_INVALIDARG;
332     }
333
334     if(dwWhichRef != HLINKGETREF_DEFAULT)
335         FIXME("unhandled flags: 0x%x\n", dwWhichRef);
336
337     if (ppwzTarget)
338     {
339         IMoniker* mon;
340         __GetMoniker(This, &mon);
341         if (mon)
342         {
343             IBindCtx *pbc;
344
345             CreateBindCtx( 0, &pbc);
346             IMoniker_GetDisplayName(mon, pbc, NULL, ppwzTarget);
347             IBindCtx_Release(pbc);
348             IMoniker_Release(mon);
349         }
350         else
351             *ppwzTarget = NULL;
352     }
353     if (ppwzLocation)
354         *ppwzLocation = hlink_co_strdupW( This->Location );
355
356     TRACE("(Target: %s Location: %s)\n",
357             (ppwzTarget)?debugstr_w(*ppwzTarget):"<NULL>",
358             (ppwzLocation)?debugstr_w(*ppwzLocation):"<NULL>");
359
360     return S_OK;
361 }
362
363 static HRESULT WINAPI IHlink_fnSetFriendlyName (IHlink *iface,
364         LPCWSTR pwzFriendlyName)
365 {
366     HlinkImpl  *This = (HlinkImpl*)iface;
367
368     TRACE("(%p) -> (%s)\n", This, debugstr_w(pwzFriendlyName));
369
370     heap_free(This->FriendlyName);
371     This->FriendlyName = hlink_strdupW( pwzFriendlyName );
372
373     return S_OK;
374 }
375
376 static HRESULT WINAPI IHlink_fnGetFriendlyName (IHlink* iface,
377         DWORD grfHLFNAMEF, LPWSTR* ppwzFriendlyName)
378 {
379     HlinkImpl  *This = (HlinkImpl*)iface;
380
381     TRACE("(%p) -> (%i %p)\n", This, grfHLFNAMEF, ppwzFriendlyName);
382
383     /* FIXME: Only using explicitly set and cached friendly names */
384
385     if (This->FriendlyName)
386         *ppwzFriendlyName = hlink_co_strdupW( This->FriendlyName );
387     else
388     {
389         IMoniker *moniker;
390         __GetMoniker(This, &moniker);
391         if (moniker)
392         {
393             IBindCtx *bcxt;
394             CreateBindCtx(0, &bcxt);
395
396             IMoniker_GetDisplayName(moniker, bcxt, NULL, ppwzFriendlyName);
397             IBindCtx_Release(bcxt);
398             IMoniker_Release(moniker);
399         }
400         else
401             *ppwzFriendlyName = NULL;
402     }
403
404     return S_OK;
405 }
406
407 static HRESULT WINAPI IHlink_fnSetTargetFrameName(IHlink* iface,
408         LPCWSTR pwzTargetFramename)
409 {
410     HlinkImpl  *This = (HlinkImpl*)iface;
411     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzTargetFramename));
412
413     heap_free(This->TargetFrameName);
414     This->TargetFrameName = hlink_strdupW( pwzTargetFramename );
415
416     return S_OK;
417 }
418
419 static HRESULT WINAPI IHlink_fnGetTargetFrameName(IHlink* iface,
420         LPWSTR *ppwzTargetFrameName)
421 {
422     HlinkImpl  *This = (HlinkImpl*)iface;
423
424     TRACE("(%p)->(%p)\n", This, ppwzTargetFrameName);
425     *ppwzTargetFrameName = hlink_co_strdupW( This->TargetFrameName );
426
427     return S_OK;
428 }
429
430 static HRESULT WINAPI IHlink_fnGetMiscStatus(IHlink* iface, DWORD* pdwStatus)
431 {
432     FIXME("\n");
433     return E_NOTIMPL;
434 }
435
436 static HRESULT WINAPI IHlink_fnNavigate(IHlink* iface, DWORD grfHLNF, LPBC pbc,
437         IBindStatusCallback *pbsc, IHlinkBrowseContext *phbc)
438 {
439     HlinkImpl  *This = (HlinkImpl*)iface;
440     IMoniker *mon = NULL;
441
442     FIXME("Semi-Stub:(%p)->(%i %p %p %p)\n", This, grfHLNF, pbc, pbsc, phbc);
443
444     if (This->Site)
445         IHlinkSite_ReadyToNavigate(This->Site, This->SiteData, 0);
446
447     __GetMoniker(This, &mon);
448     TRACE("Moniker %p\n", mon);
449
450     if (mon)
451     {
452         IBindCtx *bcxt;
453         IHlinkTarget *target = NULL;
454         HRESULT r = S_OK;
455
456         CreateBindCtx(0, &bcxt);
457
458         RegisterBindStatusCallback(bcxt, pbsc, NULL, 0);
459
460         r = IMoniker_BindToObject(mon, bcxt, NULL, &IID_IHlinkTarget,
461                 (LPVOID*)&target);
462         TRACE("IHlinkTarget returned 0x%x\n", r);
463         if (r == S_OK)
464         {
465             IHlinkTarget_SetBrowseContext(target, phbc);
466             IHlinkTarget_Navigate(target, grfHLNF, This->Location);
467             IHlinkTarget_Release(target);
468         }
469         else
470         {
471             static const WCHAR szOpen[] = {'o','p','e','n',0};
472             LPWSTR target = NULL;
473
474             r = IHlink_GetStringReference(iface, HLINKGETREF_DEFAULT, &target, NULL);
475             if (SUCCEEDED(r) && target)
476             {
477                 ShellExecuteW(NULL, szOpen, target, NULL, NULL, SW_SHOW);
478                 CoTaskMemFree(target);
479             }
480         }
481
482         RevokeBindStatusCallback(bcxt, pbsc);
483
484         IBindCtx_Release(bcxt);
485         IMoniker_Release(mon);
486     }
487
488     if (This->Site)
489         IHlinkSite_OnNavigationComplete(This->Site, This->SiteData, 0, 0, NULL);
490
491     TRACE("Finished Navigation\n");
492     return S_OK;
493 }
494
495 static HRESULT WINAPI IHlink_fnSetAdditonalParams(IHlink* iface,
496         LPCWSTR pwzAdditionalParams)
497 {
498     TRACE("Not implemented in native IHlink\n");
499     return E_NOTIMPL;
500 }
501
502 static HRESULT WINAPI IHlink_fnGetAdditionalParams(IHlink* iface,
503         LPWSTR* ppwzAdditionalParams)
504 {
505     TRACE("Not implemented in native IHlink\n");
506     return E_NOTIMPL;
507 }
508
509 static const IHlinkVtbl hlvt =
510 {
511     IHlink_fnQueryInterface,
512     IHlink_fnAddRef,
513     IHlink_fnRelease,
514     IHlink_fnSetHlinkSite,
515     IHlink_fnGetHlinkSite,
516     IHlink_fnSetMonikerReference,
517     IHlink_fnGetMonikerReference,
518     IHlink_fnSetStringReference,
519     IHlink_fnGetStringReference,
520     IHlink_fnSetFriendlyName,
521     IHlink_fnGetFriendlyName,
522     IHlink_fnSetTargetFrameName,
523     IHlink_fnGetTargetFrameName,
524     IHlink_fnGetMiscStatus,
525     IHlink_fnNavigate,
526     IHlink_fnSetAdditonalParams,
527     IHlink_fnGetAdditionalParams
528 };
529
530 static HRESULT WINAPI IDataObject_fnQueryInterface(IDataObject* iface,
531         REFIID riid, LPVOID *ppvObj)
532 {
533     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
534     TRACE("%p\n", This);
535     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
536 }
537
538 static ULONG WINAPI IDataObject_fnAddRef (IDataObject* iface)
539 {
540     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
541     TRACE("%p\n", This);
542     return IHlink_AddRef((IHlink*)This);
543 }
544
545 static ULONG WINAPI IDataObject_fnRelease (IDataObject* iface)
546 {
547     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
548     TRACE("%p\n", This);
549     return IHlink_Release((IHlink*)This);
550 }
551
552 static HRESULT WINAPI IDataObject_fnGetData(IDataObject* iface,
553         FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
554 {
555     FIXME("\n");
556     return E_NOTIMPL;
557 }
558
559 static HRESULT WINAPI IDataObject_fnGetDataHere(IDataObject* iface,
560         FORMATETC* pformatetc, STGMEDIUM* pmedium)
561 {
562     FIXME("\n");
563     return E_NOTIMPL;
564 }
565
566 static HRESULT WINAPI IDataObject_fnQueryGetData(IDataObject* iface,
567         FORMATETC* pformatetc)
568 {
569     FIXME("\n");
570     return E_NOTIMPL;
571 }
572
573 static HRESULT WINAPI IDataObject_fnGetConicalFormatEtc(IDataObject* iface,
574         FORMATETC* pformatetcIn, FORMATETC* pformatetcOut)
575 {
576     FIXME("\n");
577     return E_NOTIMPL;
578 }
579
580 static HRESULT WINAPI IDataObject_fnSetData(IDataObject* iface,
581         FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
582 {
583     FIXME("\n");
584     return E_NOTIMPL;
585 }
586
587 static HRESULT WINAPI IDataObject_fnEnumFormatEtc(IDataObject* iface,
588         DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
589 {
590     FIXME("\n");
591     return E_NOTIMPL;
592 }
593
594 static HRESULT WINAPI IDataObject_fnDAdvise(IDataObject* iface,
595         FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink,
596         DWORD* pdwConnection)
597 {
598     FIXME("\n");
599     return E_NOTIMPL;
600 }
601
602 static HRESULT WINAPI IDataObject_fnDUnadvise(IDataObject* iface,
603         DWORD dwConnection)
604 {
605     FIXME("\n");
606     return E_NOTIMPL;
607 }
608
609 static HRESULT WINAPI IDataObject_fnEnumDAdvise(IDataObject* iface,
610         IEnumSTATDATA** ppenumAdvise)
611 {
612     FIXME("\n");
613     return E_NOTIMPL;
614 }
615
616 static const IDataObjectVtbl dovt =
617 {
618     IDataObject_fnQueryInterface,
619     IDataObject_fnAddRef,
620     IDataObject_fnRelease,
621     IDataObject_fnGetData,
622     IDataObject_fnGetDataHere,
623     IDataObject_fnQueryGetData,
624     IDataObject_fnGetConicalFormatEtc,
625     IDataObject_fnSetData,
626     IDataObject_fnEnumFormatEtc,
627     IDataObject_fnDAdvise,
628     IDataObject_fnDUnadvise,
629     IDataObject_fnEnumDAdvise
630 };
631
632 static HRESULT WINAPI IPersistStream_fnQueryInterface(IPersistStream* iface,
633         REFIID riid, LPVOID *ppvObj)
634 {
635     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
636     TRACE("(%p)\n", This);
637     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
638 }
639
640 static ULONG WINAPI IPersistStream_fnAddRef (IPersistStream* iface)
641 {
642     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
643     TRACE("(%p)\n", This);
644     return IHlink_AddRef((IHlink*)This);
645 }
646
647 static ULONG WINAPI IPersistStream_fnRelease (IPersistStream* iface)
648 {
649     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
650     TRACE("(%p)\n", This);
651     return IHlink_Release((IHlink*)This);
652 }
653
654 static HRESULT WINAPI IPersistStream_fnGetClassID(IPersistStream* iface,
655         CLSID* pClassID)
656 {
657     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
658     TRACE("(%p)\n", This);
659     *pClassID = CLSID_StdHlink;
660     return S_OK;
661 }
662
663 static HRESULT WINAPI IPersistStream_fnIsDirty(IPersistStream* iface)
664 {
665     FIXME("\n");
666     return E_NOTIMPL;
667 }
668
669 static HRESULT write_hlink_string(IStream *pStm, LPCWSTR str)
670 {
671     DWORD len;
672     HRESULT hr;
673
674     TRACE("(%p, %s)\n", pStm, debugstr_w(str));
675
676     len = strlenW(str) + 1;
677
678     hr = IStream_Write(pStm, &len, sizeof(len), NULL);
679     if (FAILED(hr)) return hr;
680
681     hr = IStream_Write(pStm, str, len * sizeof(WCHAR), NULL);
682     if (FAILED(hr)) return hr;
683
684     return S_OK;
685 }
686
687 static inline ULONG size_hlink_string(LPCWSTR str)
688 {
689     return sizeof(DWORD) + (strlenW(str) + 1) * sizeof(WCHAR);
690 }
691
692 static HRESULT read_hlink_string(IStream *pStm, LPWSTR *out_str)
693 {
694     LPWSTR str;
695     DWORD len;
696     ULONG read;
697     HRESULT hr;
698
699     hr = IStream_Read(pStm, &len, sizeof(len), &read);
700     if (FAILED(hr)) return hr;
701     if (read != sizeof(len)) return STG_E_READFAULT;
702
703     TRACE("read len %d\n", len);
704
705     str = heap_alloc(len * sizeof(WCHAR));
706     if (!str) return E_OUTOFMEMORY;
707
708     hr = IStream_Read(pStm, str, len * sizeof(WCHAR), &read);
709     if (FAILED(hr))
710     {
711         heap_free(str);
712         return hr;
713     }
714     if (read != len * sizeof(WCHAR))
715     {
716         heap_free(str);
717         return STG_E_READFAULT;
718     }
719     TRACE("read string %s\n", debugstr_w(str));
720
721     *out_str = str;
722     return S_OK;
723 }
724
725 static HRESULT WINAPI IPersistStream_fnLoad(IPersistStream* iface,
726         IStream* pStm)
727 {
728     HRESULT r;
729     DWORD hdr[2];
730     DWORD read;
731     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
732
733     r = IStream_Read(pStm, hdr, sizeof(hdr), &read);
734     if (read != sizeof(hdr) || (hdr[0] != HLINK_SAVE_MAGIC))
735     {
736         r = E_FAIL;
737         goto end;
738     }
739     if (hdr[1] & ~HLINK_SAVE_ALL)
740         FIXME("unknown flag(s) 0x%x\n", hdr[1] & ~HLINK_SAVE_ALL);
741
742     if (hdr[1] & HLINK_SAVE_TARGET_FRAME_PRESENT)
743     {
744         TRACE("loading target frame name\n");
745         r = read_hlink_string(pStm, &This->TargetFrameName);
746         if (FAILED(r)) goto end;
747     }
748
749     if (hdr[1] & HLINK_SAVE_FRIENDLY_PRESENT)
750     {
751         TRACE("loading target friendly name\n");
752         if (!(hdr[1] & 0x4))
753             FIXME("0x4 flag not present with friendly name flag - not sure what this means\n");
754         r = read_hlink_string(pStm, &This->FriendlyName);
755         if (FAILED(r)) goto end;
756     }
757
758     if (hdr[1] & HLINK_SAVE_MONIKER_PRESENT)
759     {
760         TRACE("loading moniker\n");
761         r = OleLoadFromStream(pStm, &IID_IMoniker, (LPVOID*)&(This->Moniker));
762         if (FAILED(r))
763             goto end;
764         This->absolute = hdr[1] & HLINK_SAVE_MONIKER_IS_ABSOLUTE ? TRUE : FALSE;
765     }
766
767     if (hdr[1] & HLINK_SAVE_LOCATION_PRESENT)
768     {
769         TRACE("loading location\n");
770         r = read_hlink_string(pStm, &This->Location);
771         if (FAILED(r)) goto end;
772     }
773
774 end:
775     TRACE("Load Result 0x%x (%p)\n", r, This->Moniker);
776
777     return r;
778 }
779
780 static HRESULT WINAPI IPersistStream_fnSave(IPersistStream* iface,
781         IStream* pStm, BOOL fClearDirty)
782 {
783     HRESULT r = E_FAIL;
784     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
785     DWORD hdr[2];
786     IMoniker *moniker;
787
788     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
789
790     __GetMoniker(This, &moniker);
791
792     hdr[0] = HLINK_SAVE_MAGIC;
793     hdr[1] = 0;
794
795     if (moniker)
796         hdr[1] |= HLINK_SAVE_MONIKER_PRESENT;
797     if (This->absolute)
798         hdr[1] |= HLINK_SAVE_MONIKER_IS_ABSOLUTE;
799     if (This->Location)
800         hdr[1] |= HLINK_SAVE_LOCATION_PRESENT;
801     if (This->FriendlyName)
802         hdr[1] |= HLINK_SAVE_FRIENDLY_PRESENT | 4 /* FIXME */;
803     if (This->TargetFrameName)
804         hdr[1] |= HLINK_SAVE_TARGET_FRAME_PRESENT;
805
806     IStream_Write(pStm, hdr, sizeof(hdr), NULL);
807
808     if (This->TargetFrameName)
809     {
810         r = write_hlink_string(pStm, This->TargetFrameName);
811         if (FAILED(r)) goto end;
812     }
813
814     if (This->FriendlyName)
815     {
816         r = write_hlink_string(pStm, This->FriendlyName);
817         if (FAILED(r)) goto end;
818     }
819
820     if (moniker)
821     {
822         IPersistStream* monstream;
823
824         monstream = NULL;
825         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
826                 (LPVOID*)&monstream);
827         if (monstream)
828         {
829             r = OleSaveToStream(monstream, pStm);
830             IPersistStream_Release(monstream);
831         }
832         if (FAILED(r)) goto end;
833     }
834
835     if (This->Location)
836     {
837         r = write_hlink_string(pStm, This->Location);
838         if (FAILED(r)) goto end;
839     }
840
841 end:
842     if (moniker) IMoniker_Release(moniker);
843     TRACE("Save Result 0x%x\n", r);
844
845     return r;
846 }
847
848 static HRESULT WINAPI IPersistStream_fnGetSizeMax(IPersistStream* iface,
849         ULARGE_INTEGER* pcbSize)
850 {
851     HRESULT r = E_FAIL;
852     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
853     IMoniker *moniker;
854
855     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
856
857     pcbSize->QuadPart = sizeof(DWORD)*2;
858
859     if (This->TargetFrameName)
860         pcbSize->QuadPart += size_hlink_string(This->TargetFrameName);
861
862     if (This->FriendlyName)
863         pcbSize->QuadPart += size_hlink_string(This->FriendlyName);
864
865     __GetMoniker(This, &moniker);
866     if (moniker)
867     {
868         IPersistStream* monstream = NULL;
869         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
870                 (LPVOID*)&monstream);
871         if (monstream)
872         {
873             ULARGE_INTEGER mon_size;
874             r = IPersistStream_GetSizeMax(monstream, &mon_size);
875             pcbSize->QuadPart += mon_size.QuadPart;
876             IPersistStream_Release(monstream);
877         }
878         IMoniker_Release(moniker);
879     }
880
881     if (This->Location)
882         pcbSize->QuadPart += size_hlink_string(This->Location);
883
884     return r;
885 }
886
887 static const IPersistStreamVtbl psvt =
888 {
889     IPersistStream_fnQueryInterface,
890     IPersistStream_fnAddRef,
891     IPersistStream_fnRelease,
892     IPersistStream_fnGetClassID,
893     IPersistStream_fnIsDirty,
894     IPersistStream_fnLoad,
895     IPersistStream_fnSave,
896     IPersistStream_fnGetSizeMax,
897 };