[SHLWAPI_WINETEST]
authorChristoph von Wittich <christoph_vw@reactos.org>
Wed, 3 Mar 2010 19:40:55 +0000 (19:40 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Wed, 3 Mar 2010 19:40:55 +0000 (19:40 +0000)
sync shlwapi_winetest to wine 1.1.39

svn path=/trunk/; revision=45798

rostests/winetests/shlwapi/assoc.c
rostests/winetests/shlwapi/clist.c
rostests/winetests/shlwapi/istream.c
rostests/winetests/shlwapi/ordinal.c
rostests/winetests/shlwapi/path.c
rostests/winetests/shlwapi/shreg.c
rostests/winetests/shlwapi/string.c
rostests/winetests/shlwapi/url.c

index 3799ecc..a6f5e2d 100644 (file)
 
 #include "wine/test.h"
 #include "shlwapi.h"
+#include "shlguid.h"
 
 #define expect(expected, got) ok ( expected == got, "Expected %d, got %d\n", expected, got)
 #define expect_hr(expected, got) ok ( expected == got, "Expected %08x, got %08x\n", expected, got)
 
 static HRESULT (WINAPI *pAssocQueryStringA)(ASSOCF,ASSOCSTR,LPCSTR,LPCSTR,LPSTR,LPDWORD) = NULL;
 static HRESULT (WINAPI *pAssocQueryStringW)(ASSOCF,ASSOCSTR,LPCWSTR,LPCWSTR,LPWSTR,LPDWORD) = NULL;
+static HRESULT (WINAPI *pAssocCreate)(CLSID, REFIID, void **) = NULL;
 
 /* Every version of Windows with IE should have this association? */
 static const WCHAR dotHtml[] = { '.','h','t','m','l',0 };
@@ -235,14 +237,55 @@ cleanup:
 
 }
 
+static void test_assoc_create(void)
+{
+    HRESULT hr;
+    IQueryAssociations *pqa;
+
+    if (!pAssocCreate)
+    {
+        win_skip("AssocCreate() is missing\n");
+        return;
+    }
+
+    hr = pAssocCreate(IID_NULL, &IID_NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected result : %08x\n", hr);
+
+    hr = pAssocCreate(CLSID_QueryAssociations, &IID_NULL, (LPVOID*)&pqa);
+    ok(hr == CLASS_E_CLASSNOTAVAILABLE || hr == E_NOTIMPL || hr == E_NOINTERFACE
+        , "Unexpected result : %08x\n", hr);
+
+    hr = pAssocCreate(IID_NULL, &IID_IQueryAssociations, (LPVOID*)&pqa);
+    ok(hr == CLASS_E_CLASSNOTAVAILABLE || hr == E_NOTIMPL || hr == E_INVALIDARG
+        , "Unexpected result : %08x\n", hr);
+
+    hr = pAssocCreate(CLSID_QueryAssociations, &IID_IQueryAssociations, (LPVOID*)&pqa);
+    ok(hr == S_OK  || hr == E_NOTIMPL /* win98 */
+        , "Unexpected result : %08x\n", hr);
+    if(hr == S_OK)
+    {
+        IQueryAssociations_Release(pqa);
+    }
+
+    hr = pAssocCreate(CLSID_QueryAssociations, &IID_IUnknown, (LPVOID*)&pqa);
+    ok(hr == S_OK  || hr == E_NOTIMPL /* win98 */
+        , "Unexpected result : %08x\n", hr);
+    if(hr == S_OK)
+    {
+        IQueryAssociations_Release(pqa);
+    }
+}
+
 START_TEST(assoc)
 {
     HMODULE hshlwapi;
     hshlwapi = GetModuleHandleA("shlwapi.dll");
     pAssocQueryStringA = (void*)GetProcAddress(hshlwapi, "AssocQueryStringA");
     pAssocQueryStringW = (void*)GetProcAddress(hshlwapi, "AssocQueryStringW");
+    pAssocCreate       = (void*)GetProcAddress(hshlwapi, "AssocCreate");
 
     test_getstring_bad();
     test_getstring_basic();
     test_getstring_no_extra();
+    test_assoc_create();
 }
index 84a05e6..3b979fd 100755 (executable)
@@ -353,7 +353,8 @@ static void test_CList(void)
   InitDummyStream(&streamobj);
   streamobj.failwritesize = TRUE;
   hRet = pSHLWAPI_17(&streamobj, list);
-  ok(hRet == STG_E_MEDIUMFULL, "changed size failure return\n");
+  ok(hRet == STG_E_MEDIUMFULL || broken(hRet == E_FAIL) /* Win7 */,
+     "changed size failure return\n");
   ok(streamobj.writecalls == 1, "called object after size failure\n");
   ok(streamobj.readcalls == 0,"called Read() after failure\n");
   ok(streamobj.seekcalls == 0,"called Seek() after failure\n");
index 66465b8..67d1fe2 100644 (file)
@@ -177,8 +177,7 @@ static void test_IStream_invalid_operations(IStream * stream, DWORD mode)
 
     /* IStream::Clone */
 
-    ret = IStream_Clone(stream, NULL);
-    ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
+    /* Passing a NULL pointer for the second IStream::Clone param crashes on Win7 */
 
     clone = NULL;
     ret = IStream_Clone(stream, &clone);
index 3a50219..22437e2 100755 (executable)
 
 #include <stdio.h>
 
+#define COBJMACROS
 #include "wine/test.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "winuser.h"
 #include "ole2.h"
 #include "oaidl.h"
+#include "ocidl.h"
+#include "mlang.h"
 
 /* Function ptrs for ordinal calls */
 static HMODULE hShlwapi;
@@ -36,138 +39,255 @@ static LPVOID (WINAPI *pSHLockShared)(HANDLE,DWORD);
 static BOOL   (WINAPI *pSHUnlockShared)(LPVOID);
 static BOOL   (WINAPI *pSHFreeShared)(HANDLE,DWORD);
 static HRESULT(WINAPIV *pSHPackDispParams)(DISPPARAMS*,VARIANTARG*,UINT,...);
+static HRESULT(WINAPI *pIConnectionPoint_SimpleInvoke)(IConnectionPoint*,DISPID,DISPPARAMS*);
+static HRESULT(WINAPI *pIConnectionPoint_InvokeWithCancel)(IConnectionPoint*,DISPID,DISPPARAMS*,DWORD,DWORD);
+static HRESULT(WINAPI *pConnectToConnectionPoint)(IUnknown*,REFIID,BOOL,IUnknown*, LPDWORD,IConnectionPoint **);
+static HRESULT(WINAPI *pSHPropertyBag_ReadLONG)(IPropertyBag *,LPCWSTR,LPLONG);
+
+static HMODULE hmlang;
+static HRESULT (WINAPI *pLcidToRfc1766A)(LCID, LPSTR, INT);
+
+static const CHAR ie_international[] = {
+    'S','o','f','t','w','a','r','e','\\',
+    'M','i','c','r','o','s','o','f','t','\\',
+    'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r','\\',
+    'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
+static const CHAR acceptlanguage[] = {
+    'A','c','c','e','p','t','L','a','n','g','u','a','g','e',0};
+
 
 static void test_GetAcceptLanguagesA(void)
-{   HRESULT retval;
-    DWORD buffersize, buffersize2, exactsize;
-    char buffer[100];
+{
+    static LPCSTR table[] = {"de,en-gb;q=0.7,en;q=0.3",
+                             "de,en;q=0.3,en-gb;q=0.7", /* sorting is ignored */
+                             "winetest",    /* content is ignored */
+                             "de-de,de;q=0.5",
+                             "de",
+                             NULL};
+
+    DWORD exactsize;
+    char original[512];
+    char language[32];
+    char buffer[64];
+    HKEY hroot = NULL;
+    LONG res_query = ERROR_SUCCESS;
+    LONG lres;
+    HRESULT hr;
+    DWORD maxlen = sizeof(buffer) - 2;
+    DWORD len;
+    LCID lcid;
+    LPCSTR entry;
+    INT i = 0;
 
     if (!pGetAcceptLanguagesA) {
         win_skip("GetAcceptLanguagesA is not available\n");
-       return;
+        return;
     }
 
-    buffersize = sizeof(buffer);
-    memset(buffer, 0, sizeof(buffer));
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( buffer, &buffersize);
-    if (!retval && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
-        win_skip("GetAcceptLanguagesA is not implemented\n");
+    lcid = GetUserDefaultLCID();
+
+    /* Get the original Value */
+    lres = RegOpenKeyA(HKEY_CURRENT_USER, ie_international, &hroot);
+    if (lres) {
+        skip("RegOpenKey(%s) failed: %d\n", ie_international, lres);
         return;
     }
-    trace("GetAcceptLanguagesA: retval %08x, size %08x, buffer (%s),"
-       " last error %u\n", retval, buffersize, buffer, GetLastError());
-    if(retval != S_OK) {
-       trace("GetAcceptLanguagesA: skipping tests\n");
-       return;
-    }
-    ok( (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()) || 
-       (ERROR_CLASS_DOES_NOT_EXIST == GetLastError()) ||
-       (ERROR_PROC_NOT_FOUND == GetLastError()) ||
-       (ERROR_SUCCESS == GetLastError()), "last error set to %u\n", GetLastError());
-    exactsize = strlen(buffer);
-
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( NULL, NULL);
-    ok(retval == E_FAIL ||
-       retval == E_INVALIDARG, /* w2k8 */
-       "function result wrong: got %08x; expected E_FAIL\n", retval);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %u\n", GetLastError());
-
-    buffersize = sizeof(buffer);
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( NULL, &buffersize);
-    ok(retval == E_FAIL ||
-       retval == E_INVALIDARG, /* w2k8 */
-       "function result wrong: got %08x; expected E_FAIL\n", retval);
-    ok(buffersize == sizeof(buffer) ||
-       buffersize == 0, /* w2k8*/
-       "buffersize was changed and is not 0; size (%d))\n", buffersize);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %u\n", GetLastError());
-
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( buffer, NULL);
-    ok(retval == E_FAIL ||
-       retval == E_INVALIDARG, /* w2k8 */
-       "function result wrong: got %08x; expected E_FAIL\n", retval);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %u\n", GetLastError());
-
-    buffersize = 0;
-    memset(buffer, 0, sizeof(buffer));
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( buffer, &buffersize);
-    ok(retval == E_FAIL ||
-       retval == E_INVALIDARG, /* w2k8 */
-       "function result wrong: got %08x; expected E_FAIL\n", retval);
-    ok(buffersize == 0,
-       "buffersize wrong(changed) got %08x; expected 0 (2nd parameter; not on Win2k)\n", buffersize);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %u\n", GetLastError());
-
-    buffersize = buffersize2 = 1;
-    memset(buffer, 0, sizeof(buffer));
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( buffer, &buffersize);
-    switch(retval) {
-       case 0L:
-            if(buffersize == exactsize) {
-            ok( (ERROR_SUCCESS == GetLastError()) ||
-               (ERROR_PROC_NOT_FOUND == GetLastError()) || (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()),
-                "last error wrong: got %u; expected ERROR_SUCCESS(NT4)/"
-               "ERROR_PROC_NOT_FOUND(NT4)/ERROR_NO_IMPERSONATION_TOKEN(XP)\n", GetLastError());
-            ok(exactsize == strlen(buffer),
-                 "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), exactsize);
-            } else if((buffersize +1) == buffersize2) {
-                ok(ERROR_SUCCESS == GetLastError(),
-                    "last error wrong: got %u; expected ERROR_SUCCESS\n", GetLastError());
-                ok(buffersize == strlen(buffer),
-                    "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize);
-            } else
-                ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
-                    retval, buffersize, buffer, GetLastError());
-            break;
-       case E_INVALIDARG:
-            ok(buffersize == 0,
-               "buffersize wrong: got %08x, expected 0 (2nd parameter;Win2k)\n", buffersize);
-            ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
-               "last error wrong: got %u; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
-            ok(buffersize2 == strlen(buffer),
-               "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize2);
-            break;
-        default:
-            ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
-                retval, buffersize, buffer, GetLastError());
-            break;
-    }
-
-    buffersize = buffersize2 = exactsize;
-    memset(buffer, 0, sizeof(buffer));
-    SetLastError(ERROR_SUCCESS);
-    retval = pGetAcceptLanguagesA( buffer, &buffersize);
-    switch(retval) {
-       case 0L:
-            ok(ERROR_SUCCESS == GetLastError(),
-                 "last error wrong: got %u; expected ERROR_SUCCESS\n", GetLastError());
-            if((buffersize == exactsize) /* XP */ ||
-               ((buffersize +1)== exactsize) /* 98 */)
-                ok(buffersize == strlen(buffer),
-                    "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize);
-            else
-                ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
-                    retval, buffersize, buffer, GetLastError());
-            break;
-       case E_INVALIDARG:
-            ok(buffersize == 0,
-               "buffersize wrong: got %08x, expected 0 (2nd parameter;Win2k)\n", buffersize);
-            ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
-               "last error wrong: got %u; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
-            ok(buffersize2 == strlen(buffer),
-               "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize2);
-            break;
-        default:
-            ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
-                retval, buffersize, buffer, GetLastError());
-            break;
+    len = sizeof(original);
+    original[0] = 0;
+    res_query = RegQueryValueExA(hroot, acceptlanguage, 0, NULL, (PBYTE)original, &len);
+
+    RegDeleteValue(hroot, acceptlanguage);
+
+    /* Some windows versions use "lang-COUNTRY" as default */
+    memset(language, 0, sizeof(language));
+    len = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, language, sizeof(language));
+
+    if (len) {
+        lstrcat(language, "-");
+        memset(buffer, 0, sizeof(buffer));
+        len = GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buffer, sizeof(buffer) - len - 1);
+        lstrcat(language, buffer);
+    }
+    else
+    {
+        /* LOCALE_SNAME has additional parts in some languages. Try only as last chance */
+        memset(language, 0, sizeof(language));
+        len = GetLocaleInfoA(lcid, LOCALE_SNAME, language, sizeof(language));
+    }
+
+    /* get the default value */
+    len = maxlen;
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, &len);
+
+    if (hr != S_OK) {
+        win_skip("GetAcceptLanguagesA failed with 0x%x\n", hr);
+        goto restore_original;
+    }
+
+    if (lstrcmpA(buffer, language)) {
+        /* some windows versions use "lang" or "lang-country" as default */
+        language[0] = 0;
+        if (pLcidToRfc1766A) {
+            hr = pLcidToRfc1766A(lcid, language, sizeof(language));
+            ok(hr == S_OK, "LcidToRfc1766A returned 0x%x and %s\n", hr, language);
+        }
+    }
+
+    ok(!lstrcmpA(buffer, language),
+        "have '%s' (searching for '%s')\n", language, buffer);
+
+    if (lstrcmpA(buffer, language)) {
+        win_skip("no more ideas, how to build the default language '%s'\n", buffer);
+        goto restore_original;
+    }
+
+    trace("detected default: %s\n", language);
+    while ((entry = table[i])) {
+
+        exactsize = lstrlenA(entry);
+
+        lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) entry, exactsize + 1);
+        ok(!lres, "got %d for RegSetValueExA: %s\n", lres, entry);
+
+        /* len includes space for the terminating 0 before vista/w2k8 */
+        len = exactsize + 2;
+        memset(buffer, '#', maxlen);
+        buffer[maxlen] = 0;
+        hr = pGetAcceptLanguagesA( buffer, &len);
+        ok(((hr == E_INVALIDARG) && (len == 0)) ||
+            (SUCCEEDED(hr) &&
+            ((len == exactsize) || (len == exactsize+1)) &&
+            !lstrcmpA(buffer, entry)),
+            "+2_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+
+        len = exactsize + 1;
+        memset(buffer, '#', maxlen);
+        buffer[maxlen] = 0;
+        hr = pGetAcceptLanguagesA( buffer, &len);
+        ok(((hr == E_INVALIDARG) && (len == 0)) ||
+            (SUCCEEDED(hr) &&
+            ((len == exactsize) || (len == exactsize+1)) &&
+            !lstrcmpA(buffer, entry)),
+            "+1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+
+        len = exactsize;
+        memset(buffer, '#', maxlen);
+        buffer[maxlen] = 0;
+        hr = pGetAcceptLanguagesA( buffer, &len);
+
+        /* There is no space for the string in the registry.
+           When the buffer is large enough, the default language is returned
+
+           When the buffer is to small for that fallback, win7_32 and w2k8_64
+           and above fail with HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), but
+           recent os succeed and return a partial result while
+           older os succeed and overflow the buffer */
+
+        ok(((hr == E_INVALIDARG) && (len == 0)) ||
+            (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
+            ((hr == S_OK) && !memcmp(buffer, language, len)) ||
+            ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
+            "==_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+
+        if (exactsize > 1) {
+            len = exactsize - 1;
+            memset(buffer, '#', maxlen);
+            buffer[maxlen] = 0;
+            hr = pGetAcceptLanguagesA( buffer, &len);
+            ok(((hr == E_INVALIDARG) && (len == 0)) ||
+                (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
+                ((hr == S_OK) && !memcmp(buffer, language, len)) ||
+                ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
+                "-1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+        }
+
+        len = 1;
+        memset(buffer, '#', maxlen);
+        buffer[maxlen] = 0;
+        hr = pGetAcceptLanguagesA( buffer, &len);
+        ok(((hr == E_INVALIDARG) && (len == 0)) ||
+            (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
+            ((hr == S_OK) && !memcmp(buffer, language, len)) ||
+            ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
+            "=1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+
+        len = maxlen;
+        hr = pGetAcceptLanguagesA( NULL, &len);
+
+        /* w2k3 and below: E_FAIL and untouched len,
+           since w2k8: S_OK and needed size (excluding 0) */
+        ok( ((hr == S_OK) && (len == exactsize)) ||
+            ((hr == E_FAIL) && (len == maxlen)),
+            "NULL,max #%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
+
+        i++;
+    }
+
+    /* without a value in the registry, a default language is returned */
+    RegDeleteValue(hroot, acceptlanguage);
+
+    len = maxlen;
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, &len);
+    ok( ((hr == S_OK) && (len == lstrlenA(language))),
+        "max: got 0x%x with %d and %s (expected S_OK with %d and '%s'\n",
+        hr, len, buffer, lstrlenA(language), language);
+
+    len = 2;
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, &len);
+    ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
+        ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
+        "=2: got 0x%x with %d and %s\n", hr, len, buffer);
+
+    len = 1;
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, &len);
+    /* When the buffer is to small, win7_32 and w2k8_64 and above fail with
+       HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), other versions suceed
+       and return a partial 0 terminated result while other versions
+       fail with E_INVALIDARG and return a partial unterminated result */
+    ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
+        ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
+        "=1: got 0x%x with %d and %s\n", hr, len, buffer);
+
+    len = 0;
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, &len);
+    /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
+    ok((hr == E_FAIL) || (hr == E_INVALIDARG),
+        "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
+
+    memset(buffer, '#', maxlen);
+    buffer[maxlen] = 0;
+    hr = pGetAcceptLanguagesA( buffer, NULL);
+    /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
+    ok((hr == E_FAIL) || (hr == E_INVALIDARG),
+        "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
+
+
+    hr = pGetAcceptLanguagesA( NULL, NULL);
+    /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
+    ok((hr == E_FAIL) || (hr == E_INVALIDARG),
+        "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
+
+restore_original:
+    if (!res_query) {
+        len = lstrlenA(original);
+        lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) original, len ? len + 1: 0);
+        ok(!lres, "RegSetValueEx(%s) failed: %d\n", original, lres);
+    }
+    else
+    {
+        RegDeleteValue(hroot, acceptlanguage);
     }
+    RegCloseKey(hroot);
 }
 
 static void test_SHSearchMapInt(void)
@@ -511,6 +631,772 @@ static void test_SHPackDispParams(void)
     ok(V_BSTR(vars+3) == (void*)0xdeadbeef, "V_BSTR(vars[3]) = %p\n", V_BSTR(vars+3));
 }
 
+typedef struct _disp
+{
+    const IDispatchVtbl *vtbl;
+    LONG   refCount;
+} Disp;
+
+typedef struct _contain
+{
+    const IConnectionPointContainerVtbl *vtbl;
+    LONG   refCount;
+
+    UINT  ptCount;
+    IConnectionPoint **pt;
+} Contain;
+
+typedef struct _cntptn
+{
+    const IConnectionPointVtbl *vtbl;
+    LONG refCount;
+
+    Contain *container;
+    GUID  id;
+    UINT  sinkCount;
+    IUnknown **sink;
+} ConPt;
+
+typedef struct _enum
+{
+    const IEnumConnectionsVtbl *vtbl;
+    LONG   refCount;
+
+    UINT idx;
+    ConPt *pt;
+} EnumCon;
+
+typedef struct _enumpt
+{
+    const IEnumConnectionPointsVtbl *vtbl;
+    LONG   refCount;
+
+    int idx;
+    Contain *container;
+} EnumPt;
+
+
+static HRESULT WINAPI Disp_QueryInterface(
+        IDispatch* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Disp_AddRef(IDispatch* This)
+{
+    Disp *iface = (Disp*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI Disp_Release(IDispatch* This)
+{
+    Disp *iface = (Disp*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+        HeapFree(GetProcessHeap(),0,This);
+    return ret;
+}
+
+static HRESULT WINAPI Disp_GetTypeInfoCount(
+        IDispatch* This,
+        UINT *pctinfo)
+{
+    return ERROR_SUCCESS;
+}
+
+static HRESULT WINAPI Disp_GetTypeInfo(
+        IDispatch* This,
+        UINT iTInfo,
+        LCID lcid,
+        ITypeInfo **ppTInfo)
+{
+    return ERROR_SUCCESS;
+}
+
+static HRESULT WINAPI Disp_GetIDsOfNames(
+        IDispatch* This,
+        REFIID riid,
+        LPOLESTR *rgszNames,
+        UINT cNames,
+        LCID lcid,
+        DISPID *rgDispId)
+{
+    return ERROR_SUCCESS;
+}
+
+static HRESULT WINAPI Disp_Invoke(
+        IDispatch* This,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS *pDispParams,
+        VARIANT *pVarResult,
+        EXCEPINFO *pExcepInfo,
+        UINT *puArgErr)
+{
+    trace("%p %x %p %x %x %p %p %p %p\n",This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
+
+    ok(dispIdMember == 0xa0 || dispIdMember == 0xa1, "Unknown dispIdMember\n");
+    ok(pDispParams != NULL, "Invoked with NULL pDispParams\n");
+    ok(wFlags == DISPATCH_METHOD, "Wrong flags %x\n",wFlags);
+    ok(lcid == 0,"Wrong lcid %x\n",lcid);
+    if (dispIdMember == 0xa0)
+    {
+        ok(pDispParams->cArgs == 0, "params.cArgs = %d\n", pDispParams->cArgs);
+        ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
+        ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
+        ok(pDispParams->rgvarg == NULL, "params.rgvarg = %p\n", pDispParams->rgvarg);
+    }
+    else if (dispIdMember == 0xa1)
+    {
+        ok(pDispParams->cArgs == 2, "params.cArgs = %d\n", pDispParams->cArgs);
+        ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
+        ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
+        ok(V_VT(pDispParams->rgvarg) == VT_BSTR, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg));
+        ok(V_I4(pDispParams->rgvarg) == 0xdeadcafe , "failed %p\n", V_BSTR(pDispParams->rgvarg));
+        ok(V_VT(pDispParams->rgvarg+1) == VT_I4, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg+1));
+        ok(V_I4(pDispParams->rgvarg+1) == 0xdeadbeef, "failed %x\n", V_I4(pDispParams->rgvarg+1));
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static const IDispatchVtbl disp_vtbl = {
+    Disp_QueryInterface,
+    Disp_AddRef,
+    Disp_Release,
+
+    Disp_GetTypeInfoCount,
+    Disp_GetTypeInfo,
+    Disp_GetIDsOfNames,
+    Disp_Invoke
+};
+
+static HRESULT WINAPI Enum_QueryInterface(
+        IEnumConnections* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnections))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Enum_AddRef(IEnumConnections* This)
+{
+    EnumCon *iface = (EnumCon*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI Enum_Release(IEnumConnections* This)
+{
+    EnumCon *iface = (EnumCon*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+        HeapFree(GetProcessHeap(),0,This);
+    return ret;
+}
+
+static HRESULT WINAPI Enum_Next(
+        IEnumConnections* This,
+        ULONG cConnections,
+        LPCONNECTDATA rgcd,
+        ULONG *pcFetched)
+{
+    EnumCon *iface = (EnumCon*)This;
+
+    if (cConnections > 0 && iface->idx < iface->pt->sinkCount)
+    {
+        rgcd->pUnk = iface->pt->sink[iface->idx];
+        IUnknown_AddRef(iface->pt->sink[iface->idx]);
+        rgcd->dwCookie=0xff;
+        if (pcFetched)
+            *pcFetched = 1;
+        iface->idx++;
+        return S_OK;
+    }
+
+    return E_FAIL;
+}
+
+static HRESULT WINAPI Enum_Skip(
+        IEnumConnections* This,
+        ULONG cConnections)
+{
+    return E_FAIL;
+}
+
+static HRESULT WINAPI Enum_Reset(
+        IEnumConnections* This)
+{
+    return E_FAIL;
+}
+
+static HRESULT WINAPI Enum_Clone(
+        IEnumConnections* This,
+        IEnumConnections **ppEnum)
+{
+    return E_FAIL;
+}
+
+static const IEnumConnectionsVtbl enum_vtbl = {
+
+    Enum_QueryInterface,
+    Enum_AddRef,
+    Enum_Release,
+    Enum_Next,
+    Enum_Skip,
+    Enum_Reset,
+    Enum_Clone
+};
+
+static HRESULT WINAPI ConPt_QueryInterface(
+        IConnectionPoint* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPoint))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ConPt_AddRef(
+        IConnectionPoint* This)
+{
+    ConPt *iface = (ConPt*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI ConPt_Release(
+        IConnectionPoint* This)
+{
+    ConPt *iface = (ConPt*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+    {
+        if (iface->sinkCount > 0)
+        {
+            int i;
+            for (i = 0; i < iface->sinkCount; i++)
+            {
+                if (iface->sink[i])
+                    IUnknown_Release(iface->sink[i]);
+            }
+            HeapFree(GetProcessHeap(),0,iface->sink);
+        }
+        HeapFree(GetProcessHeap(),0,This);
+    }
+    return ret;
+}
+
+static HRESULT WINAPI ConPt_GetConnectionInterface(
+        IConnectionPoint* This,
+        IID *pIID)
+{
+    static int i = 0;
+    ConPt *iface = (ConPt*)This;
+    if (i==0)
+    {
+        i++;
+        return E_FAIL;
+    }
+    else
+        memcpy(pIID,&iface->id,sizeof(GUID));
+    return S_OK;
+}
+
+static HRESULT WINAPI ConPt_GetConnectionPointContainer(
+        IConnectionPoint* This,
+        IConnectionPointContainer **ppCPC)
+{
+    ConPt *iface = (ConPt*)This;
+
+    *ppCPC = (IConnectionPointContainer*)iface->container;
+    return S_OK;
+}
+
+static HRESULT WINAPI ConPt_Advise(
+        IConnectionPoint* This,
+        IUnknown *pUnkSink,
+        DWORD *pdwCookie)
+{
+    ConPt *iface = (ConPt*)This;
+
+    if (iface->sinkCount == 0)
+        iface->sink = HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
+    else
+        iface->sink = HeapReAlloc(GetProcessHeap(),0,iface->sink,sizeof(IUnknown*)*(iface->sinkCount+1));
+    iface->sink[iface->sinkCount] = pUnkSink;
+    IUnknown_AddRef(pUnkSink);
+    iface->sinkCount++;
+    *pdwCookie = iface->sinkCount;
+    return S_OK;
+}
+
+static HRESULT WINAPI ConPt_Unadvise(
+        IConnectionPoint* This,
+        DWORD dwCookie)
+{
+    ConPt *iface = (ConPt*)This;
+
+    if (dwCookie > iface->sinkCount)
+        return E_FAIL;
+    else
+    {
+        IUnknown_Release(iface->sink[dwCookie-1]);
+        iface->sink[dwCookie-1] = NULL;
+    }
+    return S_OK;
+}
+
+static HRESULT WINAPI ConPt_EnumConnections(
+        IConnectionPoint* This,
+        IEnumConnections **ppEnum)
+{
+    EnumCon *ec;
+
+    ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumCon));
+    ec->vtbl = &enum_vtbl;
+    ec->refCount = 1;
+    ec->pt = (ConPt*)This;
+    ec->idx = 0;
+    *ppEnum = (IEnumConnections*)ec;
+
+    return S_OK;
+}
+
+static const IConnectionPointVtbl point_vtbl = {
+    ConPt_QueryInterface,
+    ConPt_AddRef,
+    ConPt_Release,
+
+    ConPt_GetConnectionInterface,
+    ConPt_GetConnectionPointContainer,
+    ConPt_Advise,
+    ConPt_Unadvise,
+    ConPt_EnumConnections
+};
+
+static HRESULT WINAPI EnumPt_QueryInterface(
+        IEnumConnectionPoints* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnectionPoints))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI EnumPt_AddRef(IEnumConnectionPoints* This)
+{
+    EnumPt *iface = (EnumPt*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI EnumPt_Release(IEnumConnectionPoints* This)
+{
+    EnumPt *iface = (EnumPt*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+        HeapFree(GetProcessHeap(),0,This);
+    return ret;
+}
+
+static HRESULT WINAPI EnumPt_Next(
+        IEnumConnectionPoints* This,
+        ULONG cConnections,
+        IConnectionPoint **rgcd,
+        ULONG *pcFetched)
+{
+    EnumPt *iface = (EnumPt*)This;
+
+    if (cConnections > 0 && iface->idx < iface->container->ptCount)
+    {
+        *rgcd = iface->container->pt[iface->idx];
+        IUnknown_AddRef(iface->container->pt[iface->idx]);
+        if (pcFetched)
+            *pcFetched = 1;
+        iface->idx++;
+        return S_OK;
+    }
+
+    return E_FAIL;
+}
+
+static HRESULT WINAPI EnumPt_Skip(
+        IEnumConnectionPoints* This,
+        ULONG cConnections)
+{
+    return E_FAIL;
+}
+
+static HRESULT WINAPI EnumPt_Reset(
+        IEnumConnectionPoints* This)
+{
+    return E_FAIL;
+}
+
+static HRESULT WINAPI EnumPt_Clone(
+        IEnumConnectionPoints* This,
+        IEnumConnectionPoints **ppEnumPt)
+{
+    return E_FAIL;
+}
+
+static const IEnumConnectionPointsVtbl enumpt_vtbl = {
+
+    EnumPt_QueryInterface,
+    EnumPt_AddRef,
+    EnumPt_Release,
+    EnumPt_Next,
+    EnumPt_Skip,
+    EnumPt_Reset,
+    EnumPt_Clone
+};
+
+static HRESULT WINAPI Contain_QueryInterface(
+        IConnectionPointContainer* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPointContainer))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Contain_AddRef(
+        IConnectionPointContainer* This)
+{
+    Contain *iface = (Contain*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI Contain_Release(
+        IConnectionPointContainer* This)
+{
+    Contain *iface = (Contain*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+    {
+        if (iface->ptCount > 0)
+        {
+            int i;
+            for (i = 0; i < iface->ptCount; i++)
+                IUnknown_Release(iface->pt[i]);
+            HeapFree(GetProcessHeap(),0,iface->pt);
+        }
+        HeapFree(GetProcessHeap(),0,This);
+    }
+    return ret;
+}
+
+static HRESULT WINAPI Contain_EnumConnectionPoints(
+        IConnectionPointContainer* This,
+        IEnumConnectionPoints **ppEnum)
+{
+    EnumPt *ec;
+
+    ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumPt));
+    ec->vtbl = &enumpt_vtbl;
+    ec->refCount = 1;
+    ec->idx= 0;
+    ec->container = (Contain*)This;
+    *ppEnum = (IEnumConnectionPoints*)ec;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI Contain_FindConnectionPoint(
+        IConnectionPointContainer* This,
+        REFIID riid,
+        IConnectionPoint **ppCP)
+{
+    Contain *iface = (Contain*)This;
+    ConPt *pt;
+
+    if (!IsEqualIID(riid, &IID_NULL) || iface->ptCount ==0)
+    {
+        pt = HeapAlloc(GetProcessHeap(),0,sizeof(ConPt));
+        pt->vtbl = &point_vtbl;
+        pt->refCount = 1;
+        pt->sinkCount = 0;
+        pt->sink = NULL;
+        pt->container = iface;
+        pt->id = IID_IDispatch;
+
+        if (iface->ptCount == 0)
+            iface->pt =HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
+        else
+            iface->pt = HeapReAlloc(GetProcessHeap(),0,iface->pt,sizeof(IUnknown*)*(iface->ptCount+1));
+        iface->pt[iface->ptCount] = (IConnectionPoint*)pt;
+        iface->ptCount++;
+
+        *ppCP = (IConnectionPoint*)pt;
+    }
+    else
+    {
+        *ppCP = iface->pt[0];
+        IUnknown_AddRef((IUnknown*)*ppCP);
+    }
+
+    return S_OK;
+}
+
+static const IConnectionPointContainerVtbl contain_vtbl = {
+    Contain_QueryInterface,
+    Contain_AddRef,
+    Contain_Release,
+
+    Contain_EnumConnectionPoints,
+    Contain_FindConnectionPoint
+};
+
+static void test_IConnectionPoint(void)
+{
+    HRESULT rc;
+    ULONG ref;
+    IConnectionPoint *point;
+    Contain *container;
+    Disp *dispatch;
+    DWORD cookie = 0xffffffff;
+    DISPPARAMS params;
+    VARIANT vars[10];
+
+    if (!pIConnectionPoint_SimpleInvoke || !pConnectToConnectionPoint)
+    {
+        win_skip("IConnectionPoint Apis not present\n");
+        return;
+    }
+
+    container = HeapAlloc(GetProcessHeap(),0,sizeof(Contain));
+    container->vtbl = &contain_vtbl;
+    container->refCount = 1;
+    container->ptCount = 0;
+    container->pt = NULL;
+
+    dispatch = HeapAlloc(GetProcessHeap(),0,sizeof(Disp));
+    dispatch->vtbl = &disp_vtbl;
+    dispatch->refCount = 1;
+
+    rc = pConnectToConnectionPoint((IUnknown*)dispatch, &IID_NULL, TRUE, (IUnknown*)container, &cookie, &point);
+    ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
+    ok(point != NULL, "returned ConnectionPoint is NULL\n");
+    ok(cookie != 0xffffffff, "invalid cookie returned\n");
+
+    rc = pIConnectionPoint_SimpleInvoke(point,0xa0,NULL);
+    ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
+
+    if (pSHPackDispParams)
+    {
+        memset(&params, 0xc0, sizeof(params));
+        memset(vars, 0xc0, sizeof(vars));
+        rc = pSHPackDispParams(&params, vars, 2, VT_I4, 0xdeadbeef, VT_BSTR, 0xdeadcafe);
+        ok(rc == S_OK, "SHPackDispParams failed: %08x\n", rc);
+
+        rc = pIConnectionPoint_SimpleInvoke(point,0xa1,&params);
+        ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
+    }
+    else
+        win_skip("pSHPackDispParams not present\n");
+
+    rc = pConnectToConnectionPoint(NULL, &IID_NULL, FALSE, (IUnknown*)container, &cookie, NULL);
+    ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
+
+/* MSDN says this should be required but it crashs on XP
+    IUnknown_Release(point);
+*/
+    ref = IUnknown_Release((IUnknown*)container);
+    ok(ref == 0, "leftover IConnectionPointContainer reference %i\n",ref);
+    ref = IUnknown_Release((IUnknown*)dispatch);
+    ok(ref == 0, "leftover IDispatch reference %i\n",ref);
+}
+
+typedef struct _propbag
+{
+    const IPropertyBagVtbl *vtbl;
+    LONG   refCount;
+
+} PropBag;
+
+
+static HRESULT WINAPI Prop_QueryInterface(
+        IPropertyBag* This,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPropertyBag))
+    {
+        *ppvObject = This;
+    }
+
+    if (*ppvObject)
+    {
+        IUnknown_AddRef(This);
+        return S_OK;
+    }
+
+    trace("no interface\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Prop_AddRef(
+        IPropertyBag* This)
+{
+    PropBag *iface = (PropBag*)This;
+    return InterlockedIncrement(&iface->refCount);
+}
+
+static ULONG WINAPI Prop_Release(
+        IPropertyBag* This)
+{
+    PropBag *iface = (PropBag*)This;
+    ULONG ret;
+
+    ret = InterlockedDecrement(&iface->refCount);
+    if (ret == 0)
+        HeapFree(GetProcessHeap(),0,This);
+    return ret;
+}
+
+static HRESULT WINAPI Prop_Read(
+        IPropertyBag* This,
+        LPCOLESTR pszPropName,
+        VARIANT *pVar,
+        IErrorLog *pErrorLog)
+{
+    V_VT(pVar) = VT_BLOB|VT_BYREF;
+    V_BYREF(pVar) = (LPVOID)0xdeadcafe;
+    return S_OK;
+}
+
+static HRESULT WINAPI Prop_Write(
+        IPropertyBag* This,
+        LPCOLESTR pszPropName,
+        VARIANT *pVar)
+{
+    return S_OK;
+}
+
+
+static const IPropertyBagVtbl prop_vtbl = {
+    Prop_QueryInterface,
+    Prop_AddRef,
+    Prop_Release,
+
+    Prop_Read,
+    Prop_Write
+};
+
+static void test_SHPropertyBag_ReadLONG(void)
+{
+    PropBag *pb;
+    HRESULT rc;
+    LONG out;
+    static const WCHAR szName1[] = {'n','a','m','e','1',0};
+
+    if (!pSHPropertyBag_ReadLONG)
+    {
+        win_skip("SHPropertyBag_ReadLONG not present\n");
+        return;
+    }
+
+    pb = HeapAlloc(GetProcessHeap(),0,sizeof(PropBag));
+    pb->refCount = 1;
+    pb->vtbl = &prop_vtbl;
+
+    out = 0xfeedface;
+    rc = pSHPropertyBag_ReadLONG(NULL, szName1, &out);
+    ok(rc == E_INVALIDARG || broken(rc == 0), "incorrect return %x\n",rc);
+    ok(out == 0xfeedface, "value should not have changed\n");
+    rc = pSHPropertyBag_ReadLONG((IPropertyBag*)pb, NULL, &out);
+    ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
+    ok(out == 0xfeedface, "value should not have changed\n");
+    rc = pSHPropertyBag_ReadLONG((IPropertyBag*)pb, szName1, NULL);
+    ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
+    ok(out == 0xfeedface, "value should not have changed\n");
+    rc = pSHPropertyBag_ReadLONG((IPropertyBag*)pb, szName1, &out);
+    ok(rc == DISP_E_BADVARTYPE || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
+    ok(out == 0xfeedface  || broken(out == 0xfeedfa00), "value should not have changed %x\n",out);
+    IUnknown_Release((IUnknown*)pb);
+}
+
 START_TEST(ordinal)
 {
   hShlwapi = GetModuleHandleA("shlwapi.dll");
@@ -522,6 +1408,13 @@ START_TEST(ordinal)
   pSHUnlockShared=(void*)GetProcAddress(hShlwapi,(char*)9);
   pSHFreeShared=(void*)GetProcAddress(hShlwapi,(char*)10);
   pSHPackDispParams=(void*)GetProcAddress(hShlwapi,(char*)282);
+  pIConnectionPoint_SimpleInvoke=(void*)GetProcAddress(hShlwapi,(char*)284);
+  pIConnectionPoint_InvokeWithCancel=(void*)GetProcAddress(hShlwapi,(char*)283);
+  pConnectToConnectionPoint=(void*)GetProcAddress(hShlwapi,(char*)168);
+  pSHPropertyBag_ReadLONG=(void*)GetProcAddress(hShlwapi,(char*)496);
+
+  hmlang = LoadLibraryA("mlang.dll");
+  pLcidToRfc1766A = (void *)GetProcAddress(hmlang, "LcidToRfc1766A");
 
   test_GetAcceptLanguagesA();
   test_SHSearchMapInt();
@@ -529,4 +1422,6 @@ START_TEST(ordinal)
   test_fdsa();
   test_GetShellSecurityDescriptor();
   test_SHPackDispParams();
+  test_IConnectionPoint();
+  test_SHPropertyBag_ReadLONG();
 }
index 52eee2d..e52502f 100755 (executable)
@@ -32,6 +32,8 @@ static HMODULE hShlwapi;
 static HRESULT (WINAPI *pPathIsValidCharA)(char,DWORD);
 static HRESULT (WINAPI *pPathIsValidCharW)(WCHAR,DWORD);
 static LPWSTR  (WINAPI *pPathCombineW)(LPWSTR, LPCWSTR, LPCWSTR);
+static HRESULT (WINAPI *pPathCreateFromUrlA)(LPCSTR, LPSTR, LPDWORD, DWORD);
+static HRESULT (WINAPI *pPathCreateFromUrlW)(LPCWSTR, LPWSTR, LPDWORD, DWORD);
 
 /* ################ */
 
@@ -207,31 +209,39 @@ static void test_PathCreateFromUrl(void)
     WCHAR *pathW, *urlW;
     static const char url[] = "http://www.winehq.org";
 
+    if (!pPathCreateFromUrlA) {
+        win_skip("PathCreateFromUrlA not found\n");
+        return;
+    }
+
     /* Check ret_path = NULL */
     len = sizeof(url);
-    ret = PathCreateFromUrlA(url, NULL, &len, 0); 
+    ret = pPathCreateFromUrlA(url, NULL, &len, 0);
     ok ( ret == E_INVALIDARG, "got 0x%08x expected E_INVALIDARG\n", ret);
 
     for(i = 0; i < sizeof(TEST_PATHFROMURL) / sizeof(TEST_PATHFROMURL[0]); i++) {
         len = INTERNET_MAX_URL_LENGTH;
-        ret = PathCreateFromUrlA(TEST_PATHFROMURL[i].url, ret_path, &len, 0);
+        ret = pPathCreateFromUrlA(TEST_PATHFROMURL[i].url, ret_path, &len, 0);
         ok(ret == TEST_PATHFROMURL[i].ret, "ret %08x from url %s\n", ret, TEST_PATHFROMURL[i].url);
         if(TEST_PATHFROMURL[i].path) {
            ok(!lstrcmpi(ret_path, TEST_PATHFROMURL[i].path), "got %s expected %s from url %s\n", ret_path, TEST_PATHFROMURL[i].path,  TEST_PATHFROMURL[i].url);
            ok(len == strlen(ret_path), "ret len %d from url %s\n", len, TEST_PATHFROMURL[i].url);
         }
-        len = INTERNET_MAX_URL_LENGTH;
-        pathW = GetWideString(TEST_PATHFROMURL[i].path);
-        urlW = GetWideString(TEST_PATHFROMURL[i].url);
-        ret = PathCreateFromUrlW(urlW, ret_pathW, &len, 0);
-        WideCharToMultiByte(CP_ACP, 0, ret_pathW, -1, ret_path, sizeof(ret_path),0,0);
-        ok(ret == TEST_PATHFROMURL[i].ret, "ret %08x from url L\"%s\"\n", ret, TEST_PATHFROMURL[i].url);
-        if(TEST_PATHFROMURL[i].path) {
-            ok(!lstrcmpiW(ret_pathW, pathW), "got %s expected %s from url L\"%s\"\n", ret_path, TEST_PATHFROMURL[i].path, TEST_PATHFROMURL[i].url);
-            ok(len == lstrlenW(ret_pathW), "ret len %d from url L\"%s\"\n", len, TEST_PATHFROMURL[i].url);
+        if (pPathCreateFromUrlW) {
+            len = INTERNET_MAX_URL_LENGTH;
+            pathW = GetWideString(TEST_PATHFROMURL[i].path);
+            urlW = GetWideString(TEST_PATHFROMURL[i].url);
+            ret = pPathCreateFromUrlW(urlW, ret_pathW, &len, 0);
+            WideCharToMultiByte(CP_ACP, 0, ret_pathW, -1, ret_path, sizeof(ret_path),0,0);
+            ok(ret == TEST_PATHFROMURL[i].ret, "ret %08x from url L\"%s\"\n", ret, TEST_PATHFROMURL[i].url);
+            if(TEST_PATHFROMURL[i].path) {
+                ok(!lstrcmpiW(ret_pathW, pathW), "got %s expected %s from url L\"%s\"\n",
+                    ret_path, TEST_PATHFROMURL[i].path, TEST_PATHFROMURL[i].url);
+                ok(len == lstrlenW(ret_pathW), "ret len %d from url L\"%s\"\n", len, TEST_PATHFROMURL[i].url);
+            }
+            FreeWideString(urlW);
+            FreeWideString(pathW);
         }
-        FreeWideString(urlW);
-        FreeWideString(pathW);
     }
 }
 
@@ -690,7 +700,7 @@ static void test_PathAppendA(void)
 {
     char path[MAX_PATH];
     char too_long[LONG_LEN];
-    char one[HALF_LEN], two[HALF_LEN];
+    char half[HALF_LEN];
     BOOL res;
 
     lstrcpy(path, "C:\\one");
@@ -803,16 +813,16 @@ static void test_PathAppendA(void)
        "Expected length of path to be zero, got %i\n", lstrlen(path));
 
     /* both params combined are too long */
-    memset(one, 'a', HALF_LEN);
-    one[HALF_LEN - 1] = '\0';
-    memset(two, 'b', HALF_LEN);
-    two[HALF_LEN - 1] = '\0';
+    memset(path, 'a', HALF_LEN);
+    path[HALF_LEN - 1] = '\0';
+    memset(half, 'b', HALF_LEN);
+    half[HALF_LEN - 1] = '\0';
     SetLastError(0xdeadbeef);
-    res = PathAppendA(one, two);
+    res = PathAppendA(path, half);
     ok(!res, "Expected failure\n");
-    ok(lstrlen(one) == 0 ||
-       broken(lstrlen(one) == (HALF_LEN - 1)), /* Win95 and some W2K */
-       "Expected length of one to be zero, got %i\n", lstrlen(one));
+    ok(lstrlen(path) == 0 ||
+       broken(lstrlen(path) == (HALF_LEN - 1)), /* Win95 and some W2K */
+       "Expected length of path to be zero, got %i\n", lstrlen(path));
     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
 }
 
@@ -1307,6 +1317,8 @@ static void test_PathUnquoteSpaces(void)
 START_TEST(path)
 {
   hShlwapi = GetModuleHandleA("shlwapi.dll");
+  pPathCreateFromUrlA = (void*)GetProcAddress(hShlwapi, "PathCreateFromUrlA");
+  pPathCreateFromUrlW = (void*)GetProcAddress(hShlwapi, "PathCreateFromUrlW");
 
   test_PathSearchAndQualify();
   test_PathCreateFromUrl();
index 00cec4f..5fb3dac 100755 (executable)
@@ -38,6 +38,8 @@ typedef DWORD (WINAPI *SHCopyKeyA_func)(HKEY,LPCSTR,HKEY,DWORD);
 static SHCopyKeyA_func pSHCopyKeyA;
 typedef DWORD (WINAPI *SHRegGetPathA_func)(HKEY,LPCSTR,LPCSTR,LPSTR,DWORD);
 static SHRegGetPathA_func pSHRegGetPathA;
+typedef LSTATUS (WINAPI *SHRegGetValueA_func)(HKEY,LPCSTR,LPCSTR,SRRF,LPDWORD,LPVOID,LPDWORD);
+static SHRegGetValueA_func pSHRegGetValueA;
 
 static char sTestpath1[] = "%LONGSYSTEMVAR%\\subdir1";
 static char sTestpath2[] = "%FOO%\\subdir1";
@@ -138,6 +140,44 @@ static void test_SHGetValue(void)
        ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 }
 
+static void test_SHRegGetValue(void)
+{
+    LSTATUS ret;
+    DWORD size, type;
+    char data[MAX_PATH];
+
+    if(!pSHRegGetValueA)
+        return;
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", SRRF_RT_REG_EXPAND_SZ, &type, data, &size);
+    ok(ret == ERROR_INVALID_PARAMETER, "SHRegGetValue failed, ret=%u\n", ret);
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", SRRF_RT_REG_SZ, &type, data, &size);
+    ok(ret == ERROR_SUCCESS, "SHRegGetValue failed, ret=%u\n", ret);
+    ok(!strcmp(data, sExpTestpath1), "data = %s, expected %s\n", data, sExpTestpath1);
+    ok(type == REG_SZ, "type = %d, expected REG_SZ\n", type);
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", SRRF_RT_REG_DWORD, &type, data, &size);
+    ok(ret == ERROR_UNSUPPORTED_TYPE, "SHRegGetValue failed, ret=%u\n", ret);
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", SRRF_RT_REG_EXPAND_SZ, &type, data, &size);
+    ok(ret == ERROR_INVALID_PARAMETER, "SHRegGetValue failed, ret=%u\n", ret);
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", SRRF_RT_REG_SZ, &type, data, &size);
+    ok(ret == ERROR_SUCCESS, "SHRegGetValue failed, ret=%u\n", ret);
+    ok(!strcmp(data, sTestpath1), "data = %s, expected %s\n", data, sTestpath1);
+    ok(type == REG_SZ, "type = %d, expected REG_SZ\n", type);
+
+    size = MAX_PATH;
+    ret = pSHRegGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", SRRF_RT_REG_QWORD, &type, data, &size);
+    ok(ret == ERROR_UNSUPPORTED_TYPE, "SHRegGetValue failed, ret=%u\n", ret);
+}
+
 static void test_SHGetRegPath(void)
 {
        char buf[MAX_PATH];
@@ -414,7 +454,9 @@ START_TEST(shreg)
        hshlwapi = GetModuleHandleA("shlwapi.dll");
         pSHCopyKeyA=(SHCopyKeyA_func)GetProcAddress(hshlwapi,"SHCopyKeyA");
         pSHRegGetPathA=(SHRegGetPathA_func)GetProcAddress(hshlwapi,"SHRegGetPathA");
+        pSHRegGetValueA=(SHRegGetValueA_func)GetProcAddress(hshlwapi,"SHRegGetValueA");
        test_SHGetValue();
+        test_SHRegGetValue();
        test_SHQUeryValueEx();
        test_SHGetRegPath();
        test_SHCopyKey();
index fa50626..00f0b02 100755 (executable)
@@ -40,6 +40,8 @@
     ok(ret == val1 || ret == val2, "Unexpected value of '" #expr "': " #fmt " instead of " #val1 " or " #val2 "\n", ret); \
 } while (0);
 
+static BOOL    (WINAPI *pChrCmpIA)(CHAR, CHAR);
+static BOOL    (WINAPI *pChrCmpIW)(WCHAR, WCHAR);
 static BOOL    (WINAPI *pIntlStrEqWorkerA)(BOOL,LPCSTR,LPCSTR,int);
 static BOOL    (WINAPI *pIntlStrEqWorkerW)(BOOL,LPCWSTR,LPCWSTR,int);
 static DWORD   (WINAPI *pSHAnsiToAnsi)(LPCSTR,LPSTR,int);
@@ -58,6 +60,7 @@ static HRESULT (WINAPI *pStrRetToBufA)(STRRET*,LPCITEMIDLIST,LPSTR,UINT);
 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
 static INT     (WINAPIV *pwnsprintfA)(LPSTR,INT,LPCSTR, ...);
 static INT     (WINAPIV *pwnsprintfW)(LPWSTR,INT,LPCWSTR, ...);
+static LPWSTR  (WINAPI *pStrChrNW)(LPWSTR,WCHAR,UINT);
 
 static int strcmpW(const WCHAR *str1, const WCHAR *str2)
 {
@@ -373,6 +376,27 @@ static void test_StrCpyW(void)
   }
 }
 
+static void test_StrChrNW(void)
+{
+    static WCHAR string[] = {'T','e','s','t','i','n','g',' ','S','t','r','i','n','g',0};
+    LPWSTR p;
+
+    if (!pStrChrNW)
+    {
+        win_skip("StrChrNW not available\n");
+        return;
+    }
+
+    p = pStrChrNW(string,'t',10);
+    ok(*p=='t',"Found wrong 't'\n");
+    ok(*(p+1)=='i',"next should be 'i'\n");
+
+    p = pStrChrNW(string,'S',10);
+    ok(*p=='S',"Found wrong 'S'\n");
+
+    p = pStrChrNW(string,'r',10);
+    ok(p==NULL,"Should not have found 'r'\n");
+}
 
 static void test_StrToIntA(void)
 {
@@ -586,9 +610,13 @@ static void test_StrCmpA(void)
   static const char str2[] = {'a','B','c','d','e','f'};
   ok(0 != StrCmpNA(str1, str2, 6), "StrCmpNA is case-insensitive\n");
   ok(0 == StrCmpNIA(str1, str2, 6), "StrCmpNIA is case-sensitive\n");
-  ok(!ChrCmpIA('a', 'a'), "ChrCmpIA doesn't work at all!\n");
-  ok(!ChrCmpIA('b', 'B'), "ChrCmpIA is not case-insensitive\n");
-  ok(ChrCmpIA('a', 'z'), "ChrCmpIA believes that a == z!\n");
+  if (pChrCmpIA) {
+    ok(!pChrCmpIA('a', 'a'), "ChrCmpIA doesn't work at all!\n");
+    ok(!pChrCmpIA('b', 'B'), "ChrCmpIA is not case-insensitive\n");
+    ok(pChrCmpIA('a', 'z'), "ChrCmpIA believes that a == z!\n");
+  }
+  else
+    win_skip("ChrCmpIA() is not available\n");
 
   if (pStrIsIntlEqualA)
   {
@@ -613,9 +641,13 @@ static void test_StrCmpW(void)
   static const WCHAR str2[] = {'a','B','c','d','e','f'};
   ok(0 != StrCmpNW(str1, str2, 5), "StrCmpNW is case-insensitive\n");
   ok(0 == StrCmpNIW(str1, str2, 5), "StrCmpNIW is case-sensitive\n");
-  ok(!ChrCmpIW('a', 'a'), "ChrCmpIW doesn't work at all!\n");
-  ok(!ChrCmpIW('b', 'B'), "ChrCmpIW is not case-insensitive\n");
-  ok(ChrCmpIW('a', 'z'), "ChrCmpIW believes that a == z!\n");
+  if (pChrCmpIW) {
+    ok(!pChrCmpIW('a', 'a'), "ChrCmpIW doesn't work at all!\n");
+    ok(!pChrCmpIW('b', 'B'), "ChrCmpIW is not case-insensitive\n");
+    ok(pChrCmpIW('a', 'z'), "ChrCmpIW believes that a == z!\n");
+  }
+  else
+    win_skip("ChrCmpIW() is not available\n");
 
   if (pStrIsIntlEqualW)
   {
@@ -903,6 +935,8 @@ START_TEST(string)
   GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimalDelim, 8);
 
   hShlwapi = GetModuleHandleA("shlwapi");
+  pChrCmpIA = (void *)GetProcAddress(hShlwapi, "ChrCmpIA");
+  pChrCmpIW = (void *)GetProcAddress(hShlwapi, "ChrCmpIW");
   pIntlStrEqWorkerA = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerA");
   pIntlStrEqWorkerW = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerW");
   pSHAnsiToAnsi = (void *)GetProcAddress(hShlwapi, (LPSTR)345);
@@ -911,6 +945,7 @@ START_TEST(string)
   pStrCatBuffW = (void *)GetProcAddress(hShlwapi, "StrCatBuffW");
   pStrCpyNXA = (void *)GetProcAddress(hShlwapi, (LPSTR)399);
   pStrCpyNXW = (void *)GetProcAddress(hShlwapi, (LPSTR)400);
+  pStrChrNW = (void *)GetProcAddress(hShlwapi, "StrChrNW");
   pStrFormatByteSize64A = (void *)GetProcAddress(hShlwapi, "StrFormatByteSize64A");
   pStrFormatKBSizeA = (void *)GetProcAddress(hShlwapi, "StrFormatKBSizeA");
   pStrFormatKBSizeW = (void *)GetProcAddress(hShlwapi, "StrFormatKBSizeW");
@@ -929,6 +964,7 @@ START_TEST(string)
   test_StrRChrA();
   test_StrRChrW();
   test_StrCpyW();
+  test_StrChrNW();
   test_StrToIntA();
   test_StrToIntW();
   test_StrToIntExA();
index f135f2a..3d86ae7 100644 (file)
@@ -1,7 +1,7 @@
 /* Unit test suite for Path functions
  *
  * Copyright 2002 Matthew Mastracci
- * Copyright 2007,2008 Detlef Riekenberg
+ * Copyright 2007-2010 Detlef Riekenberg
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include "winreg.h"
 #include "shlwapi.h"
 #include "wininet.h"
+#include "intshcut.h"
 
 /* ################ */
 static HMODULE hShlwapi;
+static HRESULT (WINAPI *pUrlUnescapeA)(LPSTR,LPSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlUnescapeW)(LPWSTR,LPWSTR,LPDWORD,DWORD);
+static BOOL    (WINAPI *pUrlIsA)(LPCSTR,URLIS);
+static BOOL    (WINAPI *pUrlIsW)(LPCWSTR,URLIS);
+static HRESULT (WINAPI *pUrlHashA)(LPCSTR,LPBYTE,DWORD);
+static HRESULT (WINAPI *pUrlHashW)(LPCWSTR,LPBYTE,DWORD);
+static HRESULT (WINAPI *pUrlGetPartA)(LPCSTR,LPSTR,LPDWORD,DWORD,DWORD);
+static HRESULT (WINAPI *pUrlGetPartW)(LPCWSTR,LPWSTR,LPDWORD,DWORD,DWORD);
+static HRESULT (WINAPI *pUrlEscapeA)(LPCSTR,LPSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlEscapeW)(LPCWSTR,LPWSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlCreateFromPathA)(LPCSTR,LPSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlCreateFromPathW)(LPCWSTR,LPWSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlCombineA)(LPCSTR,LPCSTR,LPSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlCombineW)(LPCWSTR,LPCWSTR,LPWSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlCanonicalizeA)(LPCSTR, LPSTR, LPDWORD, DWORD);
 static HRESULT (WINAPI *pUrlCanonicalizeW)(LPCWSTR, LPWSTR, LPDWORD, DWORD);
+static HRESULT (WINAPI *pUrlApplySchemeA)(LPCSTR,LPSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pUrlApplySchemeW)(LPCWSTR,LPWSTR,LPDWORD,DWORD);
+static HRESULT (WINAPI *pParseURLA)(LPCSTR,PARSEDURLA*);
+static HRESULT (WINAPI *pParseURLW)(LPCWSTR,PARSEDURLW*);
+static HRESULT (WINAPI *pHashData)(LPBYTE, DWORD, LPBYTE, DWORD);
 
 static const char* TEST_URL_1 = "http://www.winehq.org/tests?date=10/10/1923";
 static const char* TEST_URL_2 = "http://localhost:8080/tests%2e.html?date=Mon%2010/10/1923";
@@ -120,18 +141,24 @@ static const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
     {"c:dir\\file", 0, S_OK, "file:///c:dir/file", FALSE},
     {"c:\\tests\\foo bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar", FALSE},
     {"c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo%20bar", FALSE},
+    {"res://file", 0, S_OK, "res://file/", FALSE},
+    {"res://file", URL_FILE_USE_PATHURL, S_OK, "res://file/", FALSE},
     {"res:///c:/tests/foo%20bar", URL_UNESCAPE , S_OK, "res:///c:/tests/foo bar", FALSE},
-    {"res:///c:/tests\\foo%20bar", URL_UNESCAPE , S_OK, "res:///c:/tests\\foo bar", TRUE},
+    {"res:///c:/tests\\foo%20bar", URL_UNESCAPE , S_OK, "res:///c:/tests\\foo bar", FALSE},
     {"res:///c:/tests/foo%20bar", 0, S_OK, "res:///c:/tests/foo%20bar", FALSE},
-    {"res:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res:///c:/tests/foo%20bar", TRUE},
-    {"res://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", TRUE},
-    {"res://c:/tests\\../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", TRUE},
-    {"res://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", TRUE},
-    {"res:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res:///c://tests/foo%20bar", TRUE},
-    {"res:///c:\\tests\\foo bar", 0, S_OK, "res:///c:\\tests\\foo bar", TRUE},
-    {"res:///c:\\tests\\foo bar", URL_DONT_SIMPLIFY, S_OK, "res:///c:\\tests\\foo bar", TRUE},
+    {"res:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res:///c:/tests/foo%20bar", FALSE},
+    {"res://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", FALSE},
+    {"res://c:/tests\\../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", FALSE},
+    {"res://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res://c:/tests/foo%20bar", FALSE},
+    {"res:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "res:///c://tests/foo%20bar", FALSE},
+    {"res:///c:\\tests\\foo bar", 0, S_OK, "res:///c:\\tests\\foo bar", FALSE},
+    {"res:///c:\\tests\\foo bar", URL_DONT_SIMPLIFY, S_OK, "res:///c:\\tests\\foo bar", FALSE},
+    {"res://c:\\tests\\foo bar/res", URL_FILE_USE_PATHURL, S_OK, "res://c:\\tests\\foo bar/res", FALSE},
+    {"res://c:\\tests/res\\foo%20bar/strange\\sth", 0, S_OK, "res://c:\\tests/res\\foo%20bar/strange\\sth", FALSE},
+    {"res://c:\\tests/res\\foo%20bar/strange\\sth", URL_FILE_USE_PATHURL, S_OK, "res://c:\\tests/res\\foo%20bar/strange\\sth", FALSE},
+    {"res://c:\\tests/res\\foo%20bar/strange\\sth", URL_UNESCAPE, S_OK, "res://c:\\tests/res\\foo bar/strange\\sth", FALSE},
     {"A", 0, S_OK, "A", FALSE},
-    {"/uri-res/N2R?urn:sha1:B3K", URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY /*0x82000000*/, S_OK, "/uri-res/N2R?urn:sha1:B3K", TRUE} /*LimeWire online installer calls this*/,
+    {"/uri-res/N2R?urn:sha1:B3K", URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY /*0x82000000*/, S_OK, "/uri-res/N2R?urn:sha1:B3K", FALSE} /*LimeWire online installer calls this*/,
     {"http:www.winehq.org/dir/../index.html", 0, S_OK, "http:www.winehq.org/index.html"},
 };
 
@@ -284,7 +311,8 @@ static const TEST_URL_COMBINE TEST_COMBINE[] = {
     {"http://xxxxxxxxx","outbind:wine17/dir",URL_PLUGGABLE_PROTOCOL, S_OK,"outbind:wine17/dir"},
     {"xxx://xxxxxxxxx","ftp:wine18/dir",URL_PLUGGABLE_PROTOCOL, S_OK,"ftp:wine18/dir"},
     {"ftp://xxxxxxxxx/","xxx:wine19/dir",URL_PLUGGABLE_PROTOCOL, S_OK,"xxx:wine19/dir"},
-    {"outbind://xxxxxxxxx/","http:wine20/dir",URL_PLUGGABLE_PROTOCOL, S_OK,"http:wine20/dir"}
+    {"outbind://xxxxxxxxx/","http:wine20/dir",URL_PLUGGABLE_PROTOCOL, S_OK,"http:wine20/dir"},
+    {"file:///c:/dir/file.txt","index.html?test=c:/abc",URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO,S_OK,"file:///c:/dir/index.html?test=c:/abc"}
 };
 
 /* ################ */
@@ -397,10 +425,15 @@ static void test_UrlApplyScheme(void)
     DWORD len;
     DWORD i;
 
+    if (!pUrlApplySchemeA) {
+        win_skip("UrlApplySchemeA not found\n");
+        return;
+    }
+
     for(i = 0; i < sizeof(TEST_APPLY)/sizeof(TEST_APPLY[0]); i++) {
         len = TEST_APPLY_MAX_LENGTH;
         lstrcpyA(newurl, untouchedA);
-        res = UrlApplySchemeA(TEST_APPLY[i].url, newurl, &len, TEST_APPLY[i].flags);
+        res = pUrlApplySchemeA(TEST_APPLY[i].url, newurl, &len, TEST_APPLY[i].flags);
         ok( res == TEST_APPLY[i].res,
             "#%dA: got HRESULT 0x%x (expected 0x%x)\n", i, res, TEST_APPLY[i].res);
 
@@ -416,7 +449,7 @@ static void test_UrlApplyScheme(void)
         MultiByteToWideChar(CP_ACP, 0, newurl, -1, newurlW, len);
         MultiByteToWideChar(CP_ACP, 0, TEST_APPLY[i].url, -1, urlW, len);
 
-        res = UrlApplySchemeW(urlW, newurlW, &len, TEST_APPLY[i].flags);
+        res = pUrlApplySchemeW(urlW, newurlW, &len, TEST_APPLY[i].flags);
         WideCharToMultiByte(CP_ACP, 0, newurlW, -1, newurl, TEST_APPLY_MAX_LENGTH, NULL, NULL);
         ok( res == TEST_APPLY[i].res,
             "#%dW: got HRESULT 0x%x (expected 0x%x)\n", i, res, TEST_APPLY[i].res);
@@ -432,7 +465,7 @@ static void test_UrlApplyScheme(void)
     /* buffer too small */
     lstrcpyA(newurl, untouchedA);
     len = lstrlenA(TEST_APPLY[0].newurl);
-    res = UrlApplySchemeA(TEST_APPLY[0].url, newurl, &len, TEST_APPLY[0].flags);
+    res = pUrlApplySchemeA(TEST_APPLY[0].url, newurl, &len, TEST_APPLY[0].flags);
     ok(res == E_POINTER, "got HRESULT 0x%x (expected E_POINTER)\n", res);
     /* The returned length include the space for the terminating 0 */
     i = lstrlenA(TEST_APPLY[0].newurl)+1;
@@ -442,18 +475,18 @@ static void test_UrlApplyScheme(void)
     /* NULL as parameter. The length and the buffer are not modified */
     lstrcpyA(newurl, untouchedA);
     len = TEST_APPLY_MAX_LENGTH;
-    res = UrlApplySchemeA(NULL, newurl, &len, TEST_APPLY[0].flags);
+    res = pUrlApplySchemeA(NULL, newurl, &len, TEST_APPLY[0].flags);
     ok(res == E_INVALIDARG, "got HRESULT 0x%x (expected E_INVALIDARG)\n", res);
     ok(len == TEST_APPLY_MAX_LENGTH, "got len %d\n", len);
     ok(!lstrcmpA(newurl, untouchedA), "got '%s' (expected '%s')\n", newurl, untouchedA);
 
     len = TEST_APPLY_MAX_LENGTH;
-    res = UrlApplySchemeA(TEST_APPLY[0].url, NULL, &len, TEST_APPLY[0].flags);
+    res = pUrlApplySchemeA(TEST_APPLY[0].url, NULL, &len, TEST_APPLY[0].flags);
     ok(res == E_INVALIDARG, "got HRESULT 0x%x (expected E_INVALIDARG)\n", res);
     ok(len == TEST_APPLY_MAX_LENGTH, "got len %d\n", len);
 
     lstrcpyA(newurl, untouchedA);
-    res = UrlApplySchemeA(TEST_APPLY[0].url, newurl, NULL, TEST_APPLY[0].flags);
+    res = pUrlApplySchemeA(TEST_APPLY[0].url, newurl, NULL, TEST_APPLY[0].flags);
     ok(res == E_INVALIDARG, "got HRESULT 0x%x (expected E_INVALIDARG)\n", res);
     ok(!lstrcmpA(newurl, untouchedA), "got '%s' (expected '%s')\n", newurl, untouchedA);
 
@@ -465,19 +498,29 @@ static void hash_url(const char* szUrl)
 {
   LPCSTR szTestUrl = szUrl;
   LPWSTR wszTestUrl = GetWideString(szTestUrl);
+  HRESULT res;
 
   DWORD cbSize = sizeof(DWORD);
   DWORD dwHash1, dwHash2;
-  ok(UrlHashA(szTestUrl, (LPBYTE)&dwHash1, cbSize) == S_OK, "UrlHashA didn't return S_OK\n");
-  ok(UrlHashW(wszTestUrl, (LPBYTE)&dwHash2, cbSize) == S_OK, "UrlHashW didn't return S_OK\n");
-
+  res = pUrlHashA(szTestUrl, (LPBYTE)&dwHash1, cbSize);
+  ok(res == S_OK, "UrlHashA returned 0x%x (expected S_OK) for %s\n", res, szUrl);
+  if (pUrlHashW) {
+    res = pUrlHashW(wszTestUrl, (LPBYTE)&dwHash2, cbSize);
+    ok(res == S_OK, "UrlHashW returned 0x%x (expected S_OK) for %s\n", res, szUrl);
+    ok(dwHash1 == dwHash2,
+        "Hashes didn't match (A: 0x%x, W: 0x%x) for %s\n", dwHash1, dwHash2, szUrl);
+  }
   FreeWideString(wszTestUrl);
 
-  ok(dwHash1 == dwHash2, "Hashes didn't compare\n");
 }
 
 static void test_UrlHash(void)
 {
+  if (!pUrlHashA) {
+    win_skip("UrlHashA not found\n");
+    return;
+  }
+
   hash_url(TEST_URL_1);
   hash_url(TEST_URL_2);
   hash_url(TEST_URL_3);
@@ -491,20 +534,29 @@ static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const
   WCHAR wszPart[INTERNET_MAX_URL_LENGTH];
   LPWSTR wszUrl = GetWideString(szUrl);
   LPWSTR wszConvertedPart;
-
+  HRESULT res;
   DWORD dwSize;
 
   dwSize = INTERNET_MAX_URL_LENGTH;
-  ok( UrlGetPartA(szUrl, szPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartA for \"%s\" part 0x%08x didn't return S_OK but \"%s\"\n", szUrl, dwPart, szPart);
-  dwSize = INTERNET_MAX_URL_LENGTH;
-  ok( UrlGetPartW(wszUrl, wszPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartW didn't return S_OK\n" );
+  res = pUrlGetPartA(szUrl, szPart, &dwSize, dwPart, dwFlags);
+  ok(res == S_OK,
+    "UrlGetPartA for \"%s\" part 0x%08x returned 0x%x and \"%s\"\n",
+    szUrl, dwPart, res, szPart);
+  if (pUrlGetPartW) {
+    dwSize = INTERNET_MAX_URL_LENGTH;
+    res = pUrlGetPartW(wszUrl, wszPart, &dwSize, dwPart, dwFlags);
+    ok(res == S_OK,
+      "UrlGetPartW for \"%s\" part 0x%08x returned 0x%x\n",
+      szUrl, dwPart, res);
 
-  wszConvertedPart = GetWideString(szPart);
+    wszConvertedPart = GetWideString(szPart);
 
-  ok(lstrcmpW(wszPart,wszConvertedPart)==0, "Strings didn't match between ascii and unicode UrlGetPart!\n");
+    ok(lstrcmpW(wszPart,wszConvertedPart)==0,
+        "Strings didn't match between ascii and unicode UrlGetPart!\n");
 
+    FreeWideString(wszConvertedPart);
+  }
   FreeWideString(wszUrl);
-  FreeWideString(wszConvertedPart);
 
   /* Note that v6.0 and later don't return '?' with the query */
   ok(strcmp(szPart,szExpected)==0 ||
@@ -516,20 +568,29 @@ static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const
 
 static void test_UrlGetPart(void)
 {
+  const char* file_url = "file://h o s t/c:/windows/file";
+  const char* http_url = "http://user:pass 123@www.wine hq.org";
+  const char* about_url = "about:blank";
+
   CHAR szPart[INTERNET_MAX_URL_LENGTH];
   DWORD dwSize;
   HRESULT res;
 
+  if (!pUrlGetPartA) {
+    win_skip("UrlGetPartA not found\n");
+    return;
+  }
+
   dwSize = sizeof szPart;
   szPart[0]='x'; szPart[1]=0;
-  res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_SCHEME, 0);
+  res = pUrlGetPartA("hi", szPart, &dwSize, URL_PART_SCHEME, 0);
   todo_wine {
   ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res);
   ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart);
   }
   dwSize = sizeof szPart;
   szPart[0]='x'; szPart[1]=0;
-  res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_QUERY, 0);
+  res = pUrlGetPartA("hi", szPart, &dwSize, URL_PART_QUERY, 0);
   todo_wine {
   ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res);
   ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart);
@@ -541,6 +602,28 @@ static void test_UrlGetPart(void)
   test_url_part(TEST_URL_3, URL_PART_PASSWORD, 0, "bar");
   test_url_part(TEST_URL_3, URL_PART_SCHEME, 0, "http");
   test_url_part(TEST_URL_3, URL_PART_QUERY, 0, "?query=x&return=y");
+
+  test_url_part(file_url, URL_PART_HOSTNAME, 0, "h o s t");
+
+  test_url_part(http_url, URL_PART_HOSTNAME, 0, "www.wine hq.org");
+  test_url_part(http_url, URL_PART_PASSWORD, 0, "pass 123");
+
+  test_url_part(about_url, URL_PART_SCHEME, 0, "about");
+
+  dwSize = sizeof(szPart);
+  res = pUrlGetPartA(about_url, szPart, &dwSize, URL_PART_HOSTNAME, 0);
+  ok(res==E_FAIL, "returned %08x\n", res);
+
+  dwSize = sizeof(szPart);
+  res = pUrlGetPartA("file://c:\\index.htm", szPart, &dwSize, URL_PART_HOSTNAME, 0);
+  ok(res==S_FALSE, "returned %08x\n", res);
+
+  dwSize = sizeof(szPart);
+  szPart[0] = 'x'; szPart[1] = '\0';
+  res = pUrlGetPartA("file:some text", szPart, &dwSize, URL_PART_HOSTNAME, 0);
+  ok(res==S_FALSE, "returned %08x\n", res);
+  ok(szPart[0] == '\0', "szPart[0] = %c\n", szPart[0]);
+  ok(dwSize == 0, "dwSize = %d\n", dwSize);
 }
 
 /* ########################### */
@@ -553,18 +636,23 @@ static void test_url_escape(const char *szUrl, DWORD dwFlags, HRESULT dwExpectRe
     WCHAR *urlW, *expected_urlW;
     dwEscaped=INTERNET_MAX_URL_LENGTH;
 
-    ok(UrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeA didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
+    ok(pUrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn,
+        "UrlEscapeA didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
     ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", szExpectUrl, szReturnUrl, szUrl);
 
-    dwEscaped = INTERNET_MAX_URL_LENGTH;
-    urlW = GetWideString(szUrl);
-    expected_urlW = GetWideString(szExpectUrl);
-    ok(UrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeW didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
-    WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
-    ok(lstrcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08x\n", szExpectUrl, szReturnUrl, szUrl, dwFlags);
-    FreeWideString(urlW);
-    FreeWideString(expected_urlW);
-
+    if (pUrlEscapeW) {
+        dwEscaped = INTERNET_MAX_URL_LENGTH;
+        urlW = GetWideString(szUrl);
+        expected_urlW = GetWideString(szExpectUrl);
+        ok(pUrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn,
+            "UrlEscapeW didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
+        WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
+        ok(lstrcmpW(ret_urlW, expected_urlW)==0,
+            "Expected \"%s\", but got \"%s\" from \"%s\" flags %08x\n",
+            szExpectUrl, szReturnUrl, szUrl, dwFlags);
+        FreeWideString(urlW);
+        FreeWideString(expected_urlW);
+    }
 }
 
 static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, HRESULT dwExpectReturnAlt, const char *szExpectUrl, BOOL todo)
@@ -579,8 +667,9 @@ static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, H
     DWORD dwSize;
 
     dwSize = INTERNET_MAX_URL_LENGTH;
-    ok(UrlCanonicalizeA(szUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer, index %d\n", index);
-    ret = UrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags);
+    ret = pUrlCanonicalizeA(szUrl, NULL, &dwSize, dwFlags);
+    ok(ret != dwExpectReturn, "got 0s%x: Unexpected return for NULL buffer, index %d\n", ret, index);
+    ret = pUrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags);
     ok(ret == dwExpectReturn || ret == dwExpectReturnAlt,
        "UrlCanonicalizeA failed: expected=0x%08x or 0x%08x, got=0x%08x, index %d\n",
        dwExpectReturn, dwExpectReturnAlt, ret, index);
@@ -590,13 +679,19 @@ static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, H
     else
         ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x url '%s' Expected \"%s\", but got \"%s\", index %d\n", dwFlags, szUrl, szExpectUrl, szReturnUrl, index);
 
-    dwSize = INTERNET_MAX_URL_LENGTH;
-    ok(UrlCanonicalizeW(wszUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer, index %d\n", index);
-    ok(UrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeW didn't return 0x%08x, index %d\n", dwExpectReturn, index);
-    wszConvertedUrl = GetWideString(szReturnUrl);
-    ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize, index %d!\n", index);
-    FreeWideString(wszConvertedUrl);
+    if (pUrlCanonicalizeW) {
+        dwSize = INTERNET_MAX_URL_LENGTH;
+        ret = pUrlCanonicalizeW(wszUrl, NULL, &dwSize, dwFlags);
+        ok(ret != dwExpectReturn, "got 0x%x: Unexpected return for NULL buffer, index %d\n", ret, index);
+        ret = pUrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags);
+        ok(ret == dwExpectReturn, "UrlCanonicalizeW failed: expected 0x%08x, got 0x%x, index %d\n",
+            dwExpectReturn, ret, index);
 
+        wszConvertedUrl = GetWideString(szReturnUrl);
+        ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0,
+            "Strings didn't match between ascii and unicode UrlCanonicalize, index %d!\n", index);
+        FreeWideString(wszConvertedUrl);
+    }
 
     FreeWideString(wszUrl);
     FreeWideString(wszExpectUrl);
@@ -610,27 +705,32 @@ static void test_UrlEscape(void)
     unsigned int i;
     char empty_string[] = "";
 
-    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY);
+    if (!pUrlEscapeA) {
+        win_skip("UrlEscapeA noz found\n");
+        return;
+    }
+
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY);
     ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
     ok(size == 0, "got %d, expected %d\n", size, 0);
 
     size = 0;
-    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, &size, URL_ESCAPE_SPACES_ONLY);
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, &size, URL_ESCAPE_SPACES_ONLY);
     ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
     ok(size == 0, "got %d, expected %d\n", size, 0);
 
     size = 1;
-    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY);
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY);
     ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
     ok(size == 1, "got %d, expected %d\n", size, 1);
 
     size = 1;
-    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, NULL, URL_ESCAPE_SPACES_ONLY);
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, NULL, URL_ESCAPE_SPACES_ONLY);
     ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
     ok(size == 1, "got %d, expected %d\n", size, 1);
 
     size = 1;
-    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, &size, URL_ESCAPE_SPACES_ONLY);
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, &size, URL_ESCAPE_SPACES_ONLY);
     ok(ret == E_POINTER, "got %x, expected %x\n", ret, E_POINTER);
     ok(size == 34, "got %d, expected %d\n", size, 34);
 
@@ -650,6 +750,11 @@ static void test_UrlCanonicalizeA(void)
     DWORD urllen;
     HRESULT hr;
 
+    if (!pUrlCanonicalizeA) {
+        win_skip("UrlCanonicalizeA not found\n");
+        return;
+    }
+
     urllen = lstrlenA(winehqA);
 
     /* buffer has no space for the result */
@@ -657,7 +762,7 @@ static void test_UrlCanonicalizeA(void)
     memset(szReturnUrl, '#', urllen+4);
     szReturnUrl[urllen+4] = '\0';
     SetLastError(0xdeadbeef);
-    hr = UrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY  | URL_ESCAPE_UNSAFE);
+    hr = pUrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY  | URL_ESCAPE_UNSAFE);
     ok( (hr == E_POINTER) && (dwSize == (urllen + 1)),
         "got 0x%x with %u and size %u for '%s' and %u (expected 'E_POINTER' and size %u)\n",
         hr, GetLastError(), dwSize, szReturnUrl, lstrlenA(szReturnUrl), urllen+1);
@@ -667,7 +772,7 @@ static void test_UrlCanonicalizeA(void)
     memset(szReturnUrl, '#', urllen+4);
     szReturnUrl[urllen+4] = '\0';
     SetLastError(0xdeadbeef);
-    hr = UrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
+    hr = pUrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
     ok( (hr == E_POINTER) && (dwSize == (urllen + 1)),
         "got 0x%x with %u and size %u for '%s' and %u (expected 'E_POINTER' and size %u)\n",
         hr, GetLastError(), dwSize, szReturnUrl, lstrlenA(szReturnUrl), urllen+1);
@@ -677,7 +782,7 @@ static void test_UrlCanonicalizeA(void)
     memset(szReturnUrl, '#', urllen+4);
     szReturnUrl[urllen+4] = '\0';
     SetLastError(0xdeadbeef);
-    hr = UrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
+    hr = pUrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
     ok( (hr == S_OK) && (dwSize == urllen),
         "got 0x%x with %u and size %u for '%s' and %u (expected 'S_OK' and size %u)\n",
         hr, GetLastError(), dwSize, szReturnUrl, lstrlenA(szReturnUrl), urllen);
@@ -687,7 +792,7 @@ static void test_UrlCanonicalizeA(void)
     memset(szReturnUrl, '#', urllen+4);
     szReturnUrl[urllen+4] = '\0';
     SetLastError(0xdeadbeef);
-    hr = UrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
+    hr = pUrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE);
     ok( (hr == S_OK) && (dwSize == urllen),
         "got 0x%x with %u and size %u for '%s' and %u (expected 'S_OK' and size %u)\n",
         hr, GetLastError(), dwSize, szReturnUrl, lstrlenA(szReturnUrl), urllen);
@@ -714,7 +819,7 @@ static void test_UrlCanonicalizeW(void)
 
 
     if (!pUrlCanonicalizeW) {
-        skip("UrlCanonicalizeW\n");
+        win_skip("UrlCanonicalizeW not found\n");
         return;
     }
     urllen = lstrlenW(winehqW);
@@ -792,43 +897,50 @@ static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFla
     DWORD dwSize;
     DWORD dwExpectLen = lstrlen(szExpectUrl);
 
-    hr = UrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
+    if (!pUrlCombineA) {
+        win_skip("UrlCombineA not found\n");
+        return;
+    }
+
+    hr = pUrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
     ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_INVALIDARG);
 
     dwSize = 0;
-    hr = UrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
+    hr = pUrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
     ok(hr == E_POINTER, "Checking length of string, return was 0x%08x, expected 0x%08x\n", hr, E_POINTER);
     ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
     dwSize--;
-    hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
+    hr = pUrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
     ok(hr == E_POINTER, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_POINTER);
     ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
-    hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
+    hr = pUrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
     ok(hr == dwExpectReturn, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, dwExpectReturn);
     ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
     if(SUCCEEDED(hr)) {
         ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
     }
 
-    dwSize = 0;
-    hr = UrlCombineW(wszUrl1, wszUrl2, NULL, &dwSize, dwFlags);
-    ok(hr == E_POINTER, "Checking length of string, return was 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
-
-    dwSize--;
-    hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
-    ok(hr == E_POINTER, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
-
-    hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
-    ok(hr == dwExpectReturn, "UrlCombineW returned 0x%08x, expected 0x%08x\n", hr, dwExpectReturn);
-    ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
-    if(SUCCEEDED(hr)) {
-        wszConvertedUrl = GetWideString(szReturnUrl);
-        ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
-        FreeWideString(wszConvertedUrl);
+    if (pUrlCombineW) {
+        dwSize = 0;
+        hr = pUrlCombineW(wszUrl1, wszUrl2, NULL, &dwSize, dwFlags);
+        ok(hr == E_POINTER, "Checking length of string, return was 0x%08x, expected 0x%08x\n", hr, E_POINTER);
+        ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+
+        dwSize--;
+        hr = pUrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
+        ok(hr == E_POINTER, "UrlCombineW returned 0x%08x, expected 0x%08x\n", hr, E_POINTER);
+        ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+
+        hr = pUrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
+        ok(hr == dwExpectReturn, "UrlCombineW returned 0x%08x, expected 0x%08x\n", hr, dwExpectReturn);
+        ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
+        if(SUCCEEDED(hr)) {
+            wszConvertedUrl = GetWideString(szReturnUrl);
+            ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
+            FreeWideString(wszConvertedUrl);
+        }
     }
 
     FreeWideString(wszUrl1);
@@ -857,24 +969,32 @@ static void test_UrlCreateFromPath(void)
     WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
     WCHAR *pathW, *urlW;
 
+    if (!pUrlCreateFromPathA) {
+        win_skip("UrlCreateFromPathA not found\n");
+        return;
+    }
+
     for(i = 0; i < sizeof(TEST_URLFROMPATH) / sizeof(TEST_URLFROMPATH[0]); i++) {
         len = INTERNET_MAX_URL_LENGTH;
-        ret = UrlCreateFromPathA(TEST_URLFROMPATH[i].path, ret_url, &len, 0);
+        ret = pUrlCreateFromPathA(TEST_URLFROMPATH[i].path, ret_url, &len, 0);
         ok(ret == TEST_URLFROMPATH[i].ret, "ret %08x from path %s\n", ret, TEST_URLFROMPATH[i].path);
         ok(!lstrcmpi(ret_url, TEST_URLFROMPATH[i].url), "url %s from path %s\n", ret_url, TEST_URLFROMPATH[i].path);
         ok(len == strlen(ret_url), "ret len %d from path %s\n", len, TEST_URLFROMPATH[i].path);
 
-        len = INTERNET_MAX_URL_LENGTH;
-        pathW = GetWideString(TEST_URLFROMPATH[i].path);
-        urlW = GetWideString(TEST_URLFROMPATH[i].url);
-        ret = UrlCreateFromPathW(pathW, ret_urlW, &len, 0);
-        WideCharToMultiByte(CP_ACP, 0, ret_urlW, -1, ret_url, sizeof(ret_url),0,0);
-        ok(ret == TEST_URLFROMPATH[i].ret, "ret %08x from path L\"%s\", expected %08x\n",
-           ret, TEST_URLFROMPATH[i].path, TEST_URLFROMPATH[i].ret);
-        ok(!lstrcmpiW(ret_urlW, urlW), "got %s expected %s from path L\"%s\"\n", ret_url, TEST_URLFROMPATH[i].url, TEST_URLFROMPATH[i].path);
-        ok(len == lstrlenW(ret_urlW), "ret len %d from path L\"%s\"\n", len, TEST_URLFROMPATH[i].path);
-        FreeWideString(urlW);
-        FreeWideString(pathW);
+        if (pUrlCreateFromPathW) {
+            len = INTERNET_MAX_URL_LENGTH;
+            pathW = GetWideString(TEST_URLFROMPATH[i].path);
+            urlW = GetWideString(TEST_URLFROMPATH[i].url);
+            ret = pUrlCreateFromPathW(pathW, ret_urlW, &len, 0);
+            WideCharToMultiByte(CP_ACP, 0, ret_urlW, -1, ret_url, sizeof(ret_url),0,0);
+            ok(ret == TEST_URLFROMPATH[i].ret, "ret %08x from path L\"%s\", expected %08x\n",
+                ret, TEST_URLFROMPATH[i].path, TEST_URLFROMPATH[i].ret);
+            ok(!lstrcmpiW(ret_urlW, urlW), "got %s expected %s from path L\"%s\"\n",
+                ret_url, TEST_URLFROMPATH[i].url, TEST_URLFROMPATH[i].path);
+            ok(len == lstrlenW(ret_urlW), "ret len %d from path L\"%s\"\n", len, TEST_URLFROMPATH[i].path);
+            FreeWideString(urlW);
+            FreeWideString(pathW);
+        }
     }
 }
 
@@ -886,39 +1006,48 @@ static void test_UrlIs(void)
     size_t i;
     WCHAR wurl[80];
 
+    if (!pUrlIsA) {
+        win_skip("UrlIsA not found\n");
+        return;
+    }
+
     for(i = 0; i < sizeof(TEST_PATH_IS_URL) / sizeof(TEST_PATH_IS_URL[0]); i++) {
        MultiByteToWideChar(CP_ACP, 0, TEST_PATH_IS_URL[i].path, -1, wurl, 80);
 
-        ret = UrlIsA( TEST_PATH_IS_URL[i].path, URLIS_URL );
+        ret = pUrlIsA( TEST_PATH_IS_URL[i].path, URLIS_URL );
         ok( ret == TEST_PATH_IS_URL[i].expect,
             "returned %d from path %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
             TEST_PATH_IS_URL[i].expect );
 
-        ret = UrlIsW( wurl, URLIS_URL );
-        ok( ret == TEST_PATH_IS_URL[i].expect,
-            "returned %d from path (UrlIsW) %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
-            TEST_PATH_IS_URL[i].expect );
+        if (pUrlIsW) {
+            ret = pUrlIsW( wurl, URLIS_URL );
+            ok( ret == TEST_PATH_IS_URL[i].expect,
+                "returned %d from path (UrlIsW) %s, expected %d\n", ret,
+                TEST_PATH_IS_URL[i].path, TEST_PATH_IS_URL[i].expect );
+        }
     }
     for(i = 0; i < sizeof(TEST_URLIS_ATTRIBS) / sizeof(TEST_URLIS_ATTRIBS[0]); i++) {
        MultiByteToWideChar(CP_ACP, 0, TEST_URLIS_ATTRIBS[i].url, -1, wurl, 80);
 
-        ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_OPAQUE);
+        ret = pUrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_OPAQUE);
        ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
            "returned %d for URLIS_OPAQUE, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
            TEST_URLIS_ATTRIBS[i].expectOpaque );
-        ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_FILEURL);
+        ret = pUrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_FILEURL);
        ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
            "returned %d for URLIS_FILEURL, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
            TEST_URLIS_ATTRIBS[i].expectFile );
 
-        ret = UrlIsW( wurl, URLIS_OPAQUE);
-       ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
-           "returned %d for URLIS_OPAQUE (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
-           TEST_URLIS_ATTRIBS[i].expectOpaque );
-        ret = UrlIsW( wurl, URLIS_FILEURL);
-       ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
-           "returned %d for URLIS_FILEURL (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
-           TEST_URLIS_ATTRIBS[i].expectFile );
+        if (pUrlIsW) {
+            ret = pUrlIsW( wurl, URLIS_OPAQUE);
+            ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
+                "returned %d for URLIS_OPAQUE (UrlIsW), url \"%s\", expected %d\n",
+                ret, TEST_URLIS_ATTRIBS[i].url, TEST_URLIS_ATTRIBS[i].expectOpaque );
+            ret = pUrlIsW( wurl, URLIS_FILEURL);
+            ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
+                "returned %d for URLIS_FILEURL (UrlIsW), url \"%s\", expected %d\n",
+                ret, TEST_URLIS_ATTRIBS[i].url, TEST_URLIS_ATTRIBS[i].expectFile );
+        }
     }
 }
 
@@ -935,45 +1064,235 @@ static void test_UrlUnescape(void)
     static char another_inplace[] = "file:///C:/Program%20Files";
     static const char expected[] = "file:///C:/Program Files";
     static WCHAR inplaceW[] = {'f','i','l','e',':','/','/','/','C',':','/','P','r','o','g','r','a','m',' ','F','i','l','e','s',0};
-    static WCHAR another_inplaceW[] = {'f','i','l','e',':','/','/','/','C',':','/','P','r','o','g','r','a','m','%','2','0','F','i','l','e','s',0};
+    static WCHAR another_inplaceW[] ={'f','i','l','e',':','/','/','/',
+                'C',':','/','P','r','o','g','r','a','m','%','2','0','F','i','l','e','s',0};
+    HRESULT res;
 
+    if (!pUrlUnescapeA) {
+        win_skip("UrlUnescapeA not found\n");
+        return;
+    }
     for(i=0; i<sizeof(TEST_URL_UNESCAPE)/sizeof(TEST_URL_UNESCAPE[0]); i++) {
         dwEscaped=INTERNET_MAX_URL_LENGTH;
-        ok(UrlUnescapeA(TEST_URL_UNESCAPE[i].url, szReturnUrl, &dwEscaped, 0) == S_OK, "UrlUnescapeA didn't return 0x%08x from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
+        res = pUrlUnescapeA(TEST_URL_UNESCAPE[i].url, szReturnUrl, &dwEscaped, 0);
+        ok(res == S_OK,
+            "UrlUnescapeA returned 0x%x (expected S_OK) for \"%s\"\n",
+            res, TEST_URL_UNESCAPE[i].url);
         ok(strcmp(szReturnUrl,TEST_URL_UNESCAPE[i].expect)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", TEST_URL_UNESCAPE[i].expect, szReturnUrl, TEST_URL_UNESCAPE[i].url);
 
         ZeroMemory(szReturnUrl, sizeof(szReturnUrl));
         /* if we set the bufferpointer to NULL here UrlUnescape  fails and string gets not converted */
-        ok(UrlUnescapeA(TEST_URL_UNESCAPE[i].url, szReturnUrl, NULL, 0) == E_INVALIDARG, "UrlUnescapeA didn't return 0x%08x from \"%s\"\n", E_INVALIDARG ,TEST_URL_UNESCAPE[i].url);
+        res = pUrlUnescapeA(TEST_URL_UNESCAPE[i].url, szReturnUrl, NULL, 0);
+        ok(res == E_INVALIDARG,
+            "UrlUnescapeA returned 0x%x (expected E_INVALIDARG) for \"%s\"\n",
+            res, TEST_URL_UNESCAPE[i].url);
         ok(strcmp(szReturnUrl,"")==0, "Expected empty string\n");
 
-        dwEscaped = INTERNET_MAX_URL_LENGTH;
-        urlW = GetWideString(TEST_URL_UNESCAPE[i].url);
-        expected_urlW = GetWideString(TEST_URL_UNESCAPE[i].expect);
-        ok(UrlUnescapeW(urlW, ret_urlW, &dwEscaped, 0) == S_OK, "UrlUnescapeW didn't return 0x%08x from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
-        WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
-        ok(lstrcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", TEST_URL_UNESCAPE[i].expect, szReturnUrl, TEST_URL_UNESCAPE[i].url, 0L);
-        FreeWideString(urlW);
-        FreeWideString(expected_urlW);
+        if (pUrlUnescapeW) {
+            dwEscaped = INTERNET_MAX_URL_LENGTH;
+            urlW = GetWideString(TEST_URL_UNESCAPE[i].url);
+            expected_urlW = GetWideString(TEST_URL_UNESCAPE[i].expect);
+            res = pUrlUnescapeW(urlW, ret_urlW, &dwEscaped, 0);
+            ok(res == S_OK,
+                "UrlUnescapeW returned 0x%x (expected S_OK) for \"%s\"\n",
+                res, TEST_URL_UNESCAPE[i].url);
+
+            WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
+            ok(lstrcmpW(ret_urlW, expected_urlW)==0,
+                "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n",
+                TEST_URL_UNESCAPE[i].expect, szReturnUrl, TEST_URL_UNESCAPE[i].url, 0L);
+            FreeWideString(urlW);
+            FreeWideString(expected_urlW);
+        }
     }
 
     dwEscaped = sizeof(inplace);
-    ok(UrlUnescapeA(inplace, NULL, &dwEscaped, URL_UNESCAPE_INPLACE) == S_OK, "UrlUnescapeA failed unexpectedly\n");
+    res = pUrlUnescapeA(inplace, NULL, &dwEscaped, URL_UNESCAPE_INPLACE);
+    ok(res == S_OK, "UrlUnescapeA returned 0x%x (expected S_OK)\n", res);
     ok(!strcmp(inplace, expected), "got %s expected %s\n", inplace, expected);
     ok(dwEscaped == 27, "got %d expected 27\n", dwEscaped);
 
     /* if we set the bufferpointer to NULL, the string apparently still gets converted (Google Lively does this)) */
-    ok(UrlUnescapeA(another_inplace, NULL, NULL, URL_UNESCAPE_INPLACE) == S_OK, "UrlUnescapeA failed unexpectedly\n");
+    res = pUrlUnescapeA(another_inplace, NULL, NULL, URL_UNESCAPE_INPLACE);
+    ok(res == S_OK, "UrlUnescapeA returned 0x%x (expected S_OK)\n", res);
     ok(!strcmp(another_inplace, expected), "got %s expected %s\n", another_inplace, expected);
 
-    dwEscaped = sizeof(inplaceW);
-    ok(UrlUnescapeW(inplaceW, NULL, &dwEscaped, URL_UNESCAPE_INPLACE) == S_OK, "UrlUnescapeW failed unexpectedly\n");
-    ok(dwEscaped == 50, "got %d expected 50\n", dwEscaped);
+    if (pUrlUnescapeW) {
+        dwEscaped = sizeof(inplaceW);
+        res = pUrlUnescapeW(inplaceW, NULL, &dwEscaped, URL_UNESCAPE_INPLACE);
+        ok(res == S_OK, "UrlUnescapeW returned 0x%x (expected S_OK)\n", res);
+        ok(dwEscaped == 50, "got %d expected 50\n", dwEscaped);
 
-    /* if we set the bufferpointer to NULL, the string apparently still gets converted (Google Lively does this)) */
-    ok(UrlUnescapeW(another_inplaceW, NULL, NULL, URL_UNESCAPE_INPLACE) == S_OK, "UrlUnescapeW failed unexpectedly\n");
-    ok(lstrlenW(another_inplaceW) == 24, "got %d expected 24\n", lstrlenW(another_inplaceW));
+        /* if we set the bufferpointer to NULL, the string apparently still gets converted (Google Lively does this)) */
+        res = pUrlUnescapeW(another_inplaceW, NULL, NULL, URL_UNESCAPE_INPLACE);
+        ok(res == S_OK, "UrlUnescapeW returned 0x%x (expected S_OK)\n", res);
 
+        ok(lstrlenW(another_inplaceW) == 24, "got %d expected 24\n", lstrlenW(another_inplaceW));
+    }
+}
+
+static const struct parse_url_test_t {
+    const char *url;
+    HRESULT hres;
+    UINT protocol_len;
+    UINT scheme;
+} parse_url_tests[] = {
+    {"http://www.winehq.org/",S_OK,4,URL_SCHEME_HTTP},
+    {"https://www.winehq.org/",S_OK,5,URL_SCHEME_HTTPS},
+    {"ftp://www.winehq.org/",S_OK,3,URL_SCHEME_FTP},
+    {"test.txt?test=c:/dir",URL_E_INVALID_SYNTAX},
+    {"test.txt",URL_E_INVALID_SYNTAX},
+    {"xxx://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
+    {"1xx://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
+    {"-xx://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
+    {"xx0://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
+    {"x://www.winehq.org/",URL_E_INVALID_SYNTAX},
+    {"xx$://www.winehq.org/",URL_E_INVALID_SYNTAX},
+    {"htt?p://www.winehq.org/",URL_E_INVALID_SYNTAX},
+    {"ab-://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
+    {" http://www.winehq.org/",URL_E_INVALID_SYNTAX},
+};
+
+static void test_ParseURL(void)
+{
+    const struct parse_url_test_t *test;
+    WCHAR url[INTERNET_MAX_URL_LENGTH];
+    PARSEDURLA parseda;
+    PARSEDURLW parsedw;
+    HRESULT hres;
+
+    for(test = parse_url_tests; test < parse_url_tests + sizeof(parse_url_tests)/sizeof(*parse_url_tests); test++) {
+        memset(&parseda, 0xd0, sizeof(parseda));
+        parseda.cbSize = sizeof(parseda);
+        hres = pParseURLA(test->url, &parseda);
+        ok(hres == test->hres, "ParseURL failed: %08x, expected %08x\n", hres, test->hres);
+        if(hres == S_OK) {
+            ok(parseda.pszProtocol == test->url, "parseda.pszProtocol = %s, expected %s\n",
+               parseda.pszProtocol, test->url);
+            ok(parseda.cchProtocol == test->protocol_len, "parseda.cchProtocol = %d, expected %d\n",
+               parseda.cchProtocol, test->protocol_len);
+            ok(parseda.pszSuffix == test->url+test->protocol_len+1, "parseda.pszSuffix = %s, expected %s\n",
+               parseda.pszSuffix, test->url+test->protocol_len+1);
+            ok(parseda.cchSuffix == strlen(test->url+test->protocol_len+1),
+               "parseda.pszSuffix = %d, expected %d\n",
+               parseda.cchSuffix, lstrlenA(test->url+test->protocol_len+1));
+            ok(parseda.nScheme == test->scheme, "parseda.nScheme = %d, expected %d\n",
+               parseda.nScheme, test->scheme);
+        }else {
+            ok(!parseda.pszProtocol, "parseda.pszProtocol = %p\n", parseda.pszProtocol);
+            ok(parseda.nScheme == 0xd0d0d0d0, "nScheme = %d\n", parseda.nScheme);
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, test->url, -1, url, sizeof(url)/sizeof(WCHAR));
+
+        memset(&parsedw, 0xd0, sizeof(parsedw));
+        parsedw.cbSize = sizeof(parsedw);
+        hres = pParseURLW(url, &parsedw);
+        ok(hres == test->hres, "ParseURL failed: %08x, expected %08x\n", hres, test->hres);
+        if(hres == S_OK) {
+            ok(parsedw.pszProtocol == url, "parsedw.pszProtocol = %s, expected %s\n",
+               wine_dbgstr_w(parsedw.pszProtocol), wine_dbgstr_w(url));
+            ok(parsedw.cchProtocol == test->protocol_len, "parsedw.cchProtocol = %d, expected %d\n",
+               parsedw.cchProtocol, test->protocol_len);
+            ok(parsedw.pszSuffix == url+test->protocol_len+1, "parsedw.pszSuffix = %s, expected %s\n",
+               wine_dbgstr_w(parsedw.pszSuffix), wine_dbgstr_w(url+test->protocol_len+1));
+            ok(parsedw.cchSuffix == strlen(test->url+test->protocol_len+1),
+               "parsedw.pszSuffix = %d, expected %d\n",
+               parsedw.cchSuffix, lstrlenA(test->url+test->protocol_len+1));
+            ok(parsedw.nScheme == test->scheme, "parsedw.nScheme = %d, expected %d\n",
+               parsedw.nScheme, test->scheme);
+        }else {
+            ok(!parsedw.pszProtocol, "parsedw.pszProtocol = %p\n", parseda.pszProtocol);
+            ok(parsedw.nScheme == 0xd0d0d0d0, "nScheme = %d\n", parsedw.nScheme);
+        }
+    }
+}
+
+static void test_HashData(void)
+{
+    HRESULT res;
+    BYTE input[16] = {0x51, 0x33, 0x4F, 0xA7, 0x45, 0x15, 0xF0, 0x52, 0x90,
+                      0x2B, 0xE7, 0xF5, 0xFD, 0xE1, 0xA6, 0xA7};
+    BYTE output[32];
+    static const BYTE expected[] = {0x54, 0x9C, 0x92, 0x55, 0xCD, 0x82, 0xFF,
+                                    0xA1, 0x8E, 0x0F, 0xCF, 0x93, 0x14, 0xAA,
+                                    0xE3, 0x2D};
+    static const BYTE expected2[] = {0x54, 0x9C, 0x92, 0x55, 0xCD, 0x82, 0xFF,
+                                     0xA1, 0x8E, 0x0F, 0xCF, 0x93, 0x14, 0xAA,
+                                     0xE3, 0x2D, 0x47, 0xFC, 0x80, 0xB8, 0xD0,
+                                     0x49, 0xE6, 0x13, 0x2A, 0x30, 0x51, 0x8D,
+                                     0xF9, 0x4B, 0x07, 0xA6};
+    static const BYTE expected3[] = {0x2B, 0xDC, 0x9A, 0x1B, 0xF0, 0x5A, 0xF9,
+                                     0xC6, 0xBE, 0x94, 0x6D, 0xF3, 0x33, 0xC1,
+                                     0x36, 0x07};
+    int i;
+
+    /* Test hashing with identically sized input/output buffers. */
+    res = pHashData(input, 16, output, 16);
+    ok(res == S_OK, "Expected HashData to return S_OK, got 0x%08x\n", res);
+    if(res == S_OK)
+       ok(!memcmp(output, expected, sizeof(expected)),
+          "Output buffer did not match expected contents\n");
+
+    /* Test hashing with larger output buffer. */
+    res = pHashData(input, 16, output, 32);
+    ok(res == S_OK, "Expected HashData to return S_OK, got 0x%08x\n", res);
+    if(res == S_OK)
+       ok(!memcmp(output, expected2, sizeof(expected2)),
+          "Output buffer did not match expected contents\n");
+
+    /* Test hashing with smaller input buffer. */
+    res = pHashData(input, 8, output, 16);
+    ok(res == S_OK, "Expected HashData to return S_OK, got 0x%08x\n", res);
+    if(res == S_OK)
+       ok(!memcmp(output, expected3, sizeof(expected3)),
+          "Output buffer did not match expected contents\n");
+
+    /* Test passing NULL pointers for input/output parameters. */
+    res = pHashData(NULL, 0, NULL, 0);
+    ok(res == E_INVALIDARG || broken(res == S_OK), /* Windows 2000 */
+       "Expected HashData to return E_INVALIDARG, got 0x%08x\n", res);
+
+    res = pHashData(input, 0, NULL, 0);
+    ok(res == E_INVALIDARG || broken(res == S_OK), /* Windows 2000 */
+       "Expected HashData to return E_INVALIDARG, got 0x%08x\n", res);
+
+    res = pHashData(NULL, 0, output, 0);
+    ok(res == E_INVALIDARG || broken(res == S_OK), /* Windows 2000 */
+       "Expected HashData to return E_INVALIDARG, got 0x%08x\n", res);
+
+    /* Test passing valid pointers with sizes of zero. */
+    for (i = 0; i < sizeof(input)/sizeof(BYTE); i++)
+        input[i] = 0x00;
+
+    for (i = 0; i < sizeof(output)/sizeof(BYTE); i++)
+        output[i] = 0xFF;
+
+    res = pHashData(input, 0, output, 0);
+    ok(res == S_OK, "Expected HashData to return S_OK, got 0x%08x\n", res);
+
+    /* The buffers should be unchanged. */
+    for (i = 0; i < sizeof(input)/sizeof(BYTE); i++)
+    {
+        ok(input[i] == 0x00, "Expected the input buffer to be unchanged\n");
+        if(input[i] != 0x00) break;
+    }
+
+    for (i = 0; i < sizeof(output)/sizeof(BYTE); i++)
+    {
+        ok(output[i] == 0xFF, "Expected the output buffer to be unchanged\n");
+        if(output[i] != 0xFF) break;
+    }
+
+    /* Input/output parameters are not validated. */
+    res = pHashData((BYTE *)0xdeadbeef, 0, (BYTE *)0xdeadbeef, 0);
+    ok(res == S_OK, "Expected HashData to return S_OK, got 0x%08x\n", res);
+
+    if (0)
+    {
+        res = pHashData((BYTE *)0xdeadbeef, 1, (BYTE *)0xdeadbeef, 1);
+        trace("HashData returned 0x%08x\n", res);
+    }
 }
 
 /* ########################### */
@@ -982,7 +1301,27 @@ START_TEST(url)
 {
 
   hShlwapi = GetModuleHandleA("shlwapi.dll");
+  pUrlUnescapeA = (void *) GetProcAddress(hShlwapi, "UrlUnescapeA");
+  pUrlUnescapeW = (void *) GetProcAddress(hShlwapi, "UrlUnescapeW");
+  pUrlIsA = (void *) GetProcAddress(hShlwapi, "UrlIsA");
+  pUrlIsW = (void *) GetProcAddress(hShlwapi, "UrlIsW");
+  pUrlHashA = (void *) GetProcAddress(hShlwapi, "UrlHashA");
+  pUrlHashW = (void *) GetProcAddress(hShlwapi, "UrlHashW");
+  pUrlGetPartA = (void *) GetProcAddress(hShlwapi, "UrlGetPartA");
+  pUrlGetPartW = (void *) GetProcAddress(hShlwapi, "UrlGetPartW");
+  pUrlEscapeA = (void *) GetProcAddress(hShlwapi, "UrlEscapeA");
+  pUrlEscapeW = (void *) GetProcAddress(hShlwapi, "UrlEscapeW");
+  pUrlCreateFromPathA = (void *) GetProcAddress(hShlwapi, "UrlCreateFromPathA");
+  pUrlCreateFromPathW = (void *) GetProcAddress(hShlwapi, "UrlCreateFromPathW");
+  pUrlCombineA = (void *) GetProcAddress(hShlwapi, "UrlCombineA");
+  pUrlCombineW = (void *) GetProcAddress(hShlwapi, "UrlCombineW");
+  pUrlCanonicalizeA = (void *) GetProcAddress(hShlwapi, "UrlCanonicalizeA");
   pUrlCanonicalizeW = (void *) GetProcAddress(hShlwapi, "UrlCanonicalizeW");
+  pUrlApplySchemeA = (void *) GetProcAddress(hShlwapi, "UrlApplySchemeA");
+  pUrlApplySchemeW = (void *) GetProcAddress(hShlwapi, "UrlApplySchemeW");
+  pParseURLA = (void*)GetProcAddress(hShlwapi, (LPCSTR)1);
+  pParseURLW = (void*)GetProcAddress(hShlwapi, (LPCSTR)2);
+  pHashData = (void*)GetProcAddress(hShlwapi, "HashData");
 
   test_UrlApplyScheme();
   test_UrlHash();
@@ -994,5 +1333,6 @@ START_TEST(url)
   test_UrlCreateFromPath();
   test_UrlIs();
   test_UrlUnescape();
-
+  test_ParseURL();
+  test_HashData();
 }