diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec index 61eb2c96083..8fb65923885 100644 --- a/dlls/advapi32/advapi32.spec +++ b/dlls/advapi32/advapi32.spec @@ -486,6 +486,8 @@ @ stdcall RegGetValueW(long wstr wstr long ptr ptr ptr) @ stdcall RegLoadKeyA(long str str) @ stdcall RegLoadKeyW(long wstr wstr) +@ stdcall RegLoadMUIStringA(long str str long ptr long str) +@ stdcall RegLoadMUIStringW(long wstr wstr long ptr long wstr) @ stdcall RegNotifyChangeKeyValue(long long long long long) @ stdcall RegOpenCurrentUser(long ptr) @ stdcall RegOpenKeyA(long str ptr) diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c index df569973c48..a51e8e927da 100644 --- a/dlls/advapi32/registry.c +++ b/dlls/advapi32/registry.c @@ -37,6 +37,7 @@ #include "winreg.h" #include "winerror.h" #include "winternl.h" +#include "winuser.h" #include "wine/unicode.h" #include "wine/debug.h" @@ -2378,3 +2379,189 @@ LONG WINAPI RegOpenUserClassesRoot( *phkResult = HKEY_CLASSES_ROOT; return ERROR_SUCCESS; } + +/****************************************************************************** + * load_string [Internal] + * + * This is basically a copy of user32/resource.c's LoadStringW. Necessary to + * avoid importing user32, which is higher level than advapi32. Helper for + * RegLoadMUIString. + */ +static int load_string(HINSTANCE hModule, UINT resId, LPWSTR pwszBuffer, INT cMaxChars) +{ + HGLOBAL hMemory; + HRSRC hResource; + WCHAR *pString; + int idxString; + + /* Negative values have to be inverted. */ + if (HIWORD(resId) == 0xffff) + resId = (UINT)(-((INT)resId)); + + /* Load the resource into memory and get a pointer to it. */ + hResource = FindResourceW(hModule, MAKEINTRESOURCEW(LOWORD(resId >> 4) + 1), (LPWSTR)RT_STRING); + if (!hResource) return 0; + hMemory = LoadResource(hModule, hResource); + if (!hMemory) return 0; + pString = LockResource(hMemory); + + /* Strings are length-prefixed. Lowest nibble of resId is an index. */ + idxString = resId & 0xf; + while (idxString--) pString += *pString + 1; + + /* If no buffer is given, return length of the string. */ + if (!pwszBuffer) return *pString; + + /* Else copy over the string, respecting the buffer size. */ + cMaxChars = (*pString < cMaxChars) ? *pString : (cMaxChars - 1); + if (cMaxChars >= 0) { + memcpy(pwszBuffer, pString+1, cMaxChars * sizeof(WCHAR)); + pwszBuffer[cMaxChars] = '\0'; + } + + return cMaxChars; +} + +/****************************************************************************** + * RegLoadMUIStringW [ADVAPI32.@] + * + * Load the localized version of a string resource from some PE, respective + * id and path of which are given in the registry value in the format + * @[path]\dllname,-resourceId + * + * PARAMS + * hKey [I] Key, of which to load the string value from. + * pszValue [I] The value to be loaded (Has to be of REG_EXPAND_SZ or REG_SZ type). + * pszBuffer [O] Buffer to store the localized string in. + * cbBuffer [I] Size of the destination buffer in bytes. + * pcbData [O] Number of bytes written to pszBuffer (optional, may be NULL). + * dwFlags [I] None supported yet. + * pszBaseDir [I] Not supported yet. + * + * RETURNS + * Success: ERROR_SUCCESS, + * Failure: nonzero error code from winerror.h + * + * NOTES + * This is an API of Windows Vista, which wasn't available at the time this code + * was written. We have to check for the correct behaviour once it's available. + */ +LONG WINAPI RegLoadMUIStringW(HKEY hKey, LPCWSTR pwszValue, LPWSTR pwszBuffer, DWORD cbBuffer, + LPDWORD pcbData, DWORD dwFlags, LPCWSTR pwszBaseDir) +{ + DWORD dwValueType, cbData; + LPWSTR pwszTempBuffer = NULL, pwszExpandedBuffer = NULL; + LONG result; + + TRACE("(hKey = %p, pwszValue = %s, pwszBuffer = %p, cbBuffer = %ld, pcbData = %p, " + "dwFlags = %ld, pwszBaseDir = %s) stub\n", hKey, debugstr_w(pwszValue), pwszBuffer, + cbBuffer, pcbData, dwFlags, debugstr_w(pwszBaseDir)); + + /* Parameter sanity checks. */ + if (!hKey || !pwszBuffer) + return ERROR_INVALID_PARAMETER; + + if (pwszBaseDir && *pwszBaseDir) { + FIXME("BaseDir parameter not yet supported!\n"); + return ERROR_INVALID_PARAMETER; + } + + /* Check for value existence and correctness of it's type, allocate a buffer and load it. */ + result = RegQueryValueExW(hKey, pwszValue, NULL, &dwValueType, NULL, &cbData); + if (result != ERROR_SUCCESS) goto cleanup; + if (!(dwValueType == REG_SZ || dwValueType == REG_EXPAND_SZ) || !cbData) { + result = ERROR_FILE_NOT_FOUND; + goto cleanup; + } + pwszTempBuffer = HeapAlloc(GetProcessHeap(), 0, cbData); + if (!pwszTempBuffer) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + result = RegQueryValueExW(hKey, pwszValue, NULL, &dwValueType, (LPBYTE)pwszTempBuffer, &cbData); + if (result != ERROR_SUCCESS) goto cleanup; + + /* Expand environment variables, if appropriate, or copy the original string over. */ + if (dwValueType == REG_EXPAND_SZ) { + cbData = ExpandEnvironmentStringsW(pwszTempBuffer, NULL, 0) * sizeof(WCHAR); + if (!cbData) goto cleanup; + pwszExpandedBuffer = HeapAlloc(GetProcessHeap(), 0, cbData); + if (!pwszExpandedBuffer) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + ExpandEnvironmentStringsW(pwszTempBuffer, pwszExpandedBuffer, cbData); + } else { + pwszExpandedBuffer = HeapAlloc(GetProcessHeap(), 0, cbData); + memcpy(pwszExpandedBuffer, pwszTempBuffer, cbData); + } + + /* If the value references a resource based string, parse the value and load the string. + * Else just copy over the original value. */ + result = ERROR_SUCCESS; + if (*pwszExpandedBuffer != '@') { /* '@' is the prefix for resource based string entries. */ + lstrcpynW(pwszBuffer, pwszExpandedBuffer, cbBuffer / sizeof(WCHAR)); + } else { + WCHAR *pComma = strrchrW(pwszExpandedBuffer, ','); + UINT uiStringId; + HMODULE hModule; + + /* Format of the expanded value is 'path_to_dll,-resId' */ + if (!pComma || pComma[1] != '-') { + result = ERROR_BADKEY; + goto cleanup; + } + + uiStringId = atoiW(pComma+2); + *pComma = '\0'; + + hModule = LoadLibraryW(pwszExpandedBuffer + 1); + if (!hModule || !load_string(hModule, uiStringId, pwszBuffer, cbBuffer/sizeof(WCHAR))) + result = ERROR_BADKEY; + FreeLibrary(hModule); + } + +cleanup: + HeapFree(GetProcessHeap(), 0, pwszTempBuffer); + HeapFree(GetProcessHeap(), 0, pwszExpandedBuffer); + return result; +} + +/****************************************************************************** + * RegLoadMUIStringA [ADVAPI32.@] + * + * See RegLoadMUIStringW + */ +LONG WINAPI RegLoadMUIStringA(HKEY hKey, LPCSTR pszValue, LPSTR pszBuffer, DWORD cbBuffer, + LPDWORD pcbData, DWORD dwFlags, LPCSTR pszBaseDir) +{ + UNICODE_STRING valueW, baseDirW; + WCHAR *pwszBuffer; + DWORD cbData = cbBuffer * sizeof(WCHAR); + LONG result; + + valueW.Buffer = baseDirW.Buffer = pwszBuffer = NULL; + if (!RtlCreateUnicodeStringFromAsciiz(&valueW, pszValue) || + !RtlCreateUnicodeStringFromAsciiz(&baseDirW, pszBaseDir) || + !(pwszBuffer = HeapAlloc(GetProcessHeap(), 0, cbData))) + { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + + result = RegLoadMUIStringW(hKey, valueW.Buffer, pwszBuffer, cbData, NULL, dwFlags, + baseDirW.Buffer); + + if (result == ERROR_SUCCESS) { + cbData = WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, cbBuffer, NULL, NULL); + if (pcbData) + *pcbData = cbData; + } + +cleanup: + HeapFree(GetProcessHeap(), 0, pwszBuffer); + RtlFreeUnicodeString(&baseDirW); + RtlFreeUnicodeString(&valueW); + + return result; +} diff --git a/include/winreg.h b/include/winreg.h index 85fa2ea1619..7147150e3db 100644 --- a/include/winreg.h +++ b/include/winreg.h @@ -113,6 +113,9 @@ LONG WINAPI RegGetValueW(HKEY,LPCWSTR,LPCWSTR,DWORD,LPDWORD,PVOID,LPDWORD LONG WINAPI RegLoadKeyA(HKEY,LPCSTR,LPCSTR); LONG WINAPI RegLoadKeyW(HKEY,LPCWSTR,LPCWSTR); #define RegLoadKey WINELIB_NAME_AW(RegLoadKey) +LONG WINAPI RegLoadMUIStringA(HKEY,LPCSTR,LPSTR,DWORD,LPDWORD,DWORD,LPCSTR); +LONG WINAPI RegLoadMUIStringW(HKEY,LPCWSTR,LPWSTR,DWORD,LPDWORD,DWORD,LPCWSTR); +#define RegLoadMUIString WINELIB_NAME_AW(RegLoadMUIString) LONG WINAPI RegNotifyChangeKeyValue(HKEY,BOOL,DWORD,HANDLE,BOOL); DWORD WINAPI RegOpenCurrentUser(REGSAM,PHKEY); DWORD WINAPI RegOpenKeyExW(HKEY,LPCWSTR,DWORD,REGSAM,PHKEY);