#include "shellprv.h" #include "duiinfo.h" #include "ids.h" #include "datautil.h" DWORD FormatMessageArg(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageID, DWORD dwLangID, LPWSTR pwzBuffer, DWORD cchSize, ...) { va_list vaParamList; va_start(vaParamList, cchSize); DWORD dwResult = FormatMessageW(dwFlags, lpSource, dwMessageID, dwLangID, pwzBuffer, cchSize, &vaParamList); va_end(vaParamList); return dwResult; } CNameSpaceItemUIProperty::~CNameSpaceItemUIProperty() { } void CNameSpaceItemUIProperty::_SetParentAndItem(IShellFolder2 *psf, LPCITEMIDLIST pidl) { // set aliases to current values these are not refed // it is assumed that all helpers that use these variables won't be called // unless these have been set. since this can't fail this all works out fine m_psf = psf; // alias, not refed m_pidl = pidl; // alias, not cloned } STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayName(SHCOLUMNID scid, WCHAR* pwszPropDisplayName, int cchPropDisplayName) { *pwszPropDisplayName = 0; CComPtr spPropertyUI; HRESULT hr = _GetPropertyUI(&spPropertyUI); if (SUCCEEDED(hr)) { hr = spPropertyUI->GetDisplayName( scid.fmtid, scid.pid, PUIFNF_DEFAULT, pwszPropDisplayName, cchPropDisplayName); } return hr; } STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayValue(SHCOLUMNID scid, WCHAR* pszValue, int cch, PROPERTYUI_FORMAT_FLAGS flagsFormat) { *pszValue = 0; HRESULT hr = E_FAIL; // Use GetDisplayNameOf for the SCID_NAME property if (IsEqualSCID(scid, SCID_NAME)) { hr = DisplayNameOf(m_psf, m_pidl, SHGDN_INFOLDER, pszValue, cch); } else { // Use GetDetailsEx to get the value CComVariant varPropDisplayValue; if (m_psf->GetDetailsEx(m_pidl, &scid, &varPropDisplayValue) == S_OK) // S_FALSE means property wasn't there. { if (IsEqualSCID(scid, SCID_SIZE) && ((varPropDisplayValue.vt == VT_UI8) && (varPropDisplayValue.ullVal <= 0))) { hr = E_FAIL; // Don't display 0 byte sizes } else { CComPtr spPropertyUI; hr = _GetPropertyUI(&spPropertyUI); if (SUCCEEDED(hr)) { hr = spPropertyUI->FormatForDisplay(scid.fmtid, scid.pid, (PROPVARIANT*)&varPropDisplayValue, //cast from VARIANT to PROPVARIANT should be ok flagsFormat, pszValue, cch); } } } } return hr; } STDMETHODIMP CNameSpaceItemUIProperty::GetInfoString(SHCOLUMNID scid, WCHAR* pwszInfoString, int cchInfoString) { HRESULT hr = E_FAIL; // No DisplayName for the following properties if (IsEqualSCID(scid, SCID_NAME) || IsEqualSCID(scid, SCID_TYPE) || IsEqualSCID(scid, SCID_Comment)) { hr = GetPropertyDisplayValue(scid, pwszInfoString, cchInfoString, PUIFFDF_DEFAULT); } else { // The other properties are in the format PropertyName: Value // Get the display name WCHAR wszPropertyDisplayName[50]; hr = GetPropertyDisplayName(scid, wszPropertyDisplayName, ARRAYSIZE(wszPropertyDisplayName)); if (SUCCEEDED(hr)) { // Get the display value PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT; WCHAR wszPropertyDisplayValue[INTERNET_MAX_URL_LENGTH]; hr = GetPropertyDisplayValue(scid, wszPropertyDisplayValue, ARRAYSIZE(wszPropertyDisplayValue), flagsFormat); // If the property display name or the property value is empty then we fail, so // this property will not be displayed. if (SUCCEEDED(hr)) { hr = (wszPropertyDisplayName[0] && wszPropertyDisplayValue[0]) ? S_OK : E_FAIL; } // Now, combine the display name and value, seperated by a colon if (SUCCEEDED(hr)) { // ShellConstructMessageString here to form the string WCHAR wszFormatStr[50]; LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr)); if (FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0, pwszInfoString, cchInfoString, wszPropertyDisplayName, wszPropertyDisplayValue)) { hr = S_OK; } else { hr = E_FAIL; } } } } return hr; } HRESULT CNameSpaceItemUIProperty::_GetPropertyUI(IPropertyUI **pppui) { HRESULT hr = E_FAIL; if (!m_spPropertyUI) { hr = SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &m_spPropertyUI)); } *pppui = m_spPropertyUI; if (*pppui) { (*pppui)->AddRef(); hr = S_OK; } return hr; } CNameSpaceItemInfoList::~CNameSpaceItemInfoList() { if (m_pDUIView) { m_pDUIView->SetDetailsInfoMsgWindowPtr(NULL, this); m_pDUIView->Release(); } } STDMETHODIMP CNameSpaceItemInfoList::Create(CDUIView* pDUIView, Value* pvDetailsSheet, IShellItemArray *psiItemArray, Element** ppElement) { HRESULT hr; *ppElement = NULL; CNameSpaceItemInfoList* pNSIInfoList = HNewAndZero(); if (!pNSIInfoList) { hr = E_OUTOFMEMORY; } else { hr = pNSIInfoList->Initialize(pDUIView, pvDetailsSheet, psiItemArray); if (SUCCEEDED(hr)) *ppElement = pNSIInfoList; else pNSIInfoList->Destroy(); } return hr; } STDMETHODIMP CNameSpaceItemInfoList::Initialize(CDUIView* pDUIView, Value* pvDetailsSheet, IShellItemArray *psiItemArray) { HRESULT hr = Element::Initialize(0); if (SUCCEEDED(hr)) { IDataObject *pdtobj = NULL; (m_pDUIView = pDUIView)->AddRef(); Value* pvLayout = NULL; int arVLOptions[] = { FALSE, ALIGN_LEFT, ALIGN_JUSTIFY, ALIGN_TOP }; hr = VerticalFlowLayout::Create(ARRAYSIZE(arVLOptions), arVLOptions, &pvLayout); if (SUCCEEDED(hr)) { SetValue(LayoutProp, PI_Local, pvLayout); pvLayout->Release(); } if (pvDetailsSheet) { SetValue(SheetProp, PI_Local, pvDetailsSheet); } // the HIDA format has 2 forms, one is each item in the array is a // fully qualified pidl. this is what the search folder produces // the other is the items are relative to a sigle folder pidl // the code below deals with both cases // Should just use use the ShellItemArray instead of getting the HIDA if (psiItemArray) { if (FAILED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdtobj)))) { pdtobj = NULL; } } hr = S_OK; BOOL bDetailsAvailable = FALSE; if (pdtobj) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { IShellFolder2 *psfRoot; LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida); hr = SHBindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder2, &psfRoot)); if (SUCCEEDED(hr)) { if (pida->cidl == 1) { LPCITEMIDLIST pidlItem = IDA_GetIDListPtr(pida, 0); IShellFolder2 *psf; LPCITEMIDLIST pidl; hr = SHBindToFolderIDListParent(psfRoot, pidlItem, IID_PPV_ARG(IShellFolder2, &psf), &pidl); if (SUCCEEDED(hr)) { if (!SHGetAttributes(psf, pidl, SFGAO_ISSLOW | SFGAO_FOLDER) && m_pDUIView->ShouldShowMiniPreview()) { _AddMiniPreviewerToList(psf, pidl); bDetailsAvailable = TRUE; } LPITEMIDLIST pidlFull; if (SUCCEEDED(SHILCombine(pidlFolder, pidlItem, &pidlFull))) { if (SUCCEEDED(m_pDUIView->InitializeDetailsInfo( CNameSpaceItemInfoList::WindowProc))) { m_pDUIView->SetDetailsInfoMsgWindowPtr(this, NULL); m_pDUIView->StartInfoExtraction(pidlFull); bDetailsAvailable = TRUE; } ILFree(pidlFull); } psf->Release(); } } else { hr = _OnMultiSelect(psfRoot, pida); bDetailsAvailable = SUCCEEDED(hr); } psfRoot->Release(); } HIDA_ReleaseStgMedium(pida, &medium); } pdtobj->Release(); } if (!pdtobj || !bDetailsAvailable) { pDUIView->ShowDetails(FALSE); } } return hr; } LRESULT CALLBACK CNameSpaceItemInfoList::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CNameSpaceItemInfoList* pNSIInfoList = (CNameSpaceItemInfoList*)::GetWindowPtr(hwnd, 0); switch(uMsg) { case WM_DESTROY: // ignore late messages { MSG msg; while (PeekMessage(&msg, hwnd, WM_DETAILS_INFO, WM_DETAILS_INFO, PM_REMOVE)) { if (msg.lParam) { CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)msg.lParam; // The destructor will do the necessary cleanup delete pDetailsInfoList; } } SetWindowPtr(hwnd, 0, NULL); } break; case WM_DETAILS_INFO: { // Check that pDetailsInfo is still alive and that you have a CDetailsInfoList object of the requested pidl CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)lParam; if (pDetailsInfoList && pNSIInfoList && (wParam == pNSIInfoList->m_pDUIView->_dwDetailsInfoID)) { BOOL fShow = FALSE; StartDefer(); Element * peDetailsInfoArea = pNSIInfoList->GetParent(); if (peDetailsInfoArea) { peDetailsInfoArea->RemoveLocalValue(HeightProp); } for (int i = 0; i < pDetailsInfoList->_nProperties; i++) { if (!pDetailsInfoList->_diProperty[i].bstrValue) { continue; } // 253647 - surpress the comment field from showing in the // Details section. Note, I left the support for Comments // in the code below because this decision might be reversed. if (IsEqualSCID(pDetailsInfoList->_diProperty[i].scid, SCID_Comment)) { continue; } WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH]; wszInfoString[0] = L'\0'; SHCOLUMNID scid = pDetailsInfoList->_diProperty[i].scid; // No DisplayName if we don't have one // or if it is one of the following properties if ((!pDetailsInfoList->_diProperty[i].bstrDisplayName) || ( IsEqualSCID(scid, SCID_NAME) || IsEqualSCID(scid, SCID_TYPE) || IsEqualSCID(scid, SCID_Comment) )) { StrCpyNW(wszInfoString, pDetailsInfoList->_diProperty[i].bstrValue, ARRAYSIZE(wszInfoString)); } else { // Now, combine the display name and value, seperated by a colon // ShellConstructMessageString here to form the string WCHAR wszFormatStr[50]; LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr)); FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0, wszInfoString, ARRAYSIZE(wszInfoString), pDetailsInfoList->_diProperty[i].bstrDisplayName, pDetailsInfoList->_diProperty[i].bstrValue); } if (wszInfoString[0]) { Element* pElement; HRESULT hr = CNameSpaceItemInfo::Create(wszInfoString, &pElement); if (SUCCEEDED(hr)) { hr = pNSIInfoList->Add(pElement); if (IsEqualSCID(scid, SCID_NAME)) { pElement->SetID(L"InfoName"); } else if (IsEqualSCID(scid, SCID_TYPE)) { pElement->SetID(L"InfoType"); } else if (IsEqualSCID(scid, SCID_Comment)) { pElement->SetID(L"InfoTip"); } fShow = TRUE; } } } pNSIInfoList->m_pDUIView->ShowDetails(fShow); EndDefer(); } if (pDetailsInfoList) { delete pDetailsInfoList; // The destructor will do the necessary cleanup } break; } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return (LRESULT)0; } HRESULT CNameSpaceItemInfoList::_AddMiniPreviewerToList(IShellFolder2 *psf, LPCITEMIDLIST pidl) { Element* pElement; HRESULT hr = CMiniPreviewer::Create(m_pDUIView, psf, pidl, &pElement); if (SUCCEEDED(hr)) { hr = Add(pElement); } return E_NOTIMPL; } #define MAX_FILES_FOR_COMPUTING_SIZE 100 HRESULT CNameSpaceItemInfoList::_OnMultiSelect(IShellFolder2 *psfRoot, LPIDA pida) { WCHAR wszText[INTERNET_MAX_URL_LENGTH]; // Get the format string for n selection text WCHAR wszFormatStr[128]; LoadStringW(HINST_THISDLL, IDS_NSELECTED, wszFormatStr, ARRAYSIZE(wszFormatStr)); // Now, form the n selection text wnsprintfW(wszText, ARRAYSIZE(wszText), wszFormatStr, pida->cidl); WCHAR wszTemp[MAX_PATH]; wszTemp[0] = 0; CComPtr spPropertyUI; HRESULT hr = _GetPropertyUI(&spPropertyUI); if (SUCCEEDED(hr)) { ULONGLONG ullSizeTotal = 0; if (pida->cidl <= MAX_FILES_FOR_COMPUTING_SIZE) { // Compute the total size and the names of the selected files for (UINT i = 0; i < pida->cidl; i++) { IShellFolder2 *psf; LPCITEMIDLIST pidl; hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder2, &psf), &pidl); if (SUCCEEDED(hr)) { ULONGLONG ullSize; if (SUCCEEDED(GetLongProperty(psf, pidl, &SCID_SIZE, &ullSize))) { ullSizeTotal += ullSize; } psf->Release(); } } } // Get the display string for Total Size if (ullSizeTotal > 0) { // Convert ullSizeTotal to a string PROPVARIANT propvar; propvar.vt = VT_UI8; propvar.uhVal.QuadPart = ullSizeTotal; WCHAR wszFormattedTotalSize[128]; if (SUCCEEDED(spPropertyUI->FormatForDisplay(SCID_SIZE.fmtid, SCID_SIZE.pid, &propvar, PUIFFDF_DEFAULT, wszFormattedTotalSize, ARRAYSIZE(wszFormattedTotalSize)))) { // Get the format string for Total File Size text LoadStringW(HINST_THISDLL, IDS_TOTALFILESIZE, wszFormatStr, ARRAYSIZE(wszFormatStr)); // Now, form the Total File Size text wnsprintfW(wszTemp, ARRAYSIZE(wszTemp), wszFormatStr, wszFormattedTotalSize); } } } if (wszTemp[0]) { // Append two line breaks StrCatBuffW(wszText, L"\n\n", ARRAYSIZE(wszText)); // Append the Total Size string StrCatBuffW(wszText, wszTemp, ARRAYSIZE(wszText)); } // Now make a dui gadget for wszText Element* pElement; if (SUCCEEDED(CNameSpaceItemInfo::Create(wszText, &pElement))) { Add(pElement); } return S_OK; } IClassInfo* CNameSpaceItemInfoList::Class = NULL; HRESULT CNameSpaceItemInfoList::Register() { return ClassInfo::Register(L"NameSpaceItemInfoList", NULL, 0); } STDMETHODIMP CNameSpaceItemInfo::Create(WCHAR* pwszInfoString, Element** ppElement) { *ppElement = NULL; HRESULT hr = E_FAIL; CNameSpaceItemInfo* pNSIInfo = HNewAndZero(); if (!pNSIInfo) { hr = E_OUTOFMEMORY; } else { hr = pNSIInfo->Initialize(pwszInfoString); if (SUCCEEDED(hr)) *ppElement = pNSIInfo; else pNSIInfo->Destroy(); } return hr; } STDMETHODIMP CNameSpaceItemInfo::Initialize(WCHAR* pwszInfoString) { HRESULT hr = Element::Initialize(0); if (SUCCEEDED(hr)) { hr = SetContentString(pwszInfoString); } return hr; } IClassInfo* CNameSpaceItemInfo::Class = NULL; HRESULT CNameSpaceItemInfo::Register() { return ClassInfo::Register(L"NameSpaceItemInfo", NULL, 0); } STDMETHODIMP CBitmapElement::Create(HBITMAP hBitmap, Element** ppElement) { *ppElement = NULL; HRESULT hr; CBitmapElement* pBitmapElement = HNewAndZero(); if (!pBitmapElement) { hr = E_OUTOFMEMORY; } else { hr = pBitmapElement->Initialize(hBitmap); if (SUCCEEDED(hr)) *ppElement = pBitmapElement; else pBitmapElement->Destroy(); } return hr; } STDMETHODIMP CBitmapElement::Initialize(HBITMAP hBitmap) { HRESULT hr = Element::Initialize(0); if (SUCCEEDED(hr)) { if (hBitmap) { Value* pGraphic = Value::CreateGraphic(hBitmap); if (pGraphic) { SetValue(ContentProp, PI_Local, pGraphic); pGraphic->Release(); } } } return hr; } IClassInfo* CBitmapElement::Class = NULL; HRESULT CBitmapElement::Register() { return ClassInfo::Register(L"BitmapElement", NULL, 0); } CMiniPreviewer::~CMiniPreviewer() { // We are going away if (m_pDUIView) { m_pDUIView->SetThumbnailMsgWindowPtr(NULL, this); m_pDUIView->Release(); } } STDMETHODIMP CMiniPreviewer::Create(CDUIView* pDUIView, IShellFolder2* psf, LPCITEMIDLIST pidl, Element** ppElement) { HRESULT hr; *ppElement = NULL; CMiniPreviewer* pMiniPreviewer = HNewAndZero(); if (!pMiniPreviewer) { hr = E_OUTOFMEMORY; } else { hr = pMiniPreviewer->Initialize(pDUIView, psf, pidl); if (SUCCEEDED(hr)) *ppElement = pMiniPreviewer; else pMiniPreviewer->Destroy(); } return hr; } STDMETHODIMP CMiniPreviewer::Initialize(CDUIView* pDUIView, IShellFolder2 *psf, LPCITEMIDLIST pidl) { HRESULT hr = Element::Initialize(0); if (SUCCEEDED(hr)) { (m_pDUIView = pDUIView)->AddRef(); LPITEMIDLIST pidlFull; if (SUCCEEDED(SHFullIDListFromFolderAndItem(psf, pidl, &pidlFull))) { if (SUCCEEDED(m_pDUIView->InitializeThumbnail(CMiniPreviewer::WindowProc))) { m_pDUIView->SetThumbnailMsgWindowPtr(this, NULL); m_pDUIView->StartBitmapExtraction(pidlFull); } ILFree(pidlFull); } } return hr; } // Window procedure for catching the "image-extraction-done" message // from m_pDUIView->_spThumbnailExtractor2 LRESULT CALLBACK CMiniPreviewer::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CMiniPreviewer* pMiniPreviewer = (CMiniPreviewer*)::GetWindowPtr(hwnd, 0); switch(uMsg) { case WM_DESTROY: // ignore late messages { MSG msg; while (PeekMessage(&msg, hwnd, WM_HTML_BITMAP, WM_HTML_BITMAP, PM_REMOVE)) { if (msg.lParam) { DeleteObject((HBITMAP)msg.lParam); } } SetWindowPtr(hwnd, 0, NULL); } break; case WM_HTML_BITMAP: // Check that pMiniPreviewer is still alive and that you have an HBITMAP of the requested pidl if (pMiniPreviewer && (wParam == pMiniPreviewer->m_pDUIView->_dwThumbnailID)) { if (lParam) // This is the HBITMAP of the extracted image { Element* pElement; HRESULT hr = CBitmapElement::Create((HBITMAP)lParam, &pElement); if (SUCCEEDED(hr)) { // The addition of the thumbnail comes in late. DUI is // not currently set up to handle a DisableAnimations()/ // EnableAnimations() here, which we were originally // doing to prevent jumpiness. This was discovered in // RAID 389343, because our coming off the background // thread and calling DisableAnimations() was screwing up // other animations that were already underway. Talking // with markfi, the problem is understood BUT not one to // be fixed because of the negative perf impact it would // have on DUI. So instead we'll StartDefer()/EndDefer() // to minimize jumpiness from our two layout ops below. StartDefer(); // Set the VerticalFlowLayout for our element. Otherwise, // our control will not render. Value* pvLayout = NULL; hr = FillLayout::Create(0, NULL, &pvLayout); if (SUCCEEDED(hr)) { hr = pMiniPreviewer->SetValue(LayoutProp, PI_Local, pvLayout); if (SUCCEEDED(hr)) { hr = pMiniPreviewer->Add(pElement); } pvLayout->Release(); } if (FAILED(hr)) { pElement->Destroy(); } EndDefer(); } else { DeleteObject((HBITMAP)lParam); } } } else if (lParam) // This extraction got done too late. // So, just delete the wasted HBITMAP. { DeleteObject((HBITMAP)lParam); } break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return (LRESULT)0; } IClassInfo* CMiniPreviewer::Class = NULL; HRESULT CMiniPreviewer::Register() { return ClassInfo::Register(L"MiniPreviewer", NULL, 0); } // ***** CDetailsInfoList ******* CDetailsInfoList::CDetailsInfoList() : _nProperties(0) { } CDetailsInfoList::~CDetailsInfoList() { for (int i = 0; i < _nProperties; i++) { if (_diProperty[i].bstrValue) { SysFreeString(_diProperty[i].bstrValue); } if (_diProperty[i].bstrDisplayName) { SysFreeString(_diProperty[i].bstrDisplayName); } } } // ***** CDetailsSectionInfoTask ******* CDetailsSectionInfoTask::CDetailsSectionInfoTask(HRESULT *phr, IShellFolder *psfContaining, LPCITEMIDLIST pidlAbsolute, HWND hwndMsg, UINT uMsg, DWORD dwDetailsInfoID) : CRunnableTask(RTF_DEFAULT), _hwndMsg(hwndMsg), _uMsg(uMsg), _dwDetailsInfoID(dwDetailsInfoID) { ASSERT(psfContaining && pidlAbsolute && hwndMsg); _psfContaining = psfContaining; _psfContaining->AddRef(); *phr = SHILClone(pidlAbsolute, &_pidlAbsolute); } CDetailsSectionInfoTask::~CDetailsSectionInfoTask() { _psfContaining->Release(); ILFree(_pidlAbsolute); } HRESULT CDetailsSectionInfoTask_CreateInstance(IShellFolder *psfContaining, LPCITEMIDLIST pidlAbsolute, HWND hwndMsg, UINT uMsg, DWORD dwDetailsInfoID, CDetailsSectionInfoTask **ppTask) { *ppTask = NULL; HRESULT hr; CDetailsSectionInfoTask* pNewTask = new CDetailsSectionInfoTask( &hr, psfContaining, pidlAbsolute, hwndMsg, uMsg, dwDetailsInfoID); if (pNewTask) { if (SUCCEEDED(hr)) *ppTask = pNewTask; else pNewTask->Release(); } else { hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CDetailsSectionInfoTask::RunInitRT() { ASSERT(_pidlAbsolute); BOOL bMsgPosted = FALSE; HRESULT hr = E_FAIL; CDetailsInfoList* pCDetailsInfoList = new CDetailsInfoList; if (!pCDetailsInfoList) { hr = E_OUTOFMEMORY; } else { CComPtr psf2; LPCITEMIDLIST pidlLast; hr = SHBindToIDListParent(_pidlAbsolute, IID_PPV_ARG(IShellFolder2, &psf2), &pidlLast); if (SUCCEEDED(hr)) { _SetParentAndItem(psf2, pidlLast); WCHAR wszProperties[MAX_PATH]; hr = _GetDisplayedDetailsProperties(psf2, pidlLast, wszProperties, ARRAYSIZE(wszProperties)); if (SUCCEEDED(hr)) { // pwszProperties is usually of the form "prop:Name;Type;Author" CComPtr spPropertyUI; hr = _GetPropertyUI(&spPropertyUI); if (SUCCEEDED(hr)) { SHCOLUMNID scid; WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH]; ULONG chEaten = 0; // loop var, incremented by ParsePropertyName for (pCDetailsInfoList->_nProperties = 0; pCDetailsInfoList->_nProperties < ARRAYSIZE(pCDetailsInfoList->_diProperty) && SUCCEEDED(spPropertyUI->ParsePropertyName(wszProperties, &scid.fmtid, &scid.pid, &chEaten)); pCDetailsInfoList->_nProperties++) { pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].scid = scid; PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT; // Get the display value hr = GetPropertyDisplayValue(scid, wszInfoString, ARRAYSIZE(wszInfoString), flagsFormat); if (SUCCEEDED(hr) && wszInfoString[0]) { pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrValue = SysAllocString(wszInfoString); } // Get the display name hr = GetPropertyDisplayName(scid, wszInfoString, ARRAYSIZE(wszInfoString)); if (SUCCEEDED(hr) && wszInfoString[0]) { pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrDisplayName = SysAllocString(wszInfoString); } } //The extraction is done. Now post a message. if (PostMessage(_hwndMsg, WM_DETAILS_INFO, (WPARAM)_dwDetailsInfoID, (LPARAM)pCDetailsInfoList)) { bMsgPosted = TRUE; } } } } } if (!bMsgPosted && pCDetailsInfoList) { delete pCDetailsInfoList; } return S_OK; } HRESULT CDetailsSectionInfoTask::_GetDisplayedDetailsProperties(IShellFolder2* psf, LPCITEMIDLIST pidl, WCHAR* pwszProperties, int cchProperties) { HRESULT hr = GetStringProperty(psf, pidl, &SCID_DetailsProperties, pwszProperties, cchProperties); if (FAILED(hr)) // Default properties { if (SHGetAttributes(psf, pidl, SFGAO_ISSLOW)) { // SCID_NAME;SCID_TYPE StrCpyNW(pwszProperties, L"prop:Name;Type", cchProperties); } else { // SCID_NAME;SCID_TYPE;SCID_ATTRIBUTES_DESCRIPTION;SCID_Comment;SCID_WRITETIME;SCID_SIZE;SCID_Author;SCID_CSC_STATUS StrCpyNW(pwszProperties, L"prop:Name;Type;AttributesDescription;DocComments;Write;Size;DocAuthor;CSCStatus", cchProperties); } } // Augment properties to include "Location" if in CLSID_DocFindFolder. IPersist *pPersist; ASSERT(_psfContaining); if (SUCCEEDED(_psfContaining->QueryInterface(IID_IPersist, (void**)&pPersist))) { CLSID clsid; if (SUCCEEDED(pPersist->GetClassID(&clsid)) && IsEqualCLSID(clsid, CLSID_DocFindFolder)) _AugmentDisplayedDetailsProperties(pwszProperties, cchProperties); pPersist->Release(); } return S_OK; } void CDetailsSectionInfoTask::_AugmentDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t cchDetailsProperties) { static WCHAR szDeclarator[] = L"prop:"; static size_t lenDeclarator = lstrlen(szDeclarator); static WCHAR szName[64] = { 0 }; static size_t lenName = 0; static WCHAR szType[64] = { 0 }; static size_t lenType = 0; static WCHAR szDirectory[64] = { 0 }; static size_t lenDirectory = 0; // Initialize statics once 'n only once. if (!szName[0] || !szType[0] || !szDirectory[0]) { HRESULT hr; hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_NAME, szName, ARRAYSIZE(szName)); ASSERT(SUCCEEDED(hr)); lenName = lstrlen(szName); hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_TYPE, szType, ARRAYSIZE(szType)); ASSERT(SUCCEEDED(hr)); lenType = lstrlen(szType); hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_DIRECTORY, szDirectory, ARRAYSIZE(szDirectory)); ASSERT(SUCCEEDED(hr)); lenDirectory = lstrlen(szDirectory); } // Attempt to merge the "Directory" property, in the following ways: // "prop:Name;Type;Directory;..." // "prop:Name;Directory;..." // "prop:Directory;..." // size_t lenDetailsProperties = lstrlen(pszDetailsProperties); size_t lenMerged = lenDetailsProperties + 1 + lenDirectory; if (lenMerged < cchDetailsProperties && 0 == StrCmpNI(pszDetailsProperties, szDeclarator, lenDeclarator)) { // Search for "Directory" property (in case it is already specified). if (!_SearchDisplayedDetailsProperties(pszDetailsProperties, lenDetailsProperties, szDirectory, lenDirectory)) { // Allocate a temporary buffer to merge into. size_t cchMerged = cchDetailsProperties; LPWSTR pszMerged = new WCHAR[cchMerged]; if (pszMerged) { // Determine offset in pszDetailsProperties to merge at. size_t offsetInsert; if (lenDeclarator < lenDetailsProperties) { // Search for "Name" property. LPWSTR pszName = _SearchDisplayedDetailsProperties( &pszDetailsProperties[lenDeclarator], lenDetailsProperties - lenDeclarator, szName, lenName); if (pszName) { // Search for "Type" property (immediately following "Name"). size_t offsetName = (pszName - pszDetailsProperties); size_t offsetType = offsetName + lenName + 1; size_t offsetRemainder = offsetType + lenType; if ((offsetRemainder == lenDetailsProperties || (offsetRemainder < lenDetailsProperties && pszDetailsProperties[offsetRemainder] == ';')) && !StrCmpNI(&pszDetailsProperties[offsetType], szType, lenType)) { offsetInsert = offsetRemainder; } else offsetInsert = offsetName + lenName; } else offsetInsert = lenDeclarator; } else offsetInsert = lenDeclarator; // Merge the "Directory" property. StrCpyN(pszMerged, pszDetailsProperties, offsetInsert + 1); // + 1 to account for null terminator. if (offsetInsert > lenDeclarator) StrCatBuff(pszMerged, L";", cchMerged); // ';' prepend if necessary StrCatBuff(pszMerged, szDirectory, cchMerged); // "Directory" if (offsetInsert < lenDetailsProperties) { if (pszDetailsProperties[offsetInsert] != ';') StrCatBuff(pszMerged, L";", cchMerged); // ';' append if necessary StrCatBuff(pszMerged, &pszDetailsProperties[offsetInsert], cchMerged); } // Update in/out pszDetailsProperties. StrCpyN(pszDetailsProperties, pszMerged, cchDetailsProperties); ASSERT(lenMerged == lstrlen(pszMerged)); ASSERT(lenMerged < cchDetailsProperties); delete[] pszMerged; } } } else { // Invalid format. ASSERT(FALSE); } } LPWSTR CDetailsSectionInfoTask::_SearchDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t lenDetailsProperties, LPWSTR pszProperty, size_t lenProperty) { LPWSTR psz = StrStrI(pszDetailsProperties, pszProperty); while (psz) { // Check start... if (psz == pszDetailsProperties || psz[-1] == ';') { // ... and end. size_t lenToEndOfProperty = (psz - pszDetailsProperties) + lenProperty; if (lenToEndOfProperty == lenDetailsProperties || pszDetailsProperties[lenToEndOfProperty] == ';') break; } psz = StrStrI(psz + lenProperty, pszProperty); } return psz; }