#include "dpastuff.h" // // The ORDERITEM structure is exposed via the IOrderList interface. // ORDERITEM2 contains our private hidden fields. // // The extra fields contain information about the cached icon location. // // ftModified is the modify-time on the pidl, which is used to detect // whether the cache needs to be refreshed. // // If ftModified is nonzero, then { pwszIcon, iIconIndex, pidlTarget } // describe the icon that should be displayed for the item. // // If pwszIcon is nonzero, then the item is a shortcut with a custom // icon. pwszIcon points to the file name for the icon, iIconIndex // is the icon index within the pwszIcon file. // // If pidlTarget is nonzero, then the item is a shortcut with a default // icon. pidlTarget is the target pidl, whose icon we should use. // typedef struct ORDERITEM2 { ORDERITEM oi; // part that clients see - must come first DWORD dwFlags; // User defined flags. LPWSTR pwszIcon; // for cacheing the icon location int iIconIndex; // for cacheing the icon location LPITEMIDLIST pidlTarget; // use the icon for this pidl } ORDERITEM2, *PORDERITEM2; int CALLBACK OrderItem_Compare(LPVOID pv1, LPVOID pv2, LPARAM lParam) { PORDERITEM poi1 = (PORDERITEM)pv1; PORDERITEM poi2 = (PORDERITEM)pv2; PORDERINFO poinfo = (PORDERINFO)lParam; int nRet; if (!poinfo) { ASSERT(FALSE); return 0; } switch (poinfo->dwSortBy) { case OI_SORTBYNAME: { // Make sure they're both non-null // if ( poi1->pidl && poi2->pidl ) { HRESULT hres = poinfo->psf->CompareIDs(0, poi1->pidl, poi2->pidl); nRet = (short)HRESULT_CODE(hres); } else { if ( poi1->pidl == poi2->pidl ) nRet = 0; else nRet = ((UINT_PTR)poi1->pidl < (UINT_PTR)poi2->pidl ? -1 : 1); } break; } case OI_SORTBYORDINAL: if (poi1->nOrder == poi2->nOrder) nRet = 0; else // do unsigned compare so -1 goes to end of list nRet = ((UINT)poi1->nOrder < (UINT)poi2->nOrder ? -1 : 1); break; default: ASSERT_MSG(0, "Bad dwSortBy passed to OrderItem_Compare"); nRet = 0; break; } return nRet; } void OrderItem_FreeIconInfo(PORDERITEM poi) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); if (poi2->pwszIcon) { LPWSTR pwszIcon = poi2->pwszIcon; poi2->pwszIcon = NULL; LocalFree(pwszIcon); } if (poi2->pidlTarget) { LPITEMIDLIST pidl = poi2->pidlTarget; poi2->pidlTarget = NULL; ILFree(pidl); } } LPVOID CALLBACK OrderItem_Merge(UINT uMsg, LPVOID pvDst, LPVOID pvSrc, LPARAM lParam) { PORDERITEM2 poi2Dst = CONTAINING_RECORD(pvDst, ORDERITEM2, oi); PORDERITEM2 poi2Src = CONTAINING_RECORD(pvSrc, ORDERITEM2, oi); PORDERINFO poinfo = (PORDERINFO)lParam; LPVOID pvRet = pvDst; switch (uMsg) { case DPAMM_MERGE: // Transfer the order field poi2Dst->oi.nOrder = poi2Src->oi.nOrder; // Propagate any cached icon information too... if (poi2Src->pwszIcon || poi2Src->pidlTarget) { // To avoid useless allocation, we transfer the cache across // instead of copying it. if (poinfo->psf2 && poinfo->psf2->CompareIDs(SHCIDS_ALLFIELDS, poi2Dst->oi.pidl, poi2Src->oi.pidl) == S_OK) { OrderItem_FreeIconInfo(&poi2Dst->oi); CopyMemory((LPBYTE)poi2Dst + sizeof(ORDERITEM), (LPBYTE)poi2Src + sizeof(ORDERITEM), sizeof(ORDERITEM2) - sizeof(ORDERITEM)); ZeroMemory((LPBYTE)poi2Src + sizeof(ORDERITEM), sizeof(ORDERITEM2) - sizeof(ORDERITEM)); } } break; case DPAMM_DELETE: case DPAMM_INSERT: // Don't need to implement this ASSERT(0); pvRet = NULL; break; } return pvRet; } int OrderItem_UpdatePos(LPVOID p, LPVOID pData) { PORDERITEM poi = (PORDERITEM)p; if (-1 == poi->nOrder) { poi->nOrder = (int)(INT_PTR)pData; } else if ((int)(INT_PTR)pData >= poi->nOrder) { poi->nOrder++; } return 1; } // OrderList_Merge sorts hdpaNew to match hdpaOld order, // putting any items in hdpaNew that were not in hdpaOld // at position iInsertPos (-1 means end of list). // // Assumes hdpaOld is already sorted by sort order in lParam (OI_SORTBYNAME by default) // (if hdpaOld is specified) // void OrderList_Merge(HDPA hdpaNew, HDPA hdpaOld, int iInsertPos, LPARAM lParam, LPFNORDERMERGENOMATCH pfn, LPVOID pvParam) { PORDERINFO poinfo = (PORDERINFO)lParam; BOOL fMergeOnly = FALSE; if (poinfo->dwSortBy == OI_MERGEBYNAME) { poinfo->dwSortBy = OI_SORTBYNAME; fMergeOnly = TRUE; } // hdpaNew has not been sorted, sort by name DPA_Sort(hdpaNew, OrderItem_Compare, lParam); BOOL fForceNoMatch = FALSE; if (FAILED(poinfo->psf->QueryInterface(IID_IShellFolder2, (LPVOID *)&poinfo->psf2))) { // 239390: Network Connections folder doesn't implement QI correctly. Its psf // fails QI for IID_IShellFolder2, but doesn't null out ppvObj. So do it for them. poinfo->psf2 = NULL; } // Copy order preferences over from old list to new list if (hdpaOld) { DPA_Merge(hdpaNew, hdpaOld, DPAM_SORTED | DPAM_NORMAL, OrderItem_Compare, OrderItem_Merge, lParam); // If we're waiting for the notify from a drag&drop operation, // update the new items (they will have a -1) to the insert position. if (-1 != iInsertPos) { DPA_EnumCallback(hdpaNew, OrderItem_UpdatePos, (LPVOID)(INT_PTR)iInsertPos); } if (poinfo->dwSortBy != OI_SORTBYORDINAL && !fMergeOnly) { poinfo->dwSortBy = OI_SORTBYORDINAL; DPA_Sort(hdpaNew, OrderItem_Compare, lParam); } } else fForceNoMatch = TRUE; // If the caller passed a NoMatch callback, then call it with // each item that is not matched. if (pfn) { for (int i = DPA_GetPtrCount(hdpaNew)-1 ; i >= 0 ; i--) { PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaNew, i); // Does this item have order information? if (iInsertPos == poi->nOrder || -1 == poi->nOrder || fForceNoMatch) { // No; Then pass to the "No Match" callback pfn(pvParam, poi->pidl); } } } ATOMICRELEASE(poinfo->psf2); OrderList_Reorder(hdpaNew); } // OrderList_Reorder refreshes the order info void OrderList_Reorder(HDPA hdpa) { int i; for (i = DPA_GetPtrCount(hdpa)-1 ; i >= 0 ; i--) { PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpa, i); poi->nOrder = i; } } BOOL OrderList_Append(HDPA hdpa, LPITEMIDLIST pidl, int nOrder) { PORDERITEM poi = OrderItem_Create(pidl, nOrder); if (poi) { if (-1 != DPA_AppendPtr(hdpa, poi)) return TRUE; OrderItem_Free(poi, FALSE); //don't free pidl because caller will do it } return FALSE; } // This differes from DPA_Clone in that it allocates new items! HDPA OrderList_Clone(HDPA hdpa) { HDPA hdpaNew = NULL; if (EVAL(hdpa)) { hdpaNew = DPA_Create(DPA_GetPtrCount(hdpa)); if (hdpaNew) { int i; for (i = 0 ; i < DPA_GetPtrCount(hdpa) ; i++) { PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpa, i); LPITEMIDLIST pidl = ILClone(poi->pidl); if (pidl) { if (!OrderList_Append(hdpaNew, pidl, poi->nOrder)) { ILFree(pidl); } } } } } return hdpaNew; } // Does not clone the pidl but will free it. // Does not addref the psf nor release it. PORDERITEM OrderItem_Create(LPITEMIDLIST pidl, int nOrder) { PORDERITEM2 poi = (PORDERITEM2)LocalAlloc(LPTR, SIZEOF(ORDERITEM2)); if (poi) { poi->oi.pidl = pidl; poi->oi.nOrder = nOrder; return &poi->oi; } return NULL; } void OrderItem_Free(PORDERITEM poi, BOOL fKillPidls /* = TRUE */) { if (fKillPidls) ILFree(poi->pidl); OrderItem_FreeIconInfo(poi); LocalFree(poi); } int OrderItem_FreeItem(LPVOID p, LPVOID pData) { PORDERITEM poi = (PORDERITEM)p; OrderItem_Free(poi, (BOOL)(INT_PTR)pData); return 1; } void OrderList_Destroy(HDPA* phdpa, BOOL fKillPidls /* = fTrue */) { if (*phdpa) { DPA_DestroyCallback(*phdpa, OrderItem_FreeItem, (LPVOID) (INT_PTR)fKillPidls); *phdpa = NULL; } } // // Return values: // // S_OK - icon obtained successfully // S_FALSE - icon not obtained, don't waste time trying // E_FAIL - no cached icon, need to do more work // HRESULT OrderItem_GetSystemImageListIndexFromCache(PORDERITEM poi, IShellFolder *psf, int *piOut) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); IShellFolder *psfT; LPCITEMIDLIST pidlItem; HRESULT hr; // Do we have a cached icon location? if (poi2->pwszIcon) { *piOut = 0; // Validate Path existance. if (PathFileExistsW(poi2->pwszIcon)) { *piOut = Shell_GetCachedImageIndex(poi2->pwszIcon, poi2->iIconIndex, GIL_PERINSTANCE); } return (*piOut > 0)? S_OK : E_FAIL; } // Do we have a cached pidlTarget? if (poi2->pidlTarget) { hr = SHBindToIDListParent(poi2->pidlTarget, IID_IShellFolder, (void**)&psfT, &pidlItem); if (SUCCEEDED(hr)) { // Make sure the pidl exsists before binding. because the bind does succeed if it does not exist. DWORD dwAttrib = SFGAO_VALIDATE; hr = psfT->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlItem, &dwAttrib); if (SUCCEEDED(hr)) { *piOut = SHMapPIDLToSystemImageListIndex(psfT, pidlItem, NULL); } psfT->Release(); return hr; } // Bind failed - shortcut target was deleted // Keep the cache valid because we don't want to whack the disk // all the time only to discover it's busted. return E_FAIL; } return E_FAIL; } DWORD OrderItem_GetFlags(PORDERITEM poi) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); return poi2->dwFlags; } void OrderItem_SetFlags(PORDERITEM poi, DWORD dwFlags) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); poi2->dwFlags = dwFlags; } int OrderItem_GetSystemImageListIndex(PORDERITEM poi, IShellFolder *psf, BOOL fUseCache) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); HRESULT hr; int iBitmap; DWORD dwAttr; if (fUseCache) { hr = OrderItem_GetSystemImageListIndexFromCache(poi, psf, &iBitmap); if (SUCCEEDED(hr)) { return iBitmap; } else { goto Fallback; } } else { // // Free any pointers we cached previously // if (poi2->pidlTarget) { ILFree(poi2->pidlTarget); poi2->pidlTarget = NULL; } Str_SetPtr(&poi2->pwszIcon, NULL); } // // Go find the icon. // ASSERT(poi2->pidlTarget == NULL); ASSERT(poi2->pwszIcon == NULL); // // Is this item shortcutlike at all? // dwAttr = SFGAO_LINK; hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&poi->pidl, &dwAttr); if (FAILED(hr) || !(dwAttr & SFGAO_LINK)) goto Fallback; // not a shortcut; use the fallback // // Must go for ANSI version first because client might not support // UNICODE. // // FEATURE - should QI for IExtractIcon to see if we get GIL_DONTCACHE // back. IShellLinkA *pslA; hr = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&poi->pidl, IID_IShellLinkA, 0, (LPVOID *)&pslA); if (FAILED(hr)) goto Fallback; // // If there's a UNICODE version, that's even better. // IShellLinkW *pslW; WCHAR wszIconPath[MAX_PATH]; hr = pslA->QueryInterface(IID_IShellLinkW, (LPVOID *)&pslW); if (SUCCEEDED(hr)) { hr = pslW->GetIconLocation(wszIconPath, ARRAYSIZE(wszIconPath), &poi2->iIconIndex); pslW->Release(); } else { // Only IShellLinkA supported. Thunk to UNICODE manually. CHAR szIconPath[ARRAYSIZE(wszIconPath)]; hr = pslA->GetIconLocation(szIconPath, ARRAYSIZE(szIconPath), &poi2->iIconIndex); if (SUCCEEDED(hr)) SHAnsiToUnicode(szIconPath, wszIconPath, ARRAYSIZE(wszIconPath)); } // If we have a custom icon path, then save that if (SUCCEEDED(hr) && wszIconPath[0]) { Str_SetPtr(&poi2->pwszIcon, wszIconPath); } else { // No icon path, get the target instead pslA->GetIDList(&poi2->pidlTarget); if (IsURLChild(poi2->pidlTarget, TRUE)) { // If this is a url, we want to go to the "Fallback" case. The reason for this // is that the fallback case will go through // where we will end up with the generic icon for .url files ILFree(poi2->pidlTarget); poi2->pidlTarget = NULL; pslA->Release(); goto Fallback; } } pslA->Release(); // // Aw-right, the cache is all loaded up. Let's try that again. // hr = OrderItem_GetSystemImageListIndexFromCache(poi, psf, &iBitmap); if (hr == S_OK) { return iBitmap; } Fallback: return SHMapPIDLToSystemImageListIndex(psf, poi->pidl, NULL); } // Header for file menu streams // // The file menu stream consists of an IOSTREAMHEADER followed by // a DPA_SaveStream of the order DPA. Each item in the DPA consists // of an OISTREAMITEM. // // To keep roaming profiles working between NT4 (IE4) and NT5 (IE5), // the dwVersion used by NT5 must be the same as that used by NT4. // I.e., it must be 2. typedef struct tagOISTREAMHEADER { DWORD cbSize; // Size of header DWORD dwVersion; // Version of header } OISTREAMHEADER; #define OISTREAMHEADER_VERSION 2 // // Each item in a persisted order DPA consists of an OISTREAMITEM // followed by additional goo. All pidls stored include the // terminating (USHORT)0. // // IE4: // OISTREAMITEM // pidl - the item itself // // IE5 - shortcut has custom icon // OISTREAMITEM // pidl - the item itself (last-modify time implied) // - for WCHAR alignment // dwFlags - User defined Flags // dwStringLen - Length of the icon path // UNICODEZ iconpath - icon path // iIconIndex - icon index // // IE5 - shortcut takes its icon from another pidl // OISTREAMITEM // pidl - the item itself (last-modify time implied) // - for WCHAR alignment // dwFlags - User defined Flags // (DWORD)0 - null string indicates "no custom icon" // pidlTarget - use the icon for this pidl // typedef struct tagOISTREAMITEM { DWORD cbSize; // Size including trailing goo int nOrder; // User-specified order // variable-sized trailing goo comes here. // // See above for description of trailing goo. } OISTREAMITEM; #define CB_OISTREAMITEM (sizeof(OISTREAMITEM)) // // Save a component of the orderitem to the stream. If an error has // already occurred on the stream, *phrRc contains the old error code, // and we write nothing. // // If pstm == NULL, then we are not actually writing anything. We are // merely doing a dry run. // // Otherwise, *phrRc accumulates the number of bytes actually written, // or receives an error code on failure. // void OrderItem_SaveSubitemToStream(IStream *pstm, LPCVOID pvData, ULONG cb, HRESULT* phrRc) { HRESULT hres; if (SUCCEEDED(*phrRc)) { if (pstm) { hres = IStream_Write(pstm, (LPVOID)pvData, cb); if (SUCCEEDED(hres)) { *phrRc += cb; // successful write - accumulate } else { *phrRc = hres; // error - return error code } } else { *phrRc += cb; // no output stream - accumulate } } } // // This worker function (1) computes the numer of bytes we will actually // write out, and (2) actually writes it if pstm != NULL. // // Return value is the number of bytes written (or would have been // written), or a COM error code on failure. // const BYTE c_Zeros[2] = { 0 }; // a bunch of zeros HRESULT OrderItem_SaveToStreamWorker(PORDERITEM2 poi2, OISTREAMITEM *posi, IStream *pstm, IShellFolder2 *psf2) { HRESULT hrRc = 0; // no bytes, no error ASSERT(poi2->oi.pidl); // // First comes the header. // OrderItem_SaveSubitemToStream(pstm, posi, CB_OISTREAMITEM, &hrRc); // // Then the pidl. // // We're assuming this is an immediate child pidl. If it's not, // the pidl is being truncated! ASSERT(0 == _ILNext(poi2->oi.pidl)->mkid.cb); OrderItem_SaveSubitemToStream(pstm, poi2->oi.pidl, poi2->oi.pidl->mkid.cb + sizeof(USHORT), &hrRc); // Insert padding to get back to WCHAR alignment. if (hrRc % sizeof(WCHAR)) { OrderItem_SaveSubitemToStream(pstm, &c_Zeros, 1, &hrRc); } OrderItem_SaveSubitemToStream(pstm, &poi2->dwFlags, sizeof(DWORD), &hrRc); // // If we haven't barfed yet and the IShellFolder supports identity // and there is icon information, then save it. // if (SUCCEEDED(hrRc) && psf2 && (poi2->pwszIcon || poi2->pidlTarget)) { // Optional icon is present. if (poi2->pwszIcon) { // UNICODEZ path DWORD cbString = (lstrlenW(poi2->pwszIcon) + 1) * sizeof(WCHAR); // Save the String len OrderItem_SaveSubitemToStream(pstm, &cbString, sizeof(DWORD) , &hrRc); OrderItem_SaveSubitemToStream(pstm, poi2->pwszIcon, (lstrlenW(poi2->pwszIcon) + 1) * sizeof(WCHAR), &hrRc); // icon index OrderItem_SaveSubitemToStream(pstm, &poi2->iIconIndex, sizeof(poi2->iIconIndex), &hrRc); } else { DWORD cbString = 0; OrderItem_SaveSubitemToStream(pstm, &cbString, sizeof(DWORD), &hrRc); // pidlTarget OrderItem_SaveSubitemToStream(pstm, poi2->pidlTarget, ILGetSize(poi2->pidlTarget), &hrRc); } } return hrRc; } HRESULT CALLBACK OrderItem_SaveToStream(DPASTREAMINFO * pinfo, IStream * pstm, LPVOID pvData) { PORDERITEM2 poi2 = (PORDERITEM2)pinfo->pvItem; HRESULT hres = S_FALSE; IShellFolder2 *psf2 = (IShellFolder2 *)pvData; if (poi2->oi.pidl) { OISTREAMITEM osi; // First a dry run to compute the size of this item. hres = OrderItem_SaveToStreamWorker(poi2, NULL, NULL, psf2); // Nothing actually got written, so this should always succeed. ASSERT(SUCCEEDED(hres)); osi.cbSize = hres; osi.nOrder = poi2->oi.nOrder; // Now write it out for real hres = OrderItem_SaveToStreamWorker(poi2, &osi, pstm, psf2); // On success, we must return exactly S_OK or DPA will blow us off if (SUCCEEDED(hres)) hres = S_OK; } return hres; } // // Check if a pidl we read out of a stream is a simple child pidl. // The pidl must be exactly cb bytes in length. // The pointer is known to be valid; // we just want to check that the contents are good, too. // BOOL IsValidPersistedChildPidl(LPCITEMIDLIST pidl, UINT cb) { // Must have at least room for one byte of pidl plus the terminating // zero. if (cb < 1 + sizeof(USHORT)) return FALSE; // Make sure size is at least what it's supposed to be. if (pidl->mkid.cb + sizeof(USHORT) > cb) return FALSE; // Make sure there's a zero right after it. pidl = _ILNext(pidl); return pidl->mkid.cb == 0; } // // Just like ILGetSize, but returns (UINT)-1 if the pidl is corrupt. // We use (UINT)-1 as the return value because it will be bigger than // the buffer size we eventually compare it against. UINT SafeILGetSize(LPCITEMIDLIST pidl) { __try { return ILGetSize(pidl); } _except (EXCEPTION_EXECUTE_HANDLER) { } return (UINT)-1; } HRESULT CALLBACK OrderItem_LoadFromStream(DPASTREAMINFO * pinfo, IStream * pstm, LPVOID /*pvData*/) { HRESULT hres; OISTREAMITEM osi; hres = IStream_Read(pstm, &osi, CB_OISTREAMITEM); if (SUCCEEDED(hres)) { ASSERT(CB_OISTREAMITEM < osi.cbSize); if (CB_OISTREAMITEM < osi.cbSize) { UINT cb = osi.cbSize - CB_OISTREAMITEM; LPITEMIDLIST pidl = IEILCreate(cb); if ( !pidl ) hres = E_OUTOFMEMORY; else { hres = IStream_Read(pstm, pidl, cb); if (SUCCEEDED(hres) && IsValidPersistedChildPidl(pidl, cb)) { PORDERITEM poi = OrderItem_Create(pidl, osi.nOrder); if (poi) { PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi); pinfo->pvItem = poi; // cbPos = offset to trailing gunk after pidl UINT cbPos = pidl->mkid.cb + sizeof(USHORT); cbPos = ROUNDUP(cbPos, sizeof(WCHAR)); // Do we have a DWORD hanging off the end of the pidl? This should be the flags. if (cb >= cbPos + sizeof(DWORD)) { poi2->dwFlags = *(UNALIGNED DWORD*)((LPBYTE)pidl + cbPos); } // Make sure there's at least a WCHAR to test against. if (cb >= cbPos + sizeof(WCHAR) + 2 * sizeof(DWORD)) { DWORD cbString = *(UNALIGNED DWORD*)((LPBYTE)pidl + cbPos + sizeof(DWORD)); LPWSTR pwszIcon = (LPWSTR)((LPBYTE)pidl + cbPos + 2 * sizeof(DWORD)); // Do we have a string lenght? if (pwszIcon && cbString != 0) { // Yes, then this is a string not a pidl. We want to make sure this is a // fully qualified path. if (IS_VALID_STRING_PTRW(pwszIcon, cbString) && !PathIsRelative(pwszIcon)) { poi2->pwszIcon = StrDup(pwszIcon); pwszIcon += lstrlenW(pwszIcon) + 1; poi2->iIconIndex = *(UNALIGNED int *)pwszIcon; } } else { // A string length of zero is LPITEMIDLIST pidlTarget = (LPITEMIDLIST)(pwszIcon); // We want to write // cbPos + sizeof(WCHAR) + SafeILGetSize(pidlTarget) <= cb // but SafeILGetSize returns (UINT)-1 on error, so we need // to do some algebra to avoid overflows if (SafeILGetSize(pidlTarget) <= cb - cbPos - 2 * sizeof(DWORD)) { poi2->pidlTarget = ILClone(pidlTarget); } } } hres = E_OUTOFMEMORY; // pidl Contains extranious information. Take the hit of stripping it so that // our working set doesn't bloat. LPITEMIDLIST pidlNew = ILClone(poi2->oi.pidl); if (pidlNew) { ILFree(poi2->oi.pidl); poi2->oi.pidl = pidlNew; hres = S_OK; } } else hres = E_OUTOFMEMORY; } else hres = E_FAIL; // Cleanup if (FAILED(hres)) ILFree(pidl); } } else hres = E_FAIL; } ASSERT((S_OK == hres && pinfo->pvItem) || FAILED(hres)); return hres; } HRESULT OrderList_LoadFromStream(IStream* pstm, HDPA * phdpa, IShellFolder * psfParent) { HDPA hdpa = NULL; OISTREAMHEADER oish; ASSERT(phdpa); ASSERT(pstm); // Read the header for more info if (SUCCEEDED(IStream_Read(pstm, &oish, sizeof(oish))) && sizeof(oish) == oish.cbSize) { // Load the stream. (Should be ordered by name.) DPA_LoadStream(&hdpa, OrderItem_LoadFromStream, pstm, psfParent); // if this is the wrong version, throw away the pidls. // we go through the load anyways to make suret he read pointer is set right if (OISTREAMHEADER_VERSION != oish.dwVersion) OrderList_Destroy(&hdpa, TRUE); } *phdpa = hdpa; return (NULL != hdpa) ? S_OK : E_FAIL; } HRESULT OrderList_SaveToStream(IStream* pstm, HDPA hdpaSave, IShellFolder *psf) { HRESULT hres = E_OUTOFMEMORY; OISTREAMHEADER oish; HDPA hdpa; // Clone the array and sort by name for the purpose of persisting it hdpa = DPA_Clone(hdpaSave, NULL); if (hdpa) { ORDERINFO oinfo = {0}; #ifdef DEBUG // use QI to help track down leaks if (psf) EVAL(SUCCEEDED(psf->QueryInterface(IID_IShellFolder, (LPVOID *)&oinfo.psf))); #else oinfo.psf = psf; if (psf) oinfo.psf->AddRef(); #endif oinfo.dwSortBy = OI_SORTBYNAME; DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo); // Save the header oish.cbSize = sizeof(oish); oish.dwVersion = OISTREAMHEADER_VERSION; hres = IStream_Write(pstm, &oish, sizeof(oish)); if (SUCCEEDED(hres)) { if (psf) oinfo.psf->QueryInterface(IID_IShellFolder2, (LPVOID *)&oinfo.psf2); hres = DPA_SaveStream(hdpa, OrderItem_SaveToStream, pstm, oinfo.psf2); ATOMICRELEASE(oinfo.psf2); } ATOMICRELEASE(oinfo.psf); DPA_Destroy(hdpa); } return hres; } ///////////// // // COrderList impl for export to channel installer // class COrderList : public IPersistFolder, public IOrderList2 { public: virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(void); virtual STDMETHODIMP_(ULONG) Release(void); // IPersistFolder virtual STDMETHODIMP GetClassID(CLSID *pClassID); virtual STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IOrderList virtual STDMETHODIMP GetOrderList(HDPA * phdpa); virtual STDMETHODIMP SetOrderList(HDPA hdpa, IShellFolder *psf); virtual STDMETHODIMP FreeOrderList(HDPA hdpa); virtual STDMETHODIMP SortOrderList(HDPA hdpa, DWORD dw); virtual STDMETHODIMP AllocOrderItem(PORDERITEM * ppoi, LPCITEMIDLIST pidl); virtual STDMETHODIMP FreeOrderItem(PORDERITEM poi); // IOrderList 2 virtual STDMETHODIMP LoadFromStream(IStream* pstm, HDPA* hdpa, IShellFolder* psf); virtual STDMETHODIMP SaveToStream(IStream* pstm, HDPA hdpa); protected: COrderList(IUnknown* punkOuter, LPCOBJECTINFO poi); friend IUnknown * COrderList_Create(); COrderList(); ~COrderList(); int _cRef; IShellFolder *_psf; LPITEMIDLIST _pidl; LPITEMIDLIST _pidlFavorites; }; COrderList::COrderList() { _cRef = 1; DllAddRef(); } COrderList::~COrderList() { ILFree(_pidl); ILFree(_pidlFavorites); ATOMICRELEASE(_psf); DllRelease(); } IUnknown * COrderList_Create() { COrderList * pcol = new COrderList; if (pcol) { return SAFECAST(pcol, IPersistFolder*); } return NULL; } STDAPI COrderList_CreateInstance(IUnknown * pUnkOuter, IUnknown ** punk, LPCOBJECTINFO poi) { *punk = COrderList_Create(); return *punk ? S_OK : E_OUTOFMEMORY; } ULONG COrderList::AddRef() { _cRef++; return _cRef; } ULONG COrderList::Release() { ASSERT(_cRef > 0); _cRef--; if (_cRef > 0) return _cRef; delete this; return 0; } HRESULT COrderList::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(COrderList, IPersistFolder), QITABENT(COrderList, IOrderList), QITABENTMULTI(COrderList, IOrderList2, IOrderList), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } HRESULT COrderList::GetClassID(CLSID *pClassID) { *pClassID = CLSID_OrderListExport; return S_OK; } // This is the directory setup wants to re-order HRESULT COrderList::Initialize(LPCITEMIDLIST pidl) { if (!_pidlFavorites) { SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &_pidlFavorites); if (!_pidlFavorites) return E_OUTOFMEMORY; } if (!pidl || !ILIsParent(_pidlFavorites, pidl, FALSE)) return E_INVALIDARG; // Initialize can be called multiple times ATOMICRELEASE(_psf); Pidl_Set(&_pidl, pidl); if (_pidl) IEBindToObject(_pidl, &_psf); if (!_psf) return E_OUTOFMEMORY; return S_OK; } HRESULT COrderList_GetOrderList(HDPA * phdpa, LPCITEMIDLIST pidl, IShellFolder * psf) { IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, pidl, REG_SUBKEY_FAVORITESA, STGM_READ); if (pstm) { HRESULT hres = OrderList_LoadFromStream(pstm, phdpa, psf); pstm->Release(); return hres; } *phdpa = NULL; return E_OUTOFMEMORY; } HRESULT COrderList::GetOrderList(HDPA * phdpa) { HRESULT hres = E_FAIL; *phdpa = NULL; if (_psf) hres = COrderList_GetOrderList(phdpa, _pidl, _psf); return hres; } HRESULT COrderList_SetOrderList(HDPA hdpa, LPCITEMIDLIST pidl, IShellFolder *psf) { IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, pidl, REG_SUBKEY_FAVORITESA, STGM_WRITE); if (EVAL(pstm)) { HRESULT hres = OrderList_SaveToStream(pstm, hdpa, psf); pstm->Release(); return hres; } return E_OUTOFMEMORY; } HRESULT COrderList::SetOrderList(HDPA hdpa, IShellFolder *psf) { if (!_psf) return E_FAIL; return COrderList_SetOrderList(hdpa, _pidl, psf); } HRESULT COrderList::FreeOrderList(HDPA hdpa) { OrderList_Destroy(&hdpa); return S_OK; } HRESULT COrderList::SortOrderList(HDPA hdpa, DWORD dw) { if (OI_SORTBYNAME != dw && OI_SORTBYORDINAL != dw) return E_INVALIDARG; if (!_psf) return E_FAIL; ORDERINFO oinfo; oinfo.dwSortBy = dw; oinfo.psf = _psf; #ifdef DEBUG oinfo.psf2 = (IShellFolder2 *)INVALID_HANDLE_VALUE; // force fault if someone uses it #endif DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo); return S_OK; } HRESULT COrderList::AllocOrderItem(PORDERITEM * ppoi, LPCITEMIDLIST pidl) { LPITEMIDLIST pidlClone = ILClone(pidl); *ppoi = NULL; if (pidlClone) { *ppoi = OrderItem_Create(pidlClone, -1); if (*ppoi) return S_OK; ILFree(pidlClone); } return E_OUTOFMEMORY; } HRESULT COrderList::FreeOrderItem(PORDERITEM poi) { OrderItem_Free(poi); return S_OK; } // IOrderList2::LoadFromStream STDMETHODIMP COrderList::LoadFromStream(IStream* pstm, HDPA* phdpa, IShellFolder* psf) { ASSERT(_psf == NULL); _psf = psf; if (_psf) _psf->AddRef(); return OrderList_LoadFromStream(pstm, phdpa, _psf); } // IOrderList2::SaveToStream STDMETHODIMP COrderList::SaveToStream(IStream* pstm, HDPA hdpa) { return OrderList_SaveToStream(pstm, hdpa, _psf); }