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