/*++ Copyright (c) 1994-1995 Microsoft Corporation All rights reserved. Module Name: safewrap.cxx Abstract: This implements safe-wrappers for various system APIs. Author: Mark Lawrence (MLawrenc) Revision History: --*/ #include "spllibp.hxx" #pragma hdrstop #include "splcom.h" #include "safewrap.hxx" /*++ Routine Name: LoadLibraryFromSystem32 Routine Description: Load the dll from system32 directory Arguments: lpLibFileName - Library file name Return Value: If the function succeeds, the return value is the handle; If the function fails, the return value is NULL. To get extended error information, call GetLastError. --*/ HINSTANCE LoadLibraryFromSystem32( IN LPCTSTR lpLibFileName ) { HINSTANCE hLib = NULL; static const TCHAR cszBackSlash[] = TEXT("\\"); static const TCHAR cszSystem32[] = TEXT("system32\\"); TCHAR szWindowDir[MAX_PATH]; LPTSTR pszPath = NULL; DWORD dwWinDirLen = 0; if (dwWinDirLen = GetSystemWindowsDirectory (szWindowDir, MAX_PATH)) { size_t cPathLen = dwWinDirLen + lstrlen (lpLibFileName) + COUNTOF (cszSystem32) + 2; pszPath = new TCHAR [cPathLen]; if (pszPath) { StringCchCopy (pszPath, cPathLen, szWindowDir); if (szWindowDir[dwWinDirLen - 1] != cszBackSlash[0]) { StringCchCat (pszPath, cPathLen, cszBackSlash); } StringCchCat (pszPath, cPathLen, cszSystem32); StringCchCat (pszPath, cPathLen, lpLibFileName); hLib = LoadLibrary(pszPath); } delete [] pszPath; } return hLib; } /*++ Routine Name: SafeQueryValueAsStringPointer Routine Description: This does a query value on a string type. It guarantees: 1. The value retrieved is a REG_SZ. 2. The returned string is NULL terminated. Arguments: hKey - The key to retrieve the value from. pValueName - The value field. ppszString - The returned string, use FreeSplMem to free it. cchHint - A hint for the size of the buffer we would most likely need, use 0 if you have no idea, or don't care about two round trips. Return Value: An HRESULT - S_OK - Everything succeeded. S_FALSE - The string was read, but was not NULL terminated in the registry. --*/ HRESULT SafeRegQueryValueAsStringPointer( IN HKEY hKey, IN PCWSTR pValueName, OUT PWSTR *ppszString, IN DWORD cchHint OPTIONAL ) { HRESULT hr = pValueName && ppszString? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); DWORD cbData = 0; DWORD Type = 0; BOOL bTruncated = FALSE; BOOL bNullInRegistry = FALSE; PWSTR pszString = NULL; if (SUCCEEDED(hr)) { if (cchHint) { pszString = reinterpret_cast(AllocSplMem(cchHint * sizeof(WCHAR))); hr = pszString ? S_OK : HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); } } if (SUCCEEDED(hr)) { cbData = cchHint * sizeof(WCHAR); hr = HResultFromWin32(RegQueryValueEx(hKey, pValueName, NULL, &Type, reinterpret_cast(pszString), &cbData)); // // This code in only unicode. // hr = SUCCEEDED(hr) ? (Type == REG_SZ && !(cbData & 0x01) ? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_DATA)) : hr; } // // If this succeeded and we were passed a hint, then check and NULL terminate the string. // if (SUCCEEDED(hr) && pszString) { hr = CheckAndNullTerminateRegistryBuffer(pszString, cchHint, cbData / sizeof(WCHAR), &bTruncated, &bNullInRegistry); // // If we could not NULL terminate inside the buffer, then we will re-read using // the advertised registry buffer size // hr = SUCCEEDED(hr) ? (bTruncated ? HRESULT_FROM_WIN32(ERROR_MORE_DATA) : S_OK) : hr; } if (SUCCEEDED(hr)) { if (pszString) { *ppszString = pszString; pszString = NULL; } else { hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); } } FreeSplMem(pszString); pszString = NULL; if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { // // On our next read, we always add 1 to the buffer size. This is so that // we can be guaranteed of NULL termination (assuming the registry value // hasn't changed). // cchHint = (cbData + sizeof(WCHAR) - 1) / sizeof(WCHAR) + 1; pszString = reinterpret_cast(AllocSplMem(cchHint * sizeof(WCHAR))); hr = pszString ? S_OK : HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); if (SUCCEEDED(hr)) { cbData = cchHint * sizeof(WCHAR); hr = HResultFromWin32(RegQueryValueEx(hKey, pValueName, NULL, &Type, reinterpret_cast(pszString), &cbData)); hr = SUCCEEDED(hr) ? (Type == REG_SZ && !(cbData & 0x01) ? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_DATA)) : hr; } if (SUCCEEDED(hr)) { hr = CheckAndNullTerminateRegistryBuffer(pszString, cchHint, cbData / sizeof(WCHAR), &bTruncated, &bNullInRegistry); hr = SUCCEEDED(hr) ? (bTruncated ? HRESULT_FROM_WIN32(ERROR_INVALID_DATA) : S_OK) : hr; } if (SUCCEEDED(hr)) { *ppszString = pszString; pszString = NULL; } } FreeSplMem(pszString); pszString = NULL; return SUCCEEDED(hr) ? (bNullInRegistry ? S_OK : S_FALSE) : hr; } /*++ Routine Name: CheckAndNullTerminateRegistryBuffer Routine Description: This routine runs the buffer over the size returned by the registry backwards to find a NULL, if there is a NULL in the buffer, we return success, otherwise, if we can add a NULL within the buffer we do so. Otherwise, we truncate the string. Arguments: pszBuffer - The buffer we will bve checking. cchBuffer - The size of the buffer. cchRegBuffer - The size of the data returned from the registry. pbTruncated - If TRUE, the data was truncated, this will only happen if cchBuffer == cchRegBuffer. pbNullInRegistry - If TRUE, there was a NULL termination in the given buffer. Return Value: An HRESULT. --*/ HRESULT CheckAndNullTerminateRegistryBuffer( IN PWSTR pszBuffer, IN UINT cchBuffer, IN UINT cchRegBuffer, OUT BOOL *pbTruncated, OUT BOOL *pbNullInRegistry ) { HRESULT hr = pszBuffer && cchBuffer && cchRegBuffer <= cchBuffer && cchRegBuffer && pbTruncated && pbNullInRegistry ? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); if (SUCCEEDED(hr)) { UINT iRegScan = 0; // // Start at the end of the buffer and work backwards to find the NULL, // this is faster since the NULL is likely to be at the end. // for(iRegScan = cchRegBuffer; iRegScan > 0; iRegScan--) { if (!pszBuffer[iRegScan - 1]) { break; } } // // If we reached the beginning of the buffer and there wasn't a NULL, // then see if we can NULL terminate ourselves. // if (!iRegScan) { *pbNullInRegistry = FALSE; if (cchBuffer > cchRegBuffer) { pszBuffer[cchRegBuffer] = L'\0'; *pbTruncated = FALSE; } else { pszBuffer[cchRegBuffer - 1] = L'\0'; *pbTruncated = TRUE; } } else { *pbNullInRegistry = TRUE; *pbTruncated = FALSE; } } return hr; } // // Test code follows // /*++ Routine Name: TestNullTerminateRegistryBuffer Routine Description: This is test code for SafeRegQueryValueAsStringPointer. This code is not dead and functions as a validator for the above code. Arguments: None. Return Value: An HRESULT --*/ HRESULT TestNullTerminateRegistryBuffer( VOID ) { HRESULT hr = E_FAIL; // // This string must be quadword aligned or the registry does funky stuff // on the write. // const WCHAR NonNullString[] = { L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8' }; const WCHAR NullString[] = L"1234567"; const WCHAR EmptyString[] = L""; HKEY hKey = NULL; DWORD dwDisposition = 0; hr = HResultFromWin32(RegCreateKeyEx(HKEY_CURRENT_USER, L"StringTest", 0, NULL, 0, KEY_WRITE | KEY_READ, NULL, &hKey, &dwDisposition)); if (SUCCEEDED(hr)) { hr = SubTestVariations(hKey, L"NonNull", NonNullString, COUNTOF(NonNullString), L"12345678", S_FALSE); } if (SUCCEEDED(hr)) { hr = SubTestVariations(hKey, L"Null", NullString, COUNTOF(NullString), NullString, S_OK); } if (SUCCEEDED(hr)) { hr = SubTestVariations(hKey, L"Empty", EmptyString, COUNTOF(EmptyString), EmptyString, S_OK); } if (hKey) { RegCloseKey(hKey); } RegDeleteKey(HKEY_CURRENT_USER, L"StringTest"); return hr; } /*++ Routine Name: SubTestVariations Routine Description: This test a second set of standard variations depending on the string that is written into the registry. Arguments: hKey - The key we are writing to. pszValue - The value we are testing. pWriteBuffer - The buffer to write (may not be NULL-terminated) cchBuffer - The size of the buffer we are writing. pszCompareString - The string we expect to compare it against. hrExpected - The HR we expect from the function, must be a success code. Return Value: An HRESULT --*/ HRESULT SubTestVariations( IN HKEY hKey, IN PCWSTR pszValue, IN PCWSTR pWriteBuffer, IN UINT cchBuffer, IN PCWSTR pszCompareString, IN HRESULT hrExpected ) { HRESULT hr = hKey && pszValue && pWriteBuffer && pszCompareString && cchBuffer ? S_OK : E_INVALIDARG; PWSTR pszString = NULL; // // Write the string into the registry. // if (SUCCEEDED(hr)) { hr = HResultFromWin32(RegSetValueExW(hKey, pszValue, 0, REG_SZ, reinterpret_cast(pWriteBuffer), cchBuffer * sizeof(pWriteBuffer[0]))); } // // Variation 1, our hint buffer is 0. // if (SUCCEEDED(hr)) { hr = SafeRegQueryValueAsStringPointer(hKey, pszValue, &pszString, 0); hr = hr == hrExpected && pszString ? (!wcscmp(pszString, pszCompareString) ? S_OK : E_FAIL) : (SUCCEEDED(hr) ? E_FAIL : hr); } FreeSplMem(pszString); pszString = NULL; // // Variation 2, our hint buffer is smaller than the reg size. // if (SUCCEEDED(hr)) { hr = SafeRegQueryValueAsStringPointer(hKey, pszValue, &pszString, cchBuffer / 2); hr = hr == hrExpected && pszString ? (!wcscmp(pszString, pszCompareString) ? S_OK : E_FAIL) : (SUCCEEDED(hr) ? E_FAIL : hr); } FreeSplMem(pszString); pszString = NULL; // // Variation 3, our hint buffer is exactly the same size as the reg size. // if (SUCCEEDED(hr)) { hr = SafeRegQueryValueAsStringPointer(hKey, pszValue, &pszString, cchBuffer); hr = hr == hrExpected && pszString ? (!wcscmp(pszString, pszCompareString) ? S_OK : E_FAIL) : (SUCCEEDED(hr) ? E_FAIL : hr); } // // Variation 4, our hint buffer is larger than the reg buffer. // FreeSplMem(pszString); pszString = NULL; if (SUCCEEDED(hr)) { hr = SafeRegQueryValueAsStringPointer(hKey, pszValue, &pszString, cchBuffer * 2); hr = hr == hrExpected && pszString ? (!wcscmp(pszString, pszCompareString) ? S_OK : E_FAIL) : (SUCCEEDED(hr) ? E_FAIL : hr); } FreeSplMem(pszString); pszString = NULL; return hr; }