#include "shellprv.h" #include "util.h" #include "ids.h" #include "ole2dup.h" #include "datautil.h" #include "filetbl.h" #include "copy.h" #include "prop.h" #include #include "fstreex.h" // GetIconOverlayManager() #include extern void PathStripTrailingDots(LPTSTR szPath); HRESULT IExtractIcon_Extract(IExtractIcon *pei, LPCTSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { // wrapper to let us ask an IExtractIcon for only one icon (the large one) // since many implementations will fault if you pass NULL phiconSmall HICON hiconDummy; if (phiconSmall == NULL) { phiconSmall = &hiconDummy; nIconSize = MAKELONG(nIconSize, nIconSize); } HRESULT hr = pei->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize); if (hr == S_OK && phiconSmall == &hiconDummy) { DestroyIcon(hiconDummy); } return hr; } HRESULT IExtractIconA_Extract(IExtractIconA *peia, LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { // wrapper to let us ask an IExtractIcon for only one icon (the large one) // since many dudes don't check for NULL phiconSmall HICON hiconDummy; if (phiconSmall == NULL) { phiconSmall = &hiconDummy; nIconSize = MAKELONG(nIconSize, nIconSize); } HRESULT hr = peia->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize); if (hr == S_OK && phiconSmall == &hiconDummy) { DestroyIcon(hiconDummy); } return hr; } // try to figure out if this is an icon already // in our system image list, so that we dont re-add BOOL _HijackOfficeIcons(HICON hLarge, int iIndex) { BOOL fRet = FALSE; HIMAGELIST himl; ASSERT(hLarge); if (Shell_GetImageLists(NULL, &himl)) { HICON hMaybe = ImageList_GetIcon(himl, iIndex, 0); if (hMaybe) { fRet = SHAreIconsEqual(hLarge, hMaybe); DestroyIcon(hMaybe); } } #ifdef DEBUG if (!fRet) TraceMsg(TF_WARNING, "_HijackOfficeIcons() called in suspicious circumstance"); #endif return fRet; } HRESULT _GetILIndexGivenPXIcon(IExtractIcon *pxicon, UINT uFlags, LPCITEMIDLIST pidl, int *piImage, BOOL fAnsiCrossOver) { TCHAR szIconFile[MAX_PATH]; CHAR szIconFileA[MAX_PATH]; IExtractIconA *pxiconA = (IExtractIconA *)pxicon; int iIndex; int iImage = -1; UINT wFlags=0; HRESULT hr; if (fAnsiCrossOver) { szIconFileA[0] = 0; hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL, szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags); SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile)); } else { szIconFile[0] = '\0'; hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL, szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags); } // // "*" as the file name means iIndex is already a system // icon index, we are done. // // this is a hack for our own internal icon handler // if (SUCCEEDED(hr) && (wFlags & GIL_NOTFILENAME) && szIconFile[0] == TEXT('*') && szIconFile[1] == 0) { *piImage = iIndex; return hr; } // Do not replace this with SUCCEEDED(hr). hr = S_FALSE means we need to use a default icon. if (hr == S_OK) { // If we have it in shell32, don't delay the extraction if (!(wFlags & GIL_NOTFILENAME) && lstrcmpi(PathFindFileName(szIconFile), c_szShell32Dll) == 0) { iImage = Shell_GetCachedImageIndex(szIconFile, iIndex, wFlags); } else { // // if GIL_DONTCACHE was returned by the icon handler, dont // lookup the previous icon, assume a cache miss. // if (!(wFlags & GIL_DONTCACHE) && *szIconFile) { iImage = LookupIconIndex(szIconFile, iIndex, wFlags); } } } // if we miss our cache... if (iImage == -1 && hr != S_FALSE) { if (uFlags & GIL_ASYNC) { // If we couldn't get the final icon, try to get a good temporary one if (fAnsiCrossOver) { szIconFileA[0] = 0; hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON, szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags); SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile)); } else { hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON, szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags); } if (hr == S_OK) { iImage = LookupIconIndex(szIconFile, iIndex, wFlags); } // When all else fails... if (iImage == -1) { iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0); } // force a lookup incase we are not in explorer.exe *piImage = iImage; return E_PENDING; } // try getting it from the ExtractIcon member fuction HICON rghicon[ARRAYSIZE(g_rgshil)] = {0}; BOOL fHandlerOk = FALSE; for (int i = 0; i < ARRAYSIZE(g_rgshil); i += 2) { // Ask for two at a time because // // (a) it's slightly more efficient, and // // (b) otherwise we break compatibility with IExtractIcon::Extract // implementions which ignore the size parameter (the Network // Connections folder is one). The SHIL_'s are conveniently // arranged in large/small alternating order for this purpose. // HICON *phiconSmall = NULL; HICON *phiconLarge = &rghicon[i]; UINT nIconSize = g_rgshil[i].size.cx; if (i + 1 < ARRAYSIZE(g_rgshil)) { phiconSmall = &rghicon[i+1]; nIconSize = MAKELONG(nIconSize, g_rgshil[i+1].size.cx); } if (fAnsiCrossOver) { hr = IExtractIconA_Extract(pxiconA, szIconFileA, iIndex, phiconLarge, phiconSmall, nIconSize); } else { hr = IExtractIcon_Extract(pxicon, szIconFile, iIndex, phiconLarge, phiconSmall, nIconSize); } // S_FALSE means, can you please do it...Thanks if (hr == S_FALSE && !(wFlags & GIL_NOTFILENAME)) { hr = SHDefExtractIcon(szIconFile, iIndex, wFlags, phiconLarge, phiconSmall, nIconSize); } if (SUCCEEDED(hr)) { fHandlerOk = TRUE; } } // our belief knows no bounds if (!*szIconFile && rghicon[1] && iIndex > 0 && _HijackOfficeIcons(rghicon[1], iIndex)) { // it lives! iImage = iIndex; } else { // if we extracted a icon add it to the cache. iImage = SHAddIconsToCache(rghicon, szIconFile, iIndex, wFlags); } _DestroyIcons(rghicon, ARRAYSIZE(rghicon)); // if we failed in any way pick a default icon if (iImage == -1) { if (wFlags & GIL_SIMULATEDOC) { iImage = II_DOCUMENT; } else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile)) { iImage = II_APPLICATION; } else { iImage = II_DOCNOASSOC; } // force a lookup incase we are not in explorer.exe iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iImage, 0); // if the handler failed dont cache this default icon. // so we will try again later and maybe get the right icon. // handlers should only fail if they cant access the file // or something equally bad. // // if the handler succeeded then go ahead and assume this is // a usable icon, we must be in some low memory situation, or // something. So keep mapping to the same shell icon. // if (fHandlerOk) { if (iImage != -1 && *szIconFile && !(wFlags & (GIL_DONTCACHE | GIL_NOTFILENAME))) { AddToIconTable(szIconFile, iIndex, wFlags, iImage); } } else { TraceMsg(TF_DEFVIEW, "not caching icon for '%s' because cant access file", szIconFile); } } } if (iImage < 0) { iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0); } *piImage = iImage; return hr; } // given an IShellFolder and and an Idlist that is // contained in it, get back the index into the system image list. STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage) { HRESULT hr; if (psi) { #ifdef DEBUG *piImage = -1; #endif hr = psi->GetIconOf(pidl, flags, piImage); if (hr == S_OK) { ASSERT(*piImage != -1); return hr; } if (hr == E_PENDING) { ASSERT(flags & GIL_ASYNC); ASSERT(*piImage != -1); return hr; } } *piImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0); // Be careful. Some shellfolders erroneously return S_OK when they fail IExtractIcon *pxi = NULL; hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIcon, &pxi)); if (SUCCEEDED(hr) && pxi) { hr = _GetILIndexGivenPXIcon(pxi, flags, pidl, piImage, FALSE); pxi->Release(); } else { // Try the ANSI interface, see if we are dealing with an old set of code IExtractIconA *pxiA = NULL; hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIconA, &pxiA)); if (SUCCEEDED(hr)) { if (pxiA) { hr = _GetILIndexGivenPXIcon((IExtractIcon *)pxiA, flags, pidl, piImage, TRUE); pxiA->Release(); } else { // IShellFolder lied to us - returned S_OK even though it failed hr = E_FAIL; } } } return hr; } // given an IShellFolder and and an Idlist that is // contained in it, get back the index into the system image list. STDAPI_(int) SHMapPIDLToSystemImageListIndex(IShellFolder *psf, LPCITEMIDLIST pidl, int *piIndexSel) { int iIndex; if (piIndexSel) { SHGetIconFromPIDL(psf, NULL, pidl, GIL_OPENICON, piIndexSel); } SHGetIconFromPIDL(psf, NULL, pidl, 0, &iIndex); return iIndex; } class CGetIconTask : public CRunnableTask { public: STDMETHODIMP RunInitRT(void); CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon, PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint); protected: ~CGetIconTask(); IShellFolder *_psf; IShellIcon *_psi; LPITEMIDLIST _pidl; UINT _flags; BOOL _fGetOpenIcon; PFNASYNCICONTASKBALLBACK _pfn; void *_pvData; void *_pvHint; }; CGetIconTask::CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon, PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint) : CRunnableTask(RTF_DEFAULT), _psf(psf), _psi(psi), _flags(flags), _fGetOpenIcon(fGetOpenIcon), _pfn(pfn), _pvData(pvData), _pvHint(pvHint) { *phr = SHILClone(pidl, &_pidl); _psf->AddRef(); if (_psi) _psi->AddRef(); } CGetIconTask::~CGetIconTask() { ILFree(_pidl); _psf->Release(); if (_psi) _psi->Release(); } STDMETHODIMP CGetIconTask::RunInitRT() { int iIcon = -1; int iOpenIcon = -1; ASSERT(_pidl); if (_fGetOpenIcon) { SHGetIconFromPIDL(_psf, _psi, _pidl, _flags | GIL_OPENICON, &iOpenIcon); } // get the icon for this item. SHGetIconFromPIDL(_psf, _psi, _pidl, _flags, &iIcon); _pfn(_pidl, _pvData, _pvHint, iIcon, iOpenIcon); return S_OK; } HRESULT CGetIconTask_CreateInstance(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon, PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, IRunnableTask **ppTask) { *ppTask = NULL; HRESULT hr; CGetIconTask * pNewTask = new CGetIconTask(&hr, psf, psi, pidl, flags, fGetOpenIcon, pfn, pvData, pvHint); if (pNewTask) { if (SUCCEEDED(hr)) *ppTask = SAFECAST(pNewTask, IRunnableTask *); else pNewTask->Release(); } else hr = E_OUTOFMEMORY; return hr; } // given an IShellFolder and and an Idlist that is // contained in it, get back a -possibly temporary - index into the system image list, // and get the final icon from the callback if necessary STDAPI SHMapIDListToImageListIndexAsync(IShellTaskScheduler* pts, IShellFolder *psf, LPCITEMIDLIST pidl, UINT flags, PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, int *piIndex, int *piIndexSel) { HRESULT hr = S_OK; IShellIcon *psi = NULL; psf->QueryInterface(IID_PPV_ARG(IShellIcon, &psi)); // We are doing all the ASYNC handling, not the caller. flags &= ~GIL_ASYNC; // Try asynchronous first if (pfn) { hr = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_ASYNC, piIndex); if (piIndexSel) { HRESULT hr2 = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON | GIL_ASYNC, piIndexSel); if (SUCCEEDED(hr)) { // Don't lose the result if the first GetIcon succeeds, but the second one is E_PENDING hr = hr2; } } if (hr == E_PENDING) { if (pts) { IRunnableTask *pTask; hr = CGetIconTask_CreateInstance(psf, psi, pidl, flags, (piIndexSel != NULL), pfn, pvData, pvHint, &pTask); if (SUCCEEDED(hr)) { hr = pts->AddTask(pTask, TOID_DVIconExtract, 0, ITSAT_DEFAULT_PRIORITY); if (SUCCEEDED(hr)) { hr = E_PENDING; } pTask->Release(); } } else { hr = E_POINTER; } } else if (hr == S_OK) { goto cleanup; } } // If asynchronous get failed, try synchronous if (hr != E_PENDING) { if (piIndexSel) { SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON, piIndexSel); } hr = SHGetIconFromPIDL(psf, psi, pidl, flags, piIndex); } cleanup: if (psi) { psi->Release(); } return hr; } // returns the icon handle to be used to represent the specified // file. The caller should destroy the icon eventually. STDAPI_(HICON) SHGetFileIcon(HINSTANCE hinst, LPCTSTR pszPath, DWORD dwFileAttributes, UINT uFlags) { SHFILEINFO sfi; SHGetFileInfo(pszPath, dwFileAttributes, &sfi, sizeof(sfi), uFlags | SHGFI_ICON); return sfi.hIcon; } // Return 1 on success and 0 on failure. DWORD_PTR _GetFileInfoSections(LPITEMIDLIST pidl, SHFILEINFO *psfi, UINT uFlags) { DWORD_PTR dwResult = 1; IShellFolder *psf; LPCITEMIDLIST pidlLast; HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { // get attributes for file if (uFlags & SHGFI_ATTRIBUTES) { // [New in IE 4.0] If SHGFI_ATTR_SPECIFIED is set, we use psfi->dwAttributes as is if (!(uFlags & SHGFI_ATTR_SPECIFIED)) psfi->dwAttributes = 0xFFFFFFFF; // get all of them if (FAILED(psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes))) psfi->dwAttributes = 0; } // // get icon location, place the icon path into szDisplayName // if (uFlags & SHGFI_ICONLOCATION) { IExtractIcon *pxi; if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractIcon, &pxi)))) { UINT wFlags; pxi->GetIconLocation(0, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName), &psfi->iIcon, &wFlags); pxi->Release(); // the returned location is not a filename we cant return it. // just give then nothing. if (wFlags & GIL_NOTFILENAME) { // special case one of our shell32.dll icons...... if (psfi->szDisplayName[0] != TEXT('*')) psfi->iIcon = 0; psfi->szDisplayName[0] = 0; } } } HIMAGELIST himlLarge, himlSmall; // get the icon for the file. if ((uFlags & SHGFI_SYSICONINDEX) || (uFlags & SHGFI_ICON)) { Shell_GetImageLists(&himlLarge, &himlSmall); if (uFlags & SHGFI_SYSICONINDEX) dwResult = (DWORD_PTR)((uFlags & SHGFI_SMALLICON) ? himlSmall : himlLarge); if (uFlags & SHGFI_OPENICON) SHMapPIDLToSystemImageListIndex(psf, pidlLast, &psfi->iIcon); else psfi->iIcon = SHMapPIDLToSystemImageListIndex(psf, pidlLast, NULL); } if (uFlags & SHGFI_ICON) { HIMAGELIST himl; UINT flags = 0; int cx, cy; if (uFlags & SHGFI_SMALLICON) { himl = himlSmall; cx = GetSystemMetrics(SM_CXSMICON); cy = GetSystemMetrics(SM_CYSMICON); } else { himl = himlLarge; cx = GetSystemMetrics(SM_CXICON); cy = GetSystemMetrics(SM_CYICON); } if (!(uFlags & SHGFI_ATTRIBUTES)) { psfi->dwAttributes = SFGAO_LINK; // get link only psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes); } // // check for a overlay image thing (link overlay) // if ((psfi->dwAttributes & SFGAO_LINK) || (uFlags & SHGFI_LINKOVERLAY)) { IShellIconOverlayManager *psiom; HRESULT hrT = GetIconOverlayManager(&psiom); if (SUCCEEDED(hrT)) { int iOverlayIndex = 0; hrT = psiom->GetReservedOverlayInfo(NULL, -1, &iOverlayIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_LINK); if (SUCCEEDED(hrT)) flags |= INDEXTOOVERLAYMASK(iOverlayIndex); } } if ((uFlags & SHGFI_ADDOVERLAYS) || (uFlags & SHGFI_OVERLAYINDEX)) { IShellIconOverlay * pio; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &pio)))) { int iOverlayIndex = 0; if (SUCCEEDED(pio->GetOverlayIndex(pidlLast, &iOverlayIndex))) { if (uFlags & SHGFI_ADDOVERLAYS) { flags |= INDEXTOOVERLAYMASK(iOverlayIndex); } if (uFlags & SHGFI_OVERLAYINDEX) { // use the upper 16 bits for the overlayindex psfi->iIcon |= iOverlayIndex << 24; } } pio->Release(); } } // check for selected state if (uFlags & SHGFI_SELECTED) flags |= ILD_BLEND50; psfi->hIcon = ImageList_GetIcon(himl, psfi->iIcon, flags); // if the caller does not want a "shell size" icon // convert the icon to the "system" icon size. if (psfi->hIcon && !(uFlags & SHGFI_SHELLICONSIZE)) psfi->hIcon = (HICON)CopyImage(psfi->hIcon, IMAGE_ICON, cx, cy, LR_COPYRETURNORG | LR_COPYDELETEORG); } // get display name for the path if (uFlags & SHGFI_DISPLAYNAME) { DisplayNameOf(psf, pidlLast, SHGDN_NORMAL, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName)); } if (uFlags & SHGFI_TYPENAME) { IShellFolder2 *psf2; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { VARIANT var; VariantInit(&var); if (SUCCEEDED(psf2->GetDetailsEx(pidlLast, &SCID_TYPE, &var))) { VariantToStr(&var, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName)); VariantClear(&var); } psf2->Release(); } } psf->Release(); } else dwResult = 0; return dwResult; } // // This function returns shell info about a given pathname. // a app can get the following: // // Icon (large or small) // Display Name // Name of File Type // // this function replaces SHGetFileIcon #define BUGGY_SHELL16_CBFILEINFO (sizeof(SHFILEINFO) - 4) STDAPI_(DWORD_PTR) SHGetFileInfo(LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO *psfi, UINT cbFileInfo, UINT uFlags) { LPITEMIDLIST pidlFull; DWORD_PTR res = 1; TCHAR szPath[MAX_PATH]; // this was never enforced in the past // TODDB: The 16 to 32 bit thunking layer passes in the wrong value for cbFileInfo. // The size passed in looks to be the size of the 16 bit version of the structure // rather than the size of the 32 bit version, as such it is 4 bytes shorter. // TJGREEN: Special-case that size to keep the assertion from firing and party on. // ASSERT(!psfi || cbFileInfo == sizeof(*psfi) || cbFileInfo == BUGGY_SHELL16_CBFILEINFO); // You can't use both SHGFI_ATTR_SPECIFIED and SHGFI_ICON. ASSERT(uFlags & SHGFI_ATTR_SPECIFIED ? !(uFlags & SHGFI_ICON) : TRUE); if (pszPath == NULL) return 0; if (uFlags == SHGFI_EXETYPE) return GetExeType(pszPath); // funky way to get EXE type if (psfi == NULL) return 0; psfi->hIcon = 0; // Zip Pro 6.0 relies on the fact that if you don't ask for the icon, // the iIcon field doesn't change. // // psfi->iIcon = 0; psfi->szDisplayName[0] = 0; psfi->szTypeName[0] = 0; // do some simmple check on the input path. if (!(uFlags & SHGFI_PIDL)) { // If the caller wants us to give them the file attributes, we can't trust // the attributes they gave us in the following two situations. if (uFlags & SHGFI_ATTRIBUTES) { if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) { DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a sys/ro directory (possible junction)")); uFlags &= ~SHGFI_USEFILEATTRIBUTES; } else if (PathIsRoot(pszPath)) { DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a roots")); uFlags &= ~SHGFI_USEFILEATTRIBUTES; } } if (PathIsRelative(pszPath)) { if (uFlags & SHGFI_USEFILEATTRIBUTES) { // get a shorter path than the current directory to support // long pszPath names (that might get truncated in the // long current dir case) GetWindowsDirectory(szPath, ARRAYSIZE(szPath)); } else { GetCurrentDirectory(ARRAYSIZE(szPath), szPath); } PathCombine(szPath, szPath, pszPath); pszPath = szPath; } } if (uFlags & SHGFI_PIDL) pidlFull = (LPITEMIDLIST)pszPath; else if (uFlags & SHGFI_USEFILEATTRIBUTES) { WIN32_FIND_DATA fd = {0}; fd.dwFileAttributes = dwFileAttributes; SHSimpleIDListFromFindData(pszPath, &fd, &pidlFull); } else pidlFull = ILCreateFromPath(pszPath); if (pidlFull) { if (uFlags & ( SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_ICONLOCATION | SHGFI_ICON | SHGFI_TYPENAME)) { res = _GetFileInfoSections(pidlFull, psfi, uFlags); } if (!(uFlags & SHGFI_PIDL)) ILFree(pidlFull); } else res = 0; return res; } //=========================================================================== // // SHGetFileInfoA Stub // // This function calls SHGetFileInfoW and then converts the returned // information back to ANSI. // //=========================================================================== STDAPI_(DWORD_PTR) SHGetFileInfoA(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA *psfi, UINT cbFileInfo, UINT uFlags) { WCHAR szPathW[MAX_PATH]; LPWSTR pszPathW; DWORD_PTR dwRet; if (uFlags & SHGFI_PIDL) { pszPathW = (LPWSTR)pszPath; // Its a pidl, fake it as a WSTR } else { SHAnsiToUnicode(pszPath, szPathW, ARRAYSIZE(szPathW)); pszPathW = szPathW; } if (psfi) { SHFILEINFOW sfiw; ASSERT(cbFileInfo == sizeof(*psfi)); // Zip Pro 6.0 sets SHGFI_SMALLICON | SHGFI_OPENICON but forgets to // pass SHGFI_ICON or SHGFI_SYSICONINDEX, even though they really // wanted the sys icon index. // // In Windows 95, fields of the SHFILEINFOA structure that you didn't // query for were left unchanged. They happened to have the icon for // a closed folder lying around there from a previous query, so they // got away with it by mistake. They got the wrong icon, but it was // close enough that nobody really complained. // // So pre-initialize the sfiw's iIcon with the app's iIcon. That // way, if it turns out the app didn't ask for the icon, he just // gets his old value back. // sfiw.iIcon = psfi->iIcon; sfiw.dwAttributes = psfi->dwAttributes; dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, &sfiw, sizeof(sfiw), uFlags); psfi->hIcon = sfiw.hIcon; psfi->iIcon = sfiw.iIcon; psfi->dwAttributes = sfiw.dwAttributes; SHUnicodeToAnsi(sfiw.szDisplayName, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName)); SHUnicodeToAnsi(sfiw.szTypeName, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName)); } else { dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, NULL, 0, uFlags); } return dwRet; } STDAPI ThunkFindDataWToA(WIN32_FIND_DATAW *pfd, WIN32_FIND_DATAA *pfda, int cb) { if (cb < sizeof(WIN32_FIND_DATAA)) return DISP_E_BUFFERTOOSMALL; memcpy(pfda, pfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); SHUnicodeToAnsi(pfd->cFileName, pfda->cFileName, ARRAYSIZE(pfda->cFileName)); SHUnicodeToAnsi(pfd->cAlternateFileName, pfda->cAlternateFileName, ARRAYSIZE(pfda->cAlternateFileName)); return S_OK; } STDAPI ThunkNetResourceWToA(LPNETRESOURCEW pnrw, LPNETRESOURCEA pnra, UINT cb) { HRESULT hr; if (cb >= sizeof(NETRESOURCEA)) { LPSTR psza, pszDest[4] = {NULL, NULL, NULL, NULL}; CopyMemory(pnra, pnrw, FIELD_OFFSET(NETRESOURCE, lpLocalName)); psza = (LPSTR)(pnra + 1); // Point just past the structure if (cb > sizeof(NETRESOURCE)) { LPWSTR pszSource[4]; UINT i, cchRemaining = cb - sizeof(NETRESOURCE); pszSource[0] = pnrw->lpLocalName; pszSource[1] = pnrw->lpRemoteName; pszSource[2] = pnrw->lpComment; pszSource[3] = pnrw->lpProvider; for (i = 0; i < 4; i++) { if (pszSource[i]) { UINT cchItem; pszDest[i] = psza; cchItem = SHUnicodeToAnsi(pszSource[i], pszDest[i], cchRemaining); cchRemaining -= cchItem; psza += cchItem; } } } pnra->lpLocalName = pszDest[0]; pnra->lpRemoteName = pszDest[1]; pnra->lpComment = pszDest[2]; pnra->lpProvider = pszDest[3]; hr = S_OK; } else hr = DISP_E_BUFFERTOOSMALL; return hr; } STDAPI NetResourceWVariantToBuffer(const VARIANT* pvar, void* pv, UINT cb) { HRESULT hr; if (cb >= sizeof(NETRESOURCEW)) { if (pvar && pvar->vt == (VT_ARRAY | VT_UI1)) { int i; NETRESOURCEW* pnrw = (NETRESOURCEW*) pvar->parray->pvData; UINT cbOffsets[4] = { 0, 0, 0, 0 }; UINT cbEnds[4] = { 0, 0, 0, 0 }; LPWSTR pszPtrs[4] = { pnrw->lpLocalName, pnrw->lpRemoteName, pnrw->lpComment, pnrw->lpProvider }; hr = S_OK; for (i = 0; i < ARRAYSIZE(pszPtrs); i++) { if (pszPtrs[i]) { cbOffsets[i] = (UINT) ((BYTE*) pszPtrs[i] - (BYTE*) pnrw); cbEnds[i] = cbOffsets[i] + (sizeof(WCHAR) * (lstrlenW(pszPtrs[i]) + 1)); // If any of the strings start or end too far into the buffer, then fail: if ((cbOffsets[i] >= cb) || (cbEnds[i] > cb)) { hr = DISP_E_BUFFERTOOSMALL; break; } } } if (SUCCEEDED(hr)) { hr = VariantToBuffer(pvar, pv, cb) ? S_OK : E_FAIL; pnrw = (NETRESOURCEW*) pv; if (SUCCEEDED(hr)) { // Fixup pointers in structure to point into the output buffer, // instead of the variant buffer: LPWSTR* ppszPtrs[4] = { &(pnrw->lpLocalName), &(pnrw->lpRemoteName), &(pnrw->lpComment), &(pnrw->lpProvider) }; for (i = 0; i < ARRAYSIZE(ppszPtrs); i++) { if (*ppszPtrs[i]) { *ppszPtrs[i] = (LPWSTR) ((BYTE*) pnrw + cbOffsets[i]); } } } } } else { hr = E_FAIL; } } else { hr = DISP_E_BUFFERTOOSMALL; } return hr; } // This function will extract information that is cached in the pidl such // in the information that was returned from a FindFirst file. This function // is sortof a hack as t allow outside callers to be able to get at the infomation // without knowing how we store it in the pidl. // a app can get the following: STDAPI SHGetDataFromIDListW(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb) { HRESULT hr = E_NOTIMPL; SHCOLUMNID* pscid; if (!pv || !psf || !pidl) return E_INVALIDARG; switch (nFormat) { case SHGDFIL_FINDDATA: if (cb < sizeof(WIN32_FIND_DATAW)) return DISP_E_BUFFERTOOSMALL; else pscid = (SHCOLUMNID*)&SCID_FINDDATA; break; case SHGDFIL_NETRESOURCE: if (cb < sizeof(NETRESOURCEW)) return DISP_E_BUFFERTOOSMALL; else pscid = (SHCOLUMNID*)&SCID_NETRESOURCE; break; case SHGDFIL_DESCRIPTIONID: pscid = (SHCOLUMNID*)&SCID_DESCRIPTIONID; break; default: return E_INVALIDARG; } IShellFolder2 *psf2; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { VARIANT var; VariantInit(&var); hr = psf2->GetDetailsEx(pidl, pscid, &var); if (SUCCEEDED(hr)) { if (SHGDFIL_NETRESOURCE == nFormat) { hr = NetResourceWVariantToBuffer(&var, pv, cb); } else { if (!VariantToBuffer(&var, pv, cb)) hr = E_FAIL; } VariantClear(&var); } else { TraceMsg(TF_WARNING, "Trying to retrieve find data from unknown PIDL %s", DumpPidl(pidl)); } psf2->Release(); } return hr; } STDAPI SHGetDataFromIDListA(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb) { HRESULT hr; WIN32_FIND_DATAW fdw; NETRESOURCEW *pnrw = NULL; void *pvData = pv; int cbData = cb; if (nFormat == SHGDFIL_FINDDATA) { cbData = sizeof(fdw); pvData = &fdw; } else if (nFormat == SHGDFIL_NETRESOURCE) { cbData = cb; pvData = pnrw = (NETRESOURCEW *)LocalAlloc(LPTR, cbData); if (pnrw == NULL) return E_OUTOFMEMORY; } hr = SHGetDataFromIDListW(psf, pidl, nFormat, pvData, cbData); if (SUCCEEDED(hr)) { if (nFormat == SHGDFIL_FINDDATA) { hr = ThunkFindDataWToA(&fdw, (WIN32_FIND_DATAA *)pv, cb); } else if (nFormat == SHGDFIL_NETRESOURCE) { hr = ThunkNetResourceWToA(pnrw, (NETRESOURCEA *)pv, cb); } } if (pnrw) LocalFree(pnrw); return hr; } int g_iUseLinkPrefix = -1; #define INITIALLINKPREFIXCOUNT 20 #define MAXLINKPREFIXCOUNT 30 void LoadUseLinkPrefixCount() { TraceMsg(TF_FSTREE, "LoadUseLinkPrefixCount %d", g_iUseLinkPrefix); if (g_iUseLinkPrefix < 0) { DWORD cb = sizeof(g_iUseLinkPrefix); if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, NULL, &g_iUseLinkPrefix, &cb)) || g_iUseLinkPrefix < 0) { g_iUseLinkPrefix = INITIALLINKPREFIXCOUNT; } } } void SaveUseLinkPrefixCount() { if (g_iUseLinkPrefix >= 0) { SKSetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, REG_BINARY, &g_iUseLinkPrefix, sizeof(g_iUseLinkPrefix)); } } #define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9')) // psz2 = destination // psz1 = source void StripNumber(LPTSTR psz2, LPCTSTR psz1) { // strip out the '(' and the numbers after it // We need to verify that it is either simply () or (999) but not (A) for (; *psz1; psz1 = CharNext(psz1), psz2 = CharNext(psz2)) { if (*psz1 == TEXT('(')) { LPCTSTR pszT = psz1; do { psz1 = CharNext(psz1); } while (*psz1 && ISDIGIT(*psz1)); if (*psz1 == TEXT(')')) { psz1 = CharNext(psz1); if (*psz1 == TEXT(' ')) psz1 = CharNext(psz1); // skip the extra space lstrcpy(psz2, psz1); return; } // We have a (that does not match the format correctly! psz1 = pszT; // restore pointer back to copy this char through and continue... } *psz2 = *psz1; } *psz2 = *psz1; } #define SHORTCUT_PREFIX_DECR 5 #define SHORTCUT_PREFIX_INCR 1 // this checks to see if you've renamed 'Shortcut #x To Foo' to 'Foo' void CheckShortcutRename(LPCTSTR pszOldPath, LPCTSTR pszNewPath) { ASSERT(pszOldPath); ASSERT(pszNewPath); // already at 0. if (g_iUseLinkPrefix) { LPCTSTR pszOldName = PathFindFileName(pszOldPath); if (PathIsLnk(pszOldName)) { TCHAR szBaseName[MAX_PATH]; TCHAR szLinkTo[80]; TCHAR szMockName[MAX_PATH]; LPCTSTR pszNewName = PathFindFileName(pszNewPath); lstrcpy(szBaseName, pszNewName); PathRemoveExtension(szBaseName); // mock up a name using the basename and the linkto template LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo)); wnsprintf(szMockName, ARRAYSIZE(szMockName), szLinkTo, szBaseName); StripNumber(szMockName, szMockName); StripNumber(szBaseName, pszOldName); // are the remaining gunk the same? if (!lstrcmp(szMockName, szBaseName)) { // yes! do the link count magic LoadUseLinkPrefixCount(); ASSERT(g_iUseLinkPrefix >= 0); g_iUseLinkPrefix -= SHORTCUT_PREFIX_DECR; if (g_iUseLinkPrefix < 0) g_iUseLinkPrefix = 0; SaveUseLinkPrefixCount(); } } } } STDAPI_(int) SHRenameFileEx(HWND hwnd, IUnknown *punkEnableModless, LPCTSTR pszDir, LPCTSTR pszOldName, LPCTSTR pszNewName) { int iRet = ERROR_CANCELLED; // user saw the error, don't report again TCHAR szOldPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation TCHAR szTempNewPath[MAX_PATH]; BOOL bEnableUI = hwnd || punkEnableModless; IUnknown_EnableModless(punkEnableModless, FALSE); PathCombine(szOldPathName, pszDir, pszOldName); szOldPathName[lstrlen(szOldPathName) + 1] = 0; StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath)); int err = PathCleanupSpec(pszDir, szTempNewPath); if (err) { if (bEnableUI) { ShellMessageBox(HINST_THISDLL, hwnd, err & PCS_PATHTOOLONG ? MAKEINTRESOURCE(IDS_REASONS_INVFILES) : IsLFNDrive(pszDir) ? MAKEINTRESOURCE(IDS_INVALIDFN) : MAKEINTRESOURCE(IDS_INVALIDFNFAT), MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND); } } else { // strip off leading and trailing blanks off of the new file name. StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath)); PathRemoveBlanks(szTempNewPath); if (!szTempNewPath[0] || (szTempNewPath[0] == TEXT('.'))) { if (bEnableUI) { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_NONULLNAME), MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND); } } else { int idPrompt = IDYES; TCHAR szNewPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation PathCombine(szNewPathName, pszDir, szTempNewPath); // if there was an old extension and the new and old don't match complain LPTSTR pszExt = PathFindExtension(pszOldName); if (*pszExt && lstrcmpi(pszExt, PathFindExtension(szTempNewPath))) { HKEY hk; if (!PathIsDirectory(szOldPathName) && SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszExt, NULL, &hk))) { RegCloseKey(hk); if (bEnableUI) { idPrompt = ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_WARNCHANGEEXT), MAKEINTRESOURCE(IDS_RENAME), MB_YESNO | MB_ICONEXCLAMATION); } } } if (IDYES == idPrompt) { szNewPathName[lstrlen(szNewPathName) + 1] = 0; // double NULL terminate SHFILEOPSTRUCT fo = { hwnd, FO_RENAME, szOldPathName, szNewPathName, FOF_SILENT | FOF_ALLOWUNDO, }; iRet = SHFileOperation(&fo); if (ERROR_SUCCESS == iRet) CheckShortcutRename(szOldPathName, szNewPathName); } } } IUnknown_EnableModless(punkEnableModless, TRUE); return iRet; } HKEY SHOpenShellFolderKey(const CLSID *pclsid) { HKEY hkey; return SUCCEEDED(SHRegGetCLSIDKey(*pclsid, TEXT("ShellFolder"), FALSE, FALSE, &hkey)) ? hkey : NULL; } BOOL SHQueryShellFolderValue(const CLSID *pclsid, LPCTSTR pszValueName) { BOOL bRet = FALSE; // assume no HKEY hkey = SHOpenShellFolderKey(pclsid); if (hkey) { bRet = SHQueryValueEx(hkey, pszValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; RegCloseKey(hkey); } return bRet; } // // The SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY key contains a bunch of values, // each named after a GUID. The data associated with each value is a // DWORD, which is either... // // 0 = no restriction on this CLSID // 1 = unconditional restriction on this CLSID // 0xFFFFFFFF = same as 1 (in case somebody got "creative") // any other value = pass to SHRestricted() to see what the restriction is // // We support 0xFFFFFFFF only out of paranoia. This flag was only 0 or 1 // in Windows 2000, and somebody might've decided that "all bits set" // is better than "just one bit set". // #define SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\NonEnum") BOOL _IsNonEnumPolicySet(const CLSID *pclsid) { BOOL fPolicySet = FALSE; TCHAR szCLSID[GUIDSTR_MAX]; DWORD dwDefault = 0; RESTRICTIONS rest = REST_NONE; DWORD cbSize = sizeof(rest); if (EVAL(SHStringFromGUID(*pclsid, szCLSID, ARRAYSIZE(szCLSID))) && (ERROR_SUCCESS == SHRegGetUSValue(SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY, szCLSID, NULL, &rest, &cbSize, FALSE, &dwDefault, sizeof(dwDefault))) && rest) { fPolicySet = rest == 1 || rest == 0xFFFFFFFF || SHRestricted(rest); } return fPolicySet; } // // This function returns the attributes (to be returned IShellFolder:: // GetAttributesOf) of the junction point specified by the class ID. // STDAPI_(DWORD) SHGetAttributesFromCLSID(const CLSID *pclsid, DWORD dwDefault) { return SHGetAttributesFromCLSID2(pclsid, dwDefault, (DWORD)-1); } DWORD QueryCallForAttributes(HKEY hkey, const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested) { DWORD dwAttr = dwDefAttrs; DWORD dwData, cbSize = sizeof(dwAttr); // consider caching this folder to avoid creating over and over // mydocs.dll uses this for compat with old apps // See if this folder has asked us specifically to call and get // the attributes... // if (SHQueryValueEx(hkey, TEXT("CallForAttributes"), NULL, NULL, &dwData, &cbSize) == ERROR_SUCCESS) { // CallForAttributes can be a masked value. See if it's being supplied in the value. // NOTE: MyDocs.dll registers with a NULL String, so this check works. DWORD dwMask = (DWORD)-1; if (sizeof(dwData) == cbSize) { // There is a mask, Use this. dwMask = dwData; } // Is the requested bit contained in the specified mask? if (dwMask & dwRequested) { // Yes. Then CoCreate and Query. IShellFolder *psf; if (SUCCEEDED(SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IShellFolder, &psf)))) { dwAttr = dwRequested; psf->GetAttributesOf(0, NULL, &dwAttr); psf->Release(); } else { dwAttr |= SFGAO_FILESYSTEM; } } } return dwAttr; } // dwRequested is the bits you are explicitly looking for. This is an optimization that prevents reg hits. STDAPI_(DWORD) SHGetAttributesFromCLSID2(const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested) { DWORD dwAttr = dwDefAttrs; HKEY hkey = SHOpenShellFolderKey(pclsid); if (hkey) { DWORD dwData, cbSize = sizeof(dwAttr); // We are looking for some attributes on a shell folder. These attributes can be in two locations: // 1) In the "Attributes" value in the registry. // 2) Stored in a the shell folder's GetAttributesOf. // First, Check to see if the reqested value is contained in the registry. if (SHQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwData, &cbSize) == ERROR_SUCCESS && cbSize == sizeof(dwData)) { // We have data there, but it may not contain the data we are looking for dwAttr = dwData & dwRequested; // Does it contain the bit we are looking for? if (((dwAttr & dwRequested) != dwRequested) && dwRequested != 0) { // No. Check to see if it is in the shell folder implementation goto CallForAttributes; } } else { CallForAttributes: // See if we have to talk to the shell folder. // I'm passing dwAttr, because if the previous case did not generate any attributes, then it's // equal to dwDefAttrs. If the call to CallForAttributes fails, then it will contain the value of // dwDefAttrs or whatever was in the shell folder's Attributes key dwAttr = QueryCallForAttributes(hkey, pclsid, dwAttr, dwRequested); } RegCloseKey(hkey); } if (_IsNonEnumPolicySet(pclsid)) dwAttr |= SFGAO_NONENUMERATED; if (SHGetObjectCompatFlags(NULL, pclsid) & OBJCOMPATF_NOTAFILESYSTEM) dwAttr &= ~SFGAO_FILESYSTEM; return dwAttr; } // _BuildLinkName // // Used during the creation of a shortcut, this function determines an appropriate name for the shortcut. // This is not the exact name that will be used becuase it will usually contain "() " which will either // get removed or replaced with "(x) " where x is a number that makes the name unique. This removal is done // elsewhere (currently in PathYetAnotherMakeUniqueName). // // in: // pszName file spec part // pszDir path part of name to know how to limit the long name... // // out: // pszLinkName - Full path to link name (May fit in 8.3...). Can be the same buffer as pszName. // // NOTES: If pszDir + pszLinkName is greater than MAX_PATH we will fail to create the shortcut. // In an effort to prevent void _BuildLinkName(LPTSTR pszLinkName, LPCTSTR pszName, LPCTSTR pszDir, BOOL fLinkTo) { TCHAR szLinkTo[40]; // "Shortcut to %s.lnk" TCHAR szTemp[MAX_PATH + 40]; if (fLinkTo) { // check to see if we're in the "don't ever say 'shortcut to' mode" LoadUseLinkPrefixCount(); if (!g_iUseLinkPrefix) { fLinkTo = FALSE; } else if (g_iUseLinkPrefix > 0) { if (g_iUseLinkPrefix < MAXLINKPREFIXCOUNT) { g_iUseLinkPrefix += SHORTCUT_PREFIX_INCR; SaveUseLinkPrefixCount(); } } } if (!fLinkTo) { // Generate the title of this link ("XX.lnk") LoadString(HINST_THISDLL, IDS_LINKEXTENSION, szLinkTo, ARRAYSIZE(szLinkTo)); } else { // Generate the title of this link ("Shortcut to XX.lnk") LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo)); } wnsprintf(szTemp, ARRAYSIZE(szTemp), szLinkTo, pszName); PathCleanupSpecEx(pszDir, szTemp); // get rid of illegal chars AND ensure correct filename length lstrcpyn(pszLinkName, szTemp, MAX_PATH); ASSERT(PathIsLnk(pszLinkName)); } // return a new destination path for a link // // in: // fErrorSoTryDesktop we are called because there was an error saving // the shortcut and we want to prompt to see if the // desktop should be used. // // in/out: // pszPath on input the place being tried, on output the desktop folder // // returns: // // IDYES user said yes to creating a link at new place // IDNO user said no to creating a link at new place // -1 error // int _PromptTryDesktopLinks(HWND hwnd, LPTSTR pszPath, BOOL fErrorSoTryDesktop) { TCHAR szPath[MAX_PATH]; if (!SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE)) return -1; // fail no desktop dir int idOk; if (fErrorSoTryDesktop) { // Fail, if pszPath already points to the desktop directory. if (lstrcmpi(szPath, pszPath) == 0) return -1; idOk = ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_TRYDESKTOPLINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_YESNO | MB_ICONQUESTION); } else { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_MAKINGDESKTOPLINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_OK | MB_ICONASTERISK); idOk = IDYES; } if (idOk == IDYES) lstrcpy(pszPath , szPath); // output return idOk; // return yes or no } // in: // pszpdlLinkTo LPCITEMIDLIST or LPCTSTR, target of link to create // pszDir where we will put the link // uFlags SHGNLI_ flags // // out: // pszName file name to create "c:\Shortcut to Foo.lnk" // pfMustCopy pszpdlLinkTo was a link itself, make a copy of this STDAPI_(BOOL) SHGetNewLinkInfo(LPCTSTR pszpdlLinkTo, LPCTSTR pszDir, LPTSTR pszName, BOOL *pfMustCopy, UINT uFlags) { BOOL fDosApp = FALSE; BOOL fLongFileNames = IsLFNDrive(pszDir); SHFILEINFO sfi; *pfMustCopy = FALSE; sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK | SFGAO_FOLDER; if (uFlags & SHGNLI_PIDL) { if (FAILED(SHGetNameAndFlags((LPCITEMIDLIST)pszpdlLinkTo, SHGDN_NORMAL, pszName, MAX_PATH, &sfi.dwAttributes))) return FALSE; } else { if (SHGetFileInfo(pszpdlLinkTo, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED | ((uFlags & SHGNLI_PIDL) ? SHGFI_PIDL : 0))) lstrcpy(pszName, sfi.szDisplayName); else return FALSE; } if (PathCleanupSpecEx(pszDir, pszName) & PCS_FATAL) return FALSE; // // WARNING: From this point on, sfi.szDisplayName may be re-used to // contain the file path of the PIDL we are linking to. Don't rely on // it containing the display name. // if (sfi.dwAttributes & SFGAO_FILESYSTEM) { LPTSTR pszPathSrc; if (uFlags & SHGNLI_PIDL) { pszPathSrc = sfi.szDisplayName; SHGetPathFromIDList((LPCITEMIDLIST)pszpdlLinkTo, pszPathSrc); } else { pszPathSrc = (LPTSTR)pszpdlLinkTo; } fDosApp = (lstrcmpi(PathFindExtension(pszPathSrc), TEXT(".pif")) == 0) || (LOWORD(GetExeType(pszPathSrc)) == 0x5A4D); // 'MZ' if (sfi.dwAttributes & SFGAO_LINK) { *pfMustCopy = TRUE; if (!(sfi.dwAttributes & SFGAO_FOLDER)) { uFlags &= ~SHGNLI_NOLNK; // if copying the file then don't trim the extension } lstrcpy(pszName, PathFindFileName(pszPathSrc)); } else { // // when making a link to a drive root. special case a few things // // if we are not on a LFN drive, dont use the full name, just // use the drive letter. "C.LNK" not "Label (C).LNK" // // if we are making a link to removable media, we dont want the // label as part of the name, we want the media type. // // CD-ROM drives are currently the only removable media we // show the volume label for, so we only need to special case // cdrom drives here. // if (PathIsRoot(pszPathSrc) && !PathIsUNC(pszPathSrc)) { if (!fLongFileNames) lstrcpy(pszName, pszPathSrc); else if (IsCDRomDrive(DRIVEID(pszPathSrc))) LoadString(HINST_THISDLL, IDS_DRIVES_CDROM, pszName, MAX_PATH); } } if (fLongFileNames && fDosApp) { HANDLE hPif = PifMgr_OpenProperties(pszPathSrc, NULL, 0, OPENPROPS_INHIBITPIF); if (hPif) { PROPPRG PP = {0}; if (PifMgr_GetProperties(hPif, (LPCSTR)MAKELP(0, GROUP_PRG), &PP, sizeof(PP), 0) && ((PP.flPrgInit & PRGINIT_INFSETTINGS) || ((PP.flPrgInit & (PRGINIT_NOPIF | PRGINIT_DEFAULTPIF)) == 0))) { SHAnsiToTChar(PP.achTitle, pszName, MAX_PATH); } PifMgr_CloseProperties(hPif, 0); } } } if (!*pfMustCopy) { // create full dest path name. only use template iff long file names // can be created and the caller requested it. _BuildLinkName will // truncate files on non-lfn drives and clean up any invalid chars. _BuildLinkName(pszName, pszName, pszDir, (!(*pfMustCopy) && fLongFileNames && (uFlags & SHGNLI_PREFIXNAME))); } if (fDosApp) PathRenameExtension(pszName, TEXT(".pif")); if (uFlags & SHGNLI_NOLNK) { // Don't do PathRemoveExtension because pszName might contain // internal dots ("Windows 3.1") and passing that to // PathYetAnotherMakeUniqueName will result in // "Windows 3 (2).1" which is wrong. We leave the dot at the // end so we get "Windows 3.1 (2)." back. We will strip off the // final dot later. PathRenameExtension(pszName, TEXT(".")); } // make sure the name is unique // NOTE: PathYetAnotherMakeUniqueName will return the directory+filename in the pszName buffer. // It returns FALSE if the name is not unique or the dir+filename is too long. If it returns // false then this function should return false because creation will fail. BOOL fSuccess; if (!(uFlags & SHGNLI_NOUNIQUE)) fSuccess = PathYetAnotherMakeUniqueName(pszName, pszDir, pszName, pszName); else fSuccess = TRUE; // Strip off any trailing dots that may have been generated by SHGNI_NOLNK PathStripTrailingDots(pszName); return fSuccess; } STDAPI_(BOOL) SHGetNewLinkInfoA(LPCSTR pszpdlLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL *pfMustCopy, UINT uFlags) { ThunkText * pThunkText; BOOL bResult = FALSE; if (uFlags & SHGNLI_PIDL) { // 1 string (pszpdlLinkTo is a pidl) pThunkText = ConvertStrings(2, NULL, pszDir); if (pThunkText) pThunkText->m_pStr[0] = (LPWSTR)pszpdlLinkTo; } else { // 2 strings pThunkText = ConvertStrings(2, pszpdlLinkTo, pszDir); } if (pThunkText) { WCHAR wszName[MAX_PATH]; bResult = SHGetNewLinkInfoW(pThunkText->m_pStr[0], pThunkText->m_pStr[1], wszName, pfMustCopy, uFlags); LocalFree(pThunkText); if (bResult) { if (0 == WideCharToMultiByte(CP_ACP, 0, wszName, -1, pszName, MAX_PATH, NULL, NULL)) { SetLastError((DWORD)E_FAIL); bResult = FALSE; } } } return bResult; } // // in: // pidlTo STDAPI CreateLinkToPidl(LPCITEMIDLIST pidlTo, LPCTSTR pszDir, LPITEMIDLIST *ppidl, UINT uFlags) { HRESULT hr = E_FAIL; TCHAR szPathDest[MAX_PATH]; BOOL fCopyLnk; BOOL fUseLinkTemplate = (SHCL_USETEMPLATE & uFlags); UINT uSHGNLI = fUseLinkTemplate ? SHGNLI_PIDL | SHGNLI_PREFIXNAME : SHGNLI_PIDL; if (uFlags & SHCL_MAKEFOLDERSHORTCUT) { // Don't add ".lnk" to the folder shortcut name; that's just stupid uSHGNLI |= SHGNLI_NOLNK; } if (uFlags & SHCL_NOUNIQUE) { uSHGNLI |= SHGNLI_NOUNIQUE; } if (SHGetNewLinkInfo((LPTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, uSHGNLI)) { TCHAR szPathSrc[MAX_PATH]; IShellLink *psl = NULL; // If we passed SHGNLI_NOUNIQUE then we need to do the PathCombine ourselves // because SHGetNewLinkInfo won't if (uFlags & SHCL_NOUNIQUE) { PathCombine(szPathDest, pszDir, szPathDest); } DWORD dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER; SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPathSrc, ARRAYSIZE(szPathSrc), &dwAttributes); if (fCopyLnk) { // if it is file system and not a folder (CopyFile does not work on folders) // just copy it. if (((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) && CopyFile(szPathSrc, szPathDest, TRUE)) { TouchFile(szPathDest); SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL); SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL); hr = S_OK; } else { // load the source object that will be "copied" below (with the ::Save call) hr = SHGetUIObjectFromFullPIDL(pidlTo, NULL, IID_PPV_ARG(IShellLink, &psl)); } } else { hr = SHCoCreateInstance(NULL, uFlags & SHCL_MAKEFOLDERSHORTCUT ? &CLSID_FolderShortcut : &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { hr = psl->SetIDList(pidlTo); // set the working directory to the same path // as the file we are linking too if (szPathSrc[0] && ((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM)) { PathRemoveFileSpec(szPathSrc); psl->SetWorkingDirectory(szPathSrc); } } } if (psl) { if (SUCCEEDED(hr)) { IPersistFile *ppf; hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); if (SUCCEEDED(hr)) { USES_CONVERSION; hr = ppf->Save(T2CW(szPathDest), TRUE); if (SUCCEEDED(hr)) { // in case ::Save translated the name of the // file (.LNK -> .PIF, or Folder Shortcut) WCHAR *pwsz; if (SUCCEEDED(ppf->GetCurFile(&pwsz)) && pwsz) { SHUnicodeToTChar(pwsz, szPathDest, ARRAYSIZE(szPathDest)); SHFree(pwsz); } } ppf->Release(); } } psl->Release(); } } if (ppidl) { *ppidl = SUCCEEDED(hr) ? SHSimpleIDListFromPath(szPathDest) : NULL; } return hr; } // in/out: // pszDir inital folder to try, output new folder (desktop) // out: // ppidl optional output PIDL of thing created HRESULT _CreateLinkRetryDesktop(HWND hwnd, LPCITEMIDLIST pidlTo, LPTSTR pszDir, UINT fFlags, LPITEMIDLIST *ppidl) { HRESULT hr; if (ppidl) *ppidl = NULL; // assume error if (*pszDir && (fFlags & SHCL_CONFIRM)) { hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags); } else { hr = E_FAIL; } // if we were unable to save, ask user if they want us to // try it again but change the path to the desktop. if (FAILED(hr)) { int id; if (hr == STG_E_MEDIUMFULL) { DebugMsg(TF_ERROR, TEXT("failed to create link because disk is full")); id = IDYES; } else { if (fFlags & SHCL_CONFIRM) { id = _PromptTryDesktopLinks(hwnd, pszDir, (fFlags & SHCL_CONFIRM)); } else { id = (SHGetSpecialFolderPath(hwnd, pszDir, CSIDL_DESKTOPDIRECTORY, FALSE)) ? IDYES : IDNO; } if (id == IDYES && *pszDir) { hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags); } } // we failed to create the link complain to the user. if (FAILED(hr) && id != IDNO) { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANNOTCREATELINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_OK | MB_ICONASTERISK); } } #ifdef DEBUG if (FAILED(hr) && ppidl) ASSERT(*ppidl == NULL); #endif return hr; } // // This function creates links to the stuff in the IDataObject // // Arguments: // hwnd for any UI // pszDir optional target directory (where to create links) // pDataObj data object describing files (array of idlist) // ppidl optional pointer to an array that receives pidls pointing to the new links // or NULL if not interested STDAPI SHCreateLinks(HWND hwnd, LPCTSTR pszDir, IDataObject *pDataObj, UINT fFlags, LPITEMIDLIST* ppidl) { DECLAREWAITCURSOR; STGMEDIUM medium; HRESULT hr; SetWaitCursor(); LPIDA pida = DataObj_GetHIDA(pDataObj, &medium); if (pida) { TCHAR szTargetDir[MAX_PATH]; hr = S_OK; // In case hida contains zero elements szTargetDir[0] = 0; if (pszDir) lstrcpyn(szTargetDir, pszDir, ARRAYSIZE(szTargetDir)); if (!(fFlags & SHCL_USEDESKTOP)) fFlags |= SHCL_CONFIRM; for (UINT i = 0; i < pida->cidl; i++) { LPITEMIDLIST pidlTo = IDA_ILClone(pida, i); if (pidlTo) { hr = _CreateLinkRetryDesktop(hwnd, pidlTo, szTargetDir, fFlags, ppidl ? &ppidl[i] : NULL); ILFree(pidlTo); if (FAILED(hr)) break; } } HIDA_ReleaseStgMedium(pida, &medium); } else hr = E_OUTOFMEMORY; SHChangeNotifyHandleEvents(); ResetWaitCursor(); return hr; } #if 1 HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts) { VARIANT var; HRESULT hr = InitVariantFromIDList(&var, pidl); if (SUCCEEDED(hr)) { hr = psfv->SelectItem(&var, dwOpts); VariantClear(&var); } return hr; } HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv) { *ppsfv = NULL; IWebBrowserApp *pauto; HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto); if (SUCCEEDED(hr)) { HWND hwnd; if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd))) { // Make sure we make this the active window SetForegroundWindow(hwnd); ShowWindow(hwnd, SW_SHOWNORMAL); } IDispatch *pdoc; hr = pauto->get_Document(&pdoc); if (S_OK == hr) // careful, automation returns S_FALSE { hr = pdoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv)); pdoc->Release(); } else hr = E_FAIL; pauto->Release(); } return hr; } // pidlFolder - fully qualified pidl to the folder to open // cidl/apidl - array of items in that folder to select // // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's // folder is opened and it is selected. // // dwFlags - optional flags, pass 0 for now SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags) { HRESULT hr; if (0 == cidl) { // overload the 0 item case to mean pidlFolder is the full pidl to the item LPITEMIDLIST pidlTemp; hr = SHILClone(pidlFolder, &pidlTemp); if (SUCCEEDED(hr)) { ILRemoveLastID(pidlTemp); // strip to the folder LPCITEMIDLIST pidl = ILFindLastID(pidlFolder); hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse ILFree(pidlTemp); } } else { IShellFolderViewDual *psfv; hr = OpenFolderAndGetView(pidlFolder, &psfv); if (SUCCEEDED(hr)) { DWORD dwSelFlags = SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE; for (UINT i = 0; i < cidl; i++) { hr = SelectPidlInSFV(psfv, apidl[i], dwSelFlags); dwSelFlags = SVSI_SELECT; // second items append to sel } psfv->Release(); } } return hr; } #else HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, REFIID riid, void **ppv) { *ppv = NULL; IWebBrowserApp *pauto; HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto); if (SUCCEEDED(hr)) { HWND hwnd; if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd))) { // Make sure we make this the active window SetForegroundWindow(hwnd); ShowWindow(hwnd, SW_SHOWNORMAL); } IShellBrowser* psb; hr = IUnknown_QueryService(pauto, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (SUCCEEDED(hr)) { IShellView* psv; hr = psb->QueryActiveShellView(&psv); if (SUCCEEDED(hr)) { hr = psv->QueryInterface(riid, ppv); psv->Release(); } psb->Release(); } pauto->Release(); } return hr; } // pidlFolder - fully qualified pidl to the folder to open // cidl/apidl - array of items in that folder to select // // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's // folder is opened and it is selected. // // dwFlags - optional flags, pass 0 for now SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags) { HRESULT hr; if (0 == cidl) { // overload the 0 item case to mean pidlFolder is the full pidl to the item LPITEMIDLIST pidlTemp; hr = SHILClone(pidlFolder, &pidlTemp); if (SUCCEEDED(hr)) { ILRemoveLastID(pidlTemp); // strip to the folder LPCITEMIDLIST pidl = ILFindLastID(pidlFolder); hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse ILFree(pidlTemp); } } else { IFolderView *pfv; hr = OpenFolderAndGetView(pidlFolder, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { pfv->SelectAndPositionItems(1, apidl, NULL, SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE); if (cidl > 1) pfv->SelectAndPositionItems(cidl - 1, apidl + 1, NULL, SVSI_SELECT); pfv->Release(); } } return hr; } #endif SHSTDAPI SHCreateShellItem(LPCITEMIDLIST pidlParent, IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi) { *ppsi = NULL; IShellItem *psi; HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellItem, NULL, IID_PPV_ARG(IShellItem, &psi)); if (SUCCEEDED(hr)) { if (pidlParent || psfParent) { IParentAndItem *pinit; ASSERT(pidl); hr = psi->QueryInterface(IID_PPV_ARG(IParentAndItem, &pinit)); if (SUCCEEDED(hr)) { hr = pinit->SetParentAndItem(pidlParent, psfParent, pidl); pinit->Release(); } } else { IPersistIDList *pinit; hr = psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &pinit)); if (SUCCEEDED(hr)) { hr = pinit->SetIDList(pidl); pinit->Release(); } } if (SUCCEEDED(hr)) *ppsi = psi; else psi->Release(); } return hr; } STDAPI SHCreateShellItemFromParent(IShellItem *psiParent, LPCWSTR pszName, IShellItem **ppsi) { *ppsi = NULL; IShellFolder *psf; HRESULT hr = psiParent->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = SHGetIDListFromUnk(psiParent, &pidl); if (SUCCEEDED(hr)) { ULONG cchEaten; LPITEMIDLIST pidlChild; hr = psf->ParseDisplayName(NULL, NULL, (LPWSTR)pszName, &cchEaten, &pidlChild, NULL); if (SUCCEEDED(hr)) { hr = SHCreateShellItem(pidl, psf, pidlChild, ppsi); ILFree(pidlChild); } ILFree(pidl); } psf->Release(); } return hr; } SHSTDAPI SHSetLocalizedName(LPWSTR pszPath, LPCWSTR pszResModule, int idsRes) { IShellFolder *psfDesktop; HRESULT hrInit = SHCoInitialize(); HRESULT hr = hrInit; if (SUCCEEDED(hrInit)) { hr = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL); if (SUCCEEDED(hr)) { LPCITEMIDLIST pidlChild; IShellFolder *psf; hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { // WARNING - this is a stack sensitive function - ZekeL 29-Jan-2001 // since this function is called by winlogon/userenv // we need to be sensitive to the stack limitations of those callers // the shortname will be no larger than the long name DWORD cchShort = lstrlenW(pszResModule) + 1; WCHAR *pszShort = (WCHAR *)alloca(CbFromCchW(cchShort)); DWORD cch = GetShortPathName(pszResModule, pszShort, cchShort); if (cch) { pszResModule = pszShort; } else { // GSPN() fails when the module passed in is a relative path cch = cchShort; } cch += 14; // 11 for id + ',' + '@' + null WCHAR *pszName = (WCHAR *)alloca(CbFromCchW(cch)); wnsprintfW(pszName, cch, L"@%s,%d", pszResModule, (idsRes * -1)); hr = psf->SetNameOf(NULL, pidlChild, pszName, SHGDN_NORMAL, NULL); psf->Release(); } SHFree(pidl); } psfDesktop->Release(); } } SHCoUninitialize(hrInit); return hr; } // ShellHookProc was mistakenly exported in the original NT SHELL32.DLL when // it didn't need to be (hookproc's, like wndproc's don't need to be exported // in the 32-bit world). In order to maintain loadability of a app // which might have linked to it, we stub it here. If some app ended up really // using it, then we'll look into a specific fix for that app. STDAPI_(LONG) ShellHookProc(int code, WPARAM wParam, LPARAM lParam) { return 0; } // RegisterShellHook - wrapper around RegisterShellHookWindow()/DeregisterShellHookWindow() // the GetTaskmanWindow() stuff is legacy that I don't think is really needed HWND g_hwndTaskMan = NULL; STDAPI_(BOOL) RegisterShellHook(HWND hwnd, BOOL fInstall) { BOOL fOk = TRUE; switch (fInstall) { case 0: // un-installation of shell hooks g_hwndTaskMan = GetTaskmanWindow(); if (hwnd == g_hwndTaskMan) { SetTaskmanWindow(NULL); } DeregisterShellHookWindow(hwnd); return TRUE; case 3: // explorer.exe Tray uses this if (g_hwndTaskMan != NULL) { SetTaskmanWindow(NULL); g_hwndTaskMan = NULL; } fOk = SetTaskmanWindow(hwnd); if (fOk) { g_hwndTaskMan = hwnd; } RegisterShellHookWindow(hwnd); // install break; } return TRUE; } EXTERN_C DWORD g_dwThreadBindCtx; class CThreadBindCtx : public IBindCtx { public: CThreadBindCtx(IBindCtx *pbc) : _cRef(1) { _pbc = pbc; _pbc->AddRef(); } ~CThreadBindCtx(); // *** IUnknown methods *** STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // *** IBindCtx methods *** STDMETHODIMP RegisterObjectBound(IUnknown *punk) { return _pbc->RegisterObjectBound(punk); } STDMETHODIMP RevokeObjectBound(IUnknown *punk) { return _pbc->RevokeObjectBound(punk); } STDMETHODIMP ReleaseBoundObjects(void) { return _pbc->ReleaseBoundObjects(); } STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts) { return _pbc->SetBindOptions(pbindopts); } STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts) { return _pbc->GetBindOptions(pbindopts); } STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot) { return _pbc->GetRunningObjectTable(pprot); } STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk) { return _pbc->RegisterObjectParam(pszKey, punk); } STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk) { return _pbc->GetObjectParam(pszKey, ppunk); } STDMETHODIMP EnumObjectParam(IEnumString **ppenum) { return _pbc->EnumObjectParam(ppenum); } STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey) { return _pbc->RevokeObjectParam(pszKey); } private: LONG _cRef; IBindCtx * _pbc; }; CThreadBindCtx::~CThreadBindCtx() { ATOMICRELEASE(_pbc); } HRESULT CThreadBindCtx::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CThreadBindCtx, IBindCtx), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CThreadBindCtx::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CThreadBindCtx::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; // clear ourselves out TlsSetValue(g_dwThreadBindCtx, NULL); return 0; } STDAPI TBCGetBindCtx(BOOL fCreate, IBindCtx **ppbc) { HRESULT hr = E_UNEXPECTED; *ppbc = NULL; if ((DWORD) -1 != g_dwThreadBindCtx) { CThreadBindCtx *ptbc = (CThreadBindCtx *)TlsGetValue(g_dwThreadBindCtx); if (ptbc) { ptbc->AddRef(); *ppbc = SAFECAST(ptbc, IBindCtx *); hr = S_OK; } else if (fCreate) { IBindCtx *pbcInner; hr = CreateBindCtx(0, &pbcInner); if (SUCCEEDED(hr)) { hr = E_OUTOFMEMORY; ptbc = new CThreadBindCtx(pbcInner); if (ptbc) { if (TlsSetValue(g_dwThreadBindCtx, ptbc)) { *ppbc = SAFECAST(ptbc, IBindCtx *); hr = S_OK; } else delete ptbc; } pbcInner->Release(); } } } return hr; } STDAPI TBCRegisterObjectParam(LPCOLESTR pszKey, IUnknown *punk, IBindCtx **ppbcLifetime) { IBindCtx *pbc; HRESULT hr = TBCGetBindCtx(TRUE, &pbc); if (SUCCEEDED(hr)) { hr = BindCtx_RegisterObjectParam(pbc, pszKey, punk, ppbcLifetime); pbc->Release(); } else *ppbcLifetime = 0; return hr; } STDAPI TBCGetObjectParam(LPCOLESTR pszKey, REFIID riid, void **ppv) { IBindCtx *pbc; HRESULT hr = TBCGetBindCtx(FALSE, &pbc); if (SUCCEEDED(hr)) { IUnknown *punk; hr = pbc->GetObjectParam((LPOLESTR)pszKey, &punk); if (SUCCEEDED(hr) ) { if (ppv) hr = punk->QueryInterface(riid, ppv); punk->Release(); } pbc->Release(); } return hr; } #define TBCENVOBJECT L"ThreadEnvironmentVariables" STDAPI TBCGetEnvironmentVariable(LPCWSTR pszVar, LPWSTR pszValue, DWORD cchValue) { IPropertyBag *pbag; HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag)); if (SUCCEEDED(hr)) { hr = SHPropertyBag_ReadStr(pbag, pszVar, pszValue, cchValue); pbag->Release(); } return hr; } STDAPI TBCSetEnvironmentVariable(LPCWSTR pszVar, LPCWSTR pszValue, IBindCtx **ppbcLifetime) { *ppbcLifetime = 0; IPropertyBag *pbag; HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag)); if (FAILED(hr)) hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbag)); if (SUCCEEDED(hr)) { hr = SHPropertyBag_WriteStr(pbag, pszVar, pszValue); if (SUCCEEDED(hr)) hr = TBCRegisterObjectParam(TBCENVOBJECT, pbag, ppbcLifetime); pbag->Release(); } return hr; } // create a stock IExtractIcon handler for a thing that is file like. this is typically // used by name space extensiosn that display things that are like files in the // file system. that is the extension, file attributes decrive all that is needed // for a simple icon extractor STDAPI SHCreateFileExtractIconW(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; SHFILEINFO sfi = {0}; if (SHGetFileInfo(pszFile, dwFileAttributes, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES)) { hr = SHCreateDefExtIcon(TEXT("*"), sfi.iIcon, sfi.iIcon, GIL_PERCLASS | GIL_NOTFILENAME, -1, riid, ppv); DestroyIcon(sfi.hIcon); } return hr; }