|
|
#include "shellprv.h"
#include <sfview.h>
#include "defviewp.h"
int CGenList::Add(LPVOID pv, int nInsert) { if (!_hList) { _hList = DSA_Create(_cbItem, 8); if (!_hList) { return -1; } } return DSA_InsertItem(_hList, nInsert, pv); }
int CViewsList::Add(const SFVVIEWSDATA*pView, int nInsert, BOOL bCopy) { if (bCopy) { pView = CopyData(pView); if (!pView) { return -1; } }
int iIndex = CGenList::Add((LPVOID)(&pView), nInsert);
if (bCopy && iIndex<0) { SHFree((LPVOID)pView); }
return iIndex; }
TCHAR const c_szExtViews[] = TEXT("ExtShellFolderViews");
void CViewsList::AddReg(HKEY hkParent, LPCTSTR pszSubKey) { CSHRegKey ckClass(hkParent, pszSubKey); if (!ckClass) { return; }
CSHRegKey ckShellEx(ckClass, TEXT("shellex")); if (!ckShellEx) { return; }
CSHRegKey ckViews(ckShellEx, c_szExtViews); if (!ckViews) { return; }
TCHAR szKey[40]; DWORD dwLen = sizeof(szKey); SHELLVIEWID vid;
if (ERROR_SUCCESS==SHRegQueryValue(ckViews, NULL, szKey, (LONG*)&dwLen) && SUCCEEDED(SHCLSIDFromString(szKey, &vid))) { _vidDef = vid; _bGotDef = TRUE; }
for (int i=0; ; ++i) { LONG lRet = RegEnumKey(ckViews, i, szKey, ARRAYSIZE(szKey)); if (lRet == ERROR_MORE_DATA) { continue; } else if (lRet != ERROR_SUCCESS) { // I assume this is ERROR_NO_MORE_ITEMS
break; }
SFVVIEWSDATA sView; ZeroMemory(&sView, sizeof(sView));
if (FAILED(SHCLSIDFromString(szKey, &sView.idView))) { continue; }
CSHRegKey ckView(ckViews, szKey); if (ckView) { TCHAR szFile[ARRAYSIZE(sView.wszMoniker)]; DWORD dwType;
// NOTE: This app "Nuts&Bolts" munges the registry and remove the last NULL byte
// from the PersistMoniker string. When we read that string into un-initialized
// local buffer, we do not get a properly null terminated string and we fail to
// create the moniker. So, I zero Init the mem here.
ZeroMemory(szFile, sizeof(szFile)); // Attributes affect all extended views
dwLen = sizeof(sView.dwFlags); if ((ERROR_SUCCESS != SHQueryValueEx(ckView, TEXT("Attributes"), NULL, &dwType, &sView.dwFlags, &dwLen)) || dwLen != sizeof(sView.dwFlags) || !(REG_DWORD==dwType || REG_BINARY==dwType)) { sView.dwFlags = 0; }
// We either have a PersistMoniker (docobj) extended view
// or we have an IShellView extended view
//
dwLen = sizeof(szFile); if (ERROR_SUCCESS == SHQueryValueEx(ckView, TEXT("PersistMoniker"), NULL, &dwType, szFile, &dwLen) && REG_SZ == dwType) { //if the %UserAppData% exists, expand it!
ExpandOtherVariables(szFile, ARRAYSIZE(szFile)); SHTCharToUnicode(szFile, sView.wszMoniker, ARRAYSIZE(sView.wszMoniker)); } else { dwLen = sizeof(szKey); if (ERROR_SUCCESS == SHQueryValueEx(ckView, TEXT("ISV"), NULL, &dwType, szKey, &dwLen) && REG_SZ == dwType && SUCCEEDED(SHCLSIDFromString(szKey, &vid))) { sView.idExtShellView = vid;
// Only IShellView extended vies use LParams
dwLen = sizeof(sView.lParam); if ((ERROR_SUCCESS != SHQueryValueEx(ckView, TEXT("LParam"), NULL, &dwType, &sView.lParam, &dwLen)) || dwLen != sizeof(sView.lParam) || !(REG_DWORD==dwType || REG_BINARY==dwType)) { sView.lParam = 0; } } else { if (VID_FolderState != sView.idView) { // No moniker, no IShellView extension, this must be a VID_FolderState
// kinda thing. (Otherwise it's a bad desktop.ini.)
//
RIPMSG(0, "Extended view is registered incorrectly."); continue; } } }
// It has been requested (by OEMs) to allow specifying background
// bitmap and text colors for the regular views. That way they could
// brand the Control Panel page by putting their logo recessed on
// the background. We'd do that here by pulling the stuff out of
// the registry and putting it in the LPCUSTOMVIEWSDATA section...
}
// if docobjextended view that is not webview, DO NOT ADD IT, UNSUPPORTED.
if (!(sView.dwFlags & SFVF_NOWEBVIEWFOLDERCONTENTS) && !IsEqualGUID(sView.idView, VID_WebView)) continue; Add(&sView); } }
void CViewsList::AddCLSID(CLSID const* pclsid) { CSHRegKey ckCLSID(HKEY_CLASSES_ROOT, TEXT("CLSID")); if (!ckCLSID) { return; }
TCHAR szCLSID[40]; SHStringFromGUID(*pclsid, szCLSID, ARRAYSIZE(szCLSID));
AddReg(ckCLSID, szCLSID); }
#ifdef DEBUG
//In debug, I want to see if all the realloc code-path works fine!
//So, I deliberately alloc very small amounts.
#define CUSTOM_INITIAL_ALLOC 16
#define CUSTOM_REALLOC_INCREMENT 16
#else
#define CUSTOM_INITIAL_ALLOC 20*64
#define CUSTOM_REALLOC_INCREMENT 512
#endif //DEBUG
//Returns the offset of the string read into the block of memory (in chars)
// NOTE: the index (piCurOffset) and the sizes *piTotalSize, *piSizeRemaining are
// NOTE: in WCHARs, not BYTES
int GetCustomStrData(LPWSTR *pDataBegin, int *piSizeRemaining, int *piTotalSize, int *piCurOffset, LPCTSTR szSectionName, LPCTSTR szKeyName, LPCTSTR szIniFile, LPCTSTR lpszPath) { TCHAR szStrData[INFOTIPSIZE], szTemp[INFOTIPSIZE]; #ifndef UNICODE
WCHAR wszStrData[MAX_PATH]; #endif
LPWSTR pszStrData; int iLen, iOffsetBegin = *piCurOffset;
//See if the data is present.
if (!SHGetIniString(szSectionName, szKeyName, szTemp, ARRAYSIZE(szTemp), szIniFile)) { return -1; //The given data is not present.
}
SHExpandEnvironmentStrings(szTemp, szStrData, ARRAYSIZE(szStrData)); // Expand the env vars if any
//Get the full pathname if required.
if (lpszPath) PathCombine(szStrData, lpszPath, szStrData);
#ifdef UNICODE
iLen = lstrlen(szStrData); pszStrData = szStrData; #else
iLen = MultiByteToWideChar(CP_ACP, 0, szStrData, -1, wszStrData, ARRAYSIZE(wszStrData)); pszStrData = wszStrData; #endif
iLen++; //Include the NULL character.
while(*piSizeRemaining < iLen) { LPWSTR lpNew; //We need to realloc the block of memory
if (NULL == (lpNew = (LPWSTR)SHRealloc(*pDataBegin, ( *piTotalSize + CUSTOM_REALLOC_INCREMENT) * sizeof(WCHAR)))) return -1; //Unable to realloc; out of mem.
//Note: The begining address of the block could have changed.
*pDataBegin = lpNew; *piTotalSize += CUSTOM_REALLOC_INCREMENT; *piSizeRemaining += CUSTOM_REALLOC_INCREMENT; }
//Add the current directory if required.
StrCpyW((*pDataBegin)+(*piCurOffset), pszStrData);
*piSizeRemaining -= iLen; *piCurOffset += iLen;
return iOffsetBegin; }
HRESULT ReadWebViewTemplate(LPCTSTR pszPath, LPTSTR pszWebViewTemplate, int cchWebViewTemplate) { SHFOLDERCUSTOMSETTINGS fcs = {sizeof(fcs), FCSM_WEBVIEWTEMPLATE, 0}; fcs.pszWebViewTemplate = pszWebViewTemplate; // template path
fcs.cchWebViewTemplate = cchWebViewTemplate; return SHGetSetFolderCustomSettings(&fcs, pszPath, FCS_READ); }
#define ID_EXTVIEWICONAREAIMAGE 3
#define ID_EXTVIEWCOLORSFIRST 4
#define ID_EXTVIEWSTRLAST 5
#define ID_EXTVIEWUICOUNT 6
const LPCTSTR c_szExtViewUIRegKeys[ID_EXTVIEWUICOUNT] = { TEXT("MenuName"), TEXT("HelpText"), TEXT("TooltipText"), TEXT("IconArea_Image"), TEXT("IconArea_TextBackground"), TEXT("IconArea_Text") };
void CViewsList::AddIni(LPCTSTR szIniFile, LPCTSTR szPath) { TCHAR szViewIDs[12*45]; // Room for about 12 GUIDs including Default=
SHELLVIEWID vid;
//
//First check if the INI file exists before trying to get data from it.
//
if (!PathFileExistsAndAttributes(szIniFile, NULL)) return;
if (GetPrivateProfileString(c_szExtViews, TEXT("Default"), c_szNULL, szViewIDs, ARRAYSIZE(szViewIDs), szIniFile) && SUCCEEDED(SHCLSIDFromString(szViewIDs, &vid))) { _vidDef = vid; _bGotDef = TRUE; }
GetPrivateProfileString(c_szExtViews, NULL, c_szNULL, szViewIDs, ARRAYSIZE(szViewIDs), szIniFile);
for (LPCTSTR pNextID=szViewIDs; *pNextID; pNextID+=lstrlen(pNextID)+1) { SFVVIEWSDATA sViewData; CUSTOMVIEWSDATA sCustomData; LPWSTR pszDataBegin = NULL; int iSizeRemaining = CUSTOM_INITIAL_ALLOC; //Let's begin with 12 strings.
int iTotalSize; int iCurOffset;
ZeroMemory(&sViewData, sizeof(sViewData));
ZeroMemory(&sCustomData, sizeof(sCustomData));
// there must be a view id
if (FAILED(SHCLSIDFromString(pNextID, &sViewData.idView))) { continue; }
// we blow off IE4b2 customized views. This forces them to run
// the wizard again which will clean this junk up
if (IsEqualIID(sViewData.idView, VID_DefaultCustomWebView)) { continue; }
// get the IShellView extended view, if any
BOOL fExtShellView = FALSE; TCHAR szPreProcName[45]; if (GetPrivateProfileString(c_szExtViews, pNextID, c_szNULL, szPreProcName, ARRAYSIZE(szPreProcName), szIniFile)) { fExtShellView = SUCCEEDED(SHCLSIDFromString(szPreProcName, &sViewData.idExtShellView)); }
// All extended views use Attributes
sViewData.dwFlags = GetPrivateProfileInt(pNextID, TEXT("Attributes"), 0, szIniFile) | SFVF_CUSTOMIZEDVIEW;
// For some reason this code uses a much larger buffer
// than seems necessary. I don't know why... [mikesh 29jul97]
TCHAR szViewData[MAX_PATH+MAX_PATH]; szViewData[0] = TEXT('\0'); // For the non-webview case
if (IsEqualGUID(sViewData.idView, VID_WebView) && SUCCEEDED(ReadWebViewTemplate(szPath, szViewData, MAX_PATH))) { LPTSTR pszPath = szViewData; // We want to allow relative paths for the file: protocol
//
if (0 == StrCmpNI(TEXT("file://"), szViewData, 7)) // ARRAYSIZE(TEXT("file://"))
{ pszPath += 7; // ARRAYSIZE(TEXT("file://"))
} // for webview:// compatibility, keep this working:
else if (0 == StrCmpNI(TEXT("webview://file://"), szViewData, 17)) // ARRAYSIZE(TEXT("file://"))
{ pszPath += 17; // ARRAYSIZE(TEXT("webview://file://"))
} // handle relative references...
PathCombine(pszPath, szPath, pszPath);
// Avoid overwriting buffers
szViewData[MAX_PATH-1] = NULL; } else { // only IShellView extensions use LParams
sViewData.lParam = GetPrivateProfileInt( pNextID, TEXT("LParam"), 0, szIniFile );
if (!fExtShellView && VID_FolderState != sViewData.idView) { // No moniker, no IShellView extension, this must be a VID_FolderState
// kinda thing. (Otherwise it's a bad desktop.ini.)
//
RIPMSG(0, "Extended view is registered incorrectly."); continue; } } SHTCharToUnicode(szViewData, sViewData.wszMoniker, ARRAYSIZE(sViewData.wszMoniker));
// NOTE: the size is in WCHARs not in BYTES
pszDataBegin = (LPWSTR)SHAlloc(iSizeRemaining * sizeof(WCHAR)); if (NULL == pszDataBegin) continue;
iTotalSize = iSizeRemaining; iCurOffset = 0;
// Read the custom colors
for (int i = 0; i < CRID_COLORCOUNT; i++) sCustomData.crCustomColors[i] = GetPrivateProfileInt(pNextID, c_szExtViewUIRegKeys[ID_EXTVIEWCOLORSFIRST + i], CLR_MYINVALID, szIniFile); // Read the extended view strings
for (i = 0; i <= ID_EXTVIEWSTRLAST; i++) { sCustomData.acchOffExtViewUIstr[i] = GetCustomStrData(&pszDataBegin, &iSizeRemaining, &iTotalSize, &iCurOffset, pNextID, c_szExtViewUIRegKeys[i], szIniFile, (i == ID_EXTVIEWICONAREAIMAGE ? szPath : NULL)); }
sCustomData.cchSizeOfBlock = (iTotalSize - iSizeRemaining); sCustomData.lpDataBlock = pszDataBegin; sViewData.pCustomData = &sCustomData;
// if docobjextended view that is not webview, DO NOT ADD IT, UNSUPPORTED.
if (!(sViewData.dwFlags & SFVF_NOWEBVIEWFOLDERCONTENTS) && !IsEqualGUID(sViewData.idView, VID_WebView) && !IsEqualGUID(sViewData.idView, VID_FolderState)) continue; Add(&sViewData);
//We already copied the data. So, we can free it!
SHFree(pszDataBegin); } }
void CViewsList::Empty() { _bGotDef = FALSE;
for (int i=GetItemCount()-1; i>=0; --i) { SFVVIEWSDATA *sfvData = GetPtr(i); ASSERT(sfvData); if (sfvData->dwFlags & SFVF_CUSTOMIZEDVIEW) { CUSTOMVIEWSDATA *pCustomPtr = sfvData->pCustomData; if (pCustomPtr) { if (pCustomPtr->lpDataBlock) SHFree(pCustomPtr->lpDataBlock); SHFree(pCustomPtr); } } SHFree(sfvData); }
CGenList::Empty(); }
SFVVIEWSDATA* CViewsList::CopyData(const SFVVIEWSDATA* pData) { SFVVIEWSDATA* pCopy = (SFVVIEWSDATA*)SHAlloc(sizeof(SFVVIEWSDATA)); if (pCopy) { memcpy(pCopy, pData, sizeof(SFVVIEWSDATA)); if ((pData->dwFlags & SFVF_CUSTOMIZEDVIEW) && pData->pCustomData) { CUSTOMVIEWSDATA *pCustomData = (CUSTOMVIEWSDATA *)SHAlloc(sizeof(CUSTOMVIEWSDATA)); if (pCustomData) { memcpy(pCustomData, pData->pCustomData, sizeof(CUSTOMVIEWSDATA)); pCopy->pCustomData = pCustomData;
if (pCustomData->lpDataBlock) { // NOTE: DataBlock size is in WCHARs
LPWSTR lpDataBlock = (LPWSTR)SHAlloc(pCustomData->cchSizeOfBlock * sizeof(WCHAR)); if (lpDataBlock) { // NOTE: DataBlock size is in WCHARs
memcpy(lpDataBlock, pCustomData->lpDataBlock, pCustomData->cchSizeOfBlock * sizeof(WCHAR)); pCustomData->lpDataBlock = lpDataBlock; } else { SHFree(pCustomData); goto Failed; } } } else { Failed: SHFree(pCopy); pCopy = NULL; } } }
return pCopy; }
int CViewsList::NextUnique(int nLast) { for (int nNext = nLast + 1; ; ++nNext) { SFVVIEWSDATA* pItem = GetPtr(nNext); if (!pItem) { break; }
for (int nPrev=nNext-1; nPrev>=0; --nPrev) { SFVVIEWSDATA*pPrev = GetPtr(nPrev); if (pItem->idView == pPrev->idView) { break; } }
if (nPrev < 0) { return nNext; } }
return -1; }
// Note this is 1-based
int CViewsList::NthUnique(int nUnique) { for (int nNext = -1; nUnique > 0; --nUnique) { nNext = NextUnique(nNext); if (nNext < 0) { return -1; } }
return nNext; }
void CCallback::_GetExtViews(BOOL bForce) { CDefView* pView = IToClass(CDefView, _cCallback, this);
IEnumSFVViews *pev = NULL;
if (bForce) { _bGotViews = FALSE; }
if (_bGotViews) { return; }
_lViews.Empty();
SHELLVIEWID vid = VID_LargeIcons; if (FAILED(pView->CallCB(SFVM_GETVIEWS, (WPARAM)&vid, (LPARAM)&pev)) || !pev) { return; }
_lViews.SetDef(&vid);
SFVVIEWSDATA *pData; ULONG uFetched;
while ((pev->Next(1, &pData, &uFetched) == S_OK) && (uFetched == 1)) { // The list comes to us in general to specific order, but we want
// to search it in specific->general order. Inverting the list
// is easiest here, even though it causes a bunch of memcpy calls.
//
_lViews.Prepend(pData, FALSE); }
ATOMICRELEASE(pev);
_bGotViews = TRUE; }
HRESULT CCallback::TryLegacyGetViews(SFVM_WEBVIEW_TEMPLATE_DATA* pvit) { CDefView* pView = IToClass(CDefView, _cCallback, this); HRESULT hr = E_FAIL;
CLSID clsid; HRESULT hr2 = IUnknown_GetClassID(pView->_pshf, &clsid); if (FAILED(hr2) || !(SHGetObjectCompatFlags(NULL, &clsid) & OBJCOMPATF_NOLEGACYWEBVIEW)) { _GetExtViews(FALSE); if (_bGotViews) { SFVVIEWSDATA* pItem; GetViewIdFromGUID(&VID_WebView, &pItem); if (pItem) { StrCpyNW(pvit->szWebView, pItem->wszMoniker, ARRAYSIZE(pvit->szWebView)); hr = S_OK; } } else if (SUCCEEDED(hr2)) { // check for PersistMoniker under isf's coclass (Web Folders used this in W2K to get .htt Web View)
WCHAR szCLSID[GUIDSTR_MAX]; SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID));
WCHAR szkey[MAX_PATH]; wnsprintf(szkey, ARRAYSIZE(szkey), L"CLSID\\%s\\shellex\\ExtShellFolderViews\\{5984FFE0-28D4-11CF-AE66-08002B2E1262}", szCLSID);
DWORD cbSize = sizeof(pvit->szWebView); if (ERROR_SUCCESS == SHGetValueW(HKEY_CLASSES_ROOT, szkey, L"PersistMoniker", NULL, pvit->szWebView, &cbSize)) { hr = S_OK; } } }
return hr; }
HRESULT CCallback::OnRefreshLegacy(void* pv, BOOL fPrePost) { // If we're using the SFVM_GETVIEWS layer, invalidate it
if (_bGotViews) { _lViews.Empty(); _bGotViews = FALSE; }
return S_OK; }
int CCallback::GetViewIdFromGUID(SHELLVIEWID const *pvid, SFVVIEWSDATA** ppItem) { int iView = -1; for (UINT uView=0; uView<MAX_EXT_VIEWS; ++uView) { iView = _lViews.NextUnique(iView);
SFVVIEWSDATA* pItem = _lViews.GetPtr(iView); if (!pItem) { break; }
if (*pvid == pItem->idView) { if (ppItem) *ppItem = pItem;
return (int)uView; } }
if (ppItem) *ppItem = NULL; return -1; }
|