[HLINK_WINETEST]
[reactos.git] / rostests / winetests / hlink / hlink.c
index cf031f5..4a8eb44 100644 (file)
@@ -57,6 +57,14 @@ DEFINE_EXPECT(IsSystemMoniker);
 DEFINE_EXPECT(BindToStorage);
 DEFINE_EXPECT(GetDisplayName);
 
+DEFINE_EXPECT(ComposeWith);
+DEFINE_EXPECT(OnNavigationComplete);
+DEFINE_EXPECT(Enum);
+DEFINE_EXPECT(Reduce);
+
+DEFINE_EXPECT(GetClassID);
+DEFINE_EXPECT(Save);
+
 static const char *debugstr_guid(REFIID riid)
 {
     static char buf[50];
@@ -347,7 +355,6 @@ static void test_persist_save_data(const char *testname, IHlink *lnk,
     const unsigned char *data;
     DWORD i;
     BOOL same;
-    unsigned int expected_data_win9x_size = 0;
 
     hr = IHlink_QueryInterface(lnk, &IID_IPersistStream, (void **)&ps);
     ok(hr == S_OK, "IHlink_QueryInterface failed with error 0x%08x\n", hr);
@@ -365,13 +372,9 @@ static void test_persist_save_data(const char *testname, IHlink *lnk,
 
     data = GlobalLock(hglobal);
 
-    if (expected_data_size % 4)
-        expected_data_win9x_size =  4 * ((expected_data_size / 4) + 1);
-
     /* first check we have the right amount of data */
     ok((data_size == expected_data_size) ||
-       (data_size == expected_data_alt_size) ||
-       broken(data_size == expected_data_win9x_size), /* Win9x and WinMe */
+       (data_size == expected_data_alt_size),
        "%s: Size of saved data differs (expected %d or %d, actual %d)\n",
        testname, expected_data_size, expected_data_alt_size, data_size);
 
@@ -437,9 +440,9 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinkCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     if (!lnk) {
-        skip("Can't create lnk, skipping test_persist.  Was wineprefixcreate run properly?\n");
+        skip("Can't create lnk, skipping test_persist.\n");
         return;
     }
     test_persist_save_data("url only", lnk,
@@ -449,7 +452,7 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(url, NULL, friendly_name, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     test_persist_save_data("url + friendly name", lnk,
         expected_hlink_data2, sizeof(expected_hlink_data2),
         expected_hlink_data2_ie7, sizeof(expected_hlink_data2_ie7));
@@ -457,7 +460,7 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(url, location, friendly_name, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     test_persist_save_data("url + friendly_name + location", lnk,
         expected_hlink_data3, sizeof(expected_hlink_data3),
         expected_hlink_data3_ie7, sizeof(expected_hlink_data3_ie7));
@@ -465,7 +468,7 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(rel_url, NULL, NULL, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     test_persist_save_data("relative url", lnk,
         expected_hlink_data4, sizeof(expected_hlink_data4),
         expected_hlink_data4, sizeof(expected_hlink_data4));
@@ -473,7 +476,7 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     hr = IHlink_SetTargetFrameName(lnk, target_frame_name);
     ok(hr == S_OK, "IHlink_SetTargetFrameName failed with error 0x%08x\n", hr);
     test_persist_save_data("url + target frame name", lnk,
@@ -483,7 +486,7 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(filename, NULL, NULL, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "HlinkCreateFromString failed with error 0x%08x\n", hr);
     test_persist_save_data("filename", lnk,
         expected_hlink_data6, sizeof(expected_hlink_data6),
         expected_hlink_data6, sizeof(expected_hlink_data6));
@@ -813,8 +816,9 @@ static ULONG WINAPI Moniker_Release(IMoniker *iface)
 
 static HRESULT WINAPI Moniker_GetClassID(IMoniker *iface, CLSID *pClassID)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    CHECK_EXPECT(GetClassID);
+    *pClassID = IID_IUnknown; /* not a valid CLSID */
+    return S_OK;
 }
 
 static HRESULT WINAPI Moniker_IsDirty(IMoniker *iface)
@@ -831,8 +835,8 @@ static HRESULT WINAPI Moniker_Load(IMoniker *iface, IStream *pStm)
 
 static HRESULT WINAPI Moniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    CHECK_EXPECT(Save);
+    return S_OK;
 }
 
 static HRESULT WINAPI Moniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
@@ -876,7 +880,7 @@ static HRESULT WINAPI Moniker_BindToStorage(IMoniker *iface, IBindCtx *pbc, IMon
 static HRESULT WINAPI Moniker_Reduce(IMoniker *iface, IBindCtx *pbc, DWORD dwReduceHowFar,
         IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
 {
-    ok(0, "unexpected call\n");
+    CHECK_EXPECT(Reduce);
     return E_NOTIMPL;
 }
 
@@ -889,7 +893,7 @@ static HRESULT WINAPI Moniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
 
 static HRESULT WINAPI Moniker_Enum(IMoniker *iface, BOOL fForwrd, IEnumMoniker **ppenumMoniker)
 {
-    ok(0, "unexpected call\n");
+    CHECK_EXPECT(Enum);
     return E_NOTIMPL;
 }
 
@@ -1020,6 +1024,7 @@ static void test_HlinkResolveMonikerForData(void)
     CHECK_CALLED(BindToStorage);
 
     IBindCtx_Release(bctx);
+    _bctx = NULL;
 }
 
 static void test_HlinkGetSetMonikerReference(void)
@@ -1056,6 +1061,7 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
@@ -1066,6 +1072,7 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
     ok(lstrcmpW(found_loc, two) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(two), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
@@ -1076,6 +1083,7 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
@@ -1086,17 +1094,19 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == E_INVALIDARG, "IHlink_SetMonikerReference should've failed with E_INVALIDARG (0x%08x), failed with 0x%08x\n", E_INVALIDARG, hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
     CoTaskMemFree(found_loc);
 
     /* invalid HLINKSETF flags */
-    hres = IHlink_SetMonikerReference(hlink, 12, dummy2, two);
     /* Windows returns garbage; on 32-bit it returns the flags probably because the compiler happened to store them in %eax at some point */
-    if (0) ok(hres == 12, "IHlink_SetMonikerReference should've failed with 0x%08x, failed with 0x%08x\n", 12, hres);
+    if (0) 
+        IHlink_SetMonikerReference(hlink, 12, dummy2, two);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy, "Found target should've been %p, was: %p\n", dummy, found_trgt);
     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
@@ -1107,6 +1117,7 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == dummy2, "Found target should've been %p, was: %p\n", dummy2, found_trgt);
     ok(lstrcmpW(found_loc, one) == 0, "Found location should've been %s, was: %s\n", wine_dbgstr_w(one), wine_dbgstr_w(found_loc));
     IMoniker_Release(found_trgt);
@@ -1117,6 +1128,7 @@ static void test_HlinkGetSetMonikerReference(void)
     ok(hres == S_OK, "IHlink_SetMonikerReference failed: 0x%08x\n", hres);
 
     hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &found_trgt, &found_loc);
+    ok(hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
     ok(found_trgt == NULL, "Found target should've been %p, was: %p\n", NULL, found_trgt);
     ok(found_loc == NULL, "Found location should've been %s, was: %s\n", wine_dbgstr_w(NULL), wine_dbgstr_w(found_loc));
     if(found_trgt)
@@ -1125,6 +1137,26 @@ static void test_HlinkGetSetMonikerReference(void)
     IHlink_Release(hlink);
     IMoniker_Release(dummy2);
     IMoniker_Release(dummy);
+
+    SET_EXPECT(Reduce);
+    SET_EXPECT(Enum);
+    SET_EXPECT(IsSystemMoniker);
+    SET_EXPECT(GetDisplayName);
+    hres = HlinkCreateFromMoniker(&Moniker, NULL, NULL, NULL, 0, NULL,
+            &IID_IHlink, (void **)&hlink);
+    ok(hres == S_OK, "CreateFromMoniker failed: %08x\n", hres);
+
+    hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_ABSOLUTE,
+            &found_trgt, &found_loc);
+    ok(hres == S_OK, "CreateFromMoniker failed: %08x\n", hres);
+    ok(found_trgt == &Moniker, "Got unexpected moniker: %p\n", found_trgt);
+    ok(found_loc == NULL, "Got unexpected location: %p\n", found_loc);
+    todo_wine CHECK_CALLED(Reduce);
+    todo_wine CHECK_CALLED(Enum);
+    CHECK_CALLED(IsSystemMoniker);
+    CHECK_CALLED(GetDisplayName);
+
+    IHlink_Release(hlink);
 }
 
 static void test_HlinkGetSetStringReference(void)
@@ -1244,12 +1276,12 @@ static void test_HlinkGetSetStringReference(void)
     CoTaskMemFree(fnd_tgt);
     CoTaskMemFree(fnd_loc);
 
-    hres = IHlink_SetStringReference(link, 4, NULL, NULL);
-    /* Windows returns garbage; on 32-bit it returns the flags probably because the compiler happened to store them in %eax at some point */
-    if (0) ok(hres == 4, "IHlink_SetStringReference should have failed with 0x4, instead: 0x%08x\n", hres);
-
-    hres = IHlink_SetStringReference(link, -4, NULL, NULL);
-    if (0) ok(hres == -4, "IHlink_SetStringReference should have failed with 0xFFFFFFFC, instead: 0x%08x\n", hres);
+    if (0)
+    {
+        /* Windows returns garbage; on 32-bit it returns the flags probably because the compiler happened to store them in %eax at some point */
+        IHlink_SetStringReference(link, 4, NULL, NULL);
+        IHlink_SetStringReference(link, -4, NULL, NULL);
+    }
 
     IHlink_Release(link);
 }
@@ -1294,14 +1326,14 @@ static void r_setMonikerRef(unsigned line, IHlink *hlink, DWORD flags, IMoniker
 }
 
 /* passing 0xFFFFFFFF as exp_tgt will return the retrieved target & not test it */
-#define getMonikerRef(h,t,l) r_getMonikerRef(__LINE__,h,t,l)
-static IMoniker *r_getMonikerRef(unsigned line, IHlink *hlink, IMoniker *exp_tgt, const WCHAR *exp_loc)
+#define getMonikerRef(h,t,l,r) r_getMonikerRef(__LINE__,h,t,l,r)
+static IMoniker *r_getMonikerRef(unsigned line, IHlink *hlink, IMoniker *exp_tgt, const WCHAR *exp_loc, DWORD ref)
 {
     HRESULT hres;
     IMoniker *fnd_tgt;
     WCHAR *fnd_loc;
 
-    hres = IHlink_GetMonikerReference(hlink, HLINKGETREF_DEFAULT, &fnd_tgt, &fnd_loc);
+    hres = IHlink_GetMonikerReference(hlink, ref, &fnd_tgt, &fnd_loc);
     ok_(__FILE__,line) (hres == S_OK, "IHlink_GetMonikerReference failed: 0x%08x\n", hres);
 
     if(exp_loc)
@@ -1335,12 +1367,12 @@ static void test_HlinkMoniker(void)
     hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hlink);
     ok(hres == S_OK, "HlinkCreateFromString failed: 0x%08x\n", hres);
     getStringRef(hlink, NULL, NULL);
-    getMonikerRef(hlink, NULL, NULL);
+    getMonikerRef(hlink, NULL, NULL, HLINKGETREF_RELATIVE);
 
     /* setting a string target creates a moniker reference */
     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, aW, wordsW);
     getStringRef(hlink, aW, wordsW);
-    aMon = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, wordsW);
+    aMon = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, wordsW, HLINKGETREF_RELATIVE);
     ok(aMon != NULL, "Moniker from %s target should not be NULL\n", wine_dbgstr_w(aW));
     if(aMon)
         IMoniker_Release(aMon);
@@ -1349,7 +1381,7 @@ static void test_HlinkMoniker(void)
      * reference */
     setStringRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, emptyW, emptyW);
     getStringRef(hlink, NULL, NULL);
-    getMonikerRef(hlink, NULL, NULL);
+    getMonikerRef(hlink, NULL, NULL, HLINKGETREF_RELATIVE);
 
     /* setting a moniker target also sets the target string to that moniker's
      * display name */
@@ -1358,7 +1390,7 @@ static void test_HlinkMoniker(void)
 
     setMonikerRef(hlink, HLINKSETF_TARGET | HLINKSETF_LOCATION, file_mon, wordsW);
     getStringRef(hlink, bW, wordsW);
-    getMonikerRef(hlink, file_mon, wordsW);
+    getMonikerRef(hlink, file_mon, wordsW, HLINKGETREF_RELATIVE);
 
     IMoniker_Release(file_mon);
 
@@ -1385,7 +1417,7 @@ static void test_HashLink(void)
 
     if(hlink){
         getStringRef(hlink, tgt_partW, loc_partW);
-        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW);
+        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW, HLINKGETREF_RELATIVE);
         ok(pmk != NULL, "Found moniker should not be NULL\n");
         if(pmk)
             IMoniker_Release(pmk);
@@ -1403,7 +1435,7 @@ static void test_HashLink(void)
 
     if(hlink){
         getStringRef(hlink, tgt_partW, two_hash_loc_partW);
-        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, two_hash_loc_partW);
+        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, two_hash_loc_partW, HLINKGETREF_RELATIVE);
         ok(pmk != NULL, "Found moniker should not be NULL\n");
         if(pmk)
             IMoniker_Release(pmk);
@@ -1418,7 +1450,7 @@ static void test_HashLink(void)
 
     if(hlink){
         getStringRef(hlink, tgt_partW, test_locW);
-        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, test_locW);
+        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, test_locW, HLINKGETREF_RELATIVE);
         ok(pmk != NULL, "Found moniker should not be NULL\n");
         if(pmk)
             IMoniker_Release(pmk);
@@ -1433,7 +1465,7 @@ static void test_HashLink(void)
 
     if(hlink){
         getStringRef(hlink, NULL, loc_partW);
-        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW);
+        pmk = getMonikerRef(hlink, (IMoniker*)0xFFFFFFFF, loc_partW, HLINKGETREF_RELATIVE);
         ok(pmk == NULL, "Found moniker should be NULL\n");
         if(pmk)
             IMoniker_Release(pmk);
@@ -1442,6 +1474,434 @@ static void test_HashLink(void)
     }
 }
 
+static WCHAR site_monikerW[] = {'S','I','T','E','_','M','O','N','I','K','E','R',0};
+static WCHAR ref_monikerW[] = {'R','E','F','_','M','O','N','I','K','E','R',0};
+
+static HRESULT WINAPI hls_test_Moniker_BindToStorage(IMoniker *iface,
+        IBindCtx *pbc, IMoniker *toLeft, REFIID riid, void **obj)
+{
+    ok(0, "BTS: %p %p %p %p %p\n", iface, pbc, toLeft, riid, obj);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hls_site_Moniker_ComposeWith(IMoniker *iface,
+        IMoniker *right, BOOL onlyIfNotGeneric, IMoniker **composite)
+{
+    LPOLESTR rightName;
+    HRESULT hres;
+
+    ok(onlyIfNotGeneric == 0, "Expected onlyIfNotGeneric to be FALSE\n");
+
+    CHECK_EXPECT(ComposeWith);
+
+    hres = IMoniker_GetDisplayName(right, NULL, NULL, &rightName);
+    ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
+    ok(!lstrcmpW(rightName, ref_monikerW),
+            "Expected to get moniker set via SetMonikerReference, instead got: %s\n",
+            wine_dbgstr_w(rightName));
+    CoTaskMemFree(rightName);
+
+    *composite = NULL;
+
+    /* unlikely error code to verify this return result is used */
+    return E_OUTOFMEMORY;
+}
+
+static HRESULT WINAPI hls_site_Moniker_GetDisplayName(IMoniker *iface,
+        IBindCtx *pbc, IMoniker *toLeft, LPOLESTR *displayName)
+{
+    *displayName = CoTaskMemAlloc(sizeof(site_monikerW));
+    memcpy(*displayName, site_monikerW, sizeof(site_monikerW));
+    return S_OK;
+}
+
+static HRESULT WINAPI hls_ref_Moniker_GetDisplayName(IMoniker *iface,
+        IBindCtx *pbc, IMoniker *toLeft, LPOLESTR *displayName)
+{
+    *displayName = CoTaskMemAlloc(sizeof(ref_monikerW));
+    memcpy(*displayName, ref_monikerW, sizeof(ref_monikerW));
+    return S_OK;
+}
+
+static HRESULT WINAPI hls_test_Moniker_IsSystemMoniker(IMoniker *iface,
+        DWORD *mksys)
+{
+    return S_FALSE;
+}
+
+static IMonikerVtbl hls_site_MonikerVtbl = {
+    Moniker_QueryInterface,
+    Moniker_AddRef,
+    Moniker_Release,
+    Moniker_GetClassID,
+    Moniker_IsDirty,
+    Moniker_Load,
+    Moniker_Save,
+    Moniker_GetSizeMax,
+    Moniker_BindToObject,
+    hls_test_Moniker_BindToStorage,
+    Moniker_Reduce,
+    hls_site_Moniker_ComposeWith,
+    Moniker_Enum,
+    Moniker_IsEqual,
+    Moniker_Hash,
+    Moniker_IsRunning,
+    Moniker_GetTimeOfLastChange,
+    Moniker_Inverse,
+    Moniker_CommonPrefixWith,
+    Moniker_RelativePathTo,
+    hls_site_Moniker_GetDisplayName,
+    Moniker_ParseDisplayName,
+    hls_test_Moniker_IsSystemMoniker
+};
+
+static IMonikerVtbl hls_ref_MonikerVtbl = {
+    Moniker_QueryInterface,
+    Moniker_AddRef,
+    Moniker_Release,
+    Moniker_GetClassID,
+    Moniker_IsDirty,
+    Moniker_Load,
+    Moniker_Save,
+    Moniker_GetSizeMax,
+    Moniker_BindToObject,
+    hls_test_Moniker_BindToStorage,
+    Moniker_Reduce,
+    Moniker_ComposeWith,
+    Moniker_Enum,
+    Moniker_IsEqual,
+    Moniker_Hash,
+    Moniker_IsRunning,
+    Moniker_GetTimeOfLastChange,
+    Moniker_Inverse,
+    Moniker_CommonPrefixWith,
+    Moniker_RelativePathTo,
+    hls_ref_Moniker_GetDisplayName,
+    Moniker_ParseDisplayName,
+    hls_test_Moniker_IsSystemMoniker
+};
+
+static IMoniker hls_site_Moniker = { &hls_site_MonikerVtbl };
+static IMoniker hls_ref_Moniker = { &hls_ref_MonikerVtbl };
+
+static HRESULT WINAPI hls_QueryInterface(IHlinkSite *iface, REFGUID iid,
+        void **obj)
+{
+    ok(0, "QI: %p %s %p\n", iface, debugstr_guid(iid), obj);
+    return E_NOTIMPL;
+}
+
+static ULONG WINAPI hls_AddRef(IHlinkSite *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI hls_Release(IHlinkSite *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI hls_QueryService(IHlinkSite *iface, DWORD siteData,
+        REFGUID service, REFIID riid, IUnknown **punk)
+{
+    ok(0, "QS: %p %x %s %s %p\n", iface, siteData, debugstr_guid(service),
+            debugstr_guid(riid), punk);
+    return E_NOTIMPL;
+}
+
+#define SITEDATA_SUCCESS 1
+#define SITEDATA_NOTIMPL 2
+
+static HRESULT WINAPI hls_GetMoniker(IHlinkSite *iface, DWORD siteData,
+        DWORD assign, DWORD which, IMoniker **pmk)
+{
+    ok(siteData == SITEDATA_NOTIMPL ||
+            siteData == SITEDATA_SUCCESS, "Unexpected site data: %u\n", siteData);
+
+    if(siteData == SITEDATA_SUCCESS){
+        *pmk = &hls_site_Moniker;
+        return S_OK;
+    }
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hls_ReadyToNavigate(IHlinkSite *iface, DWORD siteData,
+        DWORD reserved)
+{
+    ok(0, "RTN: %p %x %x\n", iface, siteData, reserved);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hls_OnNavigationComplete(IHlinkSite *iface,
+        DWORD siteData, DWORD reserved, HRESULT error, LPCWSTR errorStr)
+{
+    CHECK_EXPECT(OnNavigationComplete);
+    ok(siteData == SITEDATA_SUCCESS, "Unexpected site data: %u\n", siteData);
+    ok(error == E_OUTOFMEMORY, "Expected E_OUTOFMEMORY, got: %08x\n", error);
+    return E_NOTIMPL;
+}
+
+static IHlinkSiteVtbl HlinkSiteVtbl = {
+    hls_QueryInterface,
+    hls_AddRef,
+    hls_Release,
+    hls_QueryService,
+    hls_GetMoniker,
+    hls_ReadyToNavigate,
+    hls_OnNavigationComplete
+};
+
+static IHlinkSite HlinkSite = { &HlinkSiteVtbl };
+
+static void test_HlinkSite(void)
+{
+    IHlink *hl;
+    IMoniker *mon_ref;
+    IBindCtx *pbc;
+    HRESULT hres;
+
+    hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL,
+            &IID_IHlink, (LPVOID*)&hl);
+    ok(hres == S_OK, "HlinkCreateFromString failed: %08x\n", hres);
+    getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
+
+    hres = IHlink_SetHlinkSite(hl, &HlinkSite, SITEDATA_SUCCESS);
+    ok(hres == S_OK, "SetHlinkSite failed: %08x\n", hres);
+    getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
+    getStringRef(hl, NULL, NULL);
+
+    hres = IHlink_GetMonikerReference(hl, HLINKGETREF_RELATIVE, &mon_ref, NULL);
+    ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
+    ok(mon_ref == NULL, "Didn't get expected moniker, instead: %p\n", mon_ref);
+
+    hres = IHlink_GetMonikerReference(hl, HLINKGETREF_ABSOLUTE, &mon_ref, NULL);
+    ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
+    ok(mon_ref == &hls_site_Moniker, "Didn't get expected moniker, instead: %p\n", mon_ref);
+
+    SET_EXPECT(Reduce);
+    SET_EXPECT(Enum);
+    hres = IHlink_SetMonikerReference(hl, HLINKSETF_TARGET, &hls_ref_Moniker, NULL);
+    ok(hres == S_OK, "SetMonikerReference failed: %08x\n", hres);
+    todo_wine CHECK_CALLED(Reduce);
+    todo_wine CHECK_CALLED(Enum);
+
+    getMonikerRef(hl, &hls_ref_Moniker, NULL, HLINKGETREF_RELATIVE);
+
+    SET_EXPECT(Enum);
+    getStringRef(hl, ref_monikerW, NULL);
+    todo_wine CHECK_CALLED(Enum);
+
+    hres = IHlink_GetMonikerReference(hl, HLINKGETREF_RELATIVE, &mon_ref, NULL);
+    ok(hres == S_OK, "GetMonikerReference failed: %08x\n", hres);
+    ok(mon_ref == &hls_ref_Moniker, "Didn't get expected moniker, instead: %p\n", mon_ref);
+    IMoniker_Release(mon_ref);
+
+    SET_EXPECT(ComposeWith);
+    hres = IHlink_GetMonikerReference(hl, HLINKGETREF_ABSOLUTE, &mon_ref, NULL);
+    ok(hres == E_OUTOFMEMORY, "Expected E_OUTOFMEMORY, got: %08x\n", hres);
+    ok(mon_ref == NULL, "Shouldn't have got a Moniker, got: %p\n", mon_ref);
+    CHECK_CALLED(ComposeWith);
+
+    hres = CreateBindCtx(0, &pbc);
+    ok(hres == S_OK, "CreateBindCtx failed: %08x\n", hres);
+
+    SET_EXPECT(ComposeWith);
+    SET_EXPECT(OnNavigationComplete);
+    hres = IHlink_Navigate(hl, 0, pbc, NULL, NULL);
+    ok(hres == E_OUTOFMEMORY, "Navigate should've failed: %08x\n", hres);
+    CHECK_CALLED(ComposeWith);
+    CHECK_CALLED(OnNavigationComplete);
+
+    IBindCtx_Release(pbc);
+    IHlink_Release(hl);
+
+    SET_EXPECT(Reduce);
+    SET_EXPECT(Enum);
+    hres = HlinkCreateFromMoniker(&hls_ref_Moniker, NULL, NULL, &HlinkSite, SITEDATA_SUCCESS,
+            NULL, &IID_IHlink, (LPVOID*)&hl);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
+    todo_wine CHECK_CALLED(Reduce);
+    todo_wine CHECK_CALLED(Enum);
+    getMonikerRef(hl, &hls_ref_Moniker, NULL, HLINKGETREF_RELATIVE);
+    IHlink_Release(hl);
+
+    hres = HlinkCreateFromMoniker(NULL, NULL, NULL, &HlinkSite, SITEDATA_SUCCESS,
+            NULL, &IID_IHlink, (LPVOID*)&hl);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
+    getMonikerRef(hl, NULL, NULL, HLINKGETREF_RELATIVE);
+    IHlink_Release(hl);
+
+    SET_EXPECT(Reduce);
+    SET_EXPECT(Enum);
+    SET_EXPECT(IsSystemMoniker);
+    SET_EXPECT(GetDisplayName);
+    hres = HlinkCreateFromMoniker(&Moniker, NULL, NULL, &HlinkSite, SITEDATA_NOTIMPL,
+            NULL, &IID_IHlink, (LPVOID*)&hl);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: %08x\n", hres);
+    getMonikerRef(hl, &Moniker, NULL, HLINKGETREF_ABSOLUTE);
+    IHlink_Release(hl);
+    todo_wine CHECK_CALLED(Reduce);
+    todo_wine CHECK_CALLED(Enum);
+    CHECK_CALLED(IsSystemMoniker);
+    CHECK_CALLED(GetDisplayName);
+}
+
+static void test_HlinkClone(void)
+{
+    HRESULT hres;
+    IHlink *hl, *cloned = NULL;
+    IMoniker *dummy, *fnd_mk;
+    IHlinkSite *fnd_site;
+    WCHAR *fnd_name;
+    DWORD fnd_data;
+    const WCHAR one[] = {'1',0};
+    const WCHAR two[] = {'2',0};
+    const WCHAR name[] = {'a',0};
+
+    hres = HlinkClone(NULL, NULL, NULL, 0, NULL);
+    ok(hres == E_INVALIDARG, "Got wrong failure code: %08x\n", hres);
+
+    hres = HlinkCreateFromString(NULL, NULL, NULL, NULL, 0, NULL,
+            &IID_IHlink, (void**)&hl);
+    ok(hres == S_OK, "HlinkCreateFromString failed: %08x\n", hres);
+
+    hres = HlinkClone(hl, &IID_IHlink, NULL, 0, NULL);
+    ok(hres == E_INVALIDARG, "Got wrong failure code: %08x\n", hres);
+
+    if (0)
+    { 
+        /* crash on Windows XP */
+        HlinkClone(hl, NULL, NULL, 0, NULL);
+
+        HlinkClone(hl, NULL, NULL, 0, (void**)&cloned);
+    }
+
+    hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
+    ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
+    ok(cloned != NULL, "Didn't get a clone\n");
+    getMonikerRef(cloned, NULL, NULL, HLINKGETREF_RELATIVE);
+    IHlink_Release(cloned);
+
+    IHlink_Release(hl);
+
+    SET_EXPECT(Reduce);
+    SET_EXPECT(Enum);
+    hres = HlinkCreateFromMoniker(&hls_ref_Moniker, two, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hl);
+    todo_wine CHECK_CALLED(Reduce);
+    todo_wine CHECK_CALLED(Enum);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
+    getMonikerRef(hl, &hls_ref_Moniker, two, HLINKGETREF_RELATIVE);
+
+    SET_EXPECT(Save);
+    SET_EXPECT(GetClassID);
+    cloned = (IHlink*)0xdeadbeef;
+    hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
+    /* fails because of invalid CLSID given by Moniker_GetClassID */
+    ok(hres == REGDB_E_CLASSNOTREG, "Wrong error code: %08x\n", hres);
+    ok(cloned == NULL, "Shouldn't have gotten a clone\n");
+    CHECK_CALLED(Save);
+    CHECK_CALLED(GetClassID);
+
+    IHlink_Release(hl);
+
+    hres = CreateItemMoniker(one, one, &dummy);
+    ok(hres == S_OK, "CreateItemMoniker failed: 0x%08x\n", hres);
+
+    hres = HlinkCreateFromMoniker(dummy, two, name, &HlinkSite, SITEDATA_SUCCESS, NULL, &IID_IHlink, (void**)&hl);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
+    getMonikerRef(hl, dummy, two, HLINKGETREF_RELATIVE);
+
+    cloned = NULL;
+    hres = HlinkClone(hl, &IID_IHlink, NULL, 0, (void**)&cloned);
+    ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
+    ok(cloned != NULL, "Should have gotten a clone\n");
+
+    fnd_mk = getMonikerRef(cloned, (IMoniker*)0xFFFFFFFF, two, HLINKGETREF_RELATIVE);
+    ok(fnd_mk != NULL, "Expected non-null Moniker\n");
+    ok(fnd_mk != dummy, "Expected a new Moniker to be created\n");
+
+    fnd_name = NULL;
+    hres = IHlink_GetFriendlyName(cloned, HLFNAMEF_DEFAULT, &fnd_name);
+    ok(hres == S_OK, "GetFriendlyName failed: %08x\n", hres);
+    ok(fnd_name != NULL, "Expected friendly name to be non-NULL\n");
+    ok(lstrcmpW(fnd_name, name) == 0, "Expected friendly name to be %s, was %s\n",
+            wine_dbgstr_w(name), wine_dbgstr_w(fnd_name));
+    CoTaskMemFree(fnd_name);
+
+    fnd_site = (IHlinkSite*)0xdeadbeef;
+    fnd_data = 4;
+    hres = IHlink_GetHlinkSite(cloned, &fnd_site, &fnd_data);
+    ok(hres == S_OK, "GetHlinkSite failed: %08x\n", hres);
+    ok(fnd_site == NULL, "Expected NULL site\n");
+    ok(fnd_data == 4, "Expected site data to be 4, was: %d\n", fnd_data);
+
+    IHlink_Release(cloned);
+    IHlink_Release(hl);
+
+    hres = HlinkCreateFromMoniker(dummy, NULL, NULL, NULL, 0, NULL, &IID_IHlink, (void**)&hl);
+    ok(hres == S_OK, "HlinkCreateFromMoniker failed: 0x%08x\n", hres);
+    getMonikerRef(hl, dummy, NULL, HLINKGETREF_RELATIVE);
+
+    cloned = NULL;
+    hres = HlinkClone(hl, &IID_IHlink, &HlinkSite, SITEDATA_SUCCESS, (void**)&cloned);
+    ok(hres == S_OK, "HlinkClone failed: %08x\n", hres);
+    ok(cloned != NULL, "Should have gotten a clone\n");
+
+    fnd_mk = getMonikerRef(cloned, (IMoniker*)0xFFFFFFFF, NULL, HLINKGETREF_RELATIVE);
+    ok(fnd_mk != NULL, "Expected non-null Moniker\n");
+    ok(fnd_mk != dummy, "Expected a new Moniker to be created\n");
+
+    fnd_site = (IHlinkSite*)0xdeadbeef;
+    fnd_data = 4;
+    hres = IHlink_GetHlinkSite(cloned, &fnd_site, &fnd_data);
+    ok(hres == S_OK, "GetHlinkSite failed: %08x\n", hres);
+    ok(fnd_site == &HlinkSite, "Expected found site to be HlinkSite, was: %p\n", fnd_site);
+    ok(fnd_data == SITEDATA_SUCCESS, "Unexpected site data: %u\n", fnd_data);
+
+    IHlink_Release(cloned);
+    IHlink_Release(hl);
+
+    IMoniker_Release(dummy);
+}
+
+static void test_StdHlink(void)
+{
+    IHlink *hlink;
+    WCHAR *str;
+    HRESULT hres;
+
+    static const WCHAR testW[] = {'t','e','s','t',0};
+
+    hres = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IHlink, (void**)&hlink);
+    ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres);
+
+    str = (void*)0xdeadbeef;
+    hres = IHlink_GetTargetFrameName(hlink, &str);
+    ok(hres == S_FALSE, "GetTargetFrameName failed: %08x\n", hres);
+    ok(!str, "str = %s\n", wine_dbgstr_w(str));
+
+    hres = IHlink_SetTargetFrameName(hlink, testW);
+    ok(hres == S_OK, "SetTargetFrameName failed: %08x\n", hres);
+
+    str = (void*)0xdeadbeef;
+    hres = IHlink_GetTargetFrameName(hlink, &str);
+    ok(hres == S_OK, "GetTargetFrameName failed: %08x\n", hres);
+    ok(!lstrcmpW(str, testW), "str = %s\n", wine_dbgstr_w(str));
+    CoTaskMemFree(str);
+
+    hres = IHlink_SetTargetFrameName(hlink, NULL);
+    ok(hres == S_OK, "SetTargetFrameName failed: %08x\n", hres);
+
+    str = (void*)0xdeadbeef;
+    hres = IHlink_GetTargetFrameName(hlink, &str);
+    ok(hres == S_FALSE, "GetTargetFrameName failed: %08x\n", hres);
+    ok(!str, "str = %s\n", wine_dbgstr_w(str));
+
+    IHlink_Release(hlink);
+}
+
 START_TEST(hlink)
 {
     CoInitialize(NULL);
@@ -1457,6 +1917,9 @@ START_TEST(hlink)
     test_HlinkGetSetStringReference();
     test_HlinkMoniker();
     test_HashLink();
+    test_HlinkSite();
+    test_HlinkClone();
+    test_StdHlink();
 
     CoUninitialize();
 }