|
|
#include "priv.h"
// Build this file only on Win9X or NT4
#ifdef DOWNLEVEL_PLATFORM
__inline LPWSTR StrCpyNXW(LPWSTR psz1, LPCWSTR psz2, int cchMax) { ASSERT(psz1); ASSERT(psz2);
if (0 < cchMax) { // Leave room for the null terminator
while (0 < --cchMax) { if ( !(*psz1++ = *psz2++) ) { --psz1; break; } }
if (0 == cchMax) *psz1 = L'\0';
ASSERT(*psz1 == 0); }
return psz1; }
LPWSTR StrCpyNW(LPWSTR psz1, LPCWSTR psz2, int cchMax) { StrCpyNXW(psz1, psz2, cchMax); return psz1; }
LPWSTR StrCpyW(LPWSTR psz1, LPCWSTR psz2) { LPWSTR psz = psz1;
ASSERT(psz1); ASSERT(psz2);
while (*psz1++ = *psz2++) ;
return psz; }
STDAPI_(DWORD) RegData_AtoW( IN LPCVOID pvData, IN DWORD dwSize, IN DWORD dwType, /*INOUT*/ LPDWORD pcbData ) { DWORD dwRet = NO_ERROR;
if (REG_SZ == dwType || REG_EXPAND_SZ == dwType || REG_MULTI_SZ == dwType) { DWORD cbData = pcbData ? *pcbData : -1; if (pvData) { // Allocate a temporary buffer to work with
int cch = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pvData, cbData, NULL, 0); LPWSTR pwszT = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cch));
if (!pwszT) dwRet = ERROR_NOT_ENOUGH_MEMORY; else { if (CbFromCchW(cch) > dwSize) dwRet = ERROR_MORE_DATA; else { // Convert it to the temporary buffer
MultiByteToWideChar(CP_ACP, 0, (LPSTR)pvData, cbData, pwszT, cch);
// Copy it back to the output buffer
StrCpyW((LPWSTR)pvData, pwszT); } LocalFree(pwszT); }
// If string data, make room for unicode
if (pcbData) { (*pcbData) = cch * sizeof(WCHAR); } } else if (pcbData) { // We don't have the data so guess (actual value may be less)
// PERF: Does this need to be exact? For now this seem sufficient. (Stevepro)
(*pcbData) *= sizeof(WCHAR); } } return dwRet; }
/*----------------------------------------------------------
Purpose: Recursively delete the key, including all child values and keys. Mimics what RegDeleteKey does in Win95.
Returns: Cond: -- */ DWORD DeleteKeyRecursively( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet; HKEY hkSubKey;
// Open the subkey so we can enumerate any children
dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey); if (ERROR_SUCCESS == dwRet) { DWORD dwIndex; CHAR szSubKeyName[MAX_PATH + 1]; DWORD cchSubKeyName = ARRAYSIZE(szSubKeyName); CHAR szClass[MAX_PATH]; DWORD cbClass = ARRAYSIZE(szClass);
// I can't just call RegEnumKey with an ever-increasing index, because
// I'm deleting the subkeys as I go, which alters the indices of the
// remaining subkeys in an implementation-dependent way. In order to
// be safe, I have to count backwards while deleting the subkeys.
// Find out how many subkeys there are
dwRet = RegQueryInfoKeyA(hkSubKey, szClass, &cbClass, NULL, &dwIndex, // The # of subkeys -- all we need
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (NO_ERROR == dwRet) { // dwIndex is now the count of subkeys, but it needs to be
// zero-based for RegEnumKey, so I'll pre-decrement, rather
// than post-decrement.
while (ERROR_SUCCESS == RegEnumKeyA(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName)) { DeleteKeyRecursively(hkSubKey, szSubKeyName); } }
RegCloseKey(hkSubKey);
dwRet = RegDeleteKeyA(hkey, pszSubKey); }
return dwRet; }
/*----------------------------------------------------------
Purpose: Recursively delete the key, including all child values and keys.
Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteKeyA( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet;
if (g_bRunOnNT) { dwRet = DeleteKeyRecursively(hkey, pszSubKey); } else { // On Win95, RegDeleteKey does what we want
dwRet = RegDeleteKeyA(hkey, pszSubKey); }
return dwRet; }
/*----------------------------------------------------------
Purpose: Recursively delete the key, including all child values and keys.
Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteKeyW( IN HKEY hkey, IN LPCWSTR pwszSubKey) { DWORD dwRet; CHAR sz[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, sz, SIZECHARS(sz), NULL, NULL);
if (g_bRunOnNT) { dwRet = DeleteKeyRecursively(hkey, sz); } else { // On Win95, RegDeleteKey does what we want
dwRet = RegDeleteKeyA(hkey, sz); }
return dwRet; }
/*----------------------------------------------------------
Purpose: Behaves just like RegQueryValueEx, except if the data type is REG_EXPAND_SZ, then this goes ahead and expands out the string. *pdwType will always be massaged to REG_SZ if this happens.
Returns: Cond: -- */ STDAPI_(DWORD) SHQueryValueExA( IN HKEY hkey, IN LPCSTR pszValue, IN LPDWORD lpReserved, OUT LPDWORD pdwType, OUT LPVOID pvData, IN OUT LPDWORD pcbData) { DWORD dwRet; DWORD cbSize; DWORD dwType; LPSTR lpsz;
// Trying to get back data
if (pcbData) cbSize = *pcbData; // Size of output buffer
dwRet = RegQueryValueExA(hkey, pszValue, lpReserved, &dwType, pvData, &cbSize);
// Normally, we'd be done with this. But do some extra work
// if this is an expandable string (something that has system
// variables in it), or if we need to pad the buffer.
if (NO_ERROR == dwRet) { // Note: on Win95, RegSetValueEx will always write the
// full string out, including the null terminator. On NT,
// it won't unless the write length was specified.
// Hence, we have the following check.
// Pad the buffer, in case the string didn't have a null
// terminator when it was stored?
if (REG_SZ == dwType) { // Yes
if (pvData && cbSize < *pcbData) { LPSTR lpszData = pvData; lpszData[cbSize] = '\0'; } } // Expand the string?
else if (REG_EXPAND_SZ == dwType) { // Yes
// Use a temporary buffer to expand
if (pvData) { lpsz = (LPSTR)LocalAlloc(LPTR, *pcbData); if ( !lpsz ) return ERROR_OUTOFMEMORY;
cbSize = ExpandEnvironmentStringsA(pvData, lpsz, *pcbData);
// NT screws up the cbSize returned...
if (cbSize > 0) cbSize = lstrlenA(lpsz) + 1; if (cbSize > 0 && cbSize <= *pcbData) lstrcpynA(pvData, lpsz, *pcbData); else dwRet = GetLastError(); } else { //
// Find out the length of the expanded string
// we have to call in and actually get the data to do this
//
CHAR szBuff[1];
lpsz = (LPSTR)LocalAlloc(LPTR, cbSize); if (!lpsz) return ERROR_OUTOFMEMORY;
dwRet = RegQueryValueExA(hkey, pszValue, lpReserved, NULL, (LPBYTE)lpsz, &cbSize);
if (NO_ERROR == dwRet) { // dummy buffer required...
DWORD cbExpand = ExpandEnvironmentStringsA(lpsz, szBuff, ARRAYSIZE(szBuff)); cbSize = max(cbExpand, cbSize);
}
}
LocalFree(lpsz);
// Massage dwType so that callers always see REG_SZ
dwType = REG_SZ; } }
if (pdwType) *pdwType = dwType;
if (pcbData) *pcbData = cbSize;
return dwRet; }
#ifdef UNICODE
/*----------------------------------------------------------
Purpose: Behaves just like RegQueryValueEx, except if the data type is REG_EXPAND_SZ, then this goes ahead and expands out the string. *pdwType will always be massaged to REG_SZ if this happens.
Returns: Cond: -- */ STDAPI_(DWORD) SHQueryValueExW( IN HKEY hkey, IN LPCWSTR pwszValue, IN LPDWORD lpReserved, OUT LPDWORD pdwType, OUT LPVOID pvData, IN OUT LPDWORD pcbData) { DWORD dwRet; DWORD cbSize; DWORD dwType; LPWSTR lpsz;
if ( !g_bRunOnNT ) { CHAR szValue[MAX_PATH]; LPSTR pszValue = NULL; DWORD dwOriginalSize = 0;
if (pcbData) dwOriginalSize = *pcbData;
if (pwszValue) { WideCharToMultiByte(CP_ACP, 0, pwszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL); pszValue = szValue; }
if (!pdwType) pdwType = &dwType;
dwRet = SHQueryValueExA(hkey, pszValue, lpReserved, pdwType, pvData, pcbData);
if (NO_ERROR == dwRet) dwRet = RegData_AtoW(pvData, dwOriginalSize, *pdwType, pcbData); // Thunk data from ANSI->UNICODE if needed.
} else { // Running on NT
// Trying to get back data
if (pcbData) cbSize = *pcbData; // Size of output buffer
dwRet = RegQueryValueExW(hkey, pwszValue, lpReserved, &dwType, pvData, &cbSize);
// Normally, we'd be done with this. But do some extra work
// if this is an expandable string (something that has system
// variables in it), or if we need to pad the buffer.
if (NO_ERROR == dwRet) { // Note: on Win95, RegSetValueEx will always write the
// full string out, including the null terminator. On NT,
// it won't unless the write length was specified.
// Hence, we have the following check.
// Pad the buffer, in case the string didn't have a null
// terminator when it was stored?
if (REG_SZ == dwType) { // Yes
if (pvData && cbSize + sizeof(WCHAR) <= *pcbData) { LPWSTR lpszData = pvData; lpszData[cbSize / sizeof(WCHAR)] = '\0'; } } // Expand the string?
else if (REG_EXPAND_SZ == dwType) { if (pvData) { // Yes
// Use a temporary buffer to expand
lpsz = (LPWSTR)LocalAlloc(LPTR, *pcbData); if (!lpsz) return ERROR_OUTOFMEMORY;
cbSize = CbFromCchW(ExpandEnvironmentStringsW(pvData, lpsz, *pcbData / sizeof(WCHAR))); if (cbSize > 0 && cbSize <= *pcbData) StrCpyNW(pvData, lpsz, *pcbData / sizeof(WCHAR)); else dwRet = GetLastError(); } else { //
// Find out the length of the expanded string
// we have to call in and actually get the data to do this
//
WCHAR szBuff[1];
// Find out the length of the expanded string
//
lpsz = (LPWSTR)LocalAlloc(LPTR, cbSize); if (!lpsz) return ERROR_OUTOFMEMORY;
dwRet = RegQueryValueExW(hkey, pwszValue, lpReserved, NULL, (LPBYTE)lpsz, &cbSize);
if (NO_ERROR == dwRet) { DWORD cbExpand = CbFromCchW(ExpandEnvironmentStringsW(lpsz, szBuff, ARRAYSIZE(szBuff))); cbSize = max(cbExpand, cbSize); } }
LocalFree(lpsz);
// Massage dwType so that callers always see REG_SZ
dwType = REG_SZ; } }
if (pdwType) *pdwType = dwType;
if (pcbData) *pcbData = cbSize; }
return dwRet; } #endif //UNICODE
/*
* @doc INTERNAL * * @func int | SHAnsiToUnicodeNativeCP | * * Convert an ANSI string to a UNICODE string via the * specified Windows code page. If the source string is too large * for the destination buffer, then as many characters as * possible are copied. * * The resulting output string is always null-terminated. * * @parm UINT | uiCP | * * The code page in which to perform the conversion. * This must be a Windows code page. * * @parm LPCSTR | pszSrc | * * Source buffer containing ANSI string to be converted. * * @parm int | cchSrc | * * Source buffer length, including terminating null. * * @parm LPWSTR | pwszDst | * * Destination buffer to receive converted UNICODE string. * * @parm int | cwchBuf | * * Size of the destination buffer in <t WCHAR>s. * * @returns * * On success, the number of characters copied to the output * buffer is returned, including the terminating null. */
int SHAnsiToUnicodeNativeCP(UINT uiCP, LPCSTR pszSrc, int cchSrc, LPWSTR pwszDst, int cwchBuf) { int cwchRc = 0; /* Assume failure */
/*
* Checks the caller should've made. */ ASSERT(IS_VALID_STRING_PTRA(pszSrc, -1)); ASSERT(cchSrc == lstrlenA(pszSrc) + 1); ASSERT(IS_VALID_WRITE_BUFFER(pwszDst, WCHAR, cwchBuf)); ASSERT(pszSrc != NULL); ASSERT(uiCP != 1200 && uiCP != 65000 && uiCP != 50000 && uiCP != 65001); ASSERT(pwszDst); ASSERT(cwchBuf);
cwchRc = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc, pwszDst, cwchBuf); if (cwchRc) { /*
* The output buffer was big enough; no double-buffering * needed. */ } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /*
* The output buffer wasn't big enough. Need to double-buffer. */
int cwchNeeded = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc, NULL, 0);
ASSERT(cwchRc == 0); /* In case we fail later */ if (cwchNeeded) { LPWSTR pwsz = (LPWSTR)LocalAlloc(LMEM_FIXED, cwchNeeded * SIZEOF(WCHAR)); if (pwsz) { cwchRc = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc, pwsz, cwchNeeded); if (cwchRc) { StrCpyNW(pwszDst, pwsz, cwchBuf); cwchRc = cwchBuf; } LocalFree(pwsz); } } } else { /* Possibly unsupported code page */ ASSERT(!"Unexpected error in MultiByteToWideChar"); }
return cwchRc; }
int SHAnsiToUnicodeCP_ACP(LPCSTR pszSrc, LPWSTR pwszDst, int cwchBuf) { int cwchRc = 0; /* Assume failure */
ASSERT(IS_VALID_STRING_PTRA(pszSrc, -1)); ASSERT(IS_VALID_WRITE_BUFFER(pwszDst, WCHAR, cwchBuf));
/*
* Sanity check - NULL source string is treated as a null string. */ if (pszSrc == NULL) { pszSrc = ""; }
/*
* Sanity check - Output buffer must be non-NULL and must be of * nonzero size. */ if (pwszDst && cwchBuf) {
int cchSrc;
pwszDst[0] = 0; /* In case of error */
cchSrc = lstrlenA(pszSrc) + 1;
cwchRc = SHAnsiToUnicodeNativeCP(CP_ACP, pszSrc, cchSrc, pwszDst, cwchBuf); }
return cwchRc; }
/*
* @doc EXTERNAL * * @func int | SHAnsiToUnicode | * * Convert an ANSI string to a UNICODE string via the * <c CP_ACP> code page. If the source string is too large * for the destination buffer, then as many characters as * possible are copied. * * The resulting output string is always null-terminated. * * @parm LPCSTR | pszSrc | * * Source buffer containing ANSI string to be converted. * * @parm LPWSTR | pwszDst | * * Destination buffer to receive converted UNICODE string. * * @parm int | cwchBuf | * * Size of the destination buffer in <t WCHAR>s. * * @returns * * On success, the number of characters copied to the output * buffer is returned, including the terminating null. * */
int SHAnsiToUnicode(LPCSTR pszSrc, LPWSTR pwszDst, int cwchBuf) { return SHAnsiToUnicodeCP_ACP(pszSrc, pwszDst, cwchBuf); }
// dupe a string using the task allocator for returing from a COM interface
// These functions use SHAlloc, so they cannot go into shlwapi.
STDAPI SHStrDupA(LPCSTR psz, WCHAR **ppwsz) { DWORD cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0); *ppwsz = (WCHAR *)CoTaskMemAlloc((cch + 1) * SIZEOF(WCHAR)); if (*ppwsz) { MultiByteToWideChar(CP_ACP, 0, psz, -1, *ppwsz, cch); return S_OK; } return E_OUTOFMEMORY; }
// dupe a string using the task allocator for returing from a COM interface
STDAPI SHStrDupW(LPCWSTR psz, WCHAR **ppwsz) { *ppwsz = (WCHAR *)CoTaskMemAlloc((lstrlenW(psz) + 1) * SIZEOF(WCHAR)); if (*ppwsz) { StrCpyW(*ppwsz, psz); return S_OK; } return E_OUTOFMEMORY; }
// Modifies:
// szRoot
//
// Returns:
// TRUE if a drive root was found
// FALSE otherwise
//
STDAPI_(BOOL) PathStripToRoot( LPTSTR szRoot) { while(!PathIsRoot(szRoot)) { if (!PathRemoveFileSpec(szRoot)) { // If we didn't strip anything off,
// must be current drive
return(FALSE); } }
return(TRUE); }
//
// SHStringFromGUIDA
//
// converts GUID into (...) form without leading identifier; returns
// amount of data copied to lpsz if successful; 0 if buffer too small.
//
// An endian-dependant map of what bytes go where in the GUID
// text representation.
//
// Do NOT use the TEXT() macro in GuidMap... they're intended to be bytes
//
static const BYTE c_rgbGuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 };
static const CHAR c_szDigitsA[] = "0123456789ABCDEF"; static const WCHAR c_szDigitsW[] = TEXTW("0123456789ABCDEF");
STDAPI_(int) SHStringFromGUIDA( UNALIGNED REFGUID rguid, LPSTR psz, int cchMax) { int i; const BYTE * pBytes = (const BYTE *) rguid;
if (cchMax < GUIDSTR_MAX) return 0;
#ifdef BIG_ENDIAN
// This is the slow, but portable version
wnsprintf(psz, cchMax,"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid->Data1, rguid->Data2, rguid->Data3, rguid->Data4[0], rguid->Data4[1], rguid->Data4[2], rguid->Data4[3], rguid->Data4[4], rguid->Data4[5], rguid->Data4[6], rguid->Data4[7]); #else
// The following algorithm is faster than the wsprintf.
*psz++ = '{';
for (i = 0; i < SIZEOF(c_rgbGuidMap); i++) { if (c_rgbGuidMap[i] == '-') // don't TEXT() this line
{ *psz++ = '-'; } else { // Convert a byte-value into a character representation
*psz++ = c_szDigitsA[ (pBytes[c_rgbGuidMap[i]] & 0xF0) >> 4 ]; *psz++ = c_szDigitsA[ (pBytes[c_rgbGuidMap[i]] & 0x0F) ]; } } *psz++ = '}'; *psz = '\0'; #endif /* !BIG_ENDIAN */
return GUIDSTR_MAX; }
STDAPI_(int) SHStringFromGUIDW( UNALIGNED REFGUID rguid, LPWSTR psz, int cchMax) { int i; const BYTE * pBytes = (const BYTE *) rguid;
if (cchMax < GUIDSTR_MAX) return 0;
#ifdef BIG_ENDIAN
// This is the slow, but portable version
wnsprintfW(psz, cchMax, L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid->Data1, rguid->Data2, rguid->Data3, rguid->Data4[0], rguid->Data4[1], rguid->Data4[2], rguid->Data4[3], rguid->Data4[4], rguid->Data4[5], rguid->Data4[6], rguid->Data4[7]); #else
// The following algorithm is faster than the wsprintf.
*psz++ = TEXTW('{');
for (i = 0; i < SIZEOF(c_rgbGuidMap); i++) { if (c_rgbGuidMap[i] == '-') // don't TEXT() this line
{ *psz++ = TEXTW('-'); } else { // Convert a byte-value into a character representation
*psz++ = c_szDigitsW[ (pBytes[c_rgbGuidMap[i]] & 0xF0) >> 4 ]; *psz++ = c_szDigitsW[ (pBytes[c_rgbGuidMap[i]] & 0x0F) ]; } } *psz++ = TEXTW('}'); *psz = TEXTW('\0'); #endif /* !BIG_ENDIAN */
return GUIDSTR_MAX; }
//
// Why do we use the unsafe version?
//
// - Unsafe is much faster.
//
// - The safe version isn't safe after all and serves only to mask
// existing bugs. The situation the safe version "saves" is if
// two threads both try to atomicrelease the same object. This
// means that at the same moment, both threads think the object
// is alive. Change the timing slightly, and now one thread
// atomicreleases the object before the other one, so the other
// thread is now using an object after the first thread already
// atomicreleased it. Bug.
//
STDAPI_(void) IUnknown_AtomicRelease(void **ppunk) { #if 1 // Unsafe
if (ppunk && *ppunk) { IUnknown* punk = *(IUnknown**)ppunk; *ppunk = NULL; punk->lpVtbl->Release(punk); } #else // Safe
if (ppunk) { IUnknown* punk = (IUnknown *)InterlockedExchangePointer(ppunk, NULL); if (punk) { punk->Release(); } } #endif
}
#endif //DOWNLEVEL_PLATFORM
|