#include "shellprv.h" #include #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; uViewidView) { if (ppItem) *ppItem = pItem; return (int)uView; } } if (ppItem) *ppItem = NULL; return -1; }