Autosyncing with Wine HEAD
authorThe Wine Synchronizer <winesync@svn.reactos.org>
Fri, 24 Aug 2007 09:42:29 +0000 (09:42 +0000)
committerThe Wine Synchronizer <winesync@svn.reactos.org>
Fri, 24 Aug 2007 09:42:29 +0000 (09:42 +0000)
svn path=/trunk/; revision=28513

13 files changed:
rostests/winetests/directory.rbuild
rostests/winetests/mlang/mlang.c [new file with mode: 0644]
rostests/winetests/mlang/mlang.rbuild [new file with mode: 0644]
rostests/winetests/mlang/testlist.c [new file with mode: 0644]
rostests/winetests/shlwapi/clist.c
rostests/winetests/shlwapi/clsid.c
rostests/winetests/shlwapi/generated.c
rostests/winetests/shlwapi/ordinal.c
rostests/winetests/shlwapi/path.c
rostests/winetests/shlwapi/shlwapi.rbuild
rostests/winetests/shlwapi/shreg.c
rostests/winetests/shlwapi/string.c
rostests/winetests/shlwapi/testlist.c

index 2f8e1c8..d4cc460 100644 (file)
@@ -23,6 +23,9 @@
 <directory name="lz32">
        <xi:include href="lz32/lz32.rbuild" />
 </directory>
+<directory name="mlang">
+       <xi:include href="mlang/mlang.rbuild" />
+</directory>
 <directory name="msi">
        <xi:include href="msi/msi.rbuild" />
 </directory>
diff --git a/rostests/winetests/mlang/mlang.c b/rostests/winetests/mlang/mlang.c
new file mode 100644 (file)
index 0000000..fb50e2e
--- /dev/null
@@ -0,0 +1,788 @@
+/*
+ * Unit test suite for MLANG APIs.
+ *
+ * Copyright 2004 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "mlang.h"
+
+#include "wine/test.h"
+
+#ifndef CP_UNICODE
+#define CP_UNICODE 1200
+#endif
+
+#if 0
+#define DUMP_CP_INFO
+#define DUMP_SCRIPT_INFO
+
+#if defined DUMP_CP_INFO || defined DUMP_SCRIPT_INFO
+#include "wine/debug.h"
+#endif
+#endif /* 0 */
+
+#define TRACE_2 OutputDebugStringA
+
+static BOOL (WINAPI *pGetCPInfoExA)(UINT,DWORD,LPCPINFOEXA);
+
+static void test_multibyte_to_unicode_translations(IMultiLanguage2 *iML2)
+{
+    /* these APIs are broken regarding constness of the input buffer */
+    char stringA[] = "Just a test string\0"; /* double 0 for CP_UNICODE tests */
+    WCHAR stringW[] = {'J','u','s','t',' ','a',' ','t','e','s','t',' ','s','t','r','i','n','g',0};
+    char bufA[256];
+    WCHAR bufW[256];
+    UINT lenA, lenW, expected_len;
+    HRESULT ret;
+    HMODULE hMlang;
+    FARPROC pConvertINetMultiByteToUnicode;
+    FARPROC pConvertINetUnicodeToMultiByte;
+
+    hMlang = LoadLibraryA("mlang.dll");
+    ok(hMlang != 0, "couldn't load mlang.dll\n");
+
+    pConvertINetMultiByteToUnicode = GetProcAddress(hMlang, "ConvertINetMultiByteToUnicode");
+    ok(pConvertINetMultiByteToUnicode != NULL, "couldn't resolve ConvertINetMultiByteToUnicode\n");
+    pConvertINetUnicodeToMultiByte = GetProcAddress(hMlang, "ConvertINetUnicodeToMultiByte");
+    ok(pConvertINetUnicodeToMultiByte != NULL, "couldn't resolve ConvertINetUnicodeToMultiByte\n");
+
+    /* IMultiLanguage2_ConvertStringToUnicode tests */
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = 0;
+    lenW = sizeof(bufW)/sizeof(bufW[0]);
+    TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
+    ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
+    ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
+    ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = -1;
+    lenW = sizeof(bufW)/sizeof(bufW[0]);
+    TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
+    ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    if (lenW < sizeof(bufW)/sizeof(bufW[0])) {
+       /* can only happen if the convert call fails */
+       ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
+       bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
+    }
+    ok(!lstrcmpW(bufW, stringW), "bufW/stringW mismatch\n");
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = -1;
+    lenW = 5;
+    TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
+    ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
+    ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringToUnicode should fail: %08x\n", ret);
+    ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
+    /* still has to do partial conversion */
+    ok(!memcmp(bufW, stringW, 5 * sizeof(WCHAR)), "bufW/stringW mismatch\n");
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = -1;
+    lenW = sizeof(bufW)/sizeof(bufW[0]);
+    TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
+    ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, CP_UNICODE, stringA, &lenA, bufW, &lenW);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    ok(lenW == lstrlenW(stringW)/(int)sizeof(WCHAR), "wrong lenW %u\n", lenW);
+    ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
+    bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
+    ok(!lstrcmpA((LPCSTR)bufW, stringA), "bufW/stringA mismatch\n");
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = lstrlenA(stringA);
+    lenW = 0;
+    ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, NULL, &lenW);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
+    ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = lstrlenA(stringA);
+    lenW = sizeof(bufW)/sizeof(bufW[0]);
+    ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, &lenA, NULL, &lenW);
+    ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
+    ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
+
+    memset(bufW, 'x', sizeof(bufW));
+    lenA = lstrlenA(stringA);
+    lenW = 0;
+    ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, &lenA, NULL, &lenW);
+    ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
+    ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
+
+    /* IMultiLanguage2_ConvertStringFromUnicode tests */
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = 0;
+    lenA = sizeof(bufA);
+    TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
+    ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
+    ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
+    ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = -1;
+    lenA = sizeof(bufA);
+    TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
+    ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    ok(bufA[lenA] != 0, "buf should not be 0 terminated\n");
+    bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
+    ok(!lstrcmpA(bufA, stringA), "bufA/stringA mismatch\n");
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = -1;
+    lenA = 5;
+    TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
+    ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
+    ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringFromUnicode should fail: %08x\n", ret);
+    ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
+    /* still has to do partial conversion */
+    ok(!memcmp(bufA, stringA, 5), "bufW/stringW mismatch\n");
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = -1;
+    lenA = sizeof(bufA);
+    TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
+    ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, CP_UNICODE, stringW, &lenW, bufA, &lenA);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
+    ok(lenA == lstrlenA(stringA) * (int)sizeof(WCHAR), "wrong lenA %u\n", lenA);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    ok(bufA[lenA] != 0 && bufA[lenA+1] != 0, "buf should not be 0 terminated\n");
+    bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
+    bufA[lenA+1] = 0; /* sizeof(WCHAR) */
+    ok(!lstrcmpW((LPCWSTR)bufA, stringW), "bufA/stringW mismatch\n");
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = lstrlenW(stringW);
+    lenA = 0;
+    ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, NULL, &lenA);
+    ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
+    ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = lstrlenW(stringW);
+    lenA = sizeof(bufA);
+    ret = pConvertINetUnicodeToMultiByte(NULL, 1252, stringW, &lenW, NULL, &lenA);
+    ok(ret == S_OK, "ConvertINetUnicodeToMultiByte failed: %08x\n", ret);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
+    ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
+
+    memset(bufA, 'x', sizeof(bufA));
+    lenW = lstrlenW(stringW);
+    lenA = 0;
+    ret = pConvertINetUnicodeToMultiByte(NULL, 1252, stringW, &lenW, NULL, &lenA);
+    ok(ret == S_OK, "ConvertINetUnicodeToMultiByte failed: %08x\n", ret);
+    ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
+    expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
+    ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
+}
+
+static inline void cpinfo_cmp(MIMECPINFO *cpinfo1, MIMECPINFO *cpinfo2)
+{
+    ok(cpinfo1->dwFlags == cpinfo2->dwFlags, "dwFlags mismatch: %08x != %08x\n", cpinfo1->dwFlags, cpinfo2->dwFlags);
+    ok(cpinfo1->uiCodePage == cpinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", cpinfo1->uiCodePage, cpinfo2->uiCodePage);
+    ok(cpinfo1->uiFamilyCodePage == cpinfo2->uiFamilyCodePage, "uiFamilyCodePage mismatch: %u != %u\n", cpinfo1->uiFamilyCodePage, cpinfo2->uiFamilyCodePage);
+    ok(!lstrcmpW(cpinfo1->wszDescription, cpinfo2->wszDescription), "wszDescription mismatch\n");
+    ok(!lstrcmpW(cpinfo1->wszWebCharset, cpinfo2->wszWebCharset), "wszWebCharset mismatch\n");
+    ok(!lstrcmpW(cpinfo1->wszHeaderCharset, cpinfo2->wszHeaderCharset), "wszHeaderCharset mismatch\n");
+    ok(!lstrcmpW(cpinfo1->wszBodyCharset, cpinfo2->wszBodyCharset), "wszBodyCharset mismatch\n");
+    ok(!lstrcmpW(cpinfo1->wszFixedWidthFont, cpinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
+    ok(!lstrcmpW(cpinfo1->wszProportionalFont, cpinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
+    ok(cpinfo1->bGDICharset == cpinfo2->bGDICharset, "bGDICharset mismatch: %d != %d\n", cpinfo1->bGDICharset, cpinfo2->bGDICharset);
+}
+
+#ifdef DUMP_CP_INFO
+static const char *dump_mime_flags(DWORD flags)
+{
+    static char buf[1024];
+
+    buf[0] = 0;
+
+    if (flags & MIMECONTF_MAILNEWS) strcat(buf, " MIMECONTF_MAILNEWS");
+    if (flags & MIMECONTF_BROWSER) strcat(buf, " MIMECONTF_BROWSER");
+    if (flags & MIMECONTF_MINIMAL) strcat(buf, " MIMECONTF_MINIMAL");
+    if (flags & MIMECONTF_IMPORT) strcat(buf, " MIMECONTF_IMPORT");
+    if (flags & MIMECONTF_SAVABLE_MAILNEWS) strcat(buf, " MIMECONTF_SAVABLE_MAILNEWS");
+    if (flags & MIMECONTF_SAVABLE_BROWSER) strcat(buf, " MIMECONTF_SAVABLE_BROWSER");
+    if (flags & MIMECONTF_EXPORT) strcat(buf, " MIMECONTF_EXPORT");
+    if (flags & MIMECONTF_PRIVCONVERTER) strcat(buf, " MIMECONTF_PRIVCONVERTER");
+    if (flags & MIMECONTF_VALID) strcat(buf, " MIMECONTF_VALID");
+    if (flags & MIMECONTF_VALID_NLS) strcat(buf, " MIMECONTF_VALID_NLS");
+    if (flags & MIMECONTF_MIME_IE4) strcat(buf, " MIMECONTF_MIME_IE4");
+    if (flags & MIMECONTF_MIME_LATEST) strcat(buf, " MIMECONTF_MIME_LATEST");
+    if (flags & MIMECONTF_MIME_REGISTRY) strcat(buf, " MIMECONTF_MIME_REGISTRY");
+
+    return buf;
+}
+#endif
+
+static void test_EnumCodePages(IMultiLanguage2 *iML2, DWORD flags)
+{
+    IEnumCodePage *iEnumCP = NULL;
+    MIMECPINFO *cpinfo;
+    MIMECPINFO cpinfo2;
+    HRESULT ret;
+    ULONG i, n;
+    UINT total;
+
+    total = 0;
+    TRACE_2("Call IMultiLanguage2_GetNumberOfCodePageInfo\n");
+    ret = IMultiLanguage2_GetNumberOfCodePageInfo(iML2, &total);
+    ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfCodePageInfo: expected S_OK/!0, got %08x/%u\n", ret, total);
+
+    trace("total mlang supported codepages %u\n", total);
+
+    TRACE_2("Call IMultiLanguage2_EnumCodePages\n");
+    ret = IMultiLanguage2_EnumCodePages(iML2, flags, LANG_NEUTRAL, &iEnumCP);
+    trace("IMultiLanguage2_EnumCodePages = %08x, iEnumCP = %p\n", ret, iEnumCP);
+    ok(ret == S_OK && iEnumCP, "IMultiLanguage2_EnumCodePages: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumCP);
+
+    TRACE_2("Call IEnumCodePage_Reset\n");
+    ret = IEnumCodePage_Reset(iEnumCP);
+    ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
+    n = 65536;
+    TRACE_2("Call IEnumCodePage_Next\n");
+    ret = IEnumCodePage_Next(iEnumCP, 0, NULL, &n);
+    ok(n == 0 && ret == S_FALSE, "IEnumCodePage_Next: expected 0/S_FALSE, got %u/%08x\n", n, ret);
+    TRACE_2("Call IEnumCodePage_Next\n");
+    ret = IEnumCodePage_Next(iEnumCP, 0, NULL, NULL);
+    ok(ret == S_FALSE, "IEnumCodePage_Next: expected S_FALSE, got %08x\n", ret);
+
+    cpinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*cpinfo) * total * 2);
+
+    n = total * 2;
+    TRACE_2("Call IEnumCodePage_Next\n");
+    ret = IEnumCodePage_Next(iEnumCP, 0, cpinfo, &n);
+    trace("IEnumCodePage_Next = %08x, n = %u\n", ret, n);
+    ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
+
+    n = total * 2;
+    TRACE_2("Call IEnumCodePage_Next\n");
+    ret = IEnumCodePage_Next(iEnumCP, n, cpinfo, &n);
+    ok(ret == S_OK && n != 0, "IEnumCodePage_Next: expected S_OK/!0, got %08x/%u\n", ret, n);
+
+    trace("flags %08x, enumerated codepages %u\n", flags, n);
+
+    if (!flags)
+    {
+       ok(n == total, "IEnumCodePage_Next: expected %u, got %u\n", total, n);
+
+       flags = MIMECONTF_MIME_LATEST;
+    }
+
+    total = n;
+
+    for (i = 0; i < n; i++)
+    {
+       CPINFOEXA cpinfoex;
+       CHARSETINFO csi;
+       MIMECSETINFO mcsi;
+       static const WCHAR autoW[] = {'_','a','u','t','o',0};
+
+#ifdef DUMP_CP_INFO
+       trace("MIMECPINFO #%u:\n"
+             "dwFlags %08x %s\n"
+             "uiCodePage %u\n"
+             "uiFamilyCodePage %u\n"
+             "wszDescription %s\n"
+             "wszWebCharset %s\n"
+             "wszHeaderCharset %s\n"
+             "wszBodyCharset %s\n"
+             "wszFixedWidthFont %s\n"
+             "wszProportionalFont %s\n"
+             "bGDICharset %d\n\n",
+             i,
+             cpinfo[i].dwFlags, dump_mime_flags(cpinfo[i].dwFlags),
+             cpinfo[i].uiCodePage,
+             cpinfo[i].uiFamilyCodePage,
+             wine_dbgstr_w(cpinfo[i].wszDescription),
+             wine_dbgstr_w(cpinfo[i].wszWebCharset),
+             wine_dbgstr_w(cpinfo[i].wszHeaderCharset),
+             wine_dbgstr_w(cpinfo[i].wszBodyCharset),
+             wine_dbgstr_w(cpinfo[i].wszFixedWidthFont),
+             wine_dbgstr_w(cpinfo[i].wszProportionalFont),
+             cpinfo[i].bGDICharset);
+#endif
+       ok(cpinfo[i].dwFlags & flags, "enumerated flags %08x do not include requested %08x\n", cpinfo[i].dwFlags, flags);
+
+       if (TranslateCharsetInfo((DWORD *)cpinfo[i].uiFamilyCodePage, &csi, TCI_SRCCODEPAGE))
+           ok(cpinfo[i].bGDICharset == csi.ciCharset, "%d != %d\n", cpinfo[i].bGDICharset, csi.ciCharset);
+       else
+           trace("TranslateCharsetInfo failed for cp %u\n", cpinfo[i].uiFamilyCodePage);
+
+        if (pGetCPInfoExA)
+        {
+            if (pGetCPInfoExA(cpinfo[i].uiCodePage, 0, &cpinfoex))
+                trace("CodePage %u name: %s\n", cpinfo[i].uiCodePage, cpinfoex.CodePageName);
+            else
+                trace("GetCPInfoExA failed for cp %u\n", cpinfo[i].uiCodePage);
+            if (pGetCPInfoExA(cpinfo[i].uiFamilyCodePage, 0, &cpinfoex))
+                trace("CodePage %u name: %s\n", cpinfo[i].uiFamilyCodePage, cpinfoex.CodePageName);
+            else
+                trace("GetCPInfoExA failed for cp %u\n", cpinfo[i].uiFamilyCodePage);
+        }
+
+        /* Win95 does not support UTF-7 */
+        if (cpinfo[i].uiCodePage == CP_UTF7) continue;
+
+       /* support files for some codepages might be not installed, or
+        * the codepage is just an alias.
+        */
+       if (IsValidCodePage(cpinfo[i].uiCodePage))
+       {
+           TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+           ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UNICODE);
+           ok(ret == S_OK, "IMultiLanguage2_IsConvertible(%u -> CP_UNICODE) = %08x\n", cpinfo[i].uiCodePage, ret);
+           TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+           ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, cpinfo[i].uiCodePage);
+           ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
+
+           TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+           ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UTF8);
+           ok(ret == S_OK, "IMultiLanguage2_IsConvertible(%u -> CP_UTF8) = %08x\n", cpinfo[i].uiCodePage, ret);
+           TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+           ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, cpinfo[i].uiCodePage);
+           ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UTF8 -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
+       }
+       else
+           trace("IsValidCodePage failed for cp %u\n", cpinfo[i].uiCodePage);
+
+       ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszWebCharset, &mcsi);
+       /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
+       if (memcmp(cpinfo[i].wszWebCharset, autoW, 5 * sizeof(WCHAR)))
+       {
+           ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
+#ifdef DUMP_CP_INFO
+           trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszWebCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
+#endif
+           ok(!lstrcmpiW(cpinfo[i].wszWebCharset, mcsi.wszCharset),
+#ifdef DUMP_CP_INFO
+                "%s != %s\n",
+               wine_dbgstr_w(cpinfo[i].wszWebCharset), wine_dbgstr_w(mcsi.wszCharset));
+#else
+                "wszWebCharset mismatch");
+#endif
+
+       if (0)
+       {
+           /* native mlang returns completely messed up encodings in some cases */
+           ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+           ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+        }
+       }
+
+       ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszHeaderCharset, &mcsi);
+       /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
+       if (memcmp(cpinfo[i].wszHeaderCharset, autoW, 5 * sizeof(WCHAR)))
+       {
+           ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
+#ifdef DUMP_CP_INFO
+           trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszHeaderCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
+#endif
+           ok(!lstrcmpiW(cpinfo[i].wszHeaderCharset, mcsi.wszCharset),
+#ifdef DUMP_CP_INFO
+                "%s != %s\n",
+               wine_dbgstr_w(cpinfo[i].wszHeaderCharset), wine_dbgstr_w(mcsi.wszCharset));
+#else
+                "wszHeaderCharset mismatch");
+#endif
+
+       if (0)
+       {
+           /* native mlang returns completely messed up encodings in some cases */
+           ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+           ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+       }
+       }
+
+       ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszBodyCharset, &mcsi);
+       /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
+       if (memcmp(cpinfo[i].wszBodyCharset, autoW, 5 * sizeof(WCHAR)))
+       {
+           ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
+#ifdef DUMP_CP_INFO
+           trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszBodyCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
+#endif
+           ok(!lstrcmpiW(cpinfo[i].wszBodyCharset, mcsi.wszCharset),
+#ifdef DUMP_CP_INFO
+                "%s != %s\n",
+               wine_dbgstr_w(cpinfo[i].wszBodyCharset), wine_dbgstr_w(mcsi.wszCharset));
+#else
+                "wszBodyCharset mismatch");
+#endif
+
+       if (0)
+       {
+           /* native mlang returns completely messed up encodings in some cases */
+           ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+           ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
+               "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
+       }
+       }
+
+       trace("---\n");
+    }
+
+    /* now IEnumCodePage_Next should fail, since pointer is at the end */
+    n = 1;
+    ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
+    ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
+
+    ret = IEnumCodePage_Reset(iEnumCP);
+    ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
+    n = 0;
+    ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
+    ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
+    cpinfo_cmp(&cpinfo[0], &cpinfo2);
+
+    if (0)
+    {
+    /* Due to a bug in MS' implementation of IEnumCodePage_Skip
+     * it's not used here.
+     */
+    ret = IEnumCodePage_Skip(iEnumCP, 1);
+    ok(ret == S_OK, "IEnumCodePage_Skip: expected S_OK, got %08x\n", ret);
+    }
+    for (i = 0; i < total - 1; i++)
+    {
+        n = 0;
+        ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
+        ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
+        cpinfo_cmp(&cpinfo[i + 1], &cpinfo2);
+    }
+
+    HeapFree(GetProcessHeap(), 0, cpinfo);
+    IEnumCodePage_Release(iEnumCP);
+}
+
+static inline void scriptinfo_cmp(SCRIPTINFO *sinfo1, SCRIPTINFO *sinfo2)
+{
+    ok(sinfo1->ScriptId == sinfo2->ScriptId, "ScriptId mismatch: %d != %d\n", sinfo1->ScriptId, sinfo2->ScriptId);
+    ok(sinfo1->uiCodePage == sinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", sinfo1->uiCodePage, sinfo2->uiCodePage);
+    ok(!lstrcmpW(sinfo1->wszDescription, sinfo2->wszDescription), "wszDescription mismatch\n");
+    ok(!lstrcmpW(sinfo1->wszFixedWidthFont, sinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
+    ok(!lstrcmpW(sinfo1->wszProportionalFont, sinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
+}
+
+static void test_EnumScripts(IMultiLanguage2 *iML2, DWORD flags)
+{
+    IEnumScript *iEnumScript = NULL;
+    SCRIPTINFO *sinfo;
+    SCRIPTINFO sinfo2;
+    HRESULT ret;
+    ULONG i, n;
+    UINT total;
+
+    total = 0;
+    TRACE_2("Call IMultiLanguage2_GetNumberOfScripts\n");
+    ret = IMultiLanguage2_GetNumberOfScripts(iML2, &total);
+    ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfScripts: expected S_OK/!0, got %08x/%u\n", ret, total);
+
+    trace("total mlang supported scripts %u\n", total);
+
+    TRACE_2("Call IMultiLanguage2_EnumScripts\n");
+    ret = IMultiLanguage2_EnumScripts(iML2, flags, LANG_NEUTRAL, &iEnumScript);
+    trace("IMultiLanguage2_EnumScripts = %08x, iEnumScript = %p\n", ret, iEnumScript);
+    ok(ret == S_OK && iEnumScript, "IMultiLanguage2_EnumScripts: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumScript);
+
+    TRACE_2("Call IEnumScript_Reset\n");
+    ret = IEnumScript_Reset(iEnumScript);
+    ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
+    n = 65536;
+    TRACE_2("Call IEnumScript_Next\n");
+    ret = IEnumScript_Next(iEnumScript, 0, NULL, &n);
+    ok(n == 65536 && ret == E_FAIL, "IEnumScript_Next: expected 65536/E_FAIL, got %u/%08x\n", n, ret);
+    TRACE_2("Call IEnumScript_Next\n");
+    ret = IEnumScript_Next(iEnumScript, 0, NULL, NULL);
+    ok(ret == E_FAIL, "IEnumScript_Next: expected E_FAIL, got %08x\n", ret);
+
+    sinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*sinfo) * total * 2);
+
+    n = total * 2;
+    TRACE_2("Call IEnumScript_Next\n");
+    ret = IEnumScript_Next(iEnumScript, 0, sinfo, &n);
+    ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
+
+    n = total * 2;
+    TRACE_2("Call IEnumScript_Next\n");
+    ret = IEnumScript_Next(iEnumScript, n, sinfo, &n);
+    ok(ret == S_OK && n != 0, "IEnumScript_Next: expected S_OK, got %08x/%u\n", ret, n);
+
+    trace("flags %08x, enumerated scripts %u\n", flags, n);
+
+    if (!flags)
+    {
+       ok(n == total, "IEnumScript_Next: expected %u, got %u\n", total, n);
+       flags = SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM;
+    }
+
+    total = n;
+
+    for (i = 0; pGetCPInfoExA && i < n; i++)
+    {
+       CPINFOEXA cpinfoex;
+#ifdef DUMP_SCRIPT_INFO
+       trace("SCRIPTINFO #%u:\n"
+             "ScriptId %08x\n"
+             "uiCodePage %u\n"
+             "wszDescription %s\n"
+             "wszFixedWidthFont %s\n"
+             "wszProportionalFont %s\n\n",
+             i,
+             sinfo[i].ScriptId,
+             sinfo[i].uiCodePage,
+             wine_dbgstr_w(sinfo[i].wszDescription),
+             wine_dbgstr_w(sinfo[i].wszFixedWidthFont),
+             wine_dbgstr_w(sinfo[i].wszProportionalFont));
+#endif
+       if (pGetCPInfoExA(sinfo[i].uiCodePage, 0, &cpinfoex))
+           trace("CodePage %u name: %s\n", sinfo[i].uiCodePage, cpinfoex.CodePageName);
+       else
+           trace("GetCPInfoExA failed for cp %u\n", sinfo[i].uiCodePage);
+
+       trace("---\n");
+    }
+
+    /* now IEnumScript_Next should fail, since pointer is at the end */
+    n = 1;
+    ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
+    ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
+
+    ret = IEnumScript_Reset(iEnumScript);
+    ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
+    n = 0;
+    ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
+    ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
+    scriptinfo_cmp(&sinfo[0], &sinfo2);
+
+    if (0)
+    {
+    /* Due to a bug in MS' implementation of IEnumScript_Skip
+     * it's not used here.
+     */
+    ret = IEnumScript_Skip(iEnumScript, 1);
+    ok(ret == S_OK, "IEnumScript_Skip: expected S_OK, got %08x\n", ret);
+    }
+    for (i = 0; i < total - 1; i++)
+    {
+        n = 0;
+        ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
+        ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
+        scriptinfo_cmp(&sinfo[i + 1], &sinfo2);
+    }
+
+    HeapFree(GetProcessHeap(), 0, sinfo);
+    IEnumScript_Release(iEnumScript);
+}
+
+static void IMLangFontLink_Test(IMLangFontLink* iMLFL)
+{
+    DWORD   dwCodePages = 0;
+    DWORD   dwManyCodePages = 0;
+    UINT    CodePage = 0;
+
+    ok(IMLangFontLink_CodePageToCodePages(iMLFL, 932, &dwCodePages)==S_OK,
+            "IMLangFontLink_CodePageToCodePages failed\n");
+    ok (dwCodePages != 0, "No CodePages returned\n");
+    ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwCodePages, 1035,
+                &CodePage)==S_OK, 
+            "IMLangFontLink_CodePagesToCodePage failed\n");
+    ok(CodePage == 932, "Incorrect CodePage Returned (%i)\n",CodePage);
+
+    ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1252, &dwCodePages)==S_OK,
+            "IMLangFontLink_CodePageToCodePages failed\n");
+    dwManyCodePages = dwManyCodePages | dwCodePages;
+    ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1256, &dwCodePages)==S_OK,
+            "IMLangFontLink_CodePageToCodePages failed\n");
+    dwManyCodePages = dwManyCodePages | dwCodePages;
+    ok(IMLangFontLink_CodePageToCodePages(iMLFL, 874, &dwCodePages)==S_OK,
+            "IMLangFontLink_CodePageToCodePages failed\n");
+    dwManyCodePages = dwManyCodePages | dwCodePages;
+
+    ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 1256,
+                &CodePage)==S_OK, 
+            "IMLangFontLink_CodePagesToCodePage failed\n");
+    ok(CodePage == 1256, "Incorrect CodePage Returned (%i)\n",CodePage);
+
+    ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 936,
+                &CodePage)==S_OK, 
+            "IMLangFontLink_CodePagesToCodePage failed\n");
+    ok(CodePage == 1252, "Incorrect CodePage Returned (%i)\n",CodePage);
+}
+
+static void test_rfc1766(IMultiLanguage2 *iML2)
+{
+    IEnumRfc1766 *pEnumRfc1766;
+    RFC1766INFO info;
+    ULONG n;
+    HRESULT ret;
+
+    ret = IMultiLanguage2_EnumRfc1766(iML2, LANG_NEUTRAL, &pEnumRfc1766);
+    ok(ret == S_OK, "IMultiLanguage2_EnumRfc1766 error %08x\n", ret);
+
+    while (1)
+    {
+        ret = IEnumRfc1766_Next(pEnumRfc1766, 1, &info, &n);
+        if (ret != S_OK) break;
+
+#ifdef DUMP_CP_INFO
+        trace("lcid %04x rfc_name %s locale_name %s\n",
+              info.lcid, wine_dbgstr_w(info.wszRfc1766), wine_dbgstr_w(info.wszLocaleName));
+#endif
+
+        ok(n == 1, "couldn't fetch 1 RFC1766INFO structure\n");
+        ok(IsValidLocale(info.lcid, LCID_SUPPORTED), "invalid lcid %04x\n", info.lcid);
+    }
+}
+
+static void test_GetLcidFromRfc1766(IMultiLanguage2 *iML2)
+{
+    LCID lcid;
+    HRESULT ret;
+
+    static WCHAR e[] = { 'e',0 };
+    static WCHAR en[] = { 'e','n',0 };
+    static WCHAR empty[] = { 0 };
+    static WCHAR dash[] = { '-',0 };
+    static WCHAR e_dash[] = { 'e','-',0 };
+    static WCHAR en_gb[] = { 'e','n','-','g','b',0 };
+    static WCHAR en_us[] = { 'e','n','-','u','s',0 };
+    static WCHAR en_them[] = { 'e','n','-','t','h','e','m',0 };
+    static WCHAR english[] = { 'e','n','g','l','i','s','h',0 };
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, NULL, en);
+    ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, NULL);
+    ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, empty);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, dash);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e_dash);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_them);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, english);
+    ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
+
+    lcid = 0;
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en);
+    ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
+    ok(lcid == 9, "got wrong lcid: %04x\n", lcid);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_gb);
+    ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
+    ok(lcid == 0x809, "got wrong lcid: %04x\n", lcid);
+
+    ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_us);
+    ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
+    ok(lcid == 0x409, "got wrong lcid: %04x\n", lcid);
+}
+
+START_TEST(mlang)
+{
+    IMultiLanguage2 *iML2 = NULL;
+    IMLangFontLink  *iMLFL = NULL;
+    HRESULT ret;
+
+    pGetCPInfoExA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetCPInfoExA");
+
+    CoInitialize(NULL);
+    TRACE_2("Call CoCreateInstance\n");
+    ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_IMultiLanguage2, (void **)&iML2);
+
+    trace("ret = %08x, MultiLanguage2 iML2 = %p\n", ret, iML2);
+    if (ret != S_OK || !iML2) return;
+
+    test_rfc1766(iML2);
+    test_GetLcidFromRfc1766(iML2);
+
+    test_EnumCodePages(iML2, 0);
+    test_EnumCodePages(iML2, MIMECONTF_MIME_LATEST);
+    test_EnumCodePages(iML2, MIMECONTF_BROWSER);
+    test_EnumCodePages(iML2, MIMECONTF_MINIMAL);
+    test_EnumCodePages(iML2, MIMECONTF_VALID);
+    /* FIXME: why MIMECONTF_MIME_REGISTRY returns 0 of supported codepages? */
+    /*test_EnumCodePages(iML2, MIMECONTF_MIME_REGISTRY);*/
+
+    test_EnumScripts(iML2, 0);
+    test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER);
+    test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM);
+
+    TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+    ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, CP_UNICODE);
+    ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UTF8 -> CP_UNICODE) = %08x\n", ret);
+    TRACE_2("Call IMultiLanguage2_IsConvertible\n");
+    ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, CP_UTF8);
+    ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> CP_UTF8) = %08x\n", ret);
+
+    test_multibyte_to_unicode_translations(iML2);
+
+    IMultiLanguage2_Release(iML2);
+
+    ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_IMLangFontLink, (void **)&iMLFL);
+
+    trace("ret = %08x, IMLangFontLink iMLFL = %p\n", ret, iMLFL);
+    if (ret != S_OK || !iML2) return;
+
+    IMLangFontLink_Test(iMLFL);
+    IMLangFontLink_Release(iMLFL);
+    
+    CoUninitialize();
+}
diff --git a/rostests/winetests/mlang/mlang.rbuild b/rostests/winetests/mlang/mlang.rbuild
new file mode 100644 (file)
index 0000000..e3bf83c
--- /dev/null
@@ -0,0 +1,15 @@
+<module name="mlang_winetest" type="win32cui" installbase="bin" installname="mlang_winetest.exe" allowwarnings="true">
+       <include base="mlang_winetest">.</include>
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>ole32</library>
+       <library>gdi32</library>
+       <library>kernel32</library>
+       <library>uuid</library>
+       <library>ntdll</library>
+       <file>mlang.c</file>
+       <file>testlist.c</file>
+</module>
diff --git a/rostests/winetests/mlang/testlist.c b/rostests/winetests/mlang/testlist.c
new file mode 100644 (file)
index 0000000..db8217c
--- /dev/null
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_mlang(void);
+
+const struct test winetest_testlist[] =
+{
+    { "mlang", func_mlang },
+    { 0, 0 }
+};
index 6abe85a..5f94b4f 100755 (executable)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdarg.h>
@@ -214,33 +214,30 @@ static HRESULT (WINAPI *pSHLWAPI_214)(_IDummyStream*,ULARGE_INTEGER*);
 
 static void InitFunctionPtrs(void)
 {
-  SHLWAPI_hshlwapi = LoadLibraryA("shlwapi.dll");
-  ok(SHLWAPI_hshlwapi != 0, "LoadLibrary failed\n");
-  if (SHLWAPI_hshlwapi)
-  {
-    pSHLWAPI_17 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)17);
-    ok(pSHLWAPI_17 != 0, "No Ordinal 17\n");
-    pSHLWAPI_18 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)18);
-    ok(pSHLWAPI_18 != 0, "No Ordinal 18\n");
-    pSHLWAPI_19 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)19);
-    ok(pSHLWAPI_19 != 0, "No Ordinal 19\n");
-    pSHLWAPI_20 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)20);
-    ok(pSHLWAPI_20 != 0, "No Ordinal 20\n");
-    pSHLWAPI_21 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)21);
-    ok(pSHLWAPI_21 != 0, "No Ordinal 21\n");
-    pSHLWAPI_22 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)22);
-    ok(pSHLWAPI_22 != 0, "No Ordinal 22\n");
-    pSHLWAPI_166 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)166);
-    ok(pSHLWAPI_166 != 0, "No Ordinal 166\n");
-    pSHLWAPI_184 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)184);
-    ok(pSHLWAPI_184 != 0, "No Ordinal 184\n");
-    pSHLWAPI_212 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)212);
-    ok(pSHLWAPI_212 != 0, "No Ordinal 212\n");
-    pSHLWAPI_213 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)213);
-    ok(pSHLWAPI_213 != 0, "No Ordinal 213\n");
-    pSHLWAPI_214 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)214);
-    ok(pSHLWAPI_214 != 0, "No Ordinal 214\n");
-  }
+  SHLWAPI_hshlwapi = GetModuleHandleA("shlwapi.dll");
+
+  pSHLWAPI_17 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)17);
+  ok(pSHLWAPI_17 != 0, "No Ordinal 17\n");
+  pSHLWAPI_18 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)18);
+  ok(pSHLWAPI_18 != 0, "No Ordinal 18\n");
+  pSHLWAPI_19 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)19);
+  ok(pSHLWAPI_19 != 0, "No Ordinal 19\n");
+  pSHLWAPI_20 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)20);
+  ok(pSHLWAPI_20 != 0, "No Ordinal 20\n");
+  pSHLWAPI_21 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)21);
+  ok(pSHLWAPI_21 != 0, "No Ordinal 21\n");
+  pSHLWAPI_22 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)22);
+  ok(pSHLWAPI_22 != 0, "No Ordinal 22\n");
+  pSHLWAPI_166 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)166);
+  ok(pSHLWAPI_166 != 0, "No Ordinal 166\n");
+  pSHLWAPI_184 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)184);
+  ok(pSHLWAPI_184 != 0, "No Ordinal 184\n");
+  pSHLWAPI_212 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)212);
+  ok(pSHLWAPI_212 != 0, "No Ordinal 212\n");
+  pSHLWAPI_213 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)213);
+  ok(pSHLWAPI_213 != 0, "No Ordinal 213\n");
+  pSHLWAPI_214 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)214);
+  ok(pSHLWAPI_214 != 0, "No Ordinal 214\n");
 }
 
 static void InitDummyStream(_IDummyStream* iface)
@@ -299,21 +296,20 @@ static void test_CList(void)
       inserted = pSHLWAPI_22(list, item->ulId);
       ok(inserted != NULL, "lost after adding\n");
 
-      ok(!inserted || inserted->ulId != ~0UL, "find returned a container\n");
+      ok(!inserted || inserted->ulId != ~0U, "find returned a container\n");
 
       /* Check size */
       if (inserted && inserted->ulSize & 0x3)
       {
         /* Contained */
-        ok(inserted[-1].ulId == ~0UL, "invalid size is not countained\n");
+        ok(inserted[-1].ulId == ~0U, "invalid size is not countained\n");
         ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST),
            "container too small\n");
       }
       else if (inserted)
       {
         ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST),
-           "id %ld size wrong (%ld!=%ld)\n", inserted->ulId, inserted->ulSize,
-           item->ulSize+sizeof(SHLWAPI_CLIST));
+           "id %d wrong size %d\n", inserted->ulId, inserted->ulSize);
       }
       if (inserted)
       {
@@ -370,20 +366,22 @@ static void test_CList(void)
   /* The call succeeds but the item is not inserted, except on some early
    * versions which return failure. Wine behaves like later versions.
    */
-#if 0
+  if (0)
+  {
   ok(hRet == S_OK, "failed bad element size\n");
-#endif
+  }
   inserted = pSHLWAPI_22(list, 33);
   ok(inserted == NULL, "inserted bad element size\n");
 
   inserted = (LPSHLWAPI_CLIST)buff;
   inserted->ulSize = 44;
-  inserted->ulId = ~0UL;
+  inserted->ulId = ~0U;
   hRet = pSHLWAPI_20(&list, inserted);
   /* See comment above, some early versions fail this call */
-#if 0
+  if (0)
+  {
   ok(hRet == S_OK, "failed adding a container\n");
-#endif
+  }
   item = SHLWAPI_CLIST_items;
 
   /* Look for nonexistent item in populated list */
@@ -424,21 +422,20 @@ static void test_CList(void)
       inserted = pSHLWAPI_22(list, item->ulId);
       ok(inserted != NULL, "lost after adding\n");
 
-      ok(!inserted || inserted->ulId != ~0UL, "find returned a container\n");
+      ok(!inserted || inserted->ulId != ~0U, "find returned a container\n");
 
       /* Check size */
       if (inserted && inserted->ulSize & 0x3)
       {
         /* Contained */
-        ok(inserted[-1].ulId == ~0UL, "invalid size is not countained\n");
+        ok(inserted[-1].ulId == ~0U, "invalid size is not countained\n");
         ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST),
            "container too small\n");
       }
       else if (inserted)
       {
         ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST),
-           "id %ld size wrong (%ld!=%ld)\n", inserted->ulId, inserted->ulSize,
-           item->ulSize+sizeof(SHLWAPI_CLIST));
+           "id %d wrong size %d\n", inserted->ulId, inserted->ulSize);
       }
       ok(!inserted || inserted->ulId==item->ulId, "find got wrong item\n");
       if (inserted)
@@ -637,7 +634,4 @@ START_TEST(clist)
     test_SHLWAPI_213();
     test_SHLWAPI_214();
   }
-
-  if (SHLWAPI_hshlwapi)
-    FreeLibrary(SHLWAPI_hshlwapi);
 }
index ce49653..74ad6e5 100755 (executable)
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdio.h>
 
-#define INITGUID
 #include "wine/test.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "winnls.h"
 #include "winuser.h"
 #include "shlguid.h"
-#include "wine/shobjidl.h"
+#include "shobjidl.h"
+#include "olectl.h"
+
+#define INITGUID
+#include "initguid.h"
+
+/* This GUID has been removed from the PSDK */
+DEFINE_OLEGUID(WINE_IID_IDelayedRelease,     0x000214EDL, 0, 0);
 
 /* Function ptrs for ordinal calls */
 static HMODULE hShlwapi = 0;
@@ -56,7 +62,7 @@ const GUID * TEST_guids[] = {
   &IID_IPersistFolder,
   &IID_IExtractIconA,
   &IID_IShellDetails,
-  &IID_IDelayedRelease,
+  &WINE_IID_IDelayedRelease,
   &IID_IShellLinkA,
   &IID_IShellCopyHookA,
   &IID_IFileViewerA,
@@ -153,19 +159,33 @@ static void test_ClassIDs(void)
   ok(szBuff[0] == '{', "Didn't write to buffer with ok length\n");
 }
 
+static void test_CLSIDFromProgIDWrap(void)
+{
+    HRESULT (WINAPI *pCLSIDFromProgIDWrap)(LPCOLESTR,LPCLSID);
+    CLSID clsid = IID_NULL;
+    HRESULT hres;
+
+    static const WCHAR wszStdPicture[] = {'S','t','d','P','i','c','t','u','r','e',0};
+
+    pCLSIDFromProgIDWrap = (void*)GetProcAddress(hShlwapi,(char*)435);
+
+    hres = pCLSIDFromProgIDWrap(wszStdPicture, &clsid);
+    ok(hres == S_OK, "CLSIDFromProgIDWrap failed: %08x\n", hres);
+    ok(IsEqualGUID(&CLSID_StdPicture, &clsid), "wrong clsid\n");
+
+    hres = pCLSIDFromProgIDWrap(NULL, &clsid);
+    ok(hres == E_INVALIDARG, "CLSIDFromProgIDWrap failed: %08x, expected E_INVALIDARG\n", hres);
+
+    hres = pCLSIDFromProgIDWrap(wszStdPicture, NULL);
+    ok(hres == E_INVALIDARG, "CLSIDFromProgIDWrap failed: %08x, expected E_INVALIDARG\n", hres);
+}
 
 START_TEST(clsid)
 {
-  hShlwapi = LoadLibraryA("shlwapi.dll");
-  ok(hShlwapi != 0, "LoadLibraryA failed\n");
-  if (hShlwapi)
-  {
-    pSHLWAPI_269 = (void*)GetProcAddress(hShlwapi, (LPSTR)269);
-    pSHLWAPI_23 = (void*)GetProcAddress(hShlwapi, (LPSTR)23);
-  }
+  hShlwapi = GetModuleHandleA("shlwapi.dll");
+  pSHLWAPI_269 = (void*)GetProcAddress(hShlwapi, (LPSTR)269);
+  pSHLWAPI_23 = (void*)GetProcAddress(hShlwapi, (LPSTR)23);
 
   test_ClassIDs();
-
-  if (hShlwapi)
-    FreeLibrary(hShlwapi);
+  test_CLSIDFromProgIDWrap();
 }
index b1f618b..d1bfa4c 100755 (executable)
@@ -157,11 +157,6 @@ static void test_pack_HUSKEY(void)
     TEST_TYPE(HUSKEY, 4, 4);
 }
 
-static void test_pack_IQueryAssociations(void)
-{
-    /* IQueryAssociations */
-}
-
 static void test_pack_PHUSKEY(void)
 {
     /* PHUSKEY */
@@ -176,7 +171,6 @@ static void test_pack(void)
     test_pack_DLLVERSIONINFO();
     test_pack_DLLVERSIONINFO2();
     test_pack_HUSKEY();
-    test_pack_IQueryAssociations();
     test_pack_PHUSKEY();
 }
 
index 20798c7..c948274 100755 (executable)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdio.h>
@@ -46,8 +46,8 @@ static void test_GetAcceptLanguagesA(void)
     memset(buffer, 0, sizeof(buffer));
     SetLastError(ERROR_SUCCESS);
     retval = pGetAcceptLanguagesA( buffer, &buffersize);
-    trace("GetAcceptLanguagesA: retval %08lx, size %08lx, buffer (%s),"
-       " last error %ld\n", retval, buffersize, buffer, GetLastError());
+    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;
@@ -56,39 +56,39 @@ static void test_GetAcceptLanguagesA(void)
        (ERROR_CLASS_DOES_NOT_EXIST == GetLastError()) ||
        (ERROR_PROC_NOT_FOUND == GetLastError()) ||
        (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) ||
-       (ERROR_SUCCESS == GetLastError()), "last error set to %ld\n", 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,
-       "function result wrong: got %08lx; expected E_FAIL\n", retval);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+       "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,
-       "function result wrong: got %08lx; expected E_FAIL\n", retval);
+       "function result wrong: got %08x; expected E_FAIL\n", retval);
     ok(buffersize == sizeof(buffer),
        "buffersize was changed (2nd parameter; not on Win2k)\n");
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+    ok(ERROR_SUCCESS == GetLastError(), "last error set to %u\n", GetLastError());
 
     SetLastError(ERROR_SUCCESS);
     retval = pGetAcceptLanguagesA( buffer, NULL);
     ok(retval == E_FAIL,
-       "function result wrong: got %08lx; expected E_FAIL\n", retval);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+       "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,
-       "function result wrong: got %08lx; expected E_FAIL\n", retval);
+       "function result wrong: got %08x; expected E_FAIL\n", retval);
     ok(buffersize == 0,
-       "buffersize wrong(changed) got %08lx; expected 0 (2nd parameter; not on Win2k)\n", buffersize);
-    ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+       "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));
@@ -99,29 +99,29 @@ static void test_GetAcceptLanguagesA(void)
             if(buffersize == exactsize) {
             ok( (ERROR_SUCCESS == GetLastError()) || (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) ||
                (ERROR_PROC_NOT_FOUND == GetLastError()) || (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()),
-                "last error wrong: got %08lx; expected ERROR_SUCCESS(NT4)/ERROR_CALL_NOT_IMPLEMENTED(98/ME)/"
+                "last error wrong: got %u; expected ERROR_SUCCESS(NT4)/ERROR_CALL_NOT_IMPLEMENTED(98/ME)/"
                "ERROR_PROC_NOT_FOUND(NT4)/ERROR_NO_IMPERSONATION_TOKEN(XP)\n", GetLastError());
             ok(exactsize == strlen(buffer),
-                 "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), exactsize);
+                 "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 %08lx; expected ERROR_SUCCESS\n", GetLastError());
+                    "last error wrong: got %u; expected ERROR_SUCCESS\n", GetLastError());
                 ok(buffersize == strlen(buffer),
-                    "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize);
+                    "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize);
             } else
-                ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                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 %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+               "buffersize wrong: got %08x, expected 0 (2nd parameter;Win2k)\n", buffersize);
             ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
-               "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+               "last error wrong: got %u; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
             ok(buffersize2 == strlen(buffer),
-               "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+               "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize2);
             break;
         default:
-            ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+            ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
                 retval, buffersize, buffer, GetLastError());
             break;
     }
@@ -133,25 +133,25 @@ static void test_GetAcceptLanguagesA(void)
     switch(retval) {
        case 0L:
             ok(ERROR_SUCCESS == GetLastError(),
-                 "last error wrong: got %08lx; expected ERROR_SUCCESS\n", 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 %08lx \n", strlen(buffer), buffersize);
+                    "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize);
             else
-                ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                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 %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+               "buffersize wrong: got %08x, expected 0 (2nd parameter;Win2k)\n", buffersize);
             ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
-               "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+               "last error wrong: got %u; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
             ok(buffersize2 == strlen(buffer),
-               "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+               "buffer content (length) wrong: got %08x, expected %08x\n", lstrlenA(buffer), buffersize2);
             break;
         default:
-            ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+            ok( 0, "retval %08x, size %08x, buffer (%s), last error %u\n",
                 retval, buffersize, buffer, GetLastError());
             break;
     }
@@ -208,31 +208,232 @@ static void test_alloc_shared(void)
 
     procid=GetCurrentProcessId();
     hmem=pSHAllocShared(NULL,10,procid);
-    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
     ret = pSHFreeShared(hmem, procid);
-    ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+    ok( ret, "SHFreeShared failed: %u\n", GetLastError());
 
     val=0x12345678;
     hmem=pSHAllocShared(&val,4,procid);
-    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
 
     p=(int*)pSHLockShared(hmem,procid);
-    ok(p!=NULL,"SHLockShared failed: %ld\n", GetLastError());
+    ok(p!=NULL,"SHLockShared failed: %u\n", GetLastError());
     if (p!=NULL)
         ok(*p==val,"Wrong value in shared memory: %d instead of %d\n",*p,val);
     ret = pSHUnlockShared(p);
-    ok( ret, "SHUnlockShared failed: %ld\n", GetLastError());
+    ok( ret, "SHUnlockShared failed: %u\n", GetLastError());
 
     ret = pSHFreeShared(hmem, procid);
-    ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+    ok( ret, "SHFreeShared failed: %u\n", GetLastError());
+}
+
+static void test_fdsa(void)
+{
+    typedef struct
+    {
+        DWORD num_items;       /* Number of elements inserted */
+        void *mem;             /* Ptr to array */
+        DWORD blocks_alloced;  /* Number of elements allocated */
+        BYTE inc;              /* Number of elements to grow by when we need to expand */
+        BYTE block_size;       /* Size in bytes of an element */
+        BYTE flags;            /* Flags */
+    } FDSA_info;
+
+    BOOL (WINAPI *pFDSA_Initialize)(DWORD block_size, DWORD inc, FDSA_info *info, void *mem,
+                                    DWORD init_blocks);
+    BOOL (WINAPI *pFDSA_Destroy)(FDSA_info *info);
+    DWORD (WINAPI *pFDSA_InsertItem)(FDSA_info *info, DWORD where, const void *block);
+    BOOL (WINAPI *pFDSA_DeleteItem)(FDSA_info *info, DWORD where);
+
+    FDSA_info info;
+    int block_size = 10, init_blocks = 4, inc = 2;
+    DWORD ret;
+    char *mem;
+
+    pFDSA_Initialize = (void *)GetProcAddress(hShlwapi, (LPSTR)208);
+    pFDSA_Destroy    = (void *)GetProcAddress(hShlwapi, (LPSTR)209);
+    pFDSA_InsertItem = (void *)GetProcAddress(hShlwapi, (LPSTR)210);
+    pFDSA_DeleteItem = (void *)GetProcAddress(hShlwapi, (LPSTR)211);
+
+    mem = HeapAlloc(GetProcessHeap(), 0, block_size * init_blocks);
+    memset(&info, 0, sizeof(info));
+
+    ok(pFDSA_Initialize(block_size, inc, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
+    ok(info.num_items == 0, "num_items = %d\n", info.num_items);
+    ok(info.mem == mem, "mem = %p\n", info.mem);
+    ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
+    ok(info.inc == inc, "inc = %d\n", info.inc);
+    ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
+    ok(info.flags == 0, "flags = %d\n", info.flags);
+
+    ret = pFDSA_InsertItem(&info, 1234, "1234567890");
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(info.num_items == 1, "num_items = %d\n", info.num_items);
+    ok(info.mem == mem, "mem = %p\n", info.mem);
+    ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
+    ok(info.inc == inc, "inc = %d\n", info.inc);
+    ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
+    ok(info.flags == 0, "flags = %d\n", info.flags);
+
+    ret = pFDSA_InsertItem(&info, 1234, "abcdefghij");
+    ok(ret == 1, "ret = %d\n", ret);
+
+    ret = pFDSA_InsertItem(&info, 1, "klmnopqrst");
+    ok(ret == 1, "ret = %d\n", ret);
+
+    ret = pFDSA_InsertItem(&info, 0, "uvwxyzABCD");
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(info.mem == mem, "mem = %p\n", info.mem);
+    ok(info.flags == 0, "flags = %d\n", info.flags);
+
+    /* This next InsertItem will cause shlwapi to allocate its own mem buffer */
+    ret = pFDSA_InsertItem(&info, 0, "EFGHIJKLMN");
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(info.mem != mem, "mem = %p\n", info.mem);
+    ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
+    ok(info.flags == 0x1, "flags = %d\n", info.flags);
+
+    ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCD1234567890klmnopqrstabcdefghij", 50), "mem %s\n", (char*)info.mem);
+
+    ok(pFDSA_DeleteItem(&info, 2), "rets FALSE\n");
+    ok(info.mem != mem, "mem = %p\n", info.mem);
+    ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
+    ok(info.flags == 0x1, "flags = %d\n", info.flags);
+
+    ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrstabcdefghij", 40), "mem %s\n", (char*)info.mem);
+
+    ok(pFDSA_DeleteItem(&info, 3), "rets FALSE\n");
+    ok(info.mem != mem, "mem = %p\n", info.mem);
+    ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
+    ok(info.flags == 0x1, "flags = %d\n", info.flags);
+
+    ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrst", 30), "mem %s\n", (char*)info.mem);
+
+    ok(!pFDSA_DeleteItem(&info, 4), "does not ret FALSE\n");
+
+    /* As shlwapi has allocated memory internally, Destroy will ret FALSE */
+    ok(!pFDSA_Destroy(&info), "FDSA_Destroy does not ret FALSE\n");
+
+
+    /* When Initialize is called with inc = 0, set it to 1 */
+    ok(pFDSA_Initialize(block_size, 0, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
+    ok(info.inc == 1, "inc = %d\n", info.inc);
+
+    /* This time, because shlwapi hasn't had to allocate memory
+       internally, Destroy rets non-zero */
+    ok(pFDSA_Destroy(&info), "FDSA_Destroy rets FALSE\n");
+
+
+    HeapFree(GetProcessHeap(), 0, mem);
+}
+
+
+typedef struct SHELL_USER_SID {
+    SID_IDENTIFIER_AUTHORITY sidAuthority;
+    DWORD                    dwUserGroupID;
+    DWORD                    dwUserID;
+} SHELL_USER_SID, *PSHELL_USER_SID;
+typedef struct SHELL_USER_PERMISSION {
+    SHELL_USER_SID susID;
+    DWORD          dwAccessType;
+    BOOL           fInherit;
+    DWORD          dwAccessMask;
+    DWORD          dwInheritMask;
+    DWORD          dwInheritAccessMask;
+} SHELL_USER_PERMISSION, *PSHELL_USER_PERMISSION;
+static void test_GetShellSecurityDescriptor(void)
+{
+    SHELL_USER_PERMISSION supCurrentUserFull = {
+        { {SECURITY_NULL_SID_AUTHORITY}, 0, 0 },
+        ACCESS_ALLOWED_ACE_TYPE, FALSE,
+        GENERIC_ALL, 0, 0 };
+#define MY_INHERITANCE 0xBE /* invalid value to proof behavior */
+    SHELL_USER_PERMISSION supEveryoneDenied = {
+        { {SECURITY_WORLD_SID_AUTHORITY}, SECURITY_WORLD_RID, 0 },
+        ACCESS_DENIED_ACE_TYPE, TRUE,
+        GENERIC_WRITE, MY_INHERITANCE | 0xDEADBA00, GENERIC_READ };
+    PSHELL_USER_PERMISSION rgsup[2] = {
+        &supCurrentUserFull, &supEveryoneDenied,
+    };
+    SECURITY_DESCRIPTOR* psd;
+    SECURITY_DESCRIPTOR* (WINAPI*pGetShellSecurityDescriptor)(PSHELL_USER_PERMISSION*,int);
+
+    pGetShellSecurityDescriptor=(void*)GetProcAddress(hShlwapi,(char*)475);
+
+    psd = pGetShellSecurityDescriptor(NULL, 2);
+    ok(psd==NULL, "GetShellSecurityDescriptor should fail\n");
+    psd = pGetShellSecurityDescriptor(rgsup, 0);
+    ok(psd==NULL, "GetShellSecurityDescriptor should fail\n");
+
+    psd = pGetShellSecurityDescriptor(rgsup, 2);
+    ok(psd!=NULL, "GetShellSecurityDescriptor failed\n");
+    if (psd!=NULL)
+    {
+        BOOL bHasDacl = FALSE, bDefaulted;
+        PACL pAcl;
+        DWORD dwRev;
+        SECURITY_DESCRIPTOR_CONTROL control;
+
+        ok(IsValidSecurityDescriptor(psd), "returned value is not valid SD\n");
+
+        ok(GetSecurityDescriptorControl(psd, &control, &dwRev),
+                "GetSecurityDescriptorControl failed with error %u\n", GetLastError());
+        ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n");
+
+        ok(GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted), 
+            "GetSecurityDescriptorDacl failed with error %u\n", GetLastError());
+
+        ok(bHasDacl, "SD has no DACL\n");
+        if (bHasDacl)
+        {
+            ok(!bDefaulted, "DACL should not be defaulted\n");
+
+            ok(pAcl != NULL, "NULL DACL!\n");
+            if (pAcl != NULL)
+            {
+                ACL_SIZE_INFORMATION asiSize;
+
+                ok(IsValidAcl(pAcl), "DACL is not valid\n");
+
+                ok(GetAclInformation(pAcl, &asiSize, sizeof(asiSize), AclSizeInformation),
+                        "GetAclInformation failed with error %u\n", GetLastError());
+
+                ok(asiSize.AceCount == 3, "Incorrect number of ACEs: %d entries\n", asiSize.AceCount);
+                if (asiSize.AceCount == 3)
+                {
+                    ACCESS_ALLOWED_ACE *paaa; /* will use for DENIED too */
+
+                    ok(GetAce(pAcl, 0, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
+                    ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, 
+                            "Invalid ACE type %d\n", paaa->Header.AceType); 
+                    ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
+                    ok(paaa->Mask == GENERIC_ALL, "Invalid ACE mask %x\n", paaa->Mask);
+
+                    ok(GetAce(pAcl, 1, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
+                    ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
+                            "Invalid ACE type %d\n", paaa->Header.AceType); 
+                    /* first one of two ACEs generated from inheritable entry - without inheritance */
+                    ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
+                    ok(paaa->Mask == GENERIC_WRITE, "Invalid ACE mask %x\n", paaa->Mask);
+
+                    ok(GetAce(pAcl, 2, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
+                    ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
+                            "Invalid ACE type %d\n", paaa->Header.AceType); 
+                    /* second ACE - with inheritance */
+                    ok(paaa->Header.AceFlags == MY_INHERITANCE,
+                            "Invalid ACE flags %x\n", paaa->Header.AceFlags);
+                    ok(paaa->Mask == GENERIC_READ, "Invalid ACE mask %x\n", paaa->Mask);
+                }
+            }
+        }
+
+        LocalFree(psd);
+    }
 }
 
 START_TEST(ordinal)
 {
-  hShlwapi = LoadLibraryA("shlwapi.dll");
-  ok(hShlwapi != 0, "LoadLibraryA failed\n");
-  if (!hShlwapi)
-    return;
+  hShlwapi = GetModuleHandleA("shlwapi.dll");
 
   pGetAcceptLanguagesA = (void*)GetProcAddress(hShlwapi, (LPSTR)14);
   pSHSearchMapInt = (void*)GetProcAddress(hShlwapi, (LPSTR)198);
@@ -244,5 +445,6 @@ START_TEST(ordinal)
   test_GetAcceptLanguagesA();
   test_SHSearchMapInt();
   test_alloc_shared();
-  FreeLibrary(hShlwapi);
+  test_fdsa();
+  test_GetShellSecurityDescriptor();
 }
index 9812e08..d4ed84d 100755 (executable)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <assert.h>
@@ -24,7 +24,6 @@
 #include "wine/test.h"
 #include "windef.h"
 #include "winbase.h"
-#include "wine/unicode.h"
 #include "winreg.h"
 #include "shlwapi.h"
 #include "wininet.h"
@@ -32,6 +31,7 @@
 static HMODULE hShlwapi;
 static HRESULT (WINAPI *pPathIsValidCharA)(char,DWORD);
 static HRESULT (WINAPI *pPathIsValidCharW)(WCHAR,DWORD);
+static LPWSTR  (WINAPI *pPathCombineW)(LPWSTR, LPCWSTR, LPCWSTR);
 
 const char* TEST_URL_1 = "http://www.winehq.org/tests?date=10/10/1923";
 const char* TEST_URL_2 = "http://localhost:8080/tests%2e.html?date=Mon%2010/10/1923";
@@ -60,10 +60,31 @@ const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
     {"http://www.winehq.org/tests/../?query=x&return=y", 0, S_OK, "http://www.winehq.org/?query=x&return=y"},
     {"http://www.winehq.org/tests/..#example", 0, S_OK, "http://www.winehq.org/#example"},
     {"http://www.winehq.org/tests/../#example", 0, S_OK, "http://www.winehq.org/#example"},
+    {"http://www.winehq.org/tests\\../#example", 0, S_OK, "http://www.winehq.org/#example"},
+    {"http://www.winehq.org/tests/..\\#example", 0, S_OK, "http://www.winehq.org/#example"},
+    {"http://www.winehq.org\\tests/../#example", 0, S_OK, "http://www.winehq.org/#example"},
     {"http://www.winehq.org/tests/../#example", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../#example"},
     {"http://www.winehq.org/tests/foo bar", URL_ESCAPE_SPACES_ONLY| URL_DONT_ESCAPE_EXTRA_INFO , S_OK, "http://www.winehq.org/tests/foo%20bar"},
     {"http://www.winehq.org/tests/foo%20bar", URL_UNESCAPE , S_OK, "http://www.winehq.org/tests/foo bar"},
     {"file:///c:/tests/foo%20bar", URL_UNESCAPE , S_OK, "file:///c:/tests/foo bar"},
+    {"file:///c:/tests\\foo%20bar", URL_UNESCAPE , S_OK, "file:///c:/tests/foo bar"},
+    {"file:///c:/tests/foo%20bar", 0, S_OK, "file:///c:/tests/foo%20bar"},
+    {"file:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"},
+    {"file://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"},
+    {"file://c:/tests\\../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"},
+    {"file://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"},
+    {"file:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\\\tests\\foo bar"},
+    {"file:///c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo bar"},
+    {"file:///c:\\tests\\foo bar", URL_DONT_SIMPLIFY, S_OK, "file:///c:/tests/foo bar"},
+    {"http://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/site/about"},
+    {"file_://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "file_://www.winehq.org/site/about"},
+    {"c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"},
+    {"file:///c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"},
+    {"c:dir\\file", 0, S_OK, "file:///c:dir/file"},
+    {"c:\\tests\\foo bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"},
+    {"c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo%20bar"},
+    {"A", 0, S_OK, "A"},
+    {"", 0, S_OK, ""}
 };
 
 typedef struct _TEST_URL_ESCAPE {
@@ -165,8 +186,10 @@ typedef struct _TEST_URL_COMBINE {
 
 const TEST_URL_COMBINE TEST_COMBINE[] = {
     {"http://www.winehq.org/tests", "tests1", 0, S_OK, "http://www.winehq.org/tests1"},
+    {"http://www.%77inehq.org/tests", "tests1", 0, S_OK, "http://www.%77inehq.org/tests1"},
     /*FIXME {"http://www.winehq.org/tests", "../tests2", 0, S_OK, "http://www.winehq.org/tests2"},*/
     {"http://www.winehq.org/tests/", "../tests3", 0, S_OK, "http://www.winehq.org/tests3"},
+    {"http://www.winehq.org/tests/test1", "test2", 0, S_OK, "http://www.winehq.org/tests/test2"},
     {"http://www.winehq.org/tests/../tests", "tests4", 0, S_OK, "http://www.winehq.org/tests4"},
     {"http://www.winehq.org/tests/../tests/", "tests5", 0, S_OK, "http://www.winehq.org/tests/tests5"},
     {"http://www.winehq.org/tests/../tests/", "/tests6/..", 0, S_OK, "http://www.winehq.org/"},
@@ -175,6 +198,13 @@ const TEST_URL_COMBINE TEST_COMBINE[] = {
     {"http://www.winehq.org/tests/#example", "tests9", 0, S_OK, "http://www.winehq.org/tests/tests9"},
     {"http://www.winehq.org/tests/../tests/", "/tests10/..", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests10/.."},
     {"http://www.winehq.org/tests/../", "tests11", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../tests11"},
+    {"file:///C:\\dir\\file.txt", "test.txt", 0, S_OK, "file:///C:/dir/test.txt"},
+    {"http://www.winehq.org/test/", "test%20file.txt", 0, S_OK, "http://www.winehq.org/test/test%20file.txt"},
+    {"http://www.winehq.org/test/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/test/test%20file.txt"},
+    {"http://www.winehq.org%2ftest/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org%2ftest/test%20file.txt"},
+    {"xxx:@MSITStore:file.chm/file.html", "dir/file", 0, S_OK, "xxx:dir/file"},
+    {"mk:@MSITStore:file.chm::/file.html", "/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"},
+    {"mk:@MSITStore:file.chm::/file.html", "mk:@MSITStore:file.chm::/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"},
 };
 
 struct {
@@ -285,6 +315,22 @@ struct {
     {  "file:partial",                                 FALSE,  TRUE    }
 };
 
+struct {
+    const char *path;
+    const char *result;
+} TEST_PATH_UNQUOTE_SPACES[] = {
+    { "abcdef",                    "abcdef"         },
+    { "\"abcdef\"",                "abcdef"         },
+    { "\"abcdef",                  "\"abcdef"       },
+    { "abcdef\"",                  "abcdef\""       },
+    { "\"\"abcdef\"\"",            "\"abcdef\""     },
+    { "abc\"def",                  "abc\"def"       },
+    { "\"abc\"def",                "\"abc\"def"     },
+    { "\"abc\"def\"",              "abc\"def"       },
+    { "\'abcdef\'",                "\'abcdef\'"     },
+    { "\"\"",                      ""               },
+    { "\"",                        ""               }
+};
 
 static LPWSTR GetWideString(const char* szString)
 {
@@ -300,6 +346,15 @@ static void FreeWideString(LPWSTR wszString)
    HeapFree(GetProcessHeap(), 0, wszString);
 }
 
+static LPSTR strdupA(LPCSTR p)
+{
+    LPSTR ret;
+    DWORD len = (strlen(p) + 1);
+    ret = HeapAlloc(GetProcessHeap(), 0, len);
+    memcpy(ret, p, len);
+    return ret;
+}
+
 static void hash_url(const char* szUrl)
 {
   LPCSTR szTestUrl = szUrl;
@@ -332,13 +387,13 @@ static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const
   DWORD dwSize;
 
   dwSize = INTERNET_MAX_URL_LENGTH;
-  ok( UrlGetPartA(szUrl, szPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartA for \"%s\" part 0x%08lx didn't return S_OK but \"%s\"\n", szUrl, dwPart, szPart);
+  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" );
 
   wszConvertedPart = GetWideString(szPart);
 
-  ok(strcmpW(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(wszUrl);
   FreeWideString(wszConvertedPart);
@@ -351,6 +406,25 @@ static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const
 
 static void test_UrlGetPart(void)
 {
+  CHAR szPart[INTERNET_MAX_URL_LENGTH];
+  DWORD dwSize;
+  HRESULT res;
+
+  dwSize = sizeof szPart;
+  szPart[0]='x'; szPart[1]=0;
+  res = UrlGetPartA("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);
+  todo_wine {
+  ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res);
+  ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart);
+  }
+  
   test_url_part(TEST_URL_3, URL_PART_HOSTNAME, 0, "localhost");
   test_url_part(TEST_URL_3, URL_PART_PORT, 0, "21");
   test_url_part(TEST_URL_3, URL_PART_USERNAME, 0, "foo");
@@ -367,15 +441,15 @@ 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%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+    ok(UrlEscapeA(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%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+    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(strcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", szExpectUrl, szReturnUrl, szUrl, dwFlags);
+    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);
 
@@ -393,14 +467,14 @@ static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwEx
     
     dwSize = INTERNET_MAX_URL_LENGTH;
     ok(UrlCanonicalizeA(szUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer\n");
-    ok(UrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeA didn't return 0x%08lx\n", dwExpectReturn);
-    ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08lx Expected %s, but got %s\n", dwFlags, szExpectUrl, szReturnUrl);
+    ok(UrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeA didn't return 0x%08x\n", dwExpectReturn);
+    ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x Expected \"%s\", but got \"%s\"\n", dwFlags, szExpectUrl, szReturnUrl);
 
     dwSize = INTERNET_MAX_URL_LENGTH;
     ok(UrlCanonicalizeW(wszUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer\n");
-    ok(UrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeW didn't return 0x%08lx\n", dwExpectReturn);
+    ok(UrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeW didn't return 0x%08x\n", dwExpectReturn);
     wszConvertedUrl = GetWideString(szReturnUrl);
-    ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n");
+    ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n");
     FreeWideString(wszConvertedUrl);
     
             
@@ -411,7 +485,13 @@ static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwEx
 
 static void test_UrlEscape(void)
 {
+    DWORD size;
+    HRESULT ret;
     unsigned int i;
+
+    ret = UrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+
     for(i=0; i<sizeof(TEST_ESCAPE)/sizeof(TEST_ESCAPE[0]); i++) {
         test_url_escape(TEST_ESCAPE[i].url, TEST_ESCAPE[i].flags,
                               TEST_ESCAPE[i].expectret, TEST_ESCAPE[i].expecturl);
@@ -421,10 +501,23 @@ static void test_UrlEscape(void)
 static void test_UrlCanonicalize(void)
 {
     unsigned int i;
+    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+    DWORD dwSize;
+    HRESULT hr;
+
     for(i=0; i<sizeof(TEST_CANONICALIZE)/sizeof(TEST_CANONICALIZE[0]); i++) {
         test_url_canonicalize(TEST_CANONICALIZE[i].url, TEST_CANONICALIZE[i].flags,
                               TEST_CANONICALIZE[i].expectret, TEST_CANONICALIZE[i].expecturl);
     }
+
+    /* move to TEST_CANONICALIZE when fixed */
+    dwSize = sizeof szReturnUrl;
+    /*LimeWire online installer calls this*/
+    hr = UrlCanonicalizeA("/uri-res/N2R?urn:sha1:B3K", szReturnUrl, &dwSize,URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY /*0x82000000*/);
+    ok(hr==S_OK,"UrlCanonicalizeA returned 0x%08x instead of S_OK\n", hr);
+    todo_wine {
+        ok(strcmp(szReturnUrl,"/uri-res/N2R?urn:sha1:B3K")==0, "UrlCanonicalizeA got \"%s\"  instead of \"/uri-res/N2R?urn:sha1:B3K\"\n", szReturnUrl);
+    }
 }
 
 static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
@@ -441,41 +534,41 @@ static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFla
     DWORD dwExpectLen = lstrlen(szExpectUrl);
 
     hr = UrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
-    ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_INVALIDARG);
+    ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_INVALIDARG);
     
     dwSize = 0;
     hr = UrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
-    ok(hr == E_POINTER, "Checking length of string, return was 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+    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);
-    ok(hr == E_POINTER, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+    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);
-    ok(hr == dwExpectReturn, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
-    ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
+    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%08lx, expected 0x%08lx\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+    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%08lx, expected 0x%08lx\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+    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%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
-    ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
+    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(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
+        ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
         FreeWideString(wszConvertedUrl);
     }
 
@@ -504,19 +597,19 @@ static void test_UrlCreateFromPath(void)
     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);
-        ok(ret == TEST_URLFROMPATH[i].ret, "ret %08lx from path %s\n", ret, TEST_URLFROMPATH[i].path);
+        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 %ld from path %s\n", len, 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 %08lx from path L\"%s\", expected %08lx\n",
+        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 == strlenW(ret_urlW), "ret len %ld from path L\"%s\"\n", len, 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);
     }
@@ -571,22 +664,30 @@ static void test_UrlUnescape(void)
     WCHAR *urlW, *expected_urlW; 
     DWORD dwEscaped;
     size_t i;
+    static char inplace[] = "file:///C:/Program%20Files";
+    static WCHAR inplaceW[] = {'f','i','l','e',':','/','/','/','C',':','/',
+                               'P','r','o','g','r','a','m','%','2','0','F','i','l','e','s',0};
 
     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, "UrlEscapeA didn't return 0x%08lx from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
+        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);
         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);
 
         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, "UrlEscapeW didn't return 0x%08lx from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
+        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(strcmpW(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);
+        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");
+
+    dwEscaped = sizeof(inplaceW);
+    ok(UrlUnescapeW(inplaceW, NULL, &dwEscaped, URL_UNESCAPE_INPLACE) == S_OK, "UrlUnescapeW failed unexpectedly\n");
 }
 
 static void test_PathSearchAndQualify(void)
@@ -612,7 +713,7 @@ static void test_PathSearchAndQualify(void)
        "PathSearchAndQualify rets 0\n");
     GetFullPathNameW(c_drive, MAX_PATH, cur_dir, NULL);
     PathAddBackslashW(cur_dir);
-    strcatW(cur_dir, foo);
+    lstrcatW(cur_dir, foo);
     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");    
 
     /* foo */
@@ -620,14 +721,14 @@ static void test_PathSearchAndQualify(void)
        "PathSearchAndQualify rets 0\n");
     GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
     PathAddBackslashW(cur_dir);
-    strcatW(cur_dir, foo);
+    lstrcatW(cur_dir, foo);
     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");    
 
     /* \foo */
     ok(PathSearchAndQualifyW(path3, out, MAX_PATH) != 0,
        "PathSearchAndQualify rets 0\n");
     GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
-    strcpyW(cur_dir + 2, path3);
+    lstrcpyW(cur_dir + 2, path3);
     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
 
     /* win.ini */
@@ -646,24 +747,30 @@ static void test_PathCreateFromUrl(void)
     DWORD len, ret;
     WCHAR ret_pathW[INTERNET_MAX_URL_LENGTH];
     WCHAR *pathW, *urlW;
+    static const char url[] = "http://www.winehq.org";
+
+    /* Check ret_path = NULL */
+    len = sizeof(url);
+    ret = PathCreateFromUrlA(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);
-        ok(ret == TEST_PATHFROMURL[i].ret, "ret %08lx from url %s\n", ret, TEST_PATHFROMURL[i].url);
+        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 %ld from url %s\n", len, 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 %08lx from url L\"%s\"\n", ret, TEST_PATHFROMURL[i].url);
+        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 == strlenW(ret_pathW), "ret len %ld from url L\"%s\"\n", len, 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);
@@ -726,16 +833,16 @@ static void test_PathIsValidCharA(void)
     unsigned int c;
 
     ret = pPathIsValidCharA( 0x7f, 0 );
-    ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\n", (DWORD)ret );
+    ok ( !ret, "PathIsValidCharA succeeded: 0x%08x\n", (DWORD)ret );
 
     ret = pPathIsValidCharA( 0x7f, 1 );
-    ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\n", (DWORD)ret );
+    ok ( !ret, "PathIsValidCharA succeeded: 0x%08x\n", (DWORD)ret );
 
     for (c = 0; c < 0x7f; c++)
     {
         ret = pPathIsValidCharA( c, ~0U );
         ok ( ret == SHELL_charclass[c] || (ret == 1 && SHELL_charclass[c] == 0xffffffff),
-             "PathIsValidCharA failed: 0x%02x got 0x%08lx expected 0x%08lx\n",
+             "PathIsValidCharA failed: 0x%02x got 0x%08x expected 0x%08x\n",
              c, (DWORD)ret, SHELL_charclass[c] );
     }
 
@@ -743,7 +850,7 @@ static void test_PathIsValidCharA(void)
     {
         ret = pPathIsValidCharA( c, ~0U );
         ok ( ret == 0x00000100,
-             "PathIsValidCharA failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+             "PathIsValidCharA failed: 0x%02x got 0x%08x expected 0x00000100\n",
              c, (DWORD)ret );
     }
 }
@@ -751,19 +858,19 @@ static void test_PathIsValidCharA(void)
 static void test_PathIsValidCharW(void)
 {
     BOOL ret;
-    unsigned int c;
+    unsigned int c, err_count = 0;
 
     ret = pPathIsValidCharW( 0x7f, 0 );
-    ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\n", (DWORD)ret );
+    ok ( !ret, "PathIsValidCharW succeeded: 0x%08x\n", (DWORD)ret );
 
     ret = pPathIsValidCharW( 0x7f, 1 );
-    ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\n", (DWORD)ret );
+    ok ( !ret, "PathIsValidCharW succeeded: 0x%08x\n", (DWORD)ret );
 
     for (c = 0; c < 0x7f; c++)
     {
         ret = pPathIsValidCharW( c, ~0U );
         ok ( ret == SHELL_charclass[c] || (ret == 1 && SHELL_charclass[c] == 0xffffffff),
-             "PathIsValidCharW failed: 0x%02x got 0x%08lx expected 0x%08lx\n",
+             "PathIsValidCharW failed: 0x%02x got 0x%08x expected 0x%08x\n",
              c, (DWORD)ret, SHELL_charclass[c] );
     }
 
@@ -771,8 +878,16 @@ static void test_PathIsValidCharW(void)
     {
         ret = pPathIsValidCharW( c, ~0U );
         ok ( ret == 0x00000100,
-             "PathIsValidCharW failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+             "PathIsValidCharW failed: 0x%02x got 0x%08x expected 0x00000100\n",
              c, (DWORD)ret );
+        if (ret != 0x00000100)
+        {
+            if(++err_count > 100 ) {
+                trace("skipping rest of PathIsValidCharW tests "
+                      "because of the current number of errors\n");
+                break;
+            }
+        }
     }
 }
 
@@ -799,10 +914,926 @@ static void test_PathMakePretty(void)
    ok (strcmp(buff, "Test") == 0,  "PathMakePretty: 1st char lowercased %s\n", buff);
 }
 
+static void test_PathMatchSpec(void)
+{
+    static const char file[] = "c:\\foo\\bar\\filename.ext";
+    static const char spec1[] = ".ext";
+    static const char spec2[] = "*.ext";
+    static const char spec3[] = "*.ext ";
+    static const char spec4[] = "  *.ext";
+    static const char spec5[] = "* .ext";
+    static const char spec6[] = "*. ext";
+    static const char spec7[] = "* . ext";
+    static const char spec8[] = "*.e?t";
+    static const char spec9[] = "filename.ext";
+    static const char spec10[] = "*bar\\filename.ext";
+    static const char spec11[] = " foo; *.ext";
+    static const char spec12[] = "*.ext;*.bar";
+    static const char spec13[] = "*bar*";
+
+    ok (PathMatchSpecA(file, spec1) == FALSE, "PathMatchSpec: Spec1 failed\n");
+    ok (PathMatchSpecA(file, spec2) == TRUE, "PathMatchSpec: Spec2 failed\n");
+    ok (PathMatchSpecA(file, spec3) == FALSE, "PathMatchSpec: Spec3 failed\n");
+    ok (PathMatchSpecA(file, spec4) == TRUE, "PathMatchSpec: Spec4 failed\n");
+    todo_wine ok (PathMatchSpecA(file, spec5) == TRUE, "PathMatchSpec: Spec5 failed\n");
+    todo_wine ok (PathMatchSpecA(file, spec6) == TRUE, "PathMatchSpec: Spec6 failed\n");
+    ok (PathMatchSpecA(file, spec7) == FALSE, "PathMatchSpec: Spec7 failed\n");
+    ok (PathMatchSpecA(file, spec8) == TRUE, "PathMatchSpec: Spec8 failed\n");
+    ok (PathMatchSpecA(file, spec9) == FALSE, "PathMatchSpec: Spec9 failed\n");
+    ok (PathMatchSpecA(file, spec10) == TRUE, "PathMatchSpec: Spec10 failed\n");
+    ok (PathMatchSpecA(file, spec11) == TRUE, "PathMatchSpec: Spec11 failed\n");
+    ok (PathMatchSpecA(file, spec12) == TRUE, "PathMatchSpec: Spec12 failed\n");
+    ok (PathMatchSpecA(file, spec13) == TRUE, "PathMatchSpec: Spec13 failed\n");
+}
+
+static void test_PathCombineW(void)
+{
+    LPWSTR wszString, wszString2;
+    WCHAR wbuf[MAX_PATH+1], wstr1[MAX_PATH] = {'C',':','\\',0}, wstr2[MAX_PATH];
+    static const WCHAR expout[] = {'C',':','\\','A','A',0};
+    int i;
+   
+    wszString2 = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+
+    /* NULL test */
+    wszString = pPathCombineW(NULL, NULL, NULL);
+    ok (wszString == NULL, "Expected a NULL return\n");
+
+    /* Some NULL */
+    wszString2[0] = 'a';
+    wszString = pPathCombineW(wszString2, NULL, NULL);
+    ok (wszString == NULL, "Expected a NULL return\n");
+    ok (wszString2[0] == 0, "Destination string not empty\n");
+
+    HeapFree(GetProcessHeap(), 0, wszString2);
+
+    /* overflow test */
+    wstr2[0] = wstr2[1] = wstr2[2] = 'A';
+    for (i=3; i<MAX_PATH/2; i++)
+        wstr1[i] = wstr2[i] = 'A';
+    wstr1[(MAX_PATH/2) - 1] = wstr2[MAX_PATH/2] = 0;
+    memset(wbuf, 0xbf, sizeof(wbuf));
+
+    wszString = pPathCombineW(wbuf, wstr1, wstr2);
+    ok(wszString == NULL, "Expected a NULL return\n");
+    ok(wbuf[0] == 0, "Buffer contains data\n");
+
+    /* PathCombineW can be used in place */
+    wstr1[3] = 0;
+    wstr2[2] = 0;
+    ok(PathCombineW(wstr1, wstr1, wstr2) == wstr1, "Expected a wstr1 return\n");
+    ok(StrCmpW(wstr1, expout) == 0, "Unexpected PathCombine output\n");
+}
+
+
+#define LONG_LEN (MAX_PATH * 2)
+#define HALF_LEN (MAX_PATH / 2 + 1)
+
+static void test_PathCombineA(void)
+{
+    LPSTR str;
+    char dest[MAX_PATH];
+    char too_long[LONG_LEN];
+    char one[HALF_LEN], two[HALF_LEN];
+
+    /* try NULL dest */
+    SetLastError(0xdeadbeef);
+    str = PathCombineA(NULL, "C:\\", "one\\two\\three");
+    ok(str == NULL, "Expected NULL, got %p\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try NULL dest and NULL directory */
+    SetLastError(0xdeadbeef);
+    str = PathCombineA(NULL, NULL, "one\\two\\three");
+    ok(str == NULL, "Expected NULL, got %p\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try all NULL*/
+    SetLastError(0xdeadbeef);
+    str = PathCombineA(NULL, NULL, NULL);
+    ok(str == NULL, "Expected NULL, got %p\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try NULL file part */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", NULL);
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\"), "Expected C:\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try empty file part */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\"), "Expected C:\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try empty directory and file part */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "", "");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "\\"), "Expected \\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try NULL directory */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, NULL, "one\\two\\three");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "one\\two\\three"), "Expected one\\two\\three, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try NULL directory and empty file part */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, NULL, "");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "\\"), "Expected \\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try NULL directory and file part */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, NULL, NULL);
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(lstrlenA(dest) == 0, "Expected 0 length, got %i\n", lstrlenA(dest));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try directory without backslash */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:", "one\\two\\three");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try directory with backslash */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "one\\two\\three");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try directory with backslash and file with prepended backslash */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "\\one\\two\\three");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try previous test, with backslash appended as well */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "\\one\\two\\three\\");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a relative directory */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "relative\\dir", "\\one\\two\\three\\");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "one\\two\\three\\"), "Expected one\\two\\three\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try forward slashes */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "one/two/three\\");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one/two/three\\"), "Expected one/two/three\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a really weird directory */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\/\\/", "\\one\\two\\three\\");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try periods */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "one\\..\\two\\.\\three");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\two\\three"), "Expected C:\\two\\three, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try .. as file */
+    /* try forward slashes */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", "..");
+    ok(str == dest, "Expected str == dest, got %p\n", str);
+    ok(!lstrcmp(str, "C:\\"), "Expected C:\\, got %s\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+
+    /* try a file longer than MAX_PATH */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, "C:\\", too_long);
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(lstrlenA(dest) == 0, "Expected 0 length, got %i\n", lstrlenA(dest));
+    todo_wine ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a directory longer than MAX_PATH */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, too_long, "one\\two\\three");
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(lstrlenA(dest) == 0, "Expected 0 length, got %i\n", lstrlenA(dest));
+    todo_wine ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    memset(one, 'b', HALF_LEN);
+    memset(two, 'c', HALF_LEN);
+    one[HALF_LEN - 1] = '\0';
+    two[HALF_LEN - 1] = '\0';
+
+    /* destination string is longer than MAX_PATH, but not the constituent parts */
+    SetLastError(0xdeadbeef);
+    lstrcpyA(dest, "control");
+    str = PathCombineA(dest, one, two);
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(lstrlenA(dest) == 0, "Expected 0 length, got %i\n", lstrlenA(dest));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathAddBackslash(void)
+{
+    LPSTR str;
+    char path[MAX_PATH];
+    char too_long[LONG_LEN];
+
+    /* try a NULL path */
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(NULL);
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try an empty path */
+    path[0] = '\0';
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(path);
+    ok(str == (path + lstrlenA(path)), "Expected str to point to end of path, got %p\n", str);
+    ok(lstrlenA(path) == 0, "Expected empty string, got %i\n", lstrlenA(path));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a relative path */
+    lstrcpyA(path, "one\\two");
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(path);
+    ok(str == (path + lstrlenA(path)), "Expected str to point to end of path, got %p\n", str);
+    ok(!lstrcmp(path, "one\\two\\"), "Expected one\\two\\, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try periods */
+    lstrcpyA(path, "one\\..\\two");
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(path);
+    ok(str == (path + lstrlenA(path)), "Expected str to point to end of path, got %p\n", str);
+    ok(!lstrcmp(path, "one\\..\\two\\"), "Expected one\\..\\two\\, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try just a space */
+    lstrcpyA(path, " ");
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(path);
+    ok(str == (path + lstrlenA(path)), "Expected str to point to end of path, got %p\n", str);
+    ok(!lstrcmp(path, " \\"), "Expected  \\, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* path already has backslash */
+    lstrcpyA(path, "C:\\one\\");
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(path);
+    ok(str == (path + lstrlenA(path)), "Expected str to point to end of path, got %p\n", str);
+    ok(!lstrcmp(path, "C:\\one\\"), "Expected C:\\one\\, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+
+    /* path is longer than MAX_PATH */
+    SetLastError(0xdeadbeef);
+    str = PathAddBackslashA(too_long);
+    ok(str == NULL, "Expected str == NULL, got %p\n", str);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathAppendA(void)
+{
+    char path[MAX_PATH];
+    char too_long[LONG_LEN];
+    char one[HALF_LEN], two[HALF_LEN];
+    BOOL res;
+
+    lstrcpy(path, "C:\\one");
+
+    /* try NULL pszMore */
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, NULL);
+    ok(!res, "Expected failure\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one"), "Expected C:\\one, got %s\n", path);
+
+    /* try empty pszMore */
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one"), "Expected C:\\one, got %s\n", path);
+
+    /* try NULL pszPath */
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(NULL, "two\\three");
+    ok(!res, "Expected failure\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try empty pszPath */
+    path[0] = '\0';
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "two\\three");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "two\\three"), "Expected \\two\\three, got %s\n", path);
+
+    /* try empty pszPath and empty pszMore */
+    path[0] = '\0';
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "\\"), "Expected \\, got %s\n", path);
+
+    /* try legit params */
+    lstrcpy(path, "C:\\one");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "two\\three");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", path);
+
+    /* try pszPath with backslash after it */
+    lstrcpy(path, "C:\\one\\");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "two\\three");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", path);
+
+    /* try pszMore with backslash before it */
+    lstrcpy(path, "C:\\one");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "\\two\\three");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one\\two\\three"), "Expected C:\\one\\two\\three, got %s\n", path);
+
+    /* try pszMore with backslash after it */
+    lstrcpy(path, "C:\\one");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "two\\three\\");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", path);
+
+    /* try spaces in pszPath */
+    lstrcpy(path, "C: \\ one ");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, "two\\three");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C: \\ one \\two\\three"), "Expected C: \\ one \\two\\three, got %s\n", path);
+
+    /* try spaces in pszMore */
+    lstrcpy(path, "C:\\one");
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, " two \\ three ");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(path, "C:\\one\\ two \\ three "), "Expected 'C:\\one\\ two \\ three ', got %s\n", path);
+
+    /* pszPath is too long */
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(too_long, "two\\three");
+    ok(!res, "Expected failure\n");
+    todo_wine ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(lstrlen(too_long) == 0, "Expected length of too_long to be zero, got %i\n", lstrlen(too_long));
+
+    /* pszMore is too long */
+    lstrcpy(path, "C:\\one");
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(path, too_long);
+    ok(!res, "Expected failure\n");
+    todo_wine ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(lstrlen(path) == 0, "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';
+    SetLastError(0xdeadbeef);
+    res = PathAppendA(one, two);
+    ok(!res, "Expected failure\n");
+    ok(lstrlen(one) == 0, "Expected length of one to be zero, got %i\n", lstrlen(one));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathCanonicalizeA(void)
+{
+    char dest[MAX_PATH];
+    char too_long[LONG_LEN];
+    BOOL res;
+
+    /* try a NULL source */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, NULL);
+    ok(!res, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, 
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+    todo_wine
+    {
+        ok(!lstrcmp(dest, "test"), "Expected test, got %s\n", dest);
+    }
+
+    /* try an empty source */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "\\"), "Expected \\, got %s\n", dest);
+
+    /* try a NULL dest */
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(NULL, "C:\\");
+    ok(!res, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, 
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    /* try empty dest */
+    dest[0] = '\0';
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\"), "Expected C:\\, got %s\n", dest);
+
+    /* try non-empty dest */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\"), "Expected C:\\, got %s\n", dest);
+
+    /* try a space for source */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, " ");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, " "), "Expected ' ', got %s\n", dest);
+
+    /* try a relative path */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "one\\two");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "one\\two"), "Expected one\\two, got %s\n", dest);
+
+    /* try current dir and previous dir */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\one\\.\\..\\two\\three\\..");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\two"), "Expected C:\\two, got %s\n", dest);
+
+    /* try simple forward slashes */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\one/two/three\\four/five\\six");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\one/two/three\\four/five\\six"),
+       "Expected C:\\one/two/three\\four/five\\six, got %s\n", dest);
+
+    /* try simple forward slashes with same dir */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\one/.\\two");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\one/.\\two"), "Expected C:\\one/.\\two, got %s\n", dest);
+
+    /* try simple forward slashes with change dir */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\one/.\\two\\..");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\one/."), "Expected C:\\one/., got %s\n", dest);
+
+    /* try forward slashes with change dirs
+     * NOTE: if there is a forward slash in between two backslashes,
+     * everything in between the two backslashes is considered on dir
+     */
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, "C:\\one/.\\..\\two/three\\..\\four/.five");
+    ok(res, "Expected success\n");
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(!lstrcmp(dest, "C:\\four/.five"), "Expected C:\\four/.five, got %s\n", dest);
+
+    /* try src is too long */
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+    lstrcpy(dest, "test");
+    SetLastError(0xdeadbeef);
+    res = PathCanonicalizeA(dest, too_long);
+    todo_wine
+    {
+        ok(!res, "Expected failure\n");
+        ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    }
+    ok(lstrlen(too_long) == LONG_LEN - 1, "Expected length LONG_LEN - 1, got %i\n", lstrlen(too_long));
+}
+
+static void test_PathFindExtensionA(void)
+{
+    LPSTR ext;
+    char path[MAX_PATH];
+    char too_long[LONG_LEN];
+
+    /* try a NULL path */
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(NULL);
+    ok(ext == NULL, "Expected NULL, got %p\n", ext);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try an empty path */
+    path[0] = '\0';
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(path);
+    ok(ext == path, "Expected ext == path, got %p\n", ext);
+    ok(lstrlen(ext) == 0, "Expected length 0, got %i\n", lstrlen(ext));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a path without an extension */
+    lstrcpy(path, "file");
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(path);
+    ok(ext == path + lstrlen(path), "Expected ext == path, got %p\n", ext);
+    ok(lstrlen(ext) == 0, "Expected length 0, got %i\n", lstrlen(ext));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a path with an extension */
+    lstrcpy(path, "file.txt");
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(path);
+    ok(ext == path + lstrlen("file"),
+       "Expected ext == path + lstrlen(\"file\"), got %p\n", ext);
+    ok(!lstrcmp(ext, ".txt"), "Expected .txt, got %s\n", ext);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a path with two extensions */
+    lstrcpy(path, "file.txt.doc");
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(path);
+    ok(ext == path + lstrlen("file.txt"),
+       "Expected ext == path + lstrlen(\"file.txt\"), got %p\n", ext);
+    ok(!lstrcmp(ext, ".doc"), "Expected .txt, got %s\n", ext);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a path longer than MAX_PATH without an extension*/
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(too_long);
+    ok(ext == too_long + LONG_LEN - 1, "Expected ext == too_long + LONG_LEN - 1, got %p\n", ext);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try a path longer than MAX_PATH with an extension*/
+    memset(too_long, 'a', LONG_LEN);
+    too_long[LONG_LEN - 1] = '\0';
+    lstrcpy(too_long + 300, ".abcde");
+    too_long[lstrlen(too_long)] = 'a';
+    SetLastError(0xdeadbeef);
+    ext = PathFindExtensionA(too_long);
+    ok(ext == too_long + 300, "Expected ext == too_long + 300, got %p\n", ext);
+    ok(lstrlen(ext) == LONG_LEN - 301, "Expected LONG_LEN - 301, got %i\n", lstrlen(ext));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathBuildRootA(void)
+{
+    LPSTR root;
+    char path[10];
+    char root_expected[26][4];
+    char drive;
+    int j;
+
+    /* set up the expected paths */
+    for (drive = 'A'; drive <= 'Z'; drive++)
+        sprintf(root_expected[drive - 'A'], "%c:\\", drive);
+
+    /* test the expected values */
+    for (j = 0; j < 26; j++)
+    {
+        SetLastError(0xdeadbeef);
+        lstrcpy(path, "aaaaaaaaa");
+        root = PathBuildRootA(path, j);
+        ok(root == path, "Expected root == path, got %p\n", root);
+        ok(!lstrcmp(root, root_expected[j]), "Expected %s, got %s\n", root_expected[j], root);
+        ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+    }
+
+    /* test a negative drive number */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path, "aaaaaaaaa");
+    root = PathBuildRootA(path, -1);
+    ok(root == path, "Expected root == path, got %p\n", root);
+    ok(!lstrcmp(path, "aaaaaaaaa"), "Expected aaaaaaaaa, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* test a drive number greater than 25 */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path, "aaaaaaaaa");
+    root = PathBuildRootA(path, 26);
+    ok(root == path, "Expected root == path, got %p\n", root);
+    ok(!lstrcmp(path, "aaaaaaaaa"), "Expected aaaaaaaaa, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* length of path is less than 4 */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path, "aa");
+    root = PathBuildRootA(path, 0);
+    ok(root == path, "Expected root == path, got %p\n", root);
+    ok(!lstrcmp(path, "A:\\"), "Expected A:\\, got %s\n", path);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* path is NULL */
+    SetLastError(0xdeadbeef);
+    root = PathBuildRootA(NULL, 0);
+    ok(root == NULL, "Expected root == NULL, got %p\n", root);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathCommonPrefixA(void)
+{
+    char path1[MAX_PATH], path2[MAX_PATH];
+    char out[MAX_PATH];
+    int count;
+
+    /* test NULL path1 */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path2, "C:\\");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(NULL, path2, out);
+    ok(count == 0, "Expected 0, got %i\n", count);
+    todo_wine
+    {
+        ok(!lstrcmp(out, "aaa"), "Expected aaa, got %s\n", out);
+    }
+    ok(!lstrcmp(path2, "C:\\"), "Expected C:\\, got %s\n", path2);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* test NULL path2 */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "C:\\");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, NULL, out);
+    ok(count == 0, "Expected 0, got %i\n", count);
+    todo_wine
+    {
+        ok(!lstrcmp(out, "aaa"), "Expected aaa, got %s\n", out);
+    }
+    ok(!lstrcmp(path1, "C:\\"), "Expected C:\\, got %s\n", path1);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* test empty path1 */
+    SetLastError(0xdeadbeef);
+    path1[0] = '\0';
+    lstrcpy(path2, "C:\\");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 0, "Expected 0, got %i\n", count);
+    ok(lstrlen(out) == 0, "Expected 0 length out, got %i\n", lstrlen(out));
+    ok(lstrlen(path1) == 0, "Expected 0 length path1, got %i\n", lstrlen(path1));
+    ok(!lstrcmp(path2, "C:\\"), "Expected C:\\, got %s\n", path2);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* test empty path1 */
+    SetLastError(0xdeadbeef);
+    path2[0] = '\0';
+    lstrcpy(path1, "C:\\");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 0, "Expected 0, got %i\n", count);
+    ok(lstrlen(out) == 0, "Expected 0 length out, got %i\n", lstrlen(out));
+    ok(lstrlen(path2) == 0, "Expected 0 length path2, got %i\n", lstrlen(path2));
+    ok(!lstrcmp(path1, "C:\\"), "Expected C:\\, got %s\n", path1);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* paths are legit, out is NULL */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "C:\\");
+    lstrcpy(path2, "C:\\");
+    count = PathCommonPrefixA(path1, path2, NULL);
+    ok(count == 3, "Expected 3, got %i\n", count);
+    ok(!lstrcmp(path1, "C:\\"), "Expected C:\\, got %s\n", path1);
+    ok(!lstrcmp(path2, "C:\\"), "Expected C:\\, got %s\n", path2);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* all parameters legit */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "C:\\");
+    lstrcpy(path2, "C:\\");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 3, "Expected 3, got %i\n", count);
+    ok(!lstrcmp(path1, "C:\\"), "Expected C:\\, got %s\n", path1);
+    ok(!lstrcmp(path2, "C:\\"), "Expected C:\\, got %s\n", path2);
+    ok(!lstrcmp(out, "C:\\"), "Expected C:\\, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* path1 and path2 not the same, but common prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "C:\\one\\two");
+    lstrcpy(path2, "C:\\one\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 6, "Expected 6, got %i\n", count);
+    ok(!lstrcmp(path1, "C:\\one\\two"), "Expected C:\\one\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "C:\\one\\three"), "Expected C:\\one\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "C:\\one"), "Expected C:\\one, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try . prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\.two");
+    lstrcpy(path2, "one\\.three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 3, "Expected 3, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\.two"), "Expected one\\.two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\.three"), "Expected one\\.three, got %s\n", path2);
+    ok(!lstrcmp(out, "one"), "Expected one, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try .. prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\..two");
+    lstrcpy(path2, "one\\..three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 3, "Expected 3, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\..two"), "Expected one\\..two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\..three"), "Expected one\\..three, got %s\n", path2);
+    ok(!lstrcmp(out, "one"), "Expected one, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try ... prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\...two");
+    lstrcpy(path2, "one\\...three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 3, "Expected 3, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\...two"), "Expected one\\...two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\...three"), "Expected one\\...three, got %s\n", path2);
+    ok(!lstrcmp(out, "one"), "Expected one, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try .\ prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\.\\two");
+    lstrcpy(path2, "one\\.\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 5, "Expected 5, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\.\\two"), "Expected one\\.\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\.\\three"), "Expected one\\.\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "one\\."), "Expected one\\., got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try ..\ prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\..\\two");
+    lstrcpy(path2, "one\\..\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 6, "Expected 6, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\..\\two"), "Expected one\\..\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\..\\three"), "Expected one\\..\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "one\\.."), "Expected one\\.., got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try ...\\ prefix */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\...\\two");
+    lstrcpy(path2, "one\\...\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 7, "Expected 7, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\...\\two"), "Expected one\\...\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\...\\three"), "Expected one\\...\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "one\\..."), "Expected one\\..., got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try prefix that is not an msdn labeled prefix type */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "same");
+    lstrcpy(path2, "same");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 4, "Expected 4, got %i\n", count);
+    ok(!lstrcmp(path1, "same"), "Expected same, got %s\n", path1);
+    ok(!lstrcmp(path2, "same"), "Expected same, got %s\n", path2);
+    ok(!lstrcmp(out, "same"), "Expected same, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try . after directory */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\mid.\\two");
+    lstrcpy(path2, "one\\mid.\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 8, "Expected 8, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\mid.\\two"), "Expected one\\mid.\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\mid.\\three"), "Expected one\\mid.\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "one\\mid."), "Expected one\\mid., got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try . in the middle of a directory */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\mid.end\\two");
+    lstrcpy(path2, "one\\mid.end\\three");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 11, "Expected 11, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\mid.end\\two"), "Expected one\\mid.end\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "one\\mid.end\\three"), "Expected one\\mid.end\\three, got %s\n", path2);
+    ok(!lstrcmp(out, "one\\mid.end"), "Expected one\\mid.end, got %s\n", out);
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* try comparing a .. with the expanded path */
+    SetLastError(0xdeadbeef);
+    lstrcpy(path1, "one\\..\\two");
+    lstrcpy(path2, "two");
+    lstrcpy(out, "aaa");
+    count = PathCommonPrefixA(path1, path2, out);
+    ok(count == 0, "Expected 0, got %i\n", count);
+    ok(!lstrcmp(path1, "one\\..\\two"), "Expected one\\..\\two, got %s\n", path1);
+    ok(!lstrcmp(path2, "two"), "Expected two, got %s\n", path2);
+    ok(lstrlen(out) == 0, "Expected 0 length out, got %i\n", lstrlen(out));
+    ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
+}
+
+static void test_PathUnquoteSpaces(void)
+{
+    int i;
+    for(i = 0; i < sizeof(TEST_PATH_UNQUOTE_SPACES) / sizeof(TEST_PATH_UNQUOTE_SPACES[0]); i++)
+    {
+        char *path = strdupA(TEST_PATH_UNQUOTE_SPACES[i].path);
+        WCHAR *pathW = GetWideString(TEST_PATH_UNQUOTE_SPACES[i].path);
+        WCHAR *resultW = GetWideString(TEST_PATH_UNQUOTE_SPACES[i].result);
+
+        PathUnquoteSpacesA(path);
+        ok(!strcmp(path, TEST_PATH_UNQUOTE_SPACES[i].result), "%s (A): got %s expected %s\n",
+           TEST_PATH_UNQUOTE_SPACES[i].path, path,
+           TEST_PATH_UNQUOTE_SPACES[i].result);
+
+        PathUnquoteSpacesW(pathW);
+        ok(!lstrcmpW(pathW, resultW), "%s (W): strings differ\n",
+           TEST_PATH_UNQUOTE_SPACES[i].path);
+        FreeWideString(pathW);
+        FreeWideString(resultW);
+        HeapFree(GetProcessHeap(), 0, path);
+    }
+}
+
 START_TEST(path)
 {
-  hShlwapi = LoadLibraryA("shlwapi.dll");
-  if (!hShlwapi) return;
+  hShlwapi = GetModuleHandleA("shlwapi.dll");
 
   test_UrlHash();
   test_UrlGetPart();
@@ -816,8 +1847,10 @@ START_TEST(path)
   test_PathSearchAndQualify();
   test_PathCreateFromUrl();
   test_PathIsUrl();
-  
+
+  test_PathAddBackslash();
   test_PathMakePretty();
+  test_PathMatchSpec();
 
   /* For whatever reason, PathIsValidCharA and PathAppendA share the same
    * ordinal number in some native versions. Check this to prevent a crash.
@@ -830,4 +1863,16 @@ START_TEST(path)
      pPathIsValidCharW = (void*)GetProcAddress(hShlwapi, (LPSTR)456);
      if (pPathIsValidCharW) test_PathIsValidCharW();
   }
+
+  pPathCombineW = (void*)GetProcAddress(hShlwapi, "PathCombineW");
+  if (pPathCombineW)
+    test_PathCombineW();
+
+  test_PathCombineA();
+  test_PathAppendA();
+  test_PathCanonicalizeA();
+  test_PathFindExtensionA();
+  test_PathBuildRootA();
+  test_PathCommonPrefixA();
+  test_PathUnquoteSpaces();
 }
index 5c23c6d..f2d8ce1 100644 (file)
@@ -1,15 +1,23 @@
 <module name="shlwapi_winetest" type="win32cui" installbase="bin" installname="shlwapi_winetest.exe" allowwarnings="true">
-    <include base="shlwapi_winetest">.</include>
-    <define name="__USE_W32API" />
-    <library>ntdll</library>
-    <library>shlwapi</library>
-    <library>ole32</library>
-    <library>oleaut32</library>
-    <library>kernel32</library>
-    <library>advapi32</library>
-    <file>clist.c</file>
-    <file>ordinal.c</file>
-    <file>shreg.c</file>
-    <file>string.c</file>
-    <file>testlist.c</file>
+       <include base="shlwapi_winetest">.</include>
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>shlwapi</library>
+       <library>advapi32</library>
+       <library>ole32</library>
+       <library>oleaut32</library>
+       <library>kernel32</library>
+       <library>uuid</library>
+       <library>ntdll</library>
+       <file>clist.c</file>
+       <file>clsid.c</file>
+       <file>generated.c</file>
+       <file>ordinal.c</file>
+       <file>path.c</file>
+       <file>shreg.c</file>
+       <file>string.c</file>
+       <file>testlist.c</file>
 </module>
index d8d4d92..77a47e2 100755 (executable)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <assert.h>
@@ -39,21 +39,21 @@ static SHCopyKeyA_func pSHCopyKeyA;
 typedef DWORD (WINAPI *SHRegGetPathA_func)(HKEY,LPCSTR,LPCSTR,LPSTR,DWORD);
 static SHRegGetPathA_func pSHRegGetPathA;
 
-static const char * sTestpath1 = "%LONGSYSTEMVAR%\\subdir1";
-static const char * sTestpath2 = "%FOO%\\subdir1";
+static char sTestpath1[] = "%LONGSYSTEMVAR%\\subdir1";
+static char sTestpath2[] = "%FOO%\\subdir1";
 
 static const char * sEnvvar1 = "bar";
 static const char * sEnvvar2 = "ImARatherLongButIndeedNeededString";
 
 static char sExpTestpath1[MAX_PATH];
 static char sExpTestpath2[MAX_PATH];
-static unsigned sExpLen1;
-static unsigned sExpLen2;
+static DWORD nExpLen1;
+static DWORD nExpLen2;
 
 static const char * sEmptyBuffer ="0123456789";
 
 /* delete key and all its subkeys */
-static DWORD delete_key( HKEY hkey, LPSTR parent, LPSTR keyname )
+static DWORD delete_key( HKEY hkey, LPCSTR parent, LPCSTR keyname )
 {
     HKEY parentKey;
     DWORD ret;
@@ -75,12 +75,13 @@ static HKEY create_test_entries(void)
 {
        HKEY hKey;
         DWORD ret;
+        DWORD nExpectedLen1, nExpectedLen2;
 
         SetEnvironmentVariableA("LONGSYSTEMVAR", sEnvvar1);
         SetEnvironmentVariableA("FOO", sEnvvar2);
 
         ret = RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY, &hKey);
-       ok( ERROR_SUCCESS == ret, "RegCreateKeyA failed, ret=%lu\n", ret);
+       ok( ERROR_SUCCESS == ret, "RegCreateKeyA failed, ret=%u\n", ret);
 
        if (hKey)
        {
@@ -89,15 +90,24 @@ static HKEY create_test_entries(void)
            ok(!RegSetValueExA(hKey,"Test3",0,REG_EXPAND_SZ, (LPBYTE) sTestpath2, strlen(sTestpath2)+1), "RegSetValueExA failed\n");
        }
 
-       sExpLen1 = ExpandEnvironmentStringsA(sTestpath1, sExpTestpath1, sizeof(sExpTestpath1));
-       sExpLen2 = ExpandEnvironmentStringsA(sTestpath2, sExpTestpath2, sizeof(sExpTestpath2));
+       nExpLen1 = ExpandEnvironmentStringsA(sTestpath1, sExpTestpath1, sizeof(sExpTestpath1));
+       nExpLen2 = ExpandEnvironmentStringsA(sTestpath2, sExpTestpath2, sizeof(sExpTestpath2));
 
-        ok(sExpLen1 > 0, "Couldn't expand %s\n", sTestpath1);
-        trace("sExplen1 = (%d)\n", sExpLen1);
-        ok(sExpLen2 > 0, "Couldn't expand %s\n", sTestpath2);
-        trace("sExplen2 = (%d)\n", sExpLen2);
+       nExpectedLen1 = strlen(sTestpath1) - strlen("%LONGSYSTEMVAR%") + strlen(sEnvvar1) + 1;
+       nExpectedLen2 = strlen(sTestpath2) - strlen("%FOO%") + strlen(sEnvvar2) + 1;
+       /* ExpandEnvironmentStringsA on NT4 returns 2x the correct result */
+       trace("sExplen1 = (%d)\n", nExpLen1);
+       if (nExpectedLen1 != nExpLen1)
+            trace( "Expanding %s failed (expected %d) - known bug in NT4\n", sTestpath1, nExpectedLen1 );
 
-        return hKey;
+        trace("sExplen2 = (%d)\n", nExpLen2);
+       if (nExpectedLen2 != nExpLen2)
+            trace( "Expanding %s failed (expected %d) - known bug in NT4\n", sTestpath2, nExpectedLen2 );      
+
+       /* Make sure we carry on with correct values */
+       nExpLen1 = nExpectedLen1; 
+       nExpLen2 = nExpectedLen2;
+       return hKey;
 }
 
 static void test_SHGetValue(void)
@@ -111,17 +121,17 @@ static void test_SHGetValue(void)
        dwSize = MAX_PATH;
        dwType = -1;
         dwRet = SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", &dwType, buf, &dwSize);
-       ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%lu\n", dwRet);
+       ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%u\n", dwRet);
        ok( 0 == strcmp(sExpTestpath1, buf), "Comparing of (%s) with (%s) failed\n", buf, sExpTestpath1);
-       ok( REG_SZ == dwType, "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( REG_SZ == dwType, "Expected REG_SZ, got (%u)\n", dwType);
 
        strcpy(buf, sEmptyBuffer);
        dwSize = MAX_PATH;
        dwType = -1;
         dwRet = SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", &dwType, buf, &dwSize);
-       ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%lu\n", dwRet);
+       ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%u\n", dwRet);
        ok( 0 == strcmp(sTestpath1, buf) , "Comparing of (%s) with (%s) failed\n", buf, sTestpath1);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 }
 
 static void test_SHGetRegPath(void)
@@ -134,7 +144,7 @@ static void test_SHGetRegPath(void)
 
        strcpy(buf, sEmptyBuffer);
         dwRet = (*pSHRegGetPathA)(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", buf, 0);
-       ok( ERROR_SUCCESS == dwRet, "SHRegGetPathA failed, ret=%lu\n", dwRet);
+       ok( ERROR_SUCCESS == dwRet, "SHRegGetPathA failed, ret=%u\n", dwRet);
        ok( 0 == strcmp(sExpTestpath1, buf) , "Comparing (%s) with (%s) failed\n", buf, sExpTestpath1);
 }
 
@@ -150,7 +160,7 @@ static void test_SHQUeryValueEx(void)
 
         sTestedFunction = "RegOpenKeyExA";
         dwRet = RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_KEY, 0,  KEY_QUERY_VALUE, &hKey);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
 
        /****** SHQueryValueExA ******/
 
@@ -161,15 +171,15 @@ static void test_SHQUeryValueEx(void)
         * Case 1.1 All arguments are NULL
         */
         dwRet = SHQueryValueExA( hKey, "Test1", NULL, NULL, NULL, NULL);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
 
        /*
         * Case 1.2 dwType is set
         */
        dwType = -1;
         dwRet = SHQueryValueExA( hKey, "Test1", NULL, &dwType, NULL, NULL);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
        /*
         * dwSize is set
@@ -177,16 +187,16 @@ static void test_SHQUeryValueEx(void)
         */
        dwSize = 6;
         dwRet = SHQueryValueExA( hKey, "Test1", NULL, NULL, NULL, &dwSize);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
-       ok( dwSize == nUsedBuffer1, "Buffer sizes (%lu) and (%lu) are not equal\n", dwSize, nUsedBuffer1);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
+       ok( dwSize == nUsedBuffer1, "Buffer sizes (%u) and (%u) are not equal\n", dwSize, nUsedBuffer1);
 
        /*
          * dwExpanded > dwUnExpanded
         */
        dwSize = 6;
         dwRet = SHQueryValueExA( hKey, "Test3", NULL, NULL, NULL, &dwSize);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
-       ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
+       ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2);
 
        /*
         * Case 1 string shrinks during expanding
@@ -195,63 +205,65 @@ static void test_SHQUeryValueEx(void)
        dwSize = 6;
        dwType = -1;
        dwRet = SHQueryValueExA( hKey, "Test1", NULL, &dwType, buf, &dwSize);
-       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%u)\n", dwRet);
        ok( 0 == strcmp(sEmptyBuffer, buf) , "Comparing (%s) with (%s) failed\n", buf, sEmptyBuffer);
-       ok( dwSize == nUsedBuffer1, "Buffer sizes (%lu) and (%lu) are not equal\n", dwSize, nUsedBuffer1);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( dwSize == nUsedBuffer1, "Buffer sizes (%u) and (%u) are not equal\n", dwSize, nUsedBuffer1);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
        /*
         * string grows during expanding
-         * dwSize is smaller then the size of the unexpanded string
+         * dwSize is smaller than the size of the unexpanded string
         */
        strcpy(buf, sEmptyBuffer);
        dwSize = 6;
        dwType = -1;
        dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
-       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%u)\n", dwRet);
        ok( 0 == strcmp(sEmptyBuffer, buf) , "Comparing (%s) with (%s) failed\n", buf, sEmptyBuffer);
-       ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
         /*
          * string grows during expanding
-         * dwSize is larger then the size of the unexpanded string but smaller than the part before the backslash
-         * if the unexpanded string fits into the buffer it can get cut when expanded
+         * dwSize is larger than the size of the unexpanded string, but
+         * smaller than the part before the backslash. If the unexpanded
+         * string fits into the buffer, it can get cut when expanded.
          */
         strcpy(buf, sEmptyBuffer);
         dwSize = strlen(sEnvvar2) - 2;
         dwType = -1;
         dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
-        ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+        ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%u)\n", dwRet);
 
         todo_wine
         {
-                ok( (0 == strcmp("", buf)) | (0 == strcmp(sTestpath2, buf)),
+                ok( (0 == strcmp("", buf)) || (0 == strcmp(sTestpath2, buf)),
                     "Expected empty or unexpanded string (win98), got (%s)\n", buf); 
         }
 
-        ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
-        ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+        ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2);
+        ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
        /*
          * string grows during expanding
-         * dwSize is larger then the size of the part before the backslash but smaller then the expanded string
-        * if the unexpanded string fits into the buffer it can get cut when expanded
+         * dwSize is larger than the size of the part before the backslash,
+         * but smaller than the expanded string. If the unexpanded string fits
+         * into the buffer, it can get cut when expanded.
         */
        strcpy(buf, sEmptyBuffer);
-       dwSize = sExpLen2 - 4;
+       dwSize = nExpLen2 - 4;
        dwType = -1;
         dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
-       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%u)\n", dwRet);
 
         todo_wine
         {
-                ok( (0 == strcmp("", buf)) | (0 == strcmp(sEnvvar2, buf)),
+            ok( (0 == strcmp("", buf)) || (0 == strcmp(sEnvvar2, buf)),
                     "Expected empty or first part of the string \"%s\", got \"%s\"\n", sEnvvar2, buf);
         }
 
-       ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
        /*
         * The buffer is NULL but the size is set
@@ -260,9 +272,9 @@ static void test_SHQUeryValueEx(void)
        dwSize = 6;
        dwType = -1;
        dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, NULL, &dwSize);
-       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
-       ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
-       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+       ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%u\n", sTestedFunction, dwRet);
+       ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%u)\n", dwType);
 
        RegCloseKey(hKey);
 }
@@ -284,7 +296,7 @@ static void test_SHCopyKey(void)
         dwRet = RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst);
         if (dwRet || !hKeyDst)
        {
-                ok( 0, "Destination couldn't be created, RegCreateKeyA returned (%lu)\n", dwRet);
+                ok( 0, "Destination couldn't be created, RegCreateKeyA returned (%u)\n", dwRet);
                return;
        }
 
@@ -292,7 +304,7 @@ static void test_SHCopyKey(void)
         dwRet = RegOpenKeyA(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, &hKeySrc);
         if (dwRet || !hKeySrc)
        {
-                ok( 0, "Source couldn't be opened, RegOpenKeyA returned (%lu)\n", dwRet);
+                ok( 0, "Source couldn't be opened, RegOpenKeyA returned (%u)\n", dwRet);
                return;
        }
 
@@ -300,7 +312,7 @@ static void test_SHCopyKey(void)
        if (pSHCopyKeyA)
         {
                 dwRet = (*pSHCopyKeyA)(hKeySrc, NULL, hKeyDst, 0);
-                ok ( ERROR_SUCCESS == dwRet, "Copy failed, ret=(%lu)\n", dwRet);
+                ok ( ERROR_SUCCESS == dwRet, "Copy failed, ret=(%u)\n", dwRet);
         }
 
        RegCloseKey(hKeySrc);
@@ -311,7 +323,7 @@ static void test_SHCopyKey(void)
         dwRet = RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination\\Setup", &hKeyDst);
         if (dwRet || !hKeyDst)
        {
-                ok ( 0, "Copy couldn't be opened, RegOpenKeyA returned (%lu)\n", dwRet);
+                ok ( 0, "Copy couldn't be opened, RegOpenKeyA returned (%u)\n", dwRet);
                return;
        }
 
@@ -352,7 +364,7 @@ static void test_SHDeleteKey(void)
     {
 
         dwRet = SHDeleteKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC");
-        ok ( ERROR_SUCCESS == dwRet, "SHDeleteKey failed, ret=(%lu)\n", dwRet);
+        ok ( ERROR_SUCCESS == dwRet, "SHDeleteKey failed, ret=(%u)\n", dwRet);
 
         dwRet = RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC", &hKeyS);
         ok ( ERROR_FILE_NOT_FOUND == dwRet, "SHDeleteKey did not delete\n");
@@ -371,11 +383,8 @@ START_TEST(shreg)
         if (!hkey) return;
 
        hshlwapi = GetModuleHandleA("shlwapi.dll");
-       if (hshlwapi)
-       {
-               pSHCopyKeyA=(SHCopyKeyA_func)GetProcAddress(hshlwapi,"SHCopyKeyA");
-               pSHRegGetPathA=(SHRegGetPathA_func)GetProcAddress(hshlwapi,"SHRegGetPathA");
-       }
+        pSHCopyKeyA=(SHCopyKeyA_func)GetProcAddress(hshlwapi,"SHCopyKeyA");
+        pSHRegGetPathA=(SHRegGetPathA_func)GetProcAddress(hshlwapi,"SHRegGetPathA");
        test_SHGetValue();
        test_SHQUeryValueEx();
        test_SHGetRegPath();
index 47d9a6c..895d767 100755 (executable)
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdio.h>
 
-#define NONAMELESSUNION
-#define NONAMELESSSTRUCT
 #include "wine/test.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "shlwapi.h"
 #include "shtypes.h"
 
+#define expect_eq(expr, val, type, fmt) do { \
+    type ret = expr; \
+    ok(ret == val, "Unexpected value of '" #expr "': " #fmt " instead of " #val "\n", ret); \
+} while (0);
+
 static HMODULE hShlwapi;
 static LPSTR   (WINAPI *pStrCpyNXA)(LPSTR,LPCSTR,int);
 static LPWSTR  (WINAPI *pStrCpyNXW)(LPWSTR,LPCWSTR,int);
@@ -43,7 +46,7 @@ static BOOL    (WINAPI *pIntlStrEqWorkerA)(BOOL,LPCSTR,LPCSTR,int);
 static BOOL    (WINAPI *pStrIsIntlEqualW)(BOOL,LPCWSTR,LPCWSTR,int);
 static BOOL    (WINAPI *pIntlStrEqWorkerW)(BOOL,LPCWSTR,LPCWSTR,int);
 
-static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
+static int strcmpW(const WCHAR *str1, const WCHAR *str2)
 {
     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
     return *str1 - *str2;
@@ -492,7 +495,7 @@ static void test_StrFormatByteSize64A(void)
     StrFormatByteSize64A(result->value, szBuff, 256);
 
     ok(!strcmp(result->byte_size_64, szBuff),
-        "Formatted %lx%08lx wrong: got %s, expected %s\n",
+        "Formatted %x%08x wrong: got %s, expected %s\n",
        (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->byte_size_64);
 
     result++;
@@ -510,7 +513,7 @@ static void test_StrFormatKBSizeW(void)
     StrFormatKBSizeW(result->value, szBuffW, 256);
     WideCharToMultiByte(0,0,szBuffW,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR),0,0);
     ok(!strcmp(result->kb_size, szBuff),
-        "Formatted %lx%08lx wrong: got %s, expected %s\n",
+        "Formatted %x%08x wrong: got %s, expected %s\n",
        (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
     result++;
   }
@@ -526,7 +529,7 @@ static void test_StrFormatKBSizeA(void)
     StrFormatKBSizeA(result->value, szBuff, 256);
 
     ok(!strcmp(result->kb_size, szBuff),
-        "Formatted %lx%08lx wrong: got %s, expected %s\n",
+        "Formatted %x%08x wrong: got %s, expected %s\n",
        (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
     result++;
   }
@@ -541,7 +544,7 @@ static void test_StrFromTimeIntervalA(void)
   {
     StrFromTimeIntervalA(szBuff, 256, result->ms, result->digits);
 
-    ok(!strcmp(result->time_interval, szBuff), "Formatted %ld %d wrong\n",
+    ok(!strcmp(result->time_interval, szBuff), "Formatted %d %d wrong\n",
        result->ms, result->digits);
     result++;
   }
@@ -602,7 +605,7 @@ static void test_StrCmpW(void)
 static WCHAR *CoDupStrW(const char* src)
 {
   INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
-  WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
+  WCHAR* szTemp = CoTaskMemAlloc(len * sizeof(WCHAR));
   MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
   return szTemp;
 }
@@ -619,28 +622,28 @@ static void test_StrRetToBSTR(void)
     if (!pStrRetToBSTR) return;
 
     strret.uType = STRRET_WSTR;
-    strret.u.pOleStr = CoDupStrW("Test");
+    U(strret).pOleStr = CoDupStrW("Test");
     bstr = 0;
     ret = pStrRetToBSTR(&strret, NULL, &bstr);
     ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
-       "STRRET_WSTR: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+       "STRRET_WSTR: dup failed, ret=0x%08x, bstr %p\n", ret, bstr);
     if (bstr)
       SysFreeString(bstr);
 
     strret.uType = STRRET_CSTR;
-    lstrcpyA(strret.u.cStr, "Test");
+    lstrcpyA(U(strret).cStr, "Test");
     ret = pStrRetToBSTR(&strret, NULL, &bstr);
     ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
-       "STRRET_CSTR: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+       "STRRET_CSTR: dup failed, ret=0x%08x, bstr %p\n", ret, bstr);
     if (bstr)
       SysFreeString(bstr);
 
     strret.uType = STRRET_OFFSET;
-    strret.u.uOffset = 1;
+    U(strret).uOffset = 1;
     strcpy((char*)&iidl, " Test");
     ret = pStrRetToBSTR(&strret, iidl, &bstr);
     ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
-       "STRRET_OFFSET: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+       "STRRET_OFFSET: dup failed, ret=0x%08x, bstr %p\n", ret, bstr);
     if (bstr)
       SysFreeString(bstr);
 
@@ -683,6 +686,45 @@ static void test_StrCpyNXW(void)
        dest + 5, lpszRes, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
 }
 
+#define check_strrstri(type, str, pos, needle, exp) \
+    ret##type = StrRStrI##type(str, str+pos, needle); \
+    ok(ret##type == (exp), "Type " #type ", expected %p but got %p (string base %p)\n", \
+    (exp), ret##type, str);
+
+static void test_StrRStrI(void)
+{
+    static const CHAR szTest[] = "yAxxxxAy";
+    static const CHAR szTest2[] = "ABABABAB";
+    static const WCHAR wszTest[] = {'y','A','x','x','x','x','A','y',0};
+    static const WCHAR wszTest2[] = {'A','B','A','B','A','B','A','B',0};
+
+    static const WCHAR wszPattern1[] = {'A',0};
+    static const WCHAR wszPattern2[] = {'a','X',0};
+    static const WCHAR wszPattern3[] = {'A','y',0};
+    static const WCHAR wszPattern4[] = {'a','b',0};
+    LPWSTR retW;
+    LPSTR retA;
+    
+    check_strrstri(A, szTest, 4, "A", szTest+1);
+    check_strrstri(A, szTest, 4, "aX", szTest+1);
+    check_strrstri(A, szTest, 4, "Ay", NULL);
+    check_strrstri(W, wszTest, 4, wszPattern1, wszTest+1);
+    check_strrstri(W, wszTest, 4, wszPattern2, wszTest+1);
+    check_strrstri(W, wszTest, 4, wszPattern3, NULL);
+
+    check_strrstri(A, szTest2, 4, "ab", szTest2+2);
+    check_strrstri(A, szTest2, 3, "ab", szTest2+2);
+    check_strrstri(A, szTest2, 2, "ab", szTest2);
+    check_strrstri(A, szTest2, 1, "ab", szTest2);
+    check_strrstri(A, szTest2, 0, "ab", NULL);
+    check_strrstri(W, wszTest2, 4, wszPattern4, wszTest2+2);
+    check_strrstri(W, wszTest2, 3, wszPattern4, wszTest2+2);
+    check_strrstri(W, wszTest2, 2, wszPattern4, wszTest2);
+    check_strrstri(W, wszTest2, 1, wszPattern4, wszTest2);
+    check_strrstri(W, wszTest2, 0, wszPattern4, NULL);
+
+}
+
 static void test_SHAnsiToAnsi(void)
 {
   char dest[8];
@@ -695,7 +737,7 @@ static void test_SHAnsiToAnsi(void)
   memset(dest, '\n', sizeof(dest));
   dwRet = pSHAnsiToAnsi("hello", dest, sizeof(dest)/sizeof(dest[0]));
   ok(dwRet == 6 && !memcmp(dest, "hello\0\n\n", sizeof(dest)),
-     "SHAnsiToAnsi: expected 6, \"hello\\0\\n\\n\", got %ld, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+     "SHAnsiToAnsi: expected 6, \"hello\\0\\n\\n\", got %d, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
      dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
 }
 
@@ -714,17 +756,79 @@ static void test_SHUnicodeToUnicode(void)
   memcpy(dest, lpInit, sizeof(lpInit));
   dwRet = pSHUnicodeToUnicode(lpSrc, dest, sizeof(dest)/sizeof(dest[0]));
   ok(dwRet == 6 && !memcmp(dest, lpRes, sizeof(dest)),
-     "SHUnicodeToUnicode: expected 6, \"hello\\0\\n\\n\", got %ld, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+     "SHUnicodeToUnicode: expected 6, \"hello\\0\\n\\n\", got %d, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
      dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
 }
 
+static void test_StrXXX_overflows(void)
+{
+    CHAR str1[2*MAX_PATH+1], buf[2*MAX_PATH];
+    WCHAR wstr1[2*MAX_PATH+1], wbuf[2*MAX_PATH];
+    const WCHAR fmt[] = {'%','s',0};
+    STRRET strret;
+    int ret;
+    int i;
+
+    for (i=0; i<2*MAX_PATH; i++)
+    {
+        str1[i] = '0'+(i%10);
+        wstr1[i] = '0'+(i%10);
+    }
+    str1[2*MAX_PATH] = 0;
+    wstr1[2*MAX_PATH] = 0;
+
+    memset(buf, 0xbf, sizeof(buf));
+    expect_eq(StrCpyNA(buf, str1, 10), buf, PCHAR, "%p");
+    expect_eq(buf[9], 0, CHAR, "%x");
+    expect_eq(buf[10], '\xbf', CHAR, "%x");
+    expect_eq(StrCatBuffA(buf, str1, 100), buf, PCHAR, "%p");
+    expect_eq(buf[99], 0, CHAR, "%x");
+    expect_eq(buf[100], '\xbf', CHAR, "%x");
+
+    memset(wbuf, 0xbf, sizeof(wbuf));
+    expect_eq(StrCpyNW(wbuf, wstr1, 10), wbuf, PWCHAR, "%p");
+    expect_eq(wbuf[9], 0, WCHAR, "%x");
+    expect_eq(wbuf[10], (WCHAR)0xbfbf, WCHAR, "%x");
+    expect_eq(StrCatBuffW(wbuf, wstr1, 100), wbuf, PWCHAR, "%p");
+    expect_eq(wbuf[99], 0, WCHAR, "%x");
+    expect_eq(wbuf[100], (WCHAR)0xbfbf, WCHAR, "%x");
+
+    memset(wbuf, 0xbf, sizeof(wbuf));
+    strret.uType = STRRET_WSTR;
+    U(strret).pOleStr = StrDupW(wstr1);
+    expect_eq(StrRetToBufW(&strret, NULL, wbuf, 10), S_OK, HRESULT, "%x");
+    expect_eq(wbuf[9], 0, WCHAR, "%x");
+    expect_eq(wbuf[10], (WCHAR)0xbfbf, WCHAR, "%x");
+
+    memset(buf, 0xbf, sizeof(buf));
+    strret.uType = STRRET_CSTR;
+    StrCpyN(U(strret).cStr, str1, MAX_PATH);
+    expect_eq(StrRetToBufA(&strret, NULL, buf, 10), S_OK, HRESULT, "%x");
+    expect_eq(buf[9], 0, CHAR, "%x");
+    expect_eq(buf[10], (CHAR)0xbf, CHAR, "%x");
+
+    memset(buf, 0xbf, sizeof(buf));
+    ret = wnsprintfA(buf, 10, "%s", str1);
+    todo_wine ok(ret == 9, "Unexpected wsnprintfA return %d, expected 9\n", ret);
+    expect_eq(buf[9], 0, CHAR, "%x");
+    expect_eq(buf[10], (CHAR)0xbf, CHAR, "%x");
+    memset(wbuf, 0xbf, sizeof(wbuf));
+    ret = wnsprintfW(wbuf, 10, fmt, wstr1);
+    todo_wine ok(ret == 9, "Unexpected wsnprintfW return %d, expected 9\n", ret);
+    expect_eq(wbuf[9], 0, WCHAR, "%x");
+    expect_eq(wbuf[10], (WCHAR)0xbfbf, WCHAR, "%x");
+}
+
 START_TEST(string)
 {
+  TCHAR thousandDelim[8];
+  TCHAR decimalDelim[8];
   CoInitialize(0);
 
+  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousandDelim, 8);
+  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimalDelim, 8);
+
   hShlwapi = GetModuleHandleA("shlwapi");
-  if (!hShlwapi)
-     return;
 
   test_StrChrA();
   test_StrChrW();
@@ -738,23 +842,27 @@ START_TEST(string)
   test_StrToIntExA();
   test_StrToIntExW();
   test_StrDupA();
-  if (0)
+  if (lstrcmp(thousandDelim, ",")==0 && lstrcmp(decimalDelim, ".")==0)
   {
-    /* this test fails on locales which do not use '.' as a decimal separator */
+    /* these tests are locale-dependent */
     test_StrFormatByteSize64A();
-
-    /* this test fails on locales which do not use '.' as a decimal separator */
     test_StrFormatKBSizeA();
-
-    /* FIXME: Awaiting NLS fixes in kernel before these succeed */
     test_StrFormatKBSizeW();
   }
-  test_StrFromTimeIntervalA();
+
+  /* language-dependent test */
+  if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
+    trace("Skipping StrFromTimeInterval test for non English language\n");
+  else
+    test_StrFromTimeIntervalA();
+
   test_StrCmpA();
   test_StrCmpW();
   test_StrRetToBSTR();
   test_StrCpyNXA();
   test_StrCpyNXW();
+  test_StrRStrI();
   test_SHAnsiToAnsi();
   test_SHUnicodeToUnicode();
+  test_StrXXX_overflows();
 }
index de16432..ec8185d 100755 (executable)
@@ -17,10 +17,10 @@ extern void func_string(void);
 const struct test winetest_testlist[] =
 {
     { "clist", func_clist },
-//    { "clsid", func_clsid },
-//    { "generated", func_generated },
+    { "clsid", func_clsid },
+    { "generated", func_generated },
     { "ordinal", func_ordinal },
-//    { "path", func_path },
+    { "path", func_path },
     { "shreg", func_shreg },
     { "string", func_string },
     { 0, 0 }