Implementation and test for SHCopyKeyA/W.

diff --git a/dlls/shlwapi/reg.c b/dlls/shlwapi/reg.c
index 3e114d3..b5ff9b2 100644
--- a/dlls/shlwapi/reg.c
+++ b/dlls/shlwapi/reg.c
@@ -1792,3 +1792,135 @@
     TRACE("new key is %08x\n", newKey);
     return newKey;
 }
+
+
+/*************************************************************************
+ * SHCopyKeyA	[SHLWAPI.@]
+ *
+ * Copy a key and its values/sub keys to another location.
+ *
+ * PARAMS
+ *  hKeyDst    [I] Destination key
+ *  lpszSubKey [I] Sub key under hKeyDst, or NULL to use hKeyDst directly
+ *  hKeySrc    [I] Source key to copy from
+ *  dwReserved [I] Reserved, must be 0
+ *
+ * RETURNS
+ *  Success: ERROR_SUCCESS. The key is copied to the destination key.
+ *  Failure: A standard windows error code.
+ *
+ * NOTES
+ *  If hKeyDst is a key under hKeySrc, this function will misbehave
+ *  (It will loop until out of stack, or the registry is full).
+ */
+DWORD WINAPI SHCopyKeyA(HKEY hKeyDst, LPCSTR lpszSubKey, HKEY hKeySrc, DWORD dwReserved)
+{
+  WCHAR szSubKeyW[MAX_PATH];
+
+  TRACE("(hkey=0x%08x,%s,%0x08x,%ld)\n", hKeyDst, debugstr_a(lpszSubKey), hKeySrc, dwReserved);
+
+  if (lpszSubKey)
+    MultiByteToWideChar(0, 0, lpszSubKey, -1, szSubKeyW, MAX_PATH);
+
+  return SHCopyKeyW(hKeyDst, lpszSubKey ? szSubKeyW : NULL, hKeySrc, dwReserved);
+}
+
+/*************************************************************************
+ * SHCopyKeyW	[SHLWAPI.@]
+ *
+ * See SHCopyKeyA.
+ */
+DWORD WINAPI SHCopyKeyW(HKEY hKeyDst, LPCWSTR lpszSubKey, HKEY hKeySrc, DWORD dwReserved)
+{
+  DWORD dwKeyCount = 0, dwValueCount = 0, dwMaxKeyLen = 0;
+  DWORD  dwMaxValueLen = 0, dwMaxDataLen = 0, i;
+  BYTE buff[1024];
+  LPVOID lpBuff = (LPVOID)buff;
+  WCHAR szName[MAX_PATH], *lpszName = szName;
+  DWORD dwRet = S_OK;
+
+  TRACE("hkey=0x%08x,%s,%0x08x,%ld)\n", hKeyDst, debugstr_w(lpszSubKey), hKeySrc, dwReserved);
+
+  if(!hKeyDst || !hKeySrc)
+    dwRet = ERROR_INVALID_PARAMETER;
+  else
+  {
+    /* Open destination key */
+    if(lpszSubKey)
+      dwRet = RegOpenKeyExW(hKeyDst, lpszSubKey, 0, KEY_ALL_ACCESS, &hKeyDst);
+
+    if(dwRet)
+      hKeyDst = 0; /* Don't close this key since we didn't open it */
+    else
+    {
+      /* Get details about sub keys and values */
+      dwRet = RegQueryInfoKeyW(hKeySrc, NULL, NULL, NULL, &dwKeyCount, &dwMaxKeyLen,
+                               NULL, &dwValueCount, &dwMaxValueLen, &dwMaxDataLen,
+                               NULL, NULL);
+      if(!dwRet)
+      {
+        if (dwMaxValueLen > dwMaxKeyLen)
+          dwMaxKeyLen = dwMaxValueLen; /* Get max size for key/value names */
+
+        if (dwMaxKeyLen++ > MAX_PATH - 1)
+          lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLen * sizeof(WCHAR));
+
+        if (dwMaxDataLen > sizeof(buff))
+          lpBuff = HeapAlloc(GetProcessHeap(), 0, dwMaxDataLen);
+
+        if (!lpszName || !lpBuff)
+          dwRet = ERROR_NOT_ENOUGH_MEMORY;
+      }
+    }
+  }
+
+  /* Copy all the sub keys */
+  for(i = 0; i < dwKeyCount && !dwRet; i++)
+  {
+    HKEY hSubKeySrc, hSubKeyDst;
+    DWORD dwSize = dwMaxKeyLen;
+
+    dwRet = RegEnumKeyExW(hKeySrc, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
+
+    if(!dwRet)
+    {
+      /* Open source sub key */
+      dwRet = RegOpenKeyExW(hKeySrc, lpszName, 0, KEY_READ, &hSubKeySrc);
+
+      if(!dwRet)
+      {
+        /* Create destination sub key */
+        dwRet = RegCreateKeyW(hKeyDst, lpszName, &hSubKeyDst);
+
+        if(!dwRet)
+        {
+          /* Recursively copy keys and values from the sub key */
+          dwRet = SHCopyKeyW(hSubKeyDst, NULL, hSubKeySrc, 0);
+          RegCloseKey(hSubKeyDst);
+        }
+      }
+      RegCloseKey(hSubKeySrc);
+    }
+  }
+
+  /* Copy all the values in this key */
+  for (i = 0; i < dwValueCount && !dwRet; i++)
+  {
+    DWORD dwNameSize = dwMaxKeyLen, dwType, dwLen = dwMaxDataLen;
+
+    dwRet = RegEnumValueW(hKeySrc, i, lpszName, &dwNameSize, NULL, &dwType, buff, &dwLen);
+
+    if (!dwRet)
+      dwRet = SHSetValueW(hKeyDst, NULL, lpszName, dwType, lpBuff, dwLen);
+  }
+
+  /* Free buffers if allocated */
+  if (lpszName != szName)
+    HeapFree(GetProcessHeap(), 0, lpszName);
+  if (lpBuff != buff)
+    HeapFree(GetProcessHeap(), 0, lpBuff);
+
+  if (lpszSubKey && hKeyDst)
+    RegCloseKey(hKeyDst);
+  return dwRet;
+}
diff --git a/dlls/shlwapi/shlwapi.spec b/dlls/shlwapi/shlwapi.spec
index ca19e35..9687c78 100644
--- a/dlls/shlwapi/shlwapi.spec
+++ b/dlls/shlwapi/shlwapi.spec
@@ -705,8 +705,8 @@
 @ stdcall PathUndecorateW(wstr) PathUndecorateW
 @ stub    PathUnExpandEnvStringsA
 @ stub    PathUnExpandEnvStringsW
-@ stub    SHCopyKeyA
-@ stub    SHCopyKeyW
+@ stdcall SHCopyKeyA(long str long long) SHCopyKeyA
+@ stdcall SHCopyKeyW(long wstr long long) SHCopyKeyW
 @ stub    SHAutoComplete
 @ stdcall SHCreateStreamOnFileA(str long ptr) SHCreateStreamOnFileA
 @ stdcall SHCreateStreamOnFileW(wstr long ptr) SHCreateStreamOnFileW
diff --git a/dlls/shlwapi/tests/shreg.c b/dlls/shlwapi/tests/shreg.c
index fd7635a..e7e0a01 100644
--- a/dlls/shlwapi/tests/shreg.c
+++ b/dlls/shlwapi/tests/shreg.c
@@ -28,6 +28,10 @@
 #include "winuser.h"
 #include "shlwapi.h"
 
+// Keys used for testing
+#define REG_TEST_KEY        "Software\\Wine\\Test"
+#define REG_CURRENT_VERSION "Software\\Microsoft\\Windows NT\\CurrentVersion"
+
 static char * sTestpath1 = "%LONGSYSTEMVAR%\\subdir1";
 static char * sTestpath2 = "%FOO%\\subdir1";
 
@@ -45,7 +49,7 @@
         SetEnvironmentVariableA("LONGSYSTEMVAR", "bar");
         SetEnvironmentVariableA("FOO", "ImARatherLongButIndeedNeededString");
 
-	ok(!RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hKey), "RegCreateKeyA failed");
+	ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY, &hKey), "RegCreateKeyA failed");
 
 	if (hKey)
 	{
@@ -71,24 +75,24 @@
 	strcpy(buf, sEmptyBuffer);
 	dwSize = MAX_PATH;
 	dwType = -1;
-	ok(! SHGetValueA(HKEY_CURRENT_USER, "Software\\Wine\\Test", "Test1", &dwType, buf, &dwSize), "SHGetValueA failed");
+	ok(! SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", &dwType, buf, &dwSize), "SHGetValueA failed");
 	ok( 0 == strcmp(sExpTestpath1, buf), "(%s,%s)", buf, sExpTestpath1);
 	ok( REG_SZ == dwType, "(%lx)", dwType);
 
 	strcpy(buf, sEmptyBuffer);
 	dwSize = MAX_PATH;
 	dwType = -1;
-	ok(! SHGetValueA(HKEY_CURRENT_USER, "Software\\Wine\\Test", "Test2", &dwType, buf, &dwSize), "SHGetValueA failed");
+	ok(! SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", &dwType, buf, &dwSize), "SHGetValueA failed");
 	ok( 0 == strcmp(sTestpath1, buf) , "(%s)", buf);
 	ok( REG_SZ == dwType , "(%lx)", dwType);
 }
 
-static void test_SHGetTegPath(void)
+static void test_SHGetRegPath(void)
 {
 	char buf[MAX_PATH];
 
 	strcpy(buf, sEmptyBuffer);
-	ok(! SHRegGetPathA(HKEY_CURRENT_USER, "Software\\Wine\\Test", "Test1", buf, 0), "SHRegGetPathA failed");
+	ok(! SHRegGetPathA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", buf, 0), "SHRegGetPathA failed");
 	ok( 0 == strcmp(sExpTestpath1, buf) , "(%s)", buf);
 }
 
@@ -103,7 +107,7 @@
 	int nUsedBuffer1;
 	int nUsedBuffer2;
 
-	ok(! RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\Test", 0,  KEY_QUERY_VALUE, &hKey), "test4 RegOpenKey");
+	ok(! RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_KEY, 0,  KEY_QUERY_VALUE, &hKey), "test4 RegOpenKey");
 
 	/****** SHQueryValueExA ******/
 
@@ -189,10 +193,59 @@
 	RegCloseKey(hKey);
 }
 
+
+static void test_SHCopyKey(void)
+{
+	HKEY hKeySrc, hKeyDst;
+
+	// Delete existing destination sub keys
+	hKeyDst = (HKEY)0;
+	if (!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst) && hKeyDst)
+	{
+		SHDeleteKeyA(hKeyDst, NULL);
+		RegCloseKey(hKeyDst);
+	}
+
+	hKeyDst = (HKEY)0;
+	if (RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst) || !hKeyDst)
+	{
+		ok(0, "didn't open dest");
+		return;
+	}
+
+	hKeySrc = (HKEY)0;
+	if (RegOpenKeyA(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, &hKeySrc) || !hKeySrc)
+	{
+		ok(0, "didn't open source");
+		return;
+	}
+
+
+	ok (!SHCopyKeyA(hKeyDst, NULL, hKeySrc, 0), "failed copy");
+
+	RegCloseKey(hKeySrc);
+	RegCloseKey(hKeyDst);
+
+	/* Check we copied the sub keys, i.e. AeDebug from the default wine registry */
+	hKeyDst = (HKEY)0;
+	if (RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination\\AeDebug", &hKeyDst) || !hKeyDst)
+	{
+		ok(0, "didn't open copy");
+		return;
+	}
+
+	/* And the we copied the values too */
+	ok(!SHQueryValueExA(hKeyDst, "Debugger", NULL, NULL, NULL, NULL), "SHQueryValueExA failed");
+
+	RegCloseKey(hKeyDst);
+}
+
+
 START_TEST(shreg)
 {
 	create_test_entrys();
 	test_SHGetValue();
 	test_SHQUeryValueEx();
-	test_SHGetTegPath();
+	test_SHGetRegPath();
+	test_SHCopyKey();
 }