add some of the shlwapi tests
authorSteven Edwards <winehacker@gmail.com>
Sat, 6 Aug 2005 23:58:36 +0000 (23:58 +0000)
committerSteven Edwards <winehacker@gmail.com>
Sat, 6 Aug 2005 23:58:36 +0000 (23:58 +0000)
svn path=/trunk/; revision=17126

reactos/regtests/winetests/shlwapi/.cvsignore [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/clist.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/clsid.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/generated.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/ordinal.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/path.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/shlwapi_test.xml [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/shreg.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/string.c [new file with mode: 0755]
reactos/regtests/winetests/shlwapi/testlist.c [new file with mode: 0755]

diff --git a/reactos/regtests/winetests/shlwapi/.cvsignore b/reactos/regtests/winetests/shlwapi/.cvsignore
new file mode 100755 (executable)
index 0000000..7bbf417
--- /dev/null
@@ -0,0 +1,9 @@
+Makefile
+clist.ok
+clsid.ok
+generated.ok
+ordinal.ok
+path.ok
+shreg.ok
+string.ok
+testlist.c
diff --git a/reactos/regtests/winetests/shlwapi/clist.c b/reactos/regtests/winetests/shlwapi/clist.c
new file mode 100755 (executable)
index 0000000..6abe85a
--- /dev/null
@@ -0,0 +1,643 @@
+/* Unit test suite for SHLWAPI Compact List and IStream ordinal functions
+ *
+ * Copyright 2002 Jon Griffiths
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.h"
+
+typedef struct tagSHLWAPI_CLIST
+{
+  ULONG ulSize;
+  ULONG ulId;
+} SHLWAPI_CLIST, *LPSHLWAPI_CLIST;
+
+typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST;
+
+/* Items to add */
+static const SHLWAPI_CLIST SHLWAPI_CLIST_items[] =
+{
+  {4, 1},
+  {8, 3},
+  {12, 2},
+  {16, 8},
+  {20, 9},
+  {3, 11},
+  {9, 82},
+  {33, 16},
+  {32, 55},
+  {24, 100},
+  {39, 116},
+  { 0, 0}
+};
+
+/* Dummy IStream object for testing calls */
+typedef struct
+{
+  void* lpVtbl;
+  LONG  ref;
+  int   readcalls;
+  BOOL  failreadcall;
+  BOOL  failreadsize;
+  BOOL  readbeyondend;
+  BOOL  readreturnlarge;
+  int   writecalls;
+  BOOL  failwritecall;
+  BOOL  failwritesize;
+  int   seekcalls;
+  int   statcalls;
+  BOOL  failstatcall;
+  LPCSHLWAPI_CLIST item;
+  ULARGE_INTEGER   pos;
+} _IDummyStream;
+
+static
+HRESULT WINAPI QueryInterface(_IDummyStream *This,REFIID riid, LPVOID *ppvObj)
+{
+  return S_OK;
+}
+
+static ULONG WINAPI AddRef(_IDummyStream *This)
+{
+  return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI Release(_IDummyStream *This)
+{
+  return InterlockedDecrement(&This->ref);
+}
+
+static HRESULT WINAPI Read(_IDummyStream* This, LPVOID lpMem, ULONG ulSize,
+                           PULONG lpRead)
+{
+  HRESULT hRet = S_OK;
+  ++This->readcalls;
+
+  if (This->failreadcall)
+  {
+    return STG_E_ACCESSDENIED;
+  }
+  else if (This->failreadsize)
+  {
+    *lpRead = ulSize + 8;
+    return S_OK;
+  }
+  else if (This->readreturnlarge)
+  {
+    *((ULONG*)lpMem) = 0xffff01;
+    *lpRead = ulSize;
+    This->readreturnlarge = FALSE;
+    return S_OK;
+  }
+  if (ulSize == sizeof(ULONG))
+  {
+    /* Read size of item */
+    *((ULONG*)lpMem) = This->item->ulSize ? This->item->ulSize + sizeof(SHLWAPI_CLIST) : 0;
+    *lpRead = ulSize;
+  }
+  else
+  {
+    unsigned int i;
+    char* buff = (char*)lpMem;
+
+    /* Read item data */
+    if (!This->item->ulSize)
+    {
+      This->readbeyondend = TRUE;
+      *lpRead = 0;
+      return E_FAIL; /* Should never happen */
+    }
+    *((ULONG*)lpMem) = This->item->ulId;
+    *lpRead = ulSize;
+
+    for (i = 0; i < This->item->ulSize; i++)
+      buff[4+i] = i*2;
+
+    This->item++;
+  }
+  return hRet;
+}
+
+static HRESULT WINAPI Write(_IDummyStream* This, LPVOID lpMem, ULONG ulSize,
+                            PULONG lpWritten)
+{
+  HRESULT hRet = S_OK;
+
+  ++This->writecalls;
+  if (This->failwritecall)
+  {
+    return STG_E_ACCESSDENIED;
+  }
+  else if (This->failwritesize)
+  {
+    *lpWritten = 0;
+  }
+  else
+    *lpWritten = ulSize;
+  return hRet;
+}
+
+static HRESULT WINAPI Seek(_IDummyStream* This, LARGE_INTEGER dlibMove,
+                           DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
+{
+  ++This->seekcalls;
+  This->pos.QuadPart = dlibMove.QuadPart;
+  if (plibNewPosition)
+    plibNewPosition->QuadPart = dlibMove.QuadPart;
+  return S_OK;
+}
+
+static HRESULT WINAPI Stat(_IDummyStream* This, STATSTG* pstatstg,
+                           DWORD grfStatFlag)
+{
+  ++This->statcalls;
+  if (This->failstatcall)
+    return E_FAIL;
+  if (pstatstg)
+    pstatstg->cbSize.QuadPart = This->pos.QuadPart;
+  return S_OK;
+}
+
+/* VTable */
+static void* iclvt[] =
+{
+  QueryInterface,
+  AddRef,
+  Release,
+  Read,
+  Write,
+  Seek,
+  NULL, /* SetSize */
+  NULL, /* CopyTo */
+  NULL, /* Commit */
+  NULL, /* Revert */
+  NULL, /* LockRegion */
+  NULL, /* UnlockRegion */
+  Stat,
+  NULL  /* Clone */
+};
+
+/* Function ptrs for ordinal calls */
+static HMODULE SHLWAPI_hshlwapi = 0;
+
+static VOID    (WINAPI *pSHLWAPI_19)(LPSHLWAPI_CLIST);
+static HRESULT (WINAPI *pSHLWAPI_20)(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST);
+static BOOL    (WINAPI *pSHLWAPI_21)(LPSHLWAPI_CLIST*,ULONG);
+static LPSHLWAPI_CLIST (WINAPI *pSHLWAPI_22)(LPSHLWAPI_CLIST,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_17)(_IDummyStream*,LPSHLWAPI_CLIST);
+static HRESULT (WINAPI *pSHLWAPI_18)(_IDummyStream*,LPSHLWAPI_CLIST*);
+
+static BOOL    (WINAPI *pSHLWAPI_166)(_IDummyStream*);
+static HRESULT (WINAPI *pSHLWAPI_184)(_IDummyStream*,LPVOID,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_212)(_IDummyStream*,LPCVOID,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_213)(_IDummyStream*);
+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");
+  }
+}
+
+static void InitDummyStream(_IDummyStream* iface)
+{
+  iface->lpVtbl = (void*)iclvt;
+  iface->ref = 1;
+  iface->readcalls = 0;
+  iface->failreadcall = FALSE;
+  iface->failreadsize = FALSE;
+  iface->readbeyondend = FALSE;
+  iface->readreturnlarge = FALSE;
+  iface->writecalls = 0;
+  iface->failwritecall = FALSE;
+  iface->failwritesize = FALSE;
+  iface->seekcalls = 0;
+  iface->statcalls = 0;
+  iface->failstatcall = FALSE;
+  iface->item = SHLWAPI_CLIST_items;
+  iface->pos.QuadPart = 0;
+}
+
+
+static void test_CList(void)
+{
+  _IDummyStream streamobj;
+  LPSHLWAPI_CLIST list = NULL;
+  LPCSHLWAPI_CLIST item = SHLWAPI_CLIST_items;
+  HRESULT hRet;
+  LPSHLWAPI_CLIST inserted;
+  BYTE buff[64];
+  unsigned int i;
+
+  if (!pSHLWAPI_17 || !pSHLWAPI_18 || !pSHLWAPI_19 || !pSHLWAPI_20 ||
+      !pSHLWAPI_21 || !pSHLWAPI_22)
+    return;
+
+  /* Populate a list and test the items are added correctly */
+  while (item->ulSize)
+  {
+    /* Create item and fill with data */
+    inserted = (LPSHLWAPI_CLIST)buff;
+    inserted->ulSize = item->ulSize + sizeof(SHLWAPI_CLIST);
+    inserted->ulId = item->ulId;
+    for (i = 0; i < item->ulSize; i++)
+      buff[sizeof(SHLWAPI_CLIST)+i] = i*2;
+
+    /* Add it */
+    hRet = pSHLWAPI_20(&list, inserted);
+    ok(hRet > S_OK, "failed list add\n");
+
+    if (hRet > S_OK)
+    {
+      ok(list && list->ulSize, "item not added\n");
+
+      /* Find it */
+      inserted = pSHLWAPI_22(list, item->ulId);
+      ok(inserted != NULL, "lost after adding\n");
+
+      ok(!inserted || inserted->ulId != ~0UL, "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].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));
+      }
+      if (inserted)
+      {
+        BOOL bDataOK = TRUE;
+        LPBYTE bufftest = (LPBYTE)inserted;
+
+        for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++)
+          if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2)
+            bDataOK = FALSE;
+
+        ok(bDataOK == TRUE, "data corrupted on insert\n");
+      }
+      ok(!inserted || inserted->ulId==item->ulId, "find got wrong item\n");
+    }
+    item++;
+  }
+
+  /* Write the list */
+  InitDummyStream(&streamobj);
+
+  hRet = pSHLWAPI_17(&streamobj, list);
+  ok(hRet == S_OK, "write failed\n");
+  if (hRet == S_OK)
+  {
+    /* 1 call for each element, + 1 for OK (use our null element for this) */
+    ok(streamobj.writecalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST),
+       "wrong call count\n");
+    ok(streamobj.readcalls == 0,"called Read() in write\n");
+    ok(streamobj.seekcalls == 0,"called Seek() in write\n");
+  }
+
+  /* Failure cases for writing */
+  InitDummyStream(&streamobj);
+  streamobj.failwritecall = TRUE;
+  hRet = pSHLWAPI_17(&streamobj, list);
+  ok(hRet == STG_E_ACCESSDENIED, "changed object failure return\n");
+  ok(streamobj.writecalls == 1, "called object after failure\n");
+  ok(streamobj.readcalls == 0,"called Read() after failure\n");
+  ok(streamobj.seekcalls == 0,"called Seek() after failure\n");
+
+  InitDummyStream(&streamobj);
+  streamobj.failwritesize = TRUE;
+  hRet = pSHLWAPI_17(&streamobj, list);
+  ok(hRet == STG_E_MEDIUMFULL, "changed size failure return\n");
+  ok(streamobj.writecalls == 1, "called object after size failure\n");
+  ok(streamobj.readcalls == 0,"called Read() after failure\n");
+  ok(streamobj.seekcalls == 0,"called Seek() after failure\n");
+
+  /* Invalid inputs for adding */
+  inserted = (LPSHLWAPI_CLIST)buff;
+  inserted->ulSize = sizeof(SHLWAPI_CLIST) -1;
+  inserted->ulId = 33;
+  hRet = pSHLWAPI_20(&list, inserted);
+  /* The call succeeds but the item is not inserted, except on some early
+   * versions which return failure. Wine behaves like later versions.
+   */
+#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;
+  hRet = pSHLWAPI_20(&list, inserted);
+  /* See comment above, some early versions fail this call */
+#if 0
+  ok(hRet == S_OK, "failed adding a container\n");
+#endif
+  item = SHLWAPI_CLIST_items;
+
+  /* Look for nonexistent item in populated list */
+  inserted = pSHLWAPI_22(list, 99999999);
+  ok(inserted == NULL, "found a nonexistent item\n");
+
+  while (item->ulSize)
+  {
+    /* Delete items */
+    BOOL bRet = pSHLWAPI_21(&list, item->ulId);
+    ok(bRet == TRUE, "couldn't find item to delete\n");
+    item++;
+  }
+
+  /* Look for nonexistent item in empty list */
+  inserted = pSHLWAPI_22(list, 99999999);
+  ok(inserted == NULL, "found an item in empty list\n");
+
+  /* Create a list by reading in data */
+  InitDummyStream(&streamobj);
+
+  hRet = pSHLWAPI_18(&streamobj, &list);
+  ok(hRet == S_OK, "failed create from Read()\n");
+  if (hRet == S_OK)
+  {
+    ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+    /* 2 calls per item, but only 1 for the terminator */
+    ok(streamobj.readcalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST)*2-1,
+       "wrong call count\n");
+    ok(streamobj.writecalls == 0, "called Write() from create\n");
+    ok(streamobj.seekcalls == 0,"called Seek() from create\n");
+
+    item = SHLWAPI_CLIST_items;
+
+    /* Check the items were added correctly */
+    while (item->ulSize)
+    {
+      inserted = pSHLWAPI_22(list, item->ulId);
+      ok(inserted != NULL, "lost after adding\n");
+
+      ok(!inserted || inserted->ulId != ~0UL, "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].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));
+      }
+      ok(!inserted || inserted->ulId==item->ulId, "find got wrong item\n");
+      if (inserted)
+      {
+        BOOL bDataOK = TRUE;
+        LPBYTE bufftest = (LPBYTE)inserted;
+
+        for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++)
+          if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2)
+            bDataOK = FALSE;
+
+        ok(bDataOK == TRUE, "data corrupted on insert\n");
+      }
+      item++;
+    }
+  }
+
+  /* Failure cases for reading */
+  InitDummyStream(&streamobj);
+  streamobj.failreadcall = TRUE;
+  hRet = pSHLWAPI_18(&streamobj, &list);
+  ok(hRet == STG_E_ACCESSDENIED, "changed object failure return\n");
+  ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+  ok(streamobj.readcalls == 1, "called object after read failure\n");
+  ok(streamobj.writecalls == 0,"called Write() after read failure\n");
+  ok(streamobj.seekcalls == 0,"called Seek() after read failure\n");
+
+  /* Read returns large object */
+  InitDummyStream(&streamobj);
+  streamobj.readreturnlarge = TRUE;
+  hRet = pSHLWAPI_18(&streamobj, &list);
+  ok(hRet == S_OK, "failed create from Read() with large item\n");
+  ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+  ok(streamobj.readcalls == 1,"wrong call count\n");
+  ok(streamobj.writecalls == 0,"called Write() after read failure\n");
+  ok(streamobj.seekcalls == 2,"wrong Seek() call count (%d)\n", streamobj.seekcalls);
+
+  pSHLWAPI_19(list);
+}
+
+static BOOL test_SHLWAPI_166(void)
+{
+  _IDummyStream streamobj;
+  BOOL bRet;
+
+  if (!pSHLWAPI_166)
+    return FALSE;
+
+  InitDummyStream(&streamobj);
+  bRet = pSHLWAPI_166(&streamobj);
+
+  if (bRet != TRUE)
+    return FALSE; /* This version doesn't support stream ops on clists */
+
+  ok(streamobj.readcalls == 0, "called Read()\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 0, "called Seek()\n");
+  ok(streamobj.statcalls == 1, "wrong call count\n");
+
+  streamobj.statcalls = 0;
+  streamobj.pos.QuadPart = 50001;
+
+  bRet = pSHLWAPI_166(&streamobj);
+
+  ok(bRet == FALSE, "failed after seek adjusted\n");
+  ok(streamobj.readcalls == 0, "called Read()\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 0, "called Seek()\n");
+  ok(streamobj.statcalls == 1, "wrong call count\n");
+
+  /* Failure cases */
+  InitDummyStream(&streamobj);
+  streamobj.pos.QuadPart = 50001;
+  streamobj.failstatcall = TRUE; /* 1: Stat() Bad, Read() OK */
+  bRet = pSHLWAPI_166(&streamobj);
+  ok(bRet == FALSE, "should be FALSE after read is OK\n");
+  ok(streamobj.readcalls == 1, "wrong call count\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 1, "wrong call count\n");
+  ok(streamobj.statcalls == 1, "wrong call count\n");
+  ok(streamobj.pos.QuadPart == 0, "Didn't seek to start\n");
+
+  InitDummyStream(&streamobj);
+  streamobj.pos.QuadPart = 50001;
+  streamobj.failstatcall = TRUE;
+  streamobj.failreadcall = TRUE; /* 2: Stat() Bad, Read() Bad Also */
+  bRet = pSHLWAPI_166(&streamobj);
+  ok(bRet == TRUE, "Should be true after read fails\n");
+  ok(streamobj.readcalls == 1, "wrong call count\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 0, "Called Seek()\n");
+  ok(streamobj.statcalls == 1, "wrong call count\n");
+  ok(streamobj.pos.QuadPart == 50001, "called Seek() after read failed\n");
+  return TRUE;
+}
+
+static void test_SHLWAPI_184(void)
+{
+  _IDummyStream streamobj;
+  char buff[256];
+  HRESULT hRet;
+
+  if (!pSHLWAPI_184)
+    return;
+
+  InitDummyStream(&streamobj);
+  hRet = pSHLWAPI_184(&streamobj, buff, sizeof(buff));
+
+  ok(hRet == S_OK, "failed Read()\n");
+  ok(streamobj.readcalls == 1, "wrong call count\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 0, "called Seek()\n");
+}
+
+static void test_SHLWAPI_212(void)
+{
+  _IDummyStream streamobj;
+  char buff[256];
+  HRESULT hRet;
+
+  if (!pSHLWAPI_212)
+    return;
+
+  InitDummyStream(&streamobj);
+  hRet = pSHLWAPI_212(&streamobj, buff, sizeof(buff));
+
+  ok(hRet == S_OK, "failed Write()\n");
+  ok(streamobj.readcalls == 0, "called Read()\n");
+  ok(streamobj.writecalls == 1, "wrong call count\n");
+  ok(streamobj.seekcalls == 0, "called Seek()\n");
+}
+
+static void test_SHLWAPI_213(void)
+{
+  _IDummyStream streamobj;
+  ULARGE_INTEGER ul;
+  LARGE_INTEGER ll;
+  HRESULT hRet;
+
+  if (!pSHLWAPI_213 || !pSHLWAPI_214)
+    return;
+
+  InitDummyStream(&streamobj);
+  ll.QuadPart = 5000l;
+  Seek(&streamobj, ll, 0, NULL); /* Seek to 5000l */
+
+  streamobj.seekcalls = 0;
+  pSHLWAPI_213(&streamobj); /* Should rewind */
+  ok(streamobj.statcalls == 0, "called Stat()\n");
+  ok(streamobj.readcalls == 0, "called Read()\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 1, "wrong call count\n");
+
+  ul.QuadPart = 50001;
+  hRet = pSHLWAPI_214(&streamobj, &ul);
+  ok(hRet == S_OK, "failed Stat()\n");
+  ok(ul.QuadPart == 0, "213 didn't rewind stream\n");
+}
+
+static void test_SHLWAPI_214(void)
+{
+  _IDummyStream streamobj;
+  ULARGE_INTEGER ul;
+  LARGE_INTEGER ll;
+  HRESULT hRet;
+
+  if (!pSHLWAPI_214)
+    return;
+
+  InitDummyStream(&streamobj);
+  ll.QuadPart = 5000l;
+  Seek(&streamobj, ll, 0, NULL);
+  ul.QuadPart = 0;
+  streamobj.seekcalls = 0;
+  hRet = pSHLWAPI_214(&streamobj, &ul);
+
+  ok(hRet == S_OK, "failed Stat()\n");
+  ok(streamobj.statcalls == 1, "wrong call count\n");
+  ok(streamobj.readcalls == 0, "called Read()\n");
+  ok(streamobj.writecalls == 0, "called Write()\n");
+  ok(streamobj.seekcalls == 0, "called Seek()\n");
+  ok(ul.QuadPart == 5000l, "Stat gave wrong size\n");
+}
+
+START_TEST(clist)
+{
+  InitFunctionPtrs();
+
+  test_CList();
+
+  /* Test streaming if this version supports it */
+  if (test_SHLWAPI_166())
+  {
+    test_SHLWAPI_184();
+    test_SHLWAPI_212();
+    test_SHLWAPI_213();
+    test_SHLWAPI_214();
+  }
+
+  if (SHLWAPI_hshlwapi)
+    FreeLibrary(SHLWAPI_hshlwapi);
+}
diff --git a/reactos/regtests/winetests/shlwapi/clsid.c b/reactos/regtests/winetests/shlwapi/clsid.c
new file mode 100755 (executable)
index 0000000..ce49653
--- /dev/null
@@ -0,0 +1,171 @@
+/* Unit test suite for SHLWAPI Class ID functions
+ *
+ * Copyright 2003 Jon Griffiths
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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"
+
+/* Function ptrs for ordinal calls */
+static HMODULE hShlwapi = 0;
+static BOOL (WINAPI *pSHLWAPI_269)(LPCSTR, CLSID *) = 0;
+static DWORD (WINAPI *pSHLWAPI_23)(REFGUID, LPSTR, INT) = 0;
+
+/* GUIDs to test */
+const GUID * TEST_guids[] = {
+  &CLSID_ShellDesktop,
+  &CLSID_ShellLink,
+  &CATID_BrowsableShellExt,
+  &CATID_BrowseInPlace,
+  &CATID_DeskBand,
+  &CATID_InfoBand,
+  &CATID_CommBand,
+  &FMTID_Intshcut,
+  &FMTID_InternetSite,
+  &CGID_Explorer,
+  &CGID_ShellDocView,
+  &CGID_ShellServiceObject,
+  &CGID_ExplorerBarDoc,
+  &IID_INewShortcutHookA,
+  &IID_IShellIcon,
+  &IID_IShellFolder,
+  &IID_IShellExtInit,
+  &IID_IShellPropSheetExt,
+  &IID_IPersistFolder,
+  &IID_IExtractIconA,
+  &IID_IShellDetails,
+  &IID_IDelayedRelease,
+  &IID_IShellLinkA,
+  &IID_IShellCopyHookA,
+  &IID_IFileViewerA,
+  &IID_ICommDlgBrowser,
+  &IID_IEnumIDList,
+  &IID_IFileViewerSite,
+  &IID_IContextMenu2,
+  &IID_IShellExecuteHookA,
+  &IID_IPropSheetPage,
+  &IID_INewShortcutHookW,
+  &IID_IFileViewerW,
+  &IID_IShellLinkW,
+  &IID_IExtractIconW,
+  &IID_IShellExecuteHookW,
+  &IID_IShellCopyHookW,
+  &IID_IRemoteComputer,
+  &IID_IQueryInfo,
+  &IID_IDockingWindow,
+  &IID_IDockingWindowSite,
+  &CLSID_NetworkPlaces,
+  &CLSID_NetworkDomain,
+  &CLSID_NetworkServer,
+  &CLSID_NetworkShare,
+  &CLSID_MyComputer,
+  &CLSID_Internet,
+  &CLSID_ShellFSFolder,
+  &CLSID_RecycleBin,
+  &CLSID_ControlPanel,
+  &CLSID_Printers,
+  &CLSID_MyDocuments,
+  NULL
+};
+
+DEFINE_GUID(IID_Endianess, 0x01020304, 0x0506, 0x0708, 0x09, 0x0A, 0x0B,
+            0x0C, 0x0D, 0x0E, 0x0F, 0x0A);
+
+static void test_ClassIDs(void)
+{
+  const GUID **guids = TEST_guids;
+  char szBuff[256];
+  GUID guid;
+  DWORD dwLen;
+  BOOL bRet;
+  int i = 0;
+
+  if (!pSHLWAPI_269 || !pSHLWAPI_23)
+    return;
+
+  while (*guids)
+  {
+    dwLen = pSHLWAPI_23(*guids, szBuff, 256);
+    ok(dwLen == 39, "wrong size for id %d\n", i);
+
+    bRet = pSHLWAPI_269(szBuff, &guid);
+    ok(bRet != FALSE, "created invalid string '%s'\n", szBuff);
+
+    if (bRet)
+      ok(IsEqualGUID(*guids, &guid), "GUID created wrong %d\n", i);
+
+    guids++;
+    i++;
+  }
+
+  /* Test endianess */
+  dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 256);
+  ok(dwLen == 39, "wrong size for IID_Endianess\n");
+
+  ok(!strcmp(szBuff, "{01020304-0506-0708-090A-0B0C0D0E0F0A}"),
+     "Endianess Broken, got '%s'\n", szBuff);
+
+  /* test lengths */
+  szBuff[0] = ':';
+  dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 0);
+  ok(dwLen == 0, "accepted bad length\n");
+  ok(szBuff[0] == ':', "wrote to buffer with no length\n");
+
+  szBuff[0] = ':';
+  dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 38);
+  ok(dwLen == 0, "accepted bad length\n");
+  ok(szBuff[0] == ':', "wrote to buffer with no length\n");
+
+  szBuff[0] = ':';
+  dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 39);
+  ok(dwLen == 39, "rejected ok length\n");
+  ok(szBuff[0] == '{', "Didn't write to buffer with ok length\n");
+
+  /* Test string */
+  strcpy(szBuff, "{xxx-");
+  bRet = pSHLWAPI_269(szBuff, &guid);
+  ok(bRet == FALSE, "accepted invalid string\n");
+
+  dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 39);
+  ok(dwLen == 39, "rejected ok length\n");
+  ok(szBuff[0] == '{', "Didn't write to buffer with ok length\n");
+}
+
+
+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);
+  }
+
+  test_ClassIDs();
+
+  if (hShlwapi)
+    FreeLibrary(hShlwapi);
+}
diff --git a/reactos/regtests/winetests/shlwapi/generated.c b/reactos/regtests/winetests/shlwapi/generated.c
new file mode 100755 (executable)
index 0000000..b1f618b
--- /dev/null
@@ -0,0 +1,186 @@
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ *        (if type is not just a name that is, if so the normal)
+ *         TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if defined(TYPE_ALIGNMENT) && defined(_MSC_VER) && _MSC_VER >= 800 && !defined(__cplusplus)
+#pragma warning(disable:4116)
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+   ok(FIELD_ALIGNMENT(type, field) == align, \
+       "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+           (int)FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+    ok(FIELD_OFFSET(type, field) == offset, \
+        "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+             (long int)FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+    ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)_TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+    ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+    ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", ((int) sizeof(type)))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+  TEST_TYPE_SIZE(field_type, field_size); \
+  TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+  TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+  TEST_TYPE_ALIGNMENT(type, align); \
+  TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+    TEST__TYPE_ALIGNMENT(*(type)0, align); \
+    TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+    ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+     ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_ASSOCF(void)
+{
+    /* ASSOCF */
+    TEST_TYPE(ASSOCF, 4, 4);
+    TEST_TYPE_UNSIGNED(ASSOCF);
+}
+
+static void test_pack_DLLGETVERSIONPROC(void)
+{
+    /* DLLGETVERSIONPROC */
+    TEST_TYPE(DLLGETVERSIONPROC, 4, 4);
+}
+
+static void test_pack_DLLVERSIONINFO(void)
+{
+    /* DLLVERSIONINFO (pack 8) */
+    TEST_TYPE(DLLVERSIONINFO, 20, 4);
+    TEST_FIELD(DLLVERSIONINFO, DWORD, cbSize, 0, 4, 4);
+    TEST_FIELD(DLLVERSIONINFO, DWORD, dwMajorVersion, 4, 4, 4);
+    TEST_FIELD(DLLVERSIONINFO, DWORD, dwMinorVersion, 8, 4, 4);
+    TEST_FIELD(DLLVERSIONINFO, DWORD, dwBuildNumber, 12, 4, 4);
+    TEST_FIELD(DLLVERSIONINFO, DWORD, dwPlatformID, 16, 4, 4);
+}
+
+static void test_pack_DLLVERSIONINFO2(void)
+{
+    /* DLLVERSIONINFO2 (pack 8) */
+    TEST_TYPE(DLLVERSIONINFO2, 32, 8);
+    TEST_FIELD(DLLVERSIONINFO2, DLLVERSIONINFO, info1, 0, 20, 4);
+    TEST_FIELD(DLLVERSIONINFO2, DWORD, dwFlags, 20, 4, 4);
+    TEST_FIELD(DLLVERSIONINFO2, ULONGLONG, ullVersion, 24, 8, 8);
+}
+
+static void test_pack_HUSKEY(void)
+{
+    /* HUSKEY */
+    TEST_TYPE(HUSKEY, 4, 4);
+}
+
+static void test_pack_IQueryAssociations(void)
+{
+    /* IQueryAssociations */
+}
+
+static void test_pack_PHUSKEY(void)
+{
+    /* PHUSKEY */
+    TEST_TYPE(PHUSKEY, 4, 4);
+    TEST_TYPE_POINTER(PHUSKEY, 4, 4);
+}
+
+static void test_pack(void)
+{
+    test_pack_ASSOCF();
+    test_pack_DLLGETVERSIONPROC();
+    test_pack_DLLVERSIONINFO();
+    test_pack_DLLVERSIONINFO2();
+    test_pack_HUSKEY();
+    test_pack_IQueryAssociations();
+    test_pack_PHUSKEY();
+}
+
+START_TEST(generated)
+{
+    test_pack();
+}
diff --git a/reactos/regtests/winetests/shlwapi/ordinal.c b/reactos/regtests/winetests/shlwapi/ordinal.c
new file mode 100755 (executable)
index 0000000..20798c7
--- /dev/null
@@ -0,0 +1,248 @@
+/* Unit test suite for SHLWAPI ordinal functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winuser.h"
+
+/* Function ptrs for ordinal calls */
+static HMODULE hShlwapi;
+static int (WINAPI *pSHSearchMapInt)(const int*,const int*,int,int);
+static HRESULT (WINAPI *pGetAcceptLanguagesA)(LPSTR,LPDWORD);
+
+static HANDLE (WINAPI *pSHAllocShared)(LPCVOID,DWORD,DWORD);
+static LPVOID (WINAPI *pSHLockShared)(HANDLE,DWORD);
+static BOOL   (WINAPI *pSHUnlockShared)(LPVOID);
+static BOOL   (WINAPI *pSHFreeShared)(HANDLE,DWORD);
+
+static void test_GetAcceptLanguagesA(void)
+{   HRESULT retval;
+    DWORD buffersize, buffersize2, exactsize;
+    char buffer[100];
+
+    if (!pGetAcceptLanguagesA)
+       return;
+
+    buffersize = sizeof(buffer);
+    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());
+    if(retval != S_OK) {
+       trace("GetAcceptLanguagesA: skipping tests\n");
+       return;
+    }
+    ok( (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()) || 
+       (ERROR_CLASS_DOES_NOT_EXIST == GetLastError()) ||
+       (ERROR_PROC_NOT_FOUND == GetLastError()) ||
+       (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) ||
+       (ERROR_SUCCESS == GetLastError()), "last error set to %ld\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());
+
+    buffersize = sizeof(buffer);
+    SetLastError(ERROR_SUCCESS);
+    retval = pGetAcceptLanguagesA( NULL, &buffersize);
+    ok(retval == E_FAIL,
+       "function result wrong: got %08lx; 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());
+
+    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());
+
+    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);
+    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 = buffersize2 = 1;
+    memset(buffer, 0, sizeof(buffer));
+    SetLastError(ERROR_SUCCESS);
+    retval = pGetAcceptLanguagesA( buffer, &buffersize);
+    switch(retval) {
+       case 0L:
+            if(buffersize == exactsize) {
+            ok( (ERROR_SUCCESS == GetLastError()) || (ERROR_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)/"
+               "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);
+            } else if((buffersize +1) == buffersize2) {
+                ok(ERROR_SUCCESS == GetLastError(),
+                    "last error wrong: got %08lx; expected ERROR_SUCCESS\n", GetLastError());
+                ok(buffersize == strlen(buffer),
+                    "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize);
+            } else
+                ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                    retval, buffersize, buffer, GetLastError());
+            break;
+       case E_INVALIDARG:
+            ok(buffersize == 0,
+               "buffersize wrong: got %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+            ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
+               "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+            ok(buffersize2 == strlen(buffer),
+               "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+            break;
+        default:
+            ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                retval, buffersize, buffer, GetLastError());
+            break;
+    }
+
+    buffersize = buffersize2 = exactsize;
+    memset(buffer, 0, sizeof(buffer));
+    SetLastError(ERROR_SUCCESS);
+    retval = pGetAcceptLanguagesA( buffer, &buffersize);
+    switch(retval) {
+       case 0L:
+            ok(ERROR_SUCCESS == GetLastError(),
+                 "last error wrong: got %08lx; 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);
+            else
+                ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                    retval, buffersize, buffer, GetLastError());
+            break;
+       case E_INVALIDARG:
+            ok(buffersize == 0,
+               "buffersize wrong: got %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+            ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
+               "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+            ok(buffersize2 == strlen(buffer),
+               "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+            break;
+        default:
+            ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+                retval, buffersize, buffer, GetLastError());
+            break;
+    }
+}
+
+static void test_SHSearchMapInt(void)
+{
+  int keys[8], values[8];
+  int i = 0;
+
+  if (!pSHSearchMapInt)
+    return;
+
+  memset(keys, 0, sizeof(keys));
+  memset(values, 0, sizeof(values));
+  keys[0] = 99; values[0] = 101;
+
+  /* NULL key/value lists crash native, so skip testing them */
+
+  /* 1 element */
+  i = pSHSearchMapInt(keys, values, 1, keys[0]);
+  ok(i == values[0], "Len 1, expected %d, got %d\n", values[0], i);
+
+  /* Key doesn't exist */
+  i = pSHSearchMapInt(keys, values, 1, 100);
+  ok(i == -1, "Len 1 - bad key, expected -1, got %d\n", i);
+
+  /* Len = 0 => not found */
+  i = pSHSearchMapInt(keys, values, 0, keys[0]);
+  ok(i == -1, "Len 1 - passed len 0, expected -1, got %d\n", i);
+
+  /* 2 elements, len = 1 */
+  keys[1] = 98; values[1] = 102;
+  i = pSHSearchMapInt(keys, values, 1, keys[1]);
+  ok(i == -1, "Len 1 - array len 2, expected -1, got %d\n", i);
+
+  /* 2 elements, len = 2 */
+  i = pSHSearchMapInt(keys, values, 2, keys[1]);
+  ok(i == values[1], "Len 2, expected %d, got %d\n", values[1], i);
+
+  /* Searches forward */
+  keys[2] = 99; values[2] = 103;
+  i = pSHSearchMapInt(keys, values, 3, keys[0]);
+  ok(i == values[0], "Len 3, expected %d, got %d\n", values[0], i);
+}
+
+static void test_alloc_shared(void)
+{
+    DWORD procid;
+    HANDLE hmem;
+    int val;
+    int* p;
+    BOOL ret;
+
+    procid=GetCurrentProcessId();
+    hmem=pSHAllocShared(NULL,10,procid);
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+    ret = pSHFreeShared(hmem, procid);
+    ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+
+    val=0x12345678;
+    hmem=pSHAllocShared(&val,4,procid);
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+
+    p=(int*)pSHLockShared(hmem,procid);
+    ok(p!=NULL,"SHLockShared failed: %ld\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());
+
+    ret = pSHFreeShared(hmem, procid);
+    ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+}
+
+START_TEST(ordinal)
+{
+  hShlwapi = LoadLibraryA("shlwapi.dll");
+  ok(hShlwapi != 0, "LoadLibraryA failed\n");
+  if (!hShlwapi)
+    return;
+
+  pGetAcceptLanguagesA = (void*)GetProcAddress(hShlwapi, (LPSTR)14);
+  pSHSearchMapInt = (void*)GetProcAddress(hShlwapi, (LPSTR)198);
+  pSHAllocShared=(void*)GetProcAddress(hShlwapi,(char*)7);
+  pSHLockShared=(void*)GetProcAddress(hShlwapi,(char*)8);
+  pSHUnlockShared=(void*)GetProcAddress(hShlwapi,(char*)9);
+  pSHFreeShared=(void*)GetProcAddress(hShlwapi,(char*)10);
+
+  test_GetAcceptLanguagesA();
+  test_SHSearchMapInt();
+  test_alloc_shared();
+  FreeLibrary(hShlwapi);
+}
diff --git a/reactos/regtests/winetests/shlwapi/path.c b/reactos/regtests/winetests/shlwapi/path.c
new file mode 100755 (executable)
index 0000000..9812e08
--- /dev/null
@@ -0,0 +1,833 @@
+/* Unit test suite for Path functions
+ *
+ * Copyright 2002 Matthew Mastracci
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wine/unicode.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wininet.h"
+
+static HMODULE hShlwapi;
+static HRESULT (WINAPI *pPathIsValidCharA)(char,DWORD);
+static HRESULT (WINAPI *pPathIsValidCharW)(WCHAR,DWORD);
+
+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";
+const char* TEST_URL_3 = "http://foo:bar@localhost:21/internal.php?query=x&return=y";
+
+typedef struct _TEST_URL_CANONICALIZE {
+    const char *url;
+    DWORD flags;
+    HRESULT expectret;
+    const char *expecturl;
+} TEST_URL_CANONICALIZE;
+
+const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
+    /*FIXME {"http://www.winehq.org/tests/../tests/../..", 0, S_OK, "http://www.winehq.org/"},*/
+    {"http://www.winehq.org/tests/../tests", 0, S_OK, "http://www.winehq.org/tests"},
+    {"http://www.winehq.org/tests\n", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
+    {"http://www.winehq.org/tests\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
+    {"http://www.winehq.org/tests\r", 0, S_OK, "http://www.winehq.org/tests"},
+    {"http://www.winehq.org/tests\r", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests"},
+    {"http://www.winehq.org/tests/../tests/", 0, S_OK, "http://www.winehq.org/tests/"},
+    {"http://www.winehq.org/tests/../tests/..", 0, S_OK, "http://www.winehq.org/"},
+    {"http://www.winehq.org/tests/../tests/../", 0, S_OK, "http://www.winehq.org/"},
+    {"http://www.winehq.org/tests/..", 0, S_OK, "http://www.winehq.org/"},
+    {"http://www.winehq.org/tests/../", 0, S_OK, "http://www.winehq.org/"},
+    {"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/../?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", 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"},
+};
+
+typedef struct _TEST_URL_ESCAPE {
+    const char *url;
+    DWORD flags;
+    DWORD expectescaped;
+    HRESULT expectret;
+    const char *expecturl;
+} TEST_URL_ESCAPE;
+
+const TEST_URL_ESCAPE TEST_ESCAPE[] = {
+    {"http://www.winehq.org/tests0", 0, 0, S_OK, "http://www.winehq.org/tests0"},
+    {"http://www.winehq.org/tests1\n", 0, 0, S_OK, "http://www.winehq.org/tests1%0A"},
+    {"http://www.winehq.org/tests2\r", 0, 0, S_OK, "http://www.winehq.org/tests2%0D"},
+    {"http://www.winehq.org/tests3\r", URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, 0, S_OK, "http://www.winehq.org/tests3\r"},
+    {"http://www.winehq.org/tests4\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests4\r"},
+    {"http://www.winehq.org/tests5\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests5\r"},
+    {"/direct/swhelp/series6/6.2i_latestservicepack.dat\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "/direct/swhelp/series6/6.2i_latestservicepack.dat\r"},
+
+    {"file://////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file://///foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:///localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+    {"file:///foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file://loCalHost/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file://foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:/localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+    {"file:/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:foo/bar\\baz", 0, 0, S_OK, "file:foo/bar/baz"},
+    {"file:\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:\\\\foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:\\\\\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:\\\\localhost\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:///f oo/b?a r\\baz", 0, 0, S_OK, "file:///f%20oo/b?a r\\baz"},
+    {"file:///foo/b#a r\\baz", 0, 0, S_OK, "file:///foo/b%23a%20r/baz"},
+    {"file:///f o^&`{}|][\"<>\\%o/b#a r\\baz", 0, 0, S_OK, "file:///f%20o%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E/%o/b%23a%20r/baz"},
+    {"file:///f o%o/b?a r\\b%az", URL_ESCAPE_PERCENT, 0, S_OK, "file:///f%20o%25o/b?a r\\b%az"},
+    {"file:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "file:%2Ffoo%2Fbar%5Cbaz"},
+
+    {"foo/b%ar\\ba?z\\", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%ar%5Cba%3Fz%5C"},
+    {"foo/b%ar\\ba?z\\", URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%25ar%5Cba%3Fz%5C"},
+    {"foo/bar\\ba?z\\", 0, 0, S_OK, "foo/bar%5Cba?z\\"},
+    {"/foo/bar\\ba?z\\", 0, 0, S_OK, "/foo/bar%5Cba?z\\"},
+    {"/foo/bar\\ba#z\\", 0, 0, S_OK, "/foo/bar%5Cba#z\\"},
+    {"/foo/%5C", 0, 0, S_OK, "/foo/%5C"},
+    {"/foo/%5C", URL_ESCAPE_PERCENT, 0, S_OK, "/foo/%255C"},
+
+    {"http://////foo/bar\\baz", 0, 0, S_OK, "http://////foo/bar/baz"},
+    {"http://///foo/bar\\baz", 0, 0, S_OK, "http://///foo/bar/baz"},
+    {"http:////foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+    {"http:///foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+    {"http://localhost/foo/bar\\baz", 0, 0, S_OK, "http://localhost/foo/bar/baz"},
+    {"http://foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+    {"http:/foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+    {"http:foo/bar\\ba?z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba?z\\"},
+    {"http:foo/bar\\ba#z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba#z\\"},
+    {"http:\\foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+    {"http:\\\\foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+    {"http:\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+    {"http:\\\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+    {"http:/fo ?o/b ar\\baz", 0, 0, S_OK, "http:/fo%20?o/b ar\\baz"},
+    {"http:fo ?o/b ar\\baz", 0, 0, S_OK, "http:fo%20?o/b ar\\baz"},
+    {"http:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "http:%2Ffoo%2Fbar%5Cbaz"},
+
+    {"https://foo/bar\\baz", 0, 0, S_OK, "https://foo/bar/baz"},
+    {"https:/foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+    {"https:\\foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+
+    {"foo:////foo/bar\\baz", 0, 0, S_OK, "foo:////foo/bar%5Cbaz"},
+    {"foo:///foo/bar\\baz", 0, 0, S_OK, "foo:///foo/bar%5Cbaz"},
+    {"foo://localhost/foo/bar\\baz", 0, 0, S_OK, "foo://localhost/foo/bar%5Cbaz"},
+    {"foo://foo/bar\\baz", 0, 0, S_OK, "foo://foo/bar%5Cbaz"},
+    {"foo:/foo/bar\\baz", 0, 0, S_OK, "foo:/foo/bar%5Cbaz"},
+    {"foo:foo/bar\\baz", 0, 0, S_OK, "foo:foo%2Fbar%5Cbaz"},
+    {"foo:\\foo/bar\\baz", 0, 0, S_OK, "foo:%5Cfoo%2Fbar%5Cbaz"},
+    {"foo:/foo/bar\\ba?\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba?\\z"},
+    {"foo:/foo/bar\\ba#\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba#\\z"},
+
+    {"mailto:/fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:%2Ffo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
+    {"mailto:fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:fo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
+    {"mailto:fo/o@b\\%a?\\r.b#\\az", URL_ESCAPE_PERCENT, 0, S_OK, "mailto:fo%2Fo@b%5C%25a%3F%5Cr.b%23%5Caz"},
+
+    {"ftp:fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:fo%2Fo@bar.baz%2Ffoo%2Fbar"},
+    {"ftp:/fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:/fo/o@bar.baz/foo/bar"},
+    {"ftp://fo/o@bar.baz/fo?o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo?o\\bar"},
+    {"ftp://fo/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo#o\\bar"},
+    {"ftp://localhost/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://localhost/o@bar.baz/fo#o\\bar"},
+    {"ftp:///fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:///fo/o@bar.baz/foo/bar"},
+    {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"}
+};
+
+typedef struct _TEST_URL_COMBINE {
+    const char *url1;
+    const char *url2;
+    DWORD flags;
+    HRESULT expectret;
+    const char *expecturl;
+} TEST_URL_COMBINE;
+
+const TEST_URL_COMBINE TEST_COMBINE[] = {
+    {"http://www.winehq.org/tests", "tests1", 0, S_OK, "http://www.winehq.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/../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/"},
+    {"http://www.winehq.org/tests/../tests/..", "tests7/..", 0, S_OK, "http://www.winehq.org/"},
+    {"http://www.winehq.org/tests/?query=x&return=y", "tests8", 0, S_OK, "http://www.winehq.org/tests/tests8"},
+    {"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"},
+};
+
+struct {
+    const char *path;
+    const char *url;
+    DWORD ret;
+} TEST_URLFROMPATH [] = {
+    {"foo", "file:foo", S_OK},
+    {"foo\\bar", "file:foo/bar", S_OK},
+    {"\\foo\\bar", "file:///foo/bar", S_OK},
+    {"c:\\foo\\bar", "file:///c:/foo/bar", S_OK},
+    {"c:foo\\bar", "file:///c:foo/bar", S_OK},
+    {"c:\\foo/b a%r", "file:///c:/foo/b%20a%25r", S_OK},
+    {"c:\\foo\\foo bar", "file:///c:/foo/foo%20bar", S_OK},
+#if 0
+    /* The following test fails on native shlwapi as distributed with Win95/98.
+     * Wine matches the behaviour of later versions.
+     */
+    {"xx:c:\\foo\\bar", "xx:c:\\foo\\bar", S_FALSE}
+#endif
+};
+
+struct {
+    const char *url;
+    const char *path;
+    DWORD ret;
+} TEST_PATHFROMURL[] = {
+    {"file:///c:/foo/ba%5Cr", "c:\\foo\\ba\\r", S_OK},
+    {"file:///c:/foo/../ba%5Cr", "c:\\foo\\..\\ba\\r", S_OK},
+    {"file:///host/c:/foo/bar", "\\host\\c:\\foo\\bar", S_OK},
+    {"file://host/c:/foo/bar", "\\\\hostc:\\foo\\bar", S_OK},
+    {"file://host/c:/foo/bar", "\\\\hostc:\\foo\\bar", S_OK},
+    {"file:\\\\host\\c:\\foo\\bar", "\\\\hostc:\\foo\\bar", S_OK},
+    {"file:\\\\host\\ca\\foo\\bar", "\\\\host\\ca\\foo\\bar", S_OK},
+    {"file:\\\\host\\c|\\foo\\bar", "\\\\hostc|\\foo\\bar", S_OK},
+    {"file:\\%5Chost\\c:\\foo\\bar", "\\\\host\\c:\\foo\\bar", S_OK},
+    {"file:\\\\host\\cx:\\foo\\bar", "\\\\host\\cx:\\foo\\bar", S_OK},
+    {"file://c:/foo/bar", "c:\\foo\\bar", S_OK},
+    {"file://c:/d:/foo/bar", "c:\\d:\\foo\\bar", S_OK},
+    {"file://c|/d|/foo/bar", "c:\\d|\\foo\\bar", S_OK},
+    {"file://host/foo/bar", "\\\\host\\foo\\bar", S_OK},
+    {"file:/foo/bar", "\\foo\\bar", S_OK},
+    {"file:/foo/bar/", "\\foo\\bar\\", S_OK},
+    {"file:foo/bar", "foo\\bar", S_OK},
+    {"file:c:/foo/bar", "c:\\foo\\bar", S_OK},
+    {"file:c|/foo/bar", "c:\\foo\\bar", S_OK},
+    {"file:cx|/foo/bar", "cx|\\foo\\bar", S_OK},
+    {"file:////c:/foo/bar", "c:\\foo\\bar", S_OK},
+/*    {"file:////c:/foo/foo%20bar", "c:\\foo\\foo%20bar", S_OK},*/
+
+    {"c:\\foo\\bar", NULL, E_INVALIDARG},
+    {"foo/bar", NULL, E_INVALIDARG},
+    {"http://foo/bar", NULL, E_INVALIDARG},
+
+};
+
+struct {
+    char url[30];
+    const char *expect;
+} TEST_URL_UNESCAPE[] = {
+    {"file://foo/bar", "file://foo/bar"},
+    {"file://fo%20o%5Ca/bar", "file://fo o\\a/bar"}
+};
+
+
+struct {
+    const char *path;
+    BOOL expect;
+} TEST_PATH_IS_URL[] = {
+    {"http://foo/bar", TRUE},
+    {"c:\\foo\\bar", FALSE},
+    {"foo://foo/bar", TRUE},
+    {"foo\\bar", FALSE},
+    {"foo.bar", FALSE},
+    {"bogusscheme:", TRUE},
+    {"http:partial", TRUE}
+};
+
+struct {
+    const char *url;
+    BOOL expectOpaque;
+    BOOL expectFile;
+} TEST_URLIS_ATTRIBS[] = {
+    {  "ftp:",                                         FALSE,  FALSE   },
+    {  "http:",                                        FALSE,  FALSE   },
+    {  "gopher:",                                      FALSE,  FALSE   },
+    {  "mailto:",                                      TRUE,   FALSE   },
+    {  "news:",                                        FALSE,  FALSE   },
+    {  "nntp:",                                        FALSE,  FALSE   },
+    {  "telnet:",                                      FALSE,  FALSE   },
+    {  "wais:",                                        FALSE,  FALSE   },
+    {  "file:",                                        FALSE,  TRUE    },
+    {  "mk:",                                          FALSE,  FALSE   },
+    {  "https:",                                       FALSE,  FALSE   },
+    {  "shell:",                                       TRUE,   FALSE   },
+    {  "https:",                                       FALSE,  FALSE   },
+    {   "snews:",                                      FALSE,  FALSE   },
+    {   "local:",                                      FALSE,  FALSE   },
+    {  "javascript:",                                  TRUE,   FALSE   },
+    {  "vbscript:",                                    TRUE,   FALSE   },
+    {  "about:",                                       TRUE,   FALSE   },
+    {   "res:",                                                FALSE,  FALSE   },
+    {  "bogusscheme:",                                 FALSE,  FALSE   },
+    {  "file:\\\\e:\\b\\c",                            FALSE,  TRUE    },
+    {  "file://e:/b/c",                                FALSE,  TRUE    },
+    {  "http:partial",                                 FALSE,  FALSE   },
+    {  "mailto://www.winehq.org/test.html",            TRUE,   FALSE   },
+    {  "file:partial",                                 FALSE,  TRUE    }
+};
+
+
+static LPWSTR GetWideString(const char* szString)
+{
+  LPWSTR wszString = HeapAlloc(GetProcessHeap(), 0, (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
+  
+  MultiByteToWideChar(0, 0, szString, -1, wszString, INTERNET_MAX_URL_LENGTH);
+
+  return wszString;
+}
+
+static void FreeWideString(LPWSTR wszString)
+{
+   HeapFree(GetProcessHeap(), 0, wszString);
+}
+
+static void hash_url(const char* szUrl)
+{
+  LPCSTR szTestUrl = szUrl;
+  LPWSTR wszTestUrl = GetWideString(szTestUrl);
+  
+  DWORD cbSize = sizeof(DWORD);
+  DWORD dwHash1, dwHash2;
+  ok(UrlHashA(szTestUrl, (LPBYTE)&dwHash1, cbSize) == S_OK, "UrlHashA didn't return S_OK\n");
+  ok(UrlHashW(wszTestUrl, (LPBYTE)&dwHash2, cbSize) == S_OK, "UrlHashW didn't return S_OK\n");
+
+  FreeWideString(wszTestUrl);
+
+  ok(dwHash1 == dwHash2, "Hashes didn't compare\n");
+}
+
+static void test_UrlHash(void)
+{
+  hash_url(TEST_URL_1);
+  hash_url(TEST_URL_2);
+  hash_url(TEST_URL_3);
+}
+
+static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const char* szExpected)
+{
+  CHAR szPart[INTERNET_MAX_URL_LENGTH];
+  WCHAR wszPart[INTERNET_MAX_URL_LENGTH];
+  LPWSTR wszUrl = GetWideString(szUrl);
+  LPWSTR wszConvertedPart;
+
+  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);
+  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");
+
+  FreeWideString(wszUrl);
+  FreeWideString(wszConvertedPart);
+
+  /* Note that v6.0 and later don't return '?' with the query */
+  ok(strcmp(szPart,szExpected)==0 ||
+     (*szExpected=='?' && !strcmp(szPart,szExpected+1)),
+        "Expected %s, but got %s\n", szExpected, szPart);
+}
+
+static void test_UrlGetPart(void)
+{
+  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");
+  test_url_part(TEST_URL_3, URL_PART_PASSWORD, 0, "bar");
+  test_url_part(TEST_URL_3, URL_PART_SCHEME, 0, "http");
+  test_url_part(TEST_URL_3, URL_PART_QUERY, 0, "?query=x&return=y");
+}
+
+static void test_url_escape(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+    DWORD dwEscaped;
+    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+    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(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);
+    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);
+    FreeWideString(urlW);
+    FreeWideString(expected_urlW);
+
+}
+
+static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+    WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
+    LPWSTR wszUrl = GetWideString(szUrl);
+    LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
+    LPWSTR wszConvertedUrl;
+    
+    DWORD dwSize;
+    
+    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);
+
+    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);
+    wszConvertedUrl = GetWideString(szReturnUrl);
+    ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n");
+    FreeWideString(wszConvertedUrl);
+    
+            
+    FreeWideString(wszUrl);
+    FreeWideString(wszExpectUrl);
+}
+
+
+static void test_UrlEscape(void)
+{
+    unsigned int i;
+    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);
+    }
+}
+
+static void test_UrlCanonicalize(void)
+{
+    unsigned int i;
+    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);
+    }
+}
+
+static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+    HRESULT hr;
+    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+    WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
+    LPWSTR wszUrl1 = GetWideString(szUrl1);
+    LPWSTR wszUrl2 = GetWideString(szUrl2);
+    LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
+    LPWSTR wszConvertedUrl;
+
+    DWORD dwSize;
+    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);
+    
+    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);
+
+    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);
+    
+    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);
+    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);
+
+    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);
+    
+    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);
+    if(SUCCEEDED(hr)) {
+        wszConvertedUrl = GetWideString(szReturnUrl);
+        ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
+        FreeWideString(wszConvertedUrl);
+    }
+
+    FreeWideString(wszUrl1);
+    FreeWideString(wszUrl2);
+    FreeWideString(wszExpectUrl);
+}
+
+static void test_UrlCombine(void)
+{
+    unsigned int i;
+    for(i=0; i<sizeof(TEST_COMBINE)/sizeof(TEST_COMBINE[0]); i++) {
+        test_url_combine(TEST_COMBINE[i].url1, TEST_COMBINE[i].url2, TEST_COMBINE[i].flags,
+                         TEST_COMBINE[i].expectret, TEST_COMBINE[i].expecturl);
+    }
+}
+
+static void test_UrlCreateFromPath(void)
+{
+    size_t i;
+    char ret_url[INTERNET_MAX_URL_LENGTH];
+    DWORD len, ret;
+    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+    WCHAR *pathW, *urlW;
+
+    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(!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);
+
+        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",
+           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);
+        FreeWideString(urlW);
+        FreeWideString(pathW);
+    }
+}
+
+static void test_UrlIs(void)
+{
+    BOOL ret;
+    size_t i;
+    WCHAR wurl[80];
+
+    for(i = 0; i < sizeof(TEST_PATH_IS_URL) / sizeof(TEST_PATH_IS_URL[0]); i++) {
+       MultiByteToWideChar(CP_ACP, 0, TEST_PATH_IS_URL[i].path, -1, wurl, 80);
+
+        ret = UrlIsA( TEST_PATH_IS_URL[i].path, URLIS_URL );
+        ok( ret == TEST_PATH_IS_URL[i].expect,
+            "returned %d from path %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+            TEST_PATH_IS_URL[i].expect );
+
+        ret = UrlIsW( wurl, URLIS_URL );
+        ok( ret == TEST_PATH_IS_URL[i].expect,
+            "returned %d from path (UrlIsW) %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+            TEST_PATH_IS_URL[i].expect );
+    }
+    for(i = 0; i < sizeof(TEST_URLIS_ATTRIBS) / sizeof(TEST_URLIS_ATTRIBS[0]); i++) {
+       MultiByteToWideChar(CP_ACP, 0, TEST_URLIS_ATTRIBS[i].url, -1, wurl, 80);
+
+        ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_OPAQUE);
+       ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
+           "returned %d for URLIS_OPAQUE, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+           TEST_URLIS_ATTRIBS[i].expectOpaque );
+        ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_FILEURL);
+       ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
+           "returned %d for URLIS_FILEURL, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+           TEST_URLIS_ATTRIBS[i].expectFile );
+
+        ret = UrlIsW( wurl, URLIS_OPAQUE);
+       ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
+           "returned %d for URLIS_OPAQUE (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+           TEST_URLIS_ATTRIBS[i].expectOpaque );
+        ret = UrlIsW( wurl, URLIS_FILEURL);
+       ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
+           "returned %d for URLIS_FILEURL (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+           TEST_URLIS_ATTRIBS[i].expectFile );
+    }
+}
+
+static void test_UrlUnescape(void)
+{
+    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+    WCHAR *urlW, *expected_urlW; 
+    DWORD dwEscaped;
+    size_t i;
+
+    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(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);
+        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);
+        FreeWideString(urlW);
+        FreeWideString(expected_urlW);
+    }
+
+}
+
+static void test_PathSearchAndQualify(void)
+{
+    WCHAR path1[] = {'c',':','\\','f','o','o',0};
+    WCHAR expect1[] = {'c',':','\\','f','o','o',0};
+    WCHAR path2[] = {'c',':','f','o','o',0};
+    WCHAR c_drive[] = {'c',':',0}; 
+    WCHAR foo[] = {'f','o','o',0}; 
+    WCHAR path3[] = {'\\','f','o','o',0};
+    WCHAR winini[] = {'w','i','n','.','i','n','i',0};
+    WCHAR out[MAX_PATH];
+    WCHAR cur_dir[MAX_PATH];
+    WCHAR dot[] = {'.',0};
+
+    /* c:\foo */
+    ok(PathSearchAndQualifyW(path1, out, MAX_PATH) != 0,
+       "PathSearchAndQualify rets 0\n");
+    ok(!lstrcmpiW(out, expect1), "strings don't match\n");
+
+    /* c:foo */
+    ok(PathSearchAndQualifyW(path2, out, MAX_PATH) != 0,
+       "PathSearchAndQualify rets 0\n");
+    GetFullPathNameW(c_drive, MAX_PATH, cur_dir, NULL);
+    PathAddBackslashW(cur_dir);
+    strcatW(cur_dir, foo);
+    ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");    
+
+    /* foo */
+    ok(PathSearchAndQualifyW(foo, out, MAX_PATH) != 0,
+       "PathSearchAndQualify rets 0\n");
+    GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
+    PathAddBackslashW(cur_dir);
+    strcatW(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);
+    ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+    /* win.ini */
+    ok(PathSearchAndQualifyW(winini, out, MAX_PATH) != 0,
+       "PathSearchAndQualify rets 0\n");
+    if(!SearchPathW(NULL, winini, NULL, MAX_PATH, cur_dir, NULL))
+        GetFullPathNameW(winini, MAX_PATH, cur_dir, NULL);
+    ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+}
+
+static void test_PathCreateFromUrl(void)
+{
+    size_t i;
+    char ret_path[INTERNET_MAX_URL_LENGTH];
+    DWORD len, ret;
+    WCHAR ret_pathW[INTERNET_MAX_URL_LENGTH];
+    WCHAR *pathW, *urlW;
+
+    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);
+        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);
+        }
+        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);
+        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);
+        }
+        FreeWideString(urlW);
+        FreeWideString(pathW);
+    }
+}
+
+
+static void test_PathIsUrl(void)
+{
+    size_t i;
+    BOOL ret;
+
+    for(i = 0; i < sizeof(TEST_PATH_IS_URL)/sizeof(TEST_PATH_IS_URL[0]); i++) {
+        ret = PathIsURLA(TEST_PATH_IS_URL[i].path);
+        ok(ret == TEST_PATH_IS_URL[i].expect,
+           "returned %d from path %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+           TEST_PATH_IS_URL[i].expect);
+    }
+}
+
+static const DWORD SHELL_charclass[] =
+{
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000080, 0x00000100, 0x00000200, 0x00000100,
+    0x00000100, 0x00000100, 0x00000100, 0x00000100,
+    0x00000100, 0x00000100, 0x00000002, 0x00000100,
+    0x00000040, 0x00000100, 0x00000004, 0x00000000,
+    0x00000100, 0x00000100, 0x00000100, 0x00000100,
+    0x00000100, 0x00000100, 0x00000100, 0x00000100,
+    0x00000100, 0x00000100, 0x00000010, 0x00000020,
+    0x00000000, 0x00000100, 0x00000000, 0x00000001,
+    0x00000100, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0x00000100,
+    0x00000008, 0x00000100, 0x00000100, 0x00000100,
+    0x00000100, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0x00000100,
+    0x00000000, 0x00000100, 0x00000100
+};
+
+static void test_PathIsValidCharA(void)
+{
+    BOOL ret;
+    unsigned int c;
+
+    ret = pPathIsValidCharA( 0x7f, 0 );
+    ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\n", (DWORD)ret );
+
+    ret = pPathIsValidCharA( 0x7f, 1 );
+    ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\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",
+             c, (DWORD)ret, SHELL_charclass[c] );
+    }
+
+    for (c = 0x7f; c <= 0xff; c++)
+    {
+        ret = pPathIsValidCharA( c, ~0U );
+        ok ( ret == 0x00000100,
+             "PathIsValidCharA failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+             c, (DWORD)ret );
+    }
+}
+
+static void test_PathIsValidCharW(void)
+{
+    BOOL ret;
+    unsigned int c;
+
+    ret = pPathIsValidCharW( 0x7f, 0 );
+    ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\n", (DWORD)ret );
+
+    ret = pPathIsValidCharW( 0x7f, 1 );
+    ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\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",
+             c, (DWORD)ret, SHELL_charclass[c] );
+    }
+
+    for (c = 0x007f; c <= 0xffff; c++)
+    {
+        ret = pPathIsValidCharW( c, ~0U );
+        ok ( ret == 0x00000100,
+             "PathIsValidCharW failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+             c, (DWORD)ret );
+    }
+}
+
+static void test_PathMakePretty(void)
+{
+   char buff[MAX_PATH];
+
+   ok (PathMakePrettyA(NULL) == FALSE, "PathMakePretty: NULL path succeeded\n");
+   buff[0] = '\0';
+   ok (PathMakePrettyA(buff) == TRUE, "PathMakePretty: Empty path failed\n");
+
+   strcpy(buff, "C:\\A LONG FILE NAME WITH \\SPACES.TXT");
+   ok (PathMakePrettyA(buff) == TRUE, "PathMakePretty: Long UC name failed\n");
+   ok (strcmp(buff, "C:\\a long file name with \\spaces.txt") == 0,
+       "PathMakePretty: Long UC name not changed\n");
+
+   strcpy(buff, "C:\\A LONG FILE NAME WITH \\MixedCase.TXT");
+   ok (PathMakePrettyA(buff) == FALSE, "PathMakePretty: Long MC name succeeded\n");
+   ok (strcmp(buff, "C:\\A LONG FILE NAME WITH \\MixedCase.TXT") == 0,
+       "PathMakePretty: Failed but modified path\n");
+
+   strcpy(buff, "TEST");
+   ok (PathMakePrettyA(buff) == TRUE,  "PathMakePretty: Short name failed\n");
+   ok (strcmp(buff, "Test") == 0,  "PathMakePretty: 1st char lowercased %s\n", buff);
+}
+
+START_TEST(path)
+{
+  hShlwapi = LoadLibraryA("shlwapi.dll");
+  if (!hShlwapi) return;
+
+  test_UrlHash();
+  test_UrlGetPart();
+  test_UrlCanonicalize();
+  test_UrlEscape();
+  test_UrlCombine();
+  test_UrlCreateFromPath();
+  test_UrlIs();
+  test_UrlUnescape();
+
+  test_PathSearchAndQualify();
+  test_PathCreateFromUrl();
+  test_PathIsUrl();
+  
+  test_PathMakePretty();
+
+  /* For whatever reason, PathIsValidCharA and PathAppendA share the same
+   * ordinal number in some native versions. Check this to prevent a crash.
+   */
+  pPathIsValidCharA = (void*)GetProcAddress(hShlwapi, (LPSTR)455);
+  if (pPathIsValidCharA && pPathIsValidCharA != (void*)GetProcAddress(hShlwapi, "PathAppendA"))
+  {
+    test_PathIsValidCharA();
+
+     pPathIsValidCharW = (void*)GetProcAddress(hShlwapi, (LPSTR)456);
+     if (pPathIsValidCharW) test_PathIsValidCharW();
+  }
+}
diff --git a/reactos/regtests/winetests/shlwapi/shlwapi_test.xml b/reactos/regtests/winetests/shlwapi/shlwapi_test.xml
new file mode 100755 (executable)
index 0000000..c06f098
--- /dev/null
@@ -0,0 +1,13 @@
+<module name="shlwapi_test" type="win32cui" installbase="bin" installname="shlwapi_test.exe" warnings="true">
+    <include base="shlwapi_test">.</include>
+    <define name="__USE_W32API" />
+    <library>ntdll</library>
+    <library>shlwapi</library>
+    <library>ole32</library>
+    <library>oleaut32</library>
+    <file>clist.c</file>
+    <file>ordinal.c</file>
+    <file>shreg.c</file>
+    <file>string.c</file>
+    <file>testlist.c</file>
+</module>
diff --git a/reactos/regtests/winetests/shlwapi/shreg.c b/reactos/regtests/winetests/shlwapi/shreg.c
new file mode 100755 (executable)
index 0000000..d8d4d92
--- /dev/null
@@ -0,0 +1,385 @@
+/* Unit test suite for SHReg* functions
+ *
+ * Copyright 2002 Juergen Schmied
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "shlwapi.h"
+
+/* Keys used for testing */
+#define REG_TEST_KEY        "Software\\Wine\\Test"
+#define REG_CURRENT_VERSION "Software\\Microsoft\\Windows\\CurrentVersion"
+
+static HMODULE hshlwapi;
+typedef DWORD (WINAPI *SHCopyKeyA_func)(HKEY,LPCSTR,HKEY,DWORD);
+static SHCopyKeyA_func pSHCopyKeyA;
+typedef DWORD (WINAPI *SHRegGetPathA_func)(HKEY,LPCSTR,LPCSTR,LPSTR,DWORD);
+static SHRegGetPathA_func pSHRegGetPathA;
+
+static const char * sTestpath1 = "%LONGSYSTEMVAR%\\subdir1";
+static const 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 const char * sEmptyBuffer ="0123456789";
+
+/* delete key and all its subkeys */
+static DWORD delete_key( HKEY hkey, LPSTR parent, LPSTR keyname )
+{
+    HKEY parentKey;
+    DWORD ret;
+
+    RegCloseKey(hkey);
+
+    /* open the parent of the key to close */
+    ret = RegOpenKeyExA( HKEY_CURRENT_USER, parent, 0, KEY_ALL_ACCESS, &parentKey);
+    if (ret != ERROR_SUCCESS)
+        return ret;
+
+    ret = SHDeleteKeyA( parentKey, keyname );
+    RegCloseKey(parentKey);
+
+    return ret;
+}
+
+static HKEY create_test_entries(void)
+{
+       HKEY hKey;
+        DWORD ret;
+
+        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);
+
+       if (hKey)
+       {
+           ok(!RegSetValueExA(hKey,"Test1",0,REG_EXPAND_SZ, (LPBYTE) sTestpath1, strlen(sTestpath1)+1), "RegSetValueExA failed\n");
+           ok(!RegSetValueExA(hKey,"Test2",0,REG_SZ, (LPBYTE) sTestpath1, strlen(sTestpath1)+1), "RegSetValueExA failed\n");
+           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));
+
+        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);
+
+        return hKey;
+}
+
+static void test_SHGetValue(void)
+{
+       DWORD dwSize;
+       DWORD dwType;
+        DWORD dwRet;
+       char buf[MAX_PATH];
+
+       strcpy(buf, sEmptyBuffer);
+       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( 0 == strcmp(sExpTestpath1, buf), "Comparing of (%s) with (%s) failed\n", buf, sExpTestpath1);
+       ok( REG_SZ == dwType, "Expected REG_SZ, got (%lu)\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( 0 == strcmp(sTestpath1, buf) , "Comparing of (%s) with (%s) failed\n", buf, sTestpath1);
+       ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+}
+
+static void test_SHGetRegPath(void)
+{
+       char buf[MAX_PATH];
+        DWORD dwRet;
+
+       if (!pSHRegGetPathA)
+               return;
+
+       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( 0 == strcmp(sExpTestpath1, buf) , "Comparing (%s) with (%s) failed\n", buf, sExpTestpath1);
+}
+
+static void test_SHQUeryValueEx(void)
+{
+       HKEY hKey;
+       DWORD dwSize;
+       DWORD dwType;
+       char buf[MAX_PATH];
+       DWORD dwRet;
+       const char * sTestedFunction = "";
+       DWORD nUsedBuffer1,nUsedBuffer2;
+
+        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);
+
+       /****** SHQueryValueExA ******/
+
+       sTestedFunction = "SHQueryValueExA";
+       nUsedBuffer1 = max(strlen(sExpTestpath1)+1, strlen(sTestpath1)+1);
+       nUsedBuffer2 = max(strlen(sExpTestpath2)+1, strlen(sTestpath2)+1);
+       /*
+        * 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);
+
+       /*
+        * 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);
+
+       /*
+        * dwSize is set
+         * dwExpanded < dwUnExpanded
+        */
+       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);
+
+       /*
+         * 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);
+
+       /*
+        * Case 1 string shrinks during expanding
+        */
+       strcpy(buf, sEmptyBuffer);
+       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( 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);
+
+       /*
+        * string grows during expanding
+         * dwSize is smaller then 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( 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);
+
+        /*
+         * 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
+         */
+        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);
+
+        todo_wine
+        {
+                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);
+
+       /*
+         * 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
+        */
+       strcpy(buf, sEmptyBuffer);
+       dwSize = sExpLen2 - 4;
+       dwType = -1;
+        dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
+       ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+
+        todo_wine
+        {
+                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);
+
+       /*
+        * The buffer is NULL but the size is set
+        */
+       strcpy(buf, sEmptyBuffer);
+       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);
+
+       RegCloseKey(hKey);
+}
+
+static void test_SHCopyKey(void)
+{
+       HKEY hKeySrc, hKeyDst;
+        DWORD dwRet;
+
+       /* Delete existing destination sub keys */
+       hKeyDst = NULL;
+       if (!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst) && hKeyDst)
+       {
+               SHDeleteKeyA(hKeyDst, NULL);
+               RegCloseKey(hKeyDst);
+       }
+
+       hKeyDst = NULL;
+        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);
+               return;
+       }
+
+       hKeySrc = NULL;
+        dwRet = RegOpenKeyA(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, &hKeySrc);
+        if (dwRet || !hKeySrc)
+       {
+                ok( 0, "Source couldn't be opened, RegOpenKeyA returned (%lu)\n", dwRet);
+               return;
+       }
+
+
+       if (pSHCopyKeyA)
+        {
+                dwRet = (*pSHCopyKeyA)(hKeySrc, NULL, hKeyDst, 0);
+                ok ( ERROR_SUCCESS == dwRet, "Copy failed, ret=(%lu)\n", dwRet);
+        }
+
+       RegCloseKey(hKeySrc);
+       RegCloseKey(hKeyDst);
+
+        /* Check we copied the sub keys, i.e. something that's on every windows system (including Wine) */
+       hKeyDst = NULL;
+        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);
+               return;
+       }
+
+       /* And the we copied the values too */
+       ok(!SHQueryValueExA(hKeyDst, "BootDir", NULL, NULL, NULL, NULL), "SHQueryValueExA failed\n");
+
+       RegCloseKey(hKeyDst);
+}
+
+static void test_SHDeleteKey(void)
+{
+    HKEY hKeyTest, hKeyS;
+    DWORD dwRet;
+    int sysfail = 1;
+
+    if (!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY, &hKeyTest))
+    {
+        if (!RegCreateKey(hKeyTest, "ODBC", &hKeyS))
+        {
+            HKEY hKeyO;
+
+            if (!RegCreateKey(hKeyS, "ODBC.INI", &hKeyO))
+            {
+                RegCloseKey (hKeyO);
+
+                if (!RegCreateKey(hKeyS, "ODBCINST.INI", &hKeyO))
+                {
+                    RegCloseKey (hKeyO);
+                    sysfail = 0;
+                }
+            }
+            RegCloseKey (hKeyS);
+        }
+        RegCloseKey (hKeyTest);
+    }
+
+    if (!sysfail)
+    {
+
+        dwRet = SHDeleteKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC");
+        ok ( ERROR_SUCCESS == dwRet, "SHDeleteKey failed, ret=(%lu)\n", dwRet);
+
+        dwRet = RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC", &hKeyS);
+        ok ( ERROR_FILE_NOT_FOUND == dwRet, "SHDeleteKey did not delete\n");
+
+        if (dwRet == ERROR_SUCCESS)
+            RegCloseKey (hKeyS);
+    }
+    else
+        ok( 0, "Could not set up SHDeleteKey test\n");
+}
+
+START_TEST(shreg)
+{
+       HKEY hkey = create_test_entries();
+
+        if (!hkey) return;
+
+       hshlwapi = GetModuleHandleA("shlwapi.dll");
+       if (hshlwapi)
+       {
+               pSHCopyKeyA=(SHCopyKeyA_func)GetProcAddress(hshlwapi,"SHCopyKeyA");
+               pSHRegGetPathA=(SHRegGetPathA_func)GetProcAddress(hshlwapi,"SHRegGetPathA");
+       }
+       test_SHGetValue();
+       test_SHQUeryValueEx();
+       test_SHGetRegPath();
+       test_SHCopyKey();
+        test_SHDeleteKey();
+        delete_key( hkey, "Software\\Wine", "Test" );
+}
diff --git a/reactos/regtests/winetests/shlwapi/string.c b/reactos/regtests/winetests/shlwapi/string.c
new file mode 100755 (executable)
index 0000000..47d9a6c
--- /dev/null
@@ -0,0 +1,760 @@
+/* Unit test suite for SHLWAPI string functions
+ *
+ * Copyright 2003 Jon Griffiths
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+#define NO_SHLWAPI_REG
+#define NO_SHLWAPI_PATH
+#define NO_SHLWAPI_GDI
+#define NO_SHLWAPI_STREAM
+#include "shlwapi.h"
+#include "shtypes.h"
+
+static HMODULE hShlwapi;
+static LPSTR   (WINAPI *pStrCpyNXA)(LPSTR,LPCSTR,int);
+static LPWSTR  (WINAPI *pStrCpyNXW)(LPWSTR,LPCWSTR,int);
+static HRESULT (WINAPI *pStrRetToBSTR)(STRRET*,void*,BSTR*);
+static DWORD   (WINAPI *pSHAnsiToAnsi)(LPCSTR,LPSTR,int);
+static DWORD   (WINAPI *pSHUnicodeToUnicode)(LPCWSTR,LPWSTR,int);
+static BOOL    (WINAPI *pStrIsIntlEqualA)(BOOL,LPCSTR,LPCSTR,int);
+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)
+{
+    while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
+/* StrToInt/StrToIntEx results */
+typedef struct tagStrToIntResult
+{
+  const char* string;
+  int str_to_int;
+  int str_to_int_ex;
+  int str_to_int_hex;
+} StrToIntResult;
+
+static const StrToIntResult StrToInt_results[] = {
+     { "1099", 1099, 1099, 1099 },
+     { "+88987", 0, 88987, 88987 },
+     { "012", 12, 12, 12 },
+     { "-55", -55, -55, -55 },
+     { "-0", 0, 0, 0 },
+     { "0x44ff", 0, 0, 0x44ff },
+     { "+0x44f4", 0, 0, 0x44f4 },
+     { "-0x44fd", 0, 0, 0x44fd },
+     { "+ 88987", 0, 0, 0 },
+     { "- 55", 0, 0, 0 },
+     { "- 0", 0, 0, 0 },
+     { "+ 0x44f4", 0, 0, 0 },
+     { "--0x44fd", 0, 0, 0 },
+     { " 1999", 0, 1999, 1999 },
+     { " +88987", 0, 88987, 88987 },
+     { " 012", 0, 12, 12 },
+     { " -55", 0, -55, -55 },
+     { " 0x44ff", 0, 0, 0x44ff },
+     { " +0x44f4", 0, 0, 0x44f4 },
+     { " -0x44fd", 0, 0, 0x44fd },
+     { NULL, 0, 0, 0 }
+};
+
+/* pStrFormatByteSize64/StrFormatKBSize results */
+typedef struct tagStrFormatSizeResult
+{
+  LONGLONG value;
+  const char* byte_size_64;
+  const char* kb_size;
+} StrFormatSizeResult;
+
+
+static const StrFormatSizeResult StrFormatSize_results[] = {
+  { -1023, "-1023 bytes", "0 KB"},
+  { -24, "-24 bytes", "0 KB"},
+  { 309, "309 bytes", "1 KB"},
+  { 10191, "9.95 KB", "10 KB"},
+  { 100353, "98.0 KB", "99 KB"},
+  { 1022286, "998 KB", "999 KB"},
+  { 1046862, "0.99 MB", "1,023 KB"},
+  { 1048574619, "999 MB", "1,023,999 KB"},
+  { 1073741775, "0.99 GB", "1,048,576 KB"},
+  { ((LONGLONG)0x000000f9 << 32) | 0xfffff94e, "999 GB", "1,048,575,999 KB"},
+  { ((LONGLONG)0x000000ff << 32) | 0xfffffa9b, "0.99 TB", "1,073,741,823 KB"},
+  { ((LONGLONG)0x0003e7ff << 32) | 0xfffffa9b, "999 TB", "1,073,741,823,999 KB"},
+  { ((LONGLONG)0x0003ffff << 32) | 0xfffffbe8, "0.99 PB", "1,099,511,627,775 KB"},
+  { ((LONGLONG)0x0f9fffff << 32) | 0xfffffd35, "999 PB", "1,099,511,627,776,000 KB"},
+  { ((LONGLONG)0x0fffffff << 32) | 0xfffffa9b, "0.99 EB", "1,125,899,906,842,623 KB"},
+  { 0, NULL, NULL }
+};
+
+/* StrFormatByteSize64/StrFormatKBSize results */
+typedef struct tagStrFromTimeIntervalResult
+{
+  DWORD ms;
+  int   digits;
+  const char* time_interval;
+} StrFromTimeIntervalResult;
+
+
+static const StrFromTimeIntervalResult StrFromTimeInterval_results[] = {
+  { 1, 1, " 0 sec" },
+  { 1, 2, " 0 sec" },
+  { 1, 3, " 0 sec" },
+  { 1, 4, " 0 sec" },
+  { 1, 5, " 0 sec" },
+  { 1, 6, " 0 sec" },
+  { 1, 7, " 0 sec" },
+
+  { 1000000, 1, " 10 min" },
+  { 1000000, 2, " 16 min" },
+  { 1000000, 3, " 16 min 40 sec" },
+  { 1000000, 4, " 16 min 40 sec" },
+  { 1000000, 5, " 16 min 40 sec" },
+  { 1000000, 6, " 16 min 40 sec" },
+  { 1000000, 7, " 16 min 40 sec" },
+
+  { 1999999, 1, " 30 min" },
+  { 1999999, 2, " 33 min" },
+  { 1999999, 3, " 33 min 20 sec" },
+  { 1999999, 4, " 33 min 20 sec" },
+  { 1999999, 5, " 33 min 20 sec" },
+  { 1999999, 6, " 33 min 20 sec" },
+  { 1999999, 7, " 33 min 20 sec" },
+
+  { 3999997, 1, " 1 hr" },
+  { 3999997, 2, " 1 hr 6 min" },
+  { 3999997, 3, " 1 hr 6 min 40 sec" },
+  { 3999997, 4, " 1 hr 6 min 40 sec" },
+  { 3999997, 5, " 1 hr 6 min 40 sec" },
+  { 3999997, 6, " 1 hr 6 min 40 sec" },
+  { 3999997, 7, " 1 hr 6 min 40 sec" },
+
+  { 149999851, 7, " 41 hr 40 min 0 sec" },
+  { 150999850, 1, " 40 hr" },
+  { 150999850, 2, " 41 hr" },
+  { 150999850, 3, " 41 hr 50 min" },
+  { 150999850, 4, " 41 hr 56 min" },
+  { 150999850, 5, " 41 hr 56 min 40 sec" },
+  { 150999850, 6, " 41 hr 56 min 40 sec" },
+  { 150999850, 7, " 41 hr 56 min 40 sec" },
+
+  { 493999507, 1, " 100 hr" },
+  { 493999507, 2, " 130 hr" },
+  { 493999507, 3, " 137 hr" },
+  { 493999507, 4, " 137 hr 10 min" },
+  { 493999507, 5, " 137 hr 13 min" },
+  { 493999507, 6, " 137 hr 13 min 20 sec" },
+  { 493999507, 7, " 137 hr 13 min 20 sec" },
+
+  { 0, 0, NULL }
+};
+
+static void test_StrChrA(void)
+{
+  char string[129];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrChrA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 128; count++)
+    string[count] = (char)count;
+  string[128] = '\0';
+
+  for (count = 32; count < 128; count++)
+  {
+    LPSTR result = StrChrA(string+32, count);
+    ok(result - string == count,
+        "found char '%c' in wrong place: got %d, expected %d\n",
+        count, result - string, count);
+  }
+
+  for (count = 32; count < 128; count++)
+  {
+    LPSTR result = StrChrA(string+count+1, count);
+    ok(!result, "found char '%c' not in the string\n", count);
+  }
+}
+
+static void test_StrChrW(void)
+{
+  WCHAR string[16385];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrChrW(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 16384; count++)
+    string[count] = count;
+  string[16384] = '\0';
+
+  for (count = 32; count < 16384; count++)
+  {
+    LPWSTR result = StrChrW(string+32, count);
+    ok((result - string) == count, "found char %d in wrong place\n", count);
+  }
+
+  for (count = 32; count < 16384; count++)
+  {
+    LPWSTR result = StrChrW(string+count+1, count);
+    ok(!result, "found char not in the string\n");
+  }
+}
+
+static void test_StrChrIA(void)
+{
+  char string[129];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrChrIA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 128; count++)
+    string[count] = (char)count;
+  string[128] = '\0';
+
+  for (count = 'A'; count <= 'X'; count++)
+  {
+    LPSTR result = StrChrIA(string+32, count);
+
+    ok(result - string == count, "found char '%c' in wrong place\n", count);
+    ok(StrChrIA(result, count)!=NULL, "didn't find lowercase '%c'\n", count);
+  }
+
+  for (count = 'a'; count < 'z'; count++)
+  {
+    LPSTR result = StrChrIA(string+count+1, count);
+    ok(!result, "found char not in the string\n");
+  }
+}
+
+static void test_StrChrIW(void)
+{
+  WCHAR string[129];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrChrIA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 128; count++)
+    string[count] = count;
+  string[128] = '\0';
+
+  for (count = 'A'; count <= 'X'; count++)
+  {
+    LPWSTR result = StrChrIW(string+32, count);
+
+    ok(result - string == count, "found char '%c' in wrong place\n", count);
+    ok(StrChrIW(result, count)!=NULL, "didn't find lowercase '%c'\n", count);
+  }
+
+  for (count = 'a'; count < 'z'; count++)
+  {
+    LPWSTR result = StrChrIW(string+count+1, count);
+    ok(!result, "found char not in the string\n");
+  }
+}
+
+static void test_StrRChrA(void)
+{
+  char string[129];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrRChrA(NULL, NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 128; count++)
+    string[count] = (char)count;
+  string[128] = '\0';
+
+  for (count = 32; count < 128; count++)
+  {
+    LPSTR result = StrRChrA(string+32, NULL, count);
+    ok(result - string == count, "found char %d in wrong place\n", count);
+  }
+
+  for (count = 32; count < 128; count++)
+  {
+    LPSTR result = StrRChrA(string+count+1, NULL, count);
+    ok(!result, "found char not in the string\n");
+  }
+
+  for (count = 32; count < 128; count++)
+  {
+    LPSTR result = StrRChrA(string+count+1, string + 127, count);
+    ok(!result, "found char not in the string\n");
+  }
+}
+
+static void test_StrRChrW(void)
+{
+  WCHAR string[129];
+  WORD count;
+
+  /* this test crashes on win2k SP4 */
+  /*ok(!StrRChrW(NULL, NULL,'\0'), "found a character in a NULL string!\n");*/
+
+  for (count = 32; count < 128; count++)
+    string[count] = count;
+  string[128] = '\0';
+
+  for (count = 32; count < 128; count++)
+  {
+    LPWSTR result = StrRChrW(string+32, NULL, count);
+    ok(result - string == count,
+        "found char %d in wrong place: got %d, expected %d\n",
+        count, result - string, count);
+  }
+
+  for (count = 32; count < 128; count++)
+  {
+    LPWSTR result = StrRChrW(string+count+1, NULL, count);
+    ok(!result, "found char %d not in the string\n", count);
+  }
+
+  for (count = 32; count < 128; count++)
+  {
+    LPWSTR result = StrRChrW(string+count+1, string + 127, count);
+    ok(!result, "found char %d not in the string\n", count);
+  }
+}
+
+static void test_StrCpyW(void)
+{
+  WCHAR szSrc[256];
+  WCHAR szBuff[256];
+  const StrFormatSizeResult* result = StrFormatSize_results;
+
+
+  while(result->value)
+  {
+    MultiByteToWideChar(0,0,result->byte_size_64,-1,szSrc,sizeof(szSrc)/sizeof(WCHAR));
+
+    StrCpyW(szBuff, szSrc);
+    ok(!StrCmpW(szSrc, szBuff), "Copied string %s wrong\n", result->byte_size_64);
+    result++;
+  }
+}
+
+
+static void test_StrToIntA(void)
+{
+  const StrToIntResult *result = StrToInt_results;
+  int return_val;
+
+  while (result->string)
+  {
+    return_val = StrToIntA(result->string);
+    ok(return_val == result->str_to_int, "converted '%s' wrong (%d)\n",
+       result->string, return_val);
+    result++;
+  }
+}
+
+static void test_StrToIntW(void)
+{
+  WCHAR szBuff[256];
+  const StrToIntResult *result = StrToInt_results;
+  int return_val;
+
+  while (result->string)
+  {
+    MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+    return_val = StrToIntW(szBuff);
+    ok(return_val == result->str_to_int, "converted '%s' wrong (%d)\n",
+       result->string, return_val);
+    result++;
+  }
+}
+
+static void test_StrToIntExA(void)
+{
+  const StrToIntResult *result = StrToInt_results;
+  int return_val;
+  BOOL bRet;
+
+  while (result->string)
+  {
+    return_val = -1;
+    bRet = StrToIntExA(result->string,0,&return_val);
+    ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+       result->string);
+    if (bRet)
+      ok(return_val == result->str_to_int_ex, "converted '%s' wrong (%d)\n",
+         result->string, return_val);
+    result++;
+  }
+
+  result = StrToInt_results;
+  while (result->string)
+  {
+    return_val = -1;
+    bRet = StrToIntExA(result->string,STIF_SUPPORT_HEX,&return_val);
+    ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+       result->string);
+    if (bRet)
+      ok(return_val == result->str_to_int_hex, "converted '%s' wrong (%d)\n",
+         result->string, return_val);
+    result++;
+  }
+}
+
+static void test_StrToIntExW(void)
+{
+  WCHAR szBuff[256];
+  const StrToIntResult *result = StrToInt_results;
+  int return_val;
+  BOOL bRet;
+
+  while (result->string)
+  {
+    return_val = -1;
+    MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+    bRet = StrToIntExW(szBuff, 0, &return_val);
+    ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+       result->string);
+    if (bRet)
+      ok(return_val == result->str_to_int_ex, "converted '%s' wrong (%d)\n",
+         result->string, return_val);
+    result++;
+  }
+
+  result = StrToInt_results;
+  while (result->string)
+  {
+    return_val = -1;
+    MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+    bRet = StrToIntExW(szBuff, STIF_SUPPORT_HEX, &return_val);
+    ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+       result->string);
+    if (bRet)
+      ok(return_val == result->str_to_int_hex, "converted '%s' wrong (%d)\n",
+         result->string, return_val);
+    result++;
+  }
+}
+
+static void test_StrDupA(void)
+{
+  LPSTR lpszStr;
+  const StrFormatSizeResult* result = StrFormatSize_results;
+
+  while(result->value)
+  {
+    lpszStr = StrDupA(result->byte_size_64);
+
+    ok(lpszStr != NULL, "Dup failed\n");
+    if (lpszStr)
+    {
+      ok(!strcmp(result->byte_size_64, lpszStr), "Copied string wrong\n");
+      LocalFree((HLOCAL)lpszStr);
+    }
+    result++;
+  }
+
+  /* Later versions of shlwapi return NULL for this, but earlier versions
+   * returned an empty string (as Wine does).
+   */
+  lpszStr = StrDupA(NULL);
+  ok(lpszStr == NULL || *lpszStr == '\0', "NULL string returned %p\n", lpszStr);
+}
+
+static void test_StrFormatByteSize64A(void)
+{
+  char szBuff[256];
+  const StrFormatSizeResult* result = StrFormatSize_results;
+
+  while(result->value)
+  {
+    StrFormatByteSize64A(result->value, szBuff, 256);
+
+    ok(!strcmp(result->byte_size_64, szBuff),
+        "Formatted %lx%08lx wrong: got %s, expected %s\n",
+       (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->byte_size_64);
+
+    result++;
+  }
+}
+
+static void test_StrFormatKBSizeW(void)
+{
+  WCHAR szBuffW[256];
+  char szBuff[256];
+  const StrFormatSizeResult* result = StrFormatSize_results;
+
+  while(result->value)
+  {
+    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",
+       (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
+    result++;
+  }
+}
+
+static void test_StrFormatKBSizeA(void)
+{
+  char szBuff[256];
+  const StrFormatSizeResult* result = StrFormatSize_results;
+
+  while(result->value)
+  {
+    StrFormatKBSizeA(result->value, szBuff, 256);
+
+    ok(!strcmp(result->kb_size, szBuff),
+        "Formatted %lx%08lx wrong: got %s, expected %s\n",
+       (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
+    result++;
+  }
+}
+
+static void test_StrFromTimeIntervalA(void)
+{
+  char szBuff[256];
+  const StrFromTimeIntervalResult* result = StrFromTimeInterval_results;
+
+  while(result->ms)
+  {
+    StrFromTimeIntervalA(szBuff, 256, result->ms, result->digits);
+
+    ok(!strcmp(result->time_interval, szBuff), "Formatted %ld %d wrong\n",
+       result->ms, result->digits);
+    result++;
+  }
+}
+
+static void test_StrCmpA(void)
+{
+  static const char str1[] = {'a','b','c','d','e','f'};
+  static const char str2[] = {'a','B','c','d','e','f'};
+  ok(0 != StrCmpNA(str1, str2, 6), "StrCmpNA is case-insensitive\n");
+  ok(0 == StrCmpNIA(str1, str2, 6), "StrCmpNIA is case-sensitive\n");
+  ok(!ChrCmpIA('a', 'a'), "ChrCmpIA doesn't work at all!\n");
+  ok(!ChrCmpIA('b', 'B'), "ChrCmpIA is not case-insensitive\n");
+  ok(ChrCmpIA('a', 'z'), "ChrCmpIA believes that a == z!\n");
+
+  pStrIsIntlEqualA = (void *)GetProcAddress(hShlwapi, "StrIsIntlEqualA");
+  pIntlStrEqWorkerA = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerA");
+
+  if (!pStrIsIntlEqualA)
+    return;
+
+  ok(pStrIsIntlEqualA(FALSE, str1, str2, 5), "StrIsIntlEqualA(FALSE,...) isn't case-insensitive\n");
+  ok(!pStrIsIntlEqualA(TRUE, str1, str2, 5), "StrIsIntlEqualA(TRUE,...) isn't case-sensitive\n");
+
+  if (!pIntlStrEqWorkerA)
+    return;
+
+  ok(pIntlStrEqWorkerA(FALSE, str1, str2, 5), "IntlStrEqWorkerA(FALSE,...) isn't case-insensitive\n");
+  ok(!pIntlStrEqWorkerA(TRUE, str1, str2, 5), "pIntlStrEqWorkerA(TRUE,...) isn't case-sensitive\n");
+}
+
+static void test_StrCmpW(void)
+{
+  static const WCHAR str1[] = {'a','b','c','d','e','f'};
+  static const WCHAR str2[] = {'a','B','c','d','e','f'};
+  ok(0 != StrCmpNW(str1, str2, 5), "StrCmpNW is case-insensitive\n");
+  ok(0 == StrCmpNIW(str1, str2, 5), "StrCmpNIW is case-sensitive\n");
+  ok(!ChrCmpIW('a', 'a'), "ChrCmpIW doesn't work at all!\n");
+  ok(!ChrCmpIW('b', 'B'), "ChrCmpIW is not case-insensitive\n");
+  ok(ChrCmpIW('a', 'z'), "ChrCmpIW believes that a == z!\n");
+
+  pStrIsIntlEqualW = (void *)GetProcAddress(hShlwapi, "StrIsIntlEqualW");
+  pIntlStrEqWorkerW = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerW");
+
+  if (!pStrIsIntlEqualW)
+    return;
+
+  ok(pStrIsIntlEqualW(FALSE, str1, str2, 5), "StrIsIntlEqualW(FALSE,...) isn't case-insensitive\n");
+  ok(!pStrIsIntlEqualW(TRUE, str1, str2, 5), "StrIsIntlEqualW(TRUE,...) isn't case-sensitive\n");
+
+  if (!pIntlStrEqWorkerW)
+    return;
+
+  ok(pIntlStrEqWorkerW(FALSE, str1, str2, 5), "IntlStrEqWorkerW(FALSE,...) isn't case-insensitive\n");
+  ok(!pIntlStrEqWorkerW(TRUE, str1, str2, 5), "IntlStrEqWorkerW(TRUE,...) isn't case-sensitive\n");
+}
+
+static WCHAR *CoDupStrW(const char* src)
+{
+  INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
+  WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
+  MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
+  return szTemp;
+}
+
+static void test_StrRetToBSTR(void)
+{
+    static const WCHAR szTestW[] = { 'T','e','s','t','\0' };
+    ITEMIDLIST iidl[10];
+    BSTR bstr;
+    STRRET strret;
+    HRESULT ret;
+
+    pStrRetToBSTR = (void *)GetProcAddress(hShlwapi, "StrRetToBSTR");
+    if (!pStrRetToBSTR) return;
+
+    strret.uType = STRRET_WSTR;
+    strret.u.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);
+    if (bstr)
+      SysFreeString(bstr);
+
+    strret.uType = STRRET_CSTR;
+    lstrcpyA(strret.u.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);
+    if (bstr)
+      SysFreeString(bstr);
+
+    strret.uType = STRRET_OFFSET;
+    strret.u.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);
+    if (bstr)
+      SysFreeString(bstr);
+
+    /* Native crashes if str is NULL */
+}
+
+static void test_StrCpyNXA(void)
+{
+  LPCSTR lpSrc = "hello";
+  LPSTR lpszRes;
+  char dest[8];
+
+  pStrCpyNXA = (void *)GetProcAddress(hShlwapi, (LPSTR)399);
+  if (!pStrCpyNXA)
+    return;
+
+  memset(dest, '\n', sizeof(dest));
+  lpszRes = pStrCpyNXA(dest, lpSrc, sizeof(dest)/sizeof(dest[0]));
+  ok(lpszRes == dest + 5 && !memcmp(dest, "hello\0\n\n", sizeof(dest)),
+       "StrCpyNXA: expected %p, \"hello\\0\\n\\n\", got %p, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+       dest + 5, lpszRes, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_StrCpyNXW(void)
+{
+  static const WCHAR lpInit[] = { '\n','\n','\n','\n','\n','\n','\n','\n' };
+  static const WCHAR lpSrc[] = { 'h','e','l','l','o','\0' };
+  static const WCHAR lpRes[] = { 'h','e','l','l','o','\0','\n','\n' };
+  LPWSTR lpszRes;
+  WCHAR dest[8];
+
+  pStrCpyNXW = (void *)GetProcAddress(hShlwapi, (LPSTR)400);
+  if (!pStrCpyNXW)
+    return;
+
+  memcpy(dest, lpInit, sizeof(lpInit));
+  lpszRes = pStrCpyNXW(dest, lpSrc, sizeof(dest)/sizeof(dest[0]));
+  ok(lpszRes == dest + 5 && !memcmp(dest, lpRes, sizeof(dest)),
+       "StrCpyNXA: expected %p, \"hello\\0\\n\\n\", got %p, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+       dest + 5, lpszRes, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_SHAnsiToAnsi(void)
+{
+  char dest[8];
+  DWORD dwRet;
+
+  pSHAnsiToAnsi = (void *)GetProcAddress(hShlwapi, (LPSTR)345);
+  if (!pSHAnsiToAnsi)
+    return;
+
+  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",
+     dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_SHUnicodeToUnicode(void)
+{
+  static const WCHAR lpInit[] = { '\n','\n','\n','\n','\n','\n','\n','\n' };
+  static const WCHAR lpSrc[] = { 'h','e','l','l','o','\0' };
+  static const WCHAR lpRes[] = { 'h','e','l','l','o','\0','\n','\n' };
+  WCHAR dest[8];
+  DWORD dwRet;
+
+  pSHUnicodeToUnicode = (void *)GetProcAddress(hShlwapi, (LPSTR)346);
+  if (!pSHUnicodeToUnicode)
+    return;
+
+  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",
+     dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+START_TEST(string)
+{
+  CoInitialize(0);
+
+  hShlwapi = GetModuleHandleA("shlwapi");
+  if (!hShlwapi)
+     return;
+
+  test_StrChrA();
+  test_StrChrW();
+  test_StrChrIA();
+  test_StrChrIW();
+  test_StrRChrA();
+  test_StrRChrW();
+  test_StrCpyW();
+  test_StrToIntA();
+  test_StrToIntW();
+  test_StrToIntExA();
+  test_StrToIntExW();
+  test_StrDupA();
+  if (0)
+  {
+    /* this test fails on locales which do not use '.' as a decimal separator */
+    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();
+  test_StrCmpA();
+  test_StrCmpW();
+  test_StrRetToBSTR();
+  test_StrCpyNXA();
+  test_StrCpyNXW();
+  test_SHAnsiToAnsi();
+  test_SHUnicodeToUnicode();
+}
diff --git a/reactos/regtests/winetests/shlwapi/testlist.c b/reactos/regtests/winetests/shlwapi/testlist.c
new file mode 100755 (executable)
index 0000000..16fec43
--- /dev/null
@@ -0,0 +1,32 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+/* stdarg.h is needed for Winelib */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+
+extern void func_clist(void);
+extern void func_ordinal(void);
+extern void func_path(void);
+extern void func_shreg(void);
+extern void func_string(void);
+
+struct test
+{
+    const char *name;
+    void (*func)(void);
+};
+
+const struct test winetest_testlist[] =
+{
+    { "clist", func_clist },
+    { "ordinal", func_ordinal },
+    { "shreg", func_shreg },
+    { "string", func_string },
+    { 0, 0 }
+};
+
+#define WINETEST_WANT_MAIN
+#include "wine/test.h"