diff --git a/dlls/shlwapi/reg.c b/dlls/shlwapi/reg.c index 3e114d381b2..b5ff9b2d1f6 100644 --- a/dlls/shlwapi/reg.c +++ b/dlls/shlwapi/reg.c @@ -1792,3 +1792,135 @@ HKEY WINAPI SHRegDuplicateHKey(HKEY hKey) 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 ca19e3545a3..9687c786651 100644 --- a/dlls/shlwapi/shlwapi.spec +++ b/dlls/shlwapi/shlwapi.spec @@ -705,8 +705,8 @@ init SHLWAPI_LibMain @ 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 fd7635a9947..e7e0a01d1c9 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 @@ static void create_test_entrys(void) 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 @@ static void test_SHGetValue(void) 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 @@ static void test_SHQUeryValueEx(void) 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 @@ static void test_SHQUeryValueEx(void) 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(); }