#include "stock.h" #pragma hdrstop #include #include #include #include #include #include #include "ccstock2.h" #include "wininet.h" #include "w95wraps.h" #include //------------------------------------------------------------------------ // Random helpful functions //------------------------------------------------------------------------ // STDAPI_(LPCTSTR) SkipServerSlashes(LPCTSTR pszName) { for (pszName; *pszName && *pszName == TEXT('\\'); pszName++); return pszName; } // pbIsNamed is true if the i-th item in hm is a named separator STDAPI_(BOOL) _SHIsMenuSeparator2(HMENU hm, int i, BOOL *pbIsNamed) { MENUITEMINFO mii; BOOL bLocal; if (!pbIsNamed) pbIsNamed = &bLocal; *pbIsNamed = FALSE; mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE | MIIM_ID; mii.cch = 0; // WARNING: We MUST initialize it to 0!!! if (GetMenuItemInfo(hm, i, TRUE, &mii) && (mii.fType & MFT_SEPARATOR)) { // NOTE that there is a bug in either 95 or NT user!!! // 95 returns 16 bit ID's and NT 32 bit therefore there is a // the following may fail, on win9x, to evaluate to false // without casting *pbIsNamed = ((WORD)mii.wID != (WORD)-1); return TRUE; } return FALSE; } STDAPI_(BOOL) _SHIsMenuSeparator(HMENU hm, int i) { return _SHIsMenuSeparator2(hm, i, NULL); } // // _SHPrettyMenu -- make this menu look darn purty // // Prune the separators in this hmenu to ensure there isn't one in the first or last // position and there aren't any runs of >1 separator. // // Named separators take precedence over regular separators. // STDAPI_(void) _SHPrettyMenu(HMENU hm) { BOOL bSeparated = TRUE; BOOL bWasNamed = TRUE; for (int i = GetMenuItemCount(hm) - 1; i > 0; --i) { BOOL bIsNamed; if (_SHIsMenuSeparator2(hm, i, &bIsNamed)) { if (bSeparated) { // if we have two separators in a row, only one of which is named // remove the non named one! if (bIsNamed && !bWasNamed) { DeleteMenu(hm, i+1, MF_BYPOSITION); bWasNamed = bIsNamed; } else { DeleteMenu(hm, i, MF_BYPOSITION); } } else { bWasNamed = bIsNamed; bSeparated = TRUE; } } else { bSeparated = FALSE; } } // The above loop does not handle the case of many separators at // the beginning of the menu while (_SHIsMenuSeparator2(hm, 0, NULL)) { DeleteMenu(hm, 0, MF_BYPOSITION); } } STDAPI_(DWORD) SHIsButtonObscured(HWND hwnd, PRECT prc, INT_PTR i) { ASSERT(IsWindow(hwnd)); ASSERT(i < SendMessage(hwnd, TB_BUTTONCOUNT, 0, 0)); DWORD dwEdge = 0; RECT rc, rcInt; SendMessage(hwnd, TB_GETITEMRECT, i, (LPARAM)&rc); if (!IntersectRect(&rcInt, prc, &rc)) { dwEdge = EDGE_LEFT | EDGE_RIGHT | EDGE_TOP | EDGE_BOTTOM; } else { if (rc.top != rcInt.top) dwEdge |= EDGE_TOP; if (rc.bottom != rcInt.bottom) dwEdge |= EDGE_BOTTOM; if (rc.left != rcInt.left) dwEdge |= EDGE_LEFT; if (rc.right != rcInt.right) dwEdge |= EDGE_RIGHT; } return dwEdge; } STDAPI_(BYTE) SHBtnStateFromRestriction(DWORD dwRest, BYTE fsState) { if (dwRest == RESTOPT_BTN_STATE_VISIBLE) return (fsState & ~TBSTATE_HIDDEN); else if (dwRest == RESTOPT_BTN_STATE_HIDDEN) return (fsState | TBSTATE_HIDDEN); else { #ifdef DEBUG if (dwRest != RESTOPT_BTN_STATE_DEFAULT) TraceMsg(TF_ERROR, "bad toolbar button state policy %x", dwRest); #endif return fsState; } } // // SHIsDisplayable // // Figure out if this unicode string can be displayed by the system // (i.e., won't be turned into a string of question marks). // STDAPI_(BOOL) SHIsDisplayable(LPCWSTR pwszName, BOOL fRunOnFE, BOOL fRunOnNT5) { BOOL fNotDisplayable = FALSE; if (pwszName) { if (!fRunOnNT5) { // if WCtoMB has to use default characters in mapping pwszName to multibyte, // it sets fNotDisplayable == TRUE, in which case we have to use something // else for the title string. WideCharToMultiByte(CP_ACP, 0, pwszName, -1, NULL, 0, NULL, &fNotDisplayable); if (fNotDisplayable) { if (fRunOnFE) { WCHAR wzName[INTERNET_MAX_URL_LENGTH]; BOOL fReplaceNbsp = FALSE; StrCpyNW(wzName, pwszName, ARRAYSIZE(wzName)); for (int i = 0; i < ARRAYSIZE(wzName); i++) { if (0x00A0 == wzName[i]) // if   { wzName[i] = 0x0020; // replace to space fReplaceNbsp = TRUE; } else if (0 == wzName[i]) break; } if (fReplaceNbsp) { pwszName = wzName; WideCharToMultiByte(CP_ACP, 0, pwszName, -1, NULL, 0, NULL, &fNotDisplayable); } } } } } return !fNotDisplayable; } // Trident will take URLs that don't indicate their source of // origin (about:, javascript:, & vbscript:) and will append // an URL turd and then the source URL. The turd will indicate // where the source URL begins and that source URL is needed // when the action needs to be Zone Checked. // // This function will remove that URL turd and everything behind // it so the URL is presentable for the user. #define URL_TURD ((TCHAR)0x01) STDAPI_(void) SHRemoveURLTurd(LPTSTR pszUrl) { if (!pszUrl) return; while (0 != pszUrl[0]) { if (URL_TURD == pszUrl[0]) { pszUrl[0] = 0; break; } pszUrl = CharNext(pszUrl); } } STDAPI_(BOOL) SetWindowZorder(HWND hwnd, HWND hwndInsertAfter) { return SetWindowPos(hwnd, hwndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); } BOOL CALLBACK _FixZorderEnumProc(HWND hwnd, LPARAM lParam) { HWND hwndTest = (HWND)lParam; HWND hwndOwner = hwnd; while (hwndOwner = GetWindow(hwndOwner, GW_OWNER)) { if (hwndOwner == hwndTest) { TraceMsg(TF_WARNING, "_FixZorderEnumProc: Found topmost window %x owned by non-topmost window %x, fixing...", hwnd, hwndTest); SetWindowZorder(hwnd, HWND_NOTOPMOST); #ifdef DEBUG if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) TraceMsg(TF_ERROR, "_FixZorderEnumProc: window %x is still topmost", hwnd); #endif break; } } return TRUE; } STDAPI_(BOOL) SHForceWindowZorder(HWND hwnd, HWND hwndInsertAfter) { BOOL fRet = SetWindowZorder(hwnd, hwndInsertAfter); if (fRet && hwndInsertAfter == HWND_TOPMOST) { if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) { // // user didn't actually move the hwnd to topmost // // According to GerardoB, this can happen if the window has // an owned window that somehow has become topmost while the // owner remains non-topmost, i.e., the two have become // separated in the z-order. In this state, when the owner // window tries to make itself topmost, the call will // silently fail. // // TERRIBLE HORRIBLE NO GOOD VERY BAD HACK // // Hacky fix is to enumerate the toplevel windows, check to see // if any are topmost and owned by hwnd, and if so, make them // non-topmost. Then, retry the SetWindowPos call. // TraceMsg(TF_WARNING, "SHForceWindowZorder: SetWindowPos(%x, HWND_TOPMOST) failed", hwnd); // Fix up the z-order EnumWindows(_FixZorderEnumProc, (LPARAM)hwnd); // Retry the set. (This should make all owned windows topmost as well.) SetWindowZorder(hwnd, HWND_TOPMOST); #ifdef DEBUG if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) TraceMsg(TF_ERROR, "SHForceWindowZorder: window %x is still not topmost", hwnd); #endif } } return fRet; } STDAPI_(LPITEMIDLIST) ILCloneParent(LPCITEMIDLIST pidl) { LPITEMIDLIST pidlParent = ILClone(pidl); if (pidlParent) ILRemoveLastID(pidlParent); return pidlParent; } // in: // psf OPTIONAL, if NULL assume psfDesktop // pidl to bind to from psfParent // STDAPI SHBindToObject(IShellFolder *psf, REFIID riid, LPCITEMIDLIST pidl, void **ppv) { // NOTE: callers should use SHBindToObjectEx!!! return SHBindToObjectEx(psf, pidl, NULL, riid, ppv); } // in: // psf OPTIONAL, if NULL assume psfDesktop // pidl to bind to from psfParent // pbc bind context STDAPI SHBindToObjectEx(IShellFolder *psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr; IShellFolder *psfRelease = NULL; if (!psf) { SHGetDesktopFolder(&psf); psfRelease = psf; } if (psf) { if (!pidl || ILIsEmpty(pidl)) { hr = psf->QueryInterface(riid, ppv); } else { hr = psf->BindToObject(pidl, pbc, riid, ppv); } } else { *ppv = NULL; hr = E_FAIL; } if (psfRelease) { psfRelease->Release(); } if (SUCCEEDED(hr) && (*ppv == NULL)) { // Some shell extensions (eg WS_FTP) will return success and a null out pointer TraceMsg(TF_WARNING, "SHBindToObjectEx: BindToObject succeeded but returned null ppv!!"); hr = E_FAIL; } return hr; } // psfRoot is the base of the bind. If NULL, then we use the shell desktop. // If you want to bind relative to the explorer root (e.g., CabView, MSN), // then use SHBindToIDListParent. STDAPI SHBindToFolderIDListParent(IShellFolder *psfRoot, LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast) { HRESULT hr; // Old shell32 code in some cases simply whacked the pidl, // but this is unsafe. Do what shdocvw does and clone/remove: // LPITEMIDLIST pidlParent = ILCloneParent(pidl); if (pidlParent) { hr = SHBindToObjectEx(psfRoot, pidlParent, NULL, riid, ppv); ILFree(pidlParent); } else hr = E_OUTOFMEMORY; if (ppidlLast) *ppidlLast = ILFindLastID(pidl); return hr; } // // Warning! brutil.cpp overrides this function // STDAPI SHBindToIDListParent(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast) { return SHBindToFolderIDListParent(NULL, pidl, riid, ppv, ppidlLast); } // should be IUnknown_GetIDList() STDAPI SHGetIDListFromUnk(IUnknown *punk, LPITEMIDLIST *ppidl) { *ppidl = NULL; HRESULT hr = E_NOINTERFACE; if (punk) { IPersistFolder2 *ppf; IPersistIDList *pperid; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistIDList, &pperid)))) { hr = pperid->GetIDList(ppidl); pperid->Release(); } else if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf)))) { hr = ppf->GetCurFolder(ppidl); ppf->Release(); } } return hr; } // // generically useful to hide. // #pragma pack(1) typedef struct _HIDDENCLSID { HIDDENITEMID hid; CLSID clsid; } HIDDENCLSID; #pragma pack() typedef UNALIGNED HIDDENCLSID *PHIDDENCLSID; typedef const UNALIGNED HIDDENCLSID *PCHIDDENCLSID; STDAPI_(LPITEMIDLIST) ILAppendHiddenClsid(LPITEMIDLIST pidl, IDLHID id, CLSID *pclsid) { HIDDENCLSID hc = {{sizeof(hc), 0, id}}; hc.clsid = *pclsid; // WARNING - cannot use hid.wVersion for compat reasons - ZekeL - 23-OCT-2000 // on win2k and winMe we appended clsid's with wVersion // as stack garbage. this means we cannot use it for anything return ILAppendHiddenID(pidl, &hc.hid); } STDAPI_(BOOL) ILGetHiddenClsid(LPCITEMIDLIST pidl, IDLHID id, CLSID *pclsid) { PCHIDDENCLSID phc = (PCHIDDENCLSID) ILFindHiddenID(pidl, id); // WARNING - cannot use hid.wVersion for compat reasons - ZekeL - 23-OCT-2000 // on win2k and winMe we appended clsid's with wVersion // as stack garbage. this means we cannot use it for anything if (phc) { *pclsid = phc->clsid; return TRUE; } return FALSE; } #pragma pack(1) typedef struct _HIDDENSTRINGA { HIDDENITEMID hid; WORD type; CHAR sz[1]; // variable length string } HIDDENSTRINGA; #pragma pack() typedef UNALIGNED HIDDENSTRINGA *PHIDDENSTRINGA; typedef const UNALIGNED HIDDENSTRINGA *PCHIDDENSTRINGA; #pragma pack(1) typedef struct _HIDDENSTRINGW { HIDDENITEMID hid; WORD type; WCHAR sz[1]; // canonical name to be passed to ISTRING } HIDDENSTRINGW; #pragma pack() typedef UNALIGNED HIDDENSTRINGW *PHIDDENSTRINGW; typedef const UNALIGNED HIDDENSTRINGW *PCHIDDENSTRINGW; #define HIDSTRTYPE_ANSI 0x0001 #define HIDSTRTYPE_WIDE 0x0002 #define HIDSTR_MAX 0xF000 // max ushort - sizeof(HIDDENSTRINGW) - other goo for original pidl STDAPI_(LPITEMIDLIST) ILAppendHiddenStringW(LPITEMIDLIST pidl, IDLHID id, LPCWSTR psz) { // terminator is included in the ID definition size_t cbString; HRESULT hr = StringCbLengthW(psz, HIDSTR_MAX, &cbString); if (FAILED(hr)) { return NULL; } USHORT cb = (USHORT)(sizeof(HIDDENSTRINGW) + cbString); // // Use HIDDENSTRINGW* here instead of PHIDDENSTRINGW which is defined // as UNALIGNED. // HIDDENSTRINGW *phs = (HIDDENSTRINGW *) LocalAlloc(LPTR, cb); if (phs) { phs->hid.cb = cb; phs->hid.id = id; phs->type = HIDSTRTYPE_WIDE; // // Terminator is included in the ID definition but... // we need to now account for that extra character // when we copy the hidden string. // StringCbCopyW(phs->sz, cbString + sizeof(*psz), psz); pidl = ILAppendHiddenID(pidl, &phs->hid); LocalFree(phs); return pidl; } return NULL; } STDAPI_(LPITEMIDLIST) ILAppendHiddenStringA(LPITEMIDLIST pidl, IDLHID id, LPCSTR psz) { // terminator is included in the ID definition size_t cbString; HRESULT hr = StringCbLengthA(psz, HIDSTR_MAX, &cbString); if (FAILED(hr)) { return NULL; } USHORT cb = (USHORT)(sizeof(HIDDENSTRINGA) + cbString); // // Use HIDDENSTRINGA* here instead of PHIDDENSTRINGW which is defined // as UNALIGNED. // HIDDENSTRINGA *phs = (HIDDENSTRINGA *) LocalAlloc(LPTR, cb); if (phs) { phs->hid.cb = cb; phs->hid.id = id; phs->type = HIDSTRTYPE_ANSI; // // Terminator is included in the ID definition but... // we need to now account for that extra character // when we copy the hidden string. // StringCbCopyA(phs->sz, cbString + sizeof(*psz), psz); pidl = ILAppendHiddenID(pidl, &phs->hid); LocalFree(phs); return pidl; } return NULL; } STDAPI_(void *) _MemDupe(const UNALIGNED void *pv, DWORD cb) { void *pvRet = LocalAlloc(LPTR, cb); if (pvRet) { CopyMemory(pvRet, pv, cb); } return pvRet; } STDAPI_(BOOL) ILGetHiddenStringW(LPCITEMIDLIST pidl, IDLHID id, LPWSTR psz, DWORD cch) { PCHIDDENSTRINGW phs = (PCHIDDENSTRINGW) ILFindHiddenID(pidl, id); RIP(psz); if (phs) { if (phs->type == HIDSTRTYPE_WIDE) { ualstrcpynW(psz, phs->sz, cch); return TRUE; } else { ASSERT(phs->type == HIDSTRTYPE_ANSI); SHAnsiToUnicode((LPSTR)phs->sz, psz, cch); return TRUE; } } return FALSE; } STDAPI_(BOOL) ILGetHiddenStringA(LPCITEMIDLIST pidl, IDLHID id, LPSTR psz, DWORD cch) { PCHIDDENSTRINGW phs = (PCHIDDENSTRINGW) ILFindHiddenID(pidl, id); RIP(psz); if (phs) { if (phs->type == HIDSTRTYPE_ANSI) { ualstrcpynA(psz, (LPSTR)phs->sz, cch); return TRUE; } else { ASSERT(phs->type == HIDSTRTYPE_WIDE); // we need to handle the unalignment here... LPWSTR pszT = (LPWSTR) _MemDupe(phs->sz, CbFromCch(ualstrlenW(phs->sz) +1)); if (pszT) { SHUnicodeToAnsi(pszT, psz, cch); LocalFree(pszT); return TRUE; } } } return FALSE; } STDAPI_(int) ILCompareHiddenString(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, IDLHID id) { // if there are fragments in here, then they might // differentiate the two pidls PCHIDDENSTRINGW ps1 = (PCHIDDENSTRINGW)ILFindHiddenID(pidl1, id); PCHIDDENSTRINGW ps2 = (PCHIDDENSTRINGW)ILFindHiddenID(pidl2, id); if (ps1 && ps2) { if (ps1->type == ps2->type) { if (ps1->type == HIDSTRTYPE_WIDE) return ualstrcmpW(ps1->sz, ps2->sz); ASSERT(ps1->type == HIDSTRTYPE_ANSI); return lstrcmpA((LPCSTR)ps1->sz, (LPCSTR)ps2->sz); } else { SHSTRW str; if (ps1->type == HIDSTRTYPE_ANSI) { str.SetStr((LPCSTR)ps1->sz); return ualstrcmpW(str, ps2->sz); } else { ASSERT(ps2->type == HIDSTRTYPE_ANSI); str.SetStr((LPCSTR)ps2->sz); return ualstrcmpW(ps1->sz, str); } } } if (ps1) return 1; if (ps2) return -1; return 0; } STDAPI_(OBJCOMPATFLAGS) SHGetObjectCompatFlagsFromIDList(LPCITEMIDLIST pidl) { OBJCOMPATFLAGS ocf = 0; CLSID clsid; // APPCOMPAT: FileNet IDMDS (Panagon)'s shell folder extension returns // E_NOTIMPL for IPersistFolder::GetClassID, so to detect the application, // we have to crack the pidl. (B#359464: tracysh) if (!ILIsEmpty(pidl) && pidl->mkid.cb >= sizeof(IDREGITEM) && pidl->mkid.abID[0] == SHID_ROOT_REGITEM) { clsid = ((LPCIDLREGITEM)pidl)->idri.clsid; ocf = SHGetObjectCompatFlags(NULL, &clsid); } return ocf; } STDAPI_(LPITEMIDLIST) _ILCreate(UINT cbSize) { LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbSize); if (pidl) memset(pidl, 0, cbSize); // zero-init for external task allocator return pidl; } // // ILClone using Task allocator // STDAPI SHILClone(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlOut) { *ppidlOut = ILClone(pidl); return *ppidlOut ? S_OK : E_OUTOFMEMORY; } // // ILCombine using Task allocator // STDAPI SHILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, LPITEMIDLIST * ppidlOut) { *ppidlOut = ILCombine(pidl1, pidl2); return *ppidlOut ? S_OK : E_OUTOFMEMORY; } // // rooted helpers // LPCIDREGITEM _IsRooted(LPCITEMIDLIST pidl) { LPCIDREGITEM pidlr = (LPCIDREGITEM)pidl; if (!ILIsEmpty(pidl) && pidlr->cb > sizeof(IDREGITEM) && pidlr->bFlags == SHID_ROOTEDREGITEM) return pidlr; return NULL; } STDAPI_(BOOL) ILIsRooted(LPCITEMIDLIST pidl) { return (NULL != _IsRooted(pidl)); } #define _ROOTEDPIDL(pidlr) (LPITEMIDLIST)(((LPBYTE)pidlr)+sizeof(IDREGITEM)) STDAPI_(LPCITEMIDLIST) ILRootedFindIDList(LPCITEMIDLIST pidl) { LPCIDREGITEM pidlr = _IsRooted(pidl); if (pidlr && pidlr->cb > sizeof(IDREGITEM)) { // then we have a rooted IDList in there return _ROOTEDPIDL(pidlr); } return NULL; } STDAPI_(BOOL) ILRootedGetClsid(LPCITEMIDLIST pidl, CLSID *pclsid) { LPCIDREGITEM pidlr = _IsRooted(pidl); *pclsid = pidlr ? pidlr->clsid : CLSID_NULL; return (NULL != pidlr); } STDAPI_(LPITEMIDLIST) ILRootedCreateIDList(CLSID *pclsid, LPCITEMIDLIST pidl) { UINT cbPidl = ILGetSize(pidl); UINT cbTotal = sizeof(IDREGITEM) + cbPidl; LPIDREGITEM pidlr = (LPIDREGITEM) SHAlloc(cbTotal + sizeof(WORD)); if (pidlr) { pidlr->cb = (WORD)cbTotal; pidlr->bFlags = SHID_ROOTEDREGITEM; pidlr->bOrder = 0; // Nobody uses this (yet) if (pclsid) pidlr->clsid = *pclsid; else pidlr->clsid = CLSID_ShellDesktop; MoveMemory(_ROOTEDPIDL(pidlr), pidl, cbPidl); // terminate _ILNext((LPITEMIDLIST)pidlr)->mkid.cb = 0; } return (LPITEMIDLIST) pidlr; } int CompareGUID(REFGUID guid1, REFGUID guid2) { TCHAR sz1[GUIDSTR_MAX]; TCHAR sz2[GUIDSTR_MAX]; SHStringFromGUIDW(guid1, sz1, SIZECHARS(sz1)); SHStringFromGUIDW(guid2, sz2, SIZECHARS(sz2)); return lstrcmp(sz1, sz2); } STDAPI_(int) ILRootedCompare(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iRet; LPCIDREGITEM pidlr1 = _IsRooted(pidl1); LPCIDREGITEM pidlr2 = _IsRooted(pidl2); if (pidlr1 && pidlr2) { CLSID clsid1 = pidlr1->clsid; CLSID clsid2 = pidlr2->clsid; iRet = CompareGUID(clsid1, clsid2); if (0 == iRet) { if (!ILIsEqual(_ROOTEDPIDL(pidl1), _ROOTEDPIDL(pidl2))) { IShellFolder *psfDesktop; if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop))) { HRESULT hr = psfDesktop->CompareIDs(0, _ROOTEDPIDL(pidl1), _ROOTEDPIDL(pidl2)); psfDesktop->Release(); iRet = ShortFromResult(hr); } } } } else if (pidlr1) { iRet = -1; } else if (pidlr2) { iRet = 1; } else { // if neither are rootes, then they share the desktop // as the same root... iRet = 0; } return iRet; } LPITEMIDLIST ILRootedTranslate(LPCITEMIDLIST pidlRooted, LPCITEMIDLIST pidlTrans) { LPCITEMIDLIST pidlChild = ILFindChild(ILRootedFindIDList(pidlRooted), pidlTrans); if (pidlChild) { LPITEMIDLIST pidlRoot = ILCloneFirst(pidlRooted); if (pidlRoot) { LPITEMIDLIST pidlRet = ILCombine(pidlRoot, pidlChild); ILFree(pidlRoot); return pidlRet; } } return NULL; } const ITEMIDLIST s_idlNULL = { 0 } ; HRESULT ILRootedBindToRoot(LPCITEMIDLIST pidl, REFIID riid, void **ppv) { HRESULT hr; CLSID clsid; ASSERT(ILIsRooted(pidl)); ILRootedGetClsid(pidl, &clsid); pidl = ILRootedFindIDList(pidl); if (!pidl) pidl = &s_idlNULL; if (IsEqualGUID(clsid, CLSID_ShellDesktop)) { hr = SHBindToObjectEx(NULL, pidl, NULL, riid, ppv); } else { IPersistFolder* ppf; hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IPersistFolder, &ppf)); if (SUCCEEDED(hr)) { hr = ppf->Initialize(pidl); if (SUCCEEDED(hr)) { hr = ppf->QueryInterface(riid, ppv); } ppf->Release(); } } return hr; } HRESULT ILRootedBindToObject(LPCITEMIDLIST pidl, REFIID riid, void **ppv) { IShellFolder *psf; HRESULT hr = ILRootedBindToRoot(pidl, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { pidl = _ILNext(pidl); if (ILIsEmpty(pidl)) hr = psf->QueryInterface(riid, ppv); else hr = psf->BindToObject(pidl, NULL, riid, ppv); } return hr; } HRESULT ILRootedBindToParentFolder(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlChild) { // // there are three different cases to handle // // 1. Rooted pidl Alone // [ rooted id [ target pidl ] ] // return the parent folder of the target pidl // and return its last id in ppidlChild // // 2. Rooted pidl with One Child // [ rooted id [ target pidl ] ][ child id ] // return the rooted id as the parent folder // and the child id in ppidlChild // // 3. rooted pidl with many children // [ rooted id [ target pidl ] ][ parent id ][ child id ] // return rooted id bound to parent id as the folder // and the child id in ppidlchild // HRESULT hr; ASSERT(ILIsRooted(pidl)); // // if this is a rooted pidl and it is just the root // then we can bind to the target pidl of the root instead // if (ILIsEmpty(_ILNext(pidl))) { hr = SHBindToIDListParent(ILRootedFindIDList(pidl), riid, ppv, ppidlChild); } else { LPITEMIDLIST pidlParent = ILCloneParent(pidl); if (pidlParent) { hr = ILRootedBindToObject(pidlParent, riid, ppv); ILFree(pidlParent); } else hr = E_OUTOFMEMORY; if (ppidlChild) *ppidlChild = ILFindLastID(pidl); } return hr; } #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1]) #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0]) STDAPI_(LPITEMIDLIST) IDA_ILClone(LPIDA pida, UINT i) { if (i < pida->cidl) return ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, i)); return NULL; } STDAPI_(void) EnableOKButtonFromString(HWND hDlg, LPTSTR pszText) { BOOL bNonEmpty; PathRemoveBlanks(pszText); // REVIEW, should we not remove from the end of bNonEmpty = lstrlen(pszText); // Not a BOOL, but okay EnableWindow(GetDlgItem(hDlg, IDOK), bNonEmpty); if (bNonEmpty) { SendMessage(hDlg, DM_SETDEFID, IDOK, 0L); } } STDAPI_(void) EnableOKButtonFromID(HWND hDlg, int id) { TCHAR szText[MAX_PATH]; if (!GetDlgItemText(hDlg, id, szText, ARRAYSIZE(szText))) { szText[0] = 0; } EnableOKButtonFromString(hDlg, szText); } // // C-callable versions of the ATL string conversion functions. // STDAPI_(LPWSTR) SHA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars) { ASSERT(lpa != NULL); ASSERT(lpw != NULL); // verify that no illegal character present // since lpw was allocated based on the size of lpa // don't worry about the number of chars lpw[0] = '\0'; MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars); return lpw; } STDAPI_(LPSTR) SHW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars) { ASSERT(lpw != NULL); ASSERT(lpa != NULL); // verify that no illegal character present // since lpa was allocated based on the size of lpw // don't worry about the number of chars lpa[0] = '\0'; WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL); return lpa; } // // Helper functions for SHChangeMenuAsIDList // // See comment in declaration of SHChangeMenuAsIDList for caveats about // the pSender member. // // This is tricky because IE 5.0 shipped with a Win64-unfriendly version // of this notification, so we have to sniff the structure and see if // this is an IE 5.0 style notification or a new Win64 style notification. // If an IE 5.0 style notification, then it was not sent by us because // we send the new Win64-style notification. // STDAPI_(BOOL) SHChangeMenuWasSentByMe(void * self, LPCITEMIDLIST pidlNotify) { SHChangeMenuAsIDList UNALIGNED * pcmidl = (SHChangeMenuAsIDList UNALIGNED *)pidlNotify; return pcmidl->cb >= FIELD_OFFSET(SHChangeMenuAsIDList, cbZero) && pcmidl->pSender == (INT64)self && pcmidl->dwProcessID == GetCurrentProcessId(); } // // // Send out an extended event changenotify, using a SHChangeMenuAsIDList // as the pidl1 so recipients can identify whether they were the // sender or not. // // It's okay to pass self==NULL here. It means you don't care about // detecting whether it was sent by you or not. // STDAPI_(void) SHSendChangeMenuNotify(void * self, DWORD shcnee, DWORD shcnf, LPCITEMIDLIST pidl2) { SHChangeMenuAsIDList cmidl; cmidl.cb = FIELD_OFFSET(SHChangeMenuAsIDList, cbZero); cmidl.dwItem1 = shcnee; cmidl.pSender = (INT64)self; cmidl.dwProcessID = self ? GetCurrentProcessId() : 0; cmidl.cbZero = 0; // Nobody had better have specified a type; the type must be // SHCNF_IDLIST. ASSERT((shcnf & SHCNF_TYPE) == 0); SHChangeNotify(SHCNE_EXTENDED_EVENT, shcnf | SHCNF_IDLIST, (LPCITEMIDLIST)&cmidl, pidl2); } // Return FALSE if out of memory STDAPI_(BOOL) Pidl_Set(LPITEMIDLIST* ppidl, LPCITEMIDLIST pidl) { BOOL bRet = TRUE; LPITEMIDLIST pidlNew; ASSERT(IS_VALID_WRITE_PTR(ppidl, LPITEMIDLIST)); ASSERT(NULL == *ppidl || IS_VALID_PIDL(*ppidl)); ASSERT(NULL == pidl || IS_VALID_PIDL(pidl)); if (pidl) { pidlNew = ILClone(pidl); if (!pidlNew) { bRet = FALSE; // failed to clone the pidl (out of memory) } } else { pidlNew = NULL; } LPITEMIDLIST pidlToFree = (LPITEMIDLIST)InterlockedExchangePointer((void **)ppidl, (void *)pidlNew); if (pidlToFree) { ILFree(pidlToFree); } return bRet; } // this needs to be the last thing in the file that uses ILClone, because everywhere // else, ILClone becomes SafeILClone #undef ILClone STDAPI_(LPITEMIDLIST) SafeILClone(LPCITEMIDLIST pidl) { // the shell32 implementation of ILClone is different for win95 an ie4. // it doesnt check for NULL in the old version, but it does in the new... // so we need to always check return pidl ? ILClone(pidl) : NULL; } // // retrieves the UIObject interface for the specified full pidl. // STDAPI SHGetUIObjectFromFullPIDL(LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv) { *ppv = NULL; LPCITEMIDLIST pidlChild; IShellFolder* psf; HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv); psf->Release(); } return hr; } STDAPI LoadFromFileW(REFCLSID clsid, LPCWSTR pszFile, REFIID riid, void **ppv) { *ppv = NULL; IPersistFile *ppf; HRESULT hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IPersistFile, &ppf)); if (SUCCEEDED(hr)) { hr = ppf->Load(pszFile, STGM_READ); if (SUCCEEDED(hr)) hr = ppf->QueryInterface(riid, ppv); ppf->Release(); } return hr; } STDAPI LoadFromIDList(REFCLSID clsid, LPCITEMIDLIST pidl, REFIID riid, void **ppv) { *ppv = NULL; IPersistFolder *ppf; HRESULT hr = SHCoCreateInstanceAC(clsid, NULL, CLSCTX_INPROC, IID_PPV_ARG(IPersistFolder, &ppf)); if (SUCCEEDED(hr)) { hr = ppf->Initialize(pidl); if (SUCCEEDED(hr)) { hr = ppf->QueryInterface(riid, ppv); } ppf->Release(); } return hr; } // // This is a helper function for finding a specific verb's index in a context menu // STDAPI_(UINT) GetMenuIndexForCanonicalVerb(HMENU hMenu, IContextMenu *pcm, UINT idCmdFirst, LPCWSTR pwszVerb) { int cMenuItems = GetMenuItemCount(hMenu); for (int iItem = 0; iItem < cMenuItems; iItem++) { MENUITEMINFO mii = {0}; mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE | MIIM_ID; // IS_INTRESOURCE guards against mii.wID == -1 **and** against // buggy shell extensions which set their menu item IDs out of range. if (GetMenuItemInfo(hMenu, iItem, MF_BYPOSITION, &mii) && !(mii.fType & MFT_SEPARATOR) && IS_INTRESOURCE(mii.wID) && (mii.wID >= idCmdFirst)) { union { WCHAR szItemNameW[80]; char szItemNameA[80]; }; CHAR aszVerb[80]; // try both GCS_VERBA and GCS_VERBW in case it only supports one of them SHUnicodeToAnsi(pwszVerb, aszVerb, ARRAYSIZE(aszVerb)); if (SUCCEEDED(pcm->GetCommandString(mii.wID - idCmdFirst, GCS_VERBA, NULL, szItemNameA, ARRAYSIZE(szItemNameA)))) { if (StrCmpICA(szItemNameA, aszVerb) == 0) { break; // found it } } else { if (SUCCEEDED(pcm->GetCommandString(mii.wID - idCmdFirst, GCS_VERBW, NULL, (LPSTR)szItemNameW, ARRAYSIZE(szItemNameW))) && (StrCmpICW(szItemNameW, pwszVerb) == 0)) { break; // found it } } } } if (iItem == cMenuItems) { iItem = -1; // went through all the menuitems and didn't find it } return iItem; } // deal with GCS_VERBW/GCS_VERBA maddness STDAPI ContextMenu_GetCommandStringVerb(IContextMenu *pcm, UINT idCmd, LPWSTR pszVerb, int cchVerb) { // Ulead SmartSaver Pro has a 60 character verb, and // over writes out stack, ignoring the cch param and we fault. // so make sure this buffer is at least 60 chars TCHAR wszVerb[64]; wszVerb[0] = 0; HRESULT hr = pcm->GetCommandString(idCmd, GCS_VERBW, NULL, (LPSTR)wszVerb, ARRAYSIZE(wszVerb)); if (FAILED(hr)) { // be extra paranoid about requesting the ansi version -- we've // found IContextMenu implementations that return a UNICODE buffer // even though we ask for an ANSI string on NT systems -- hopefully // they will have answered the above request already, but just in // case let's not let them overrun our stack! char szVerbAnsi[128]; hr = pcm->GetCommandString(idCmd, GCS_VERBA, NULL, szVerbAnsi, ARRAYSIZE(szVerbAnsi) / 2); if (SUCCEEDED(hr)) { SHAnsiToUnicode(szVerbAnsi, wszVerb, ARRAYSIZE(wszVerb)); } } StrCpyNW(pszVerb, wszVerb, cchVerb); return hr; } // // Purpose: Deletes the menu item specified by name // // Parameters: pcm - Context menu interface // hpopup - Context menu handle // idFirst - Beginning of id range // pszCommand - Command to look for // STDAPI ContextMenu_DeleteCommandByName(IContextMenu *pcm, HMENU hpopup, UINT idFirst, LPCWSTR pszCommand) { UINT ipos = GetMenuIndexForCanonicalVerb(hpopup, pcm, idFirst, pszCommand); if (ipos != -1) { DeleteMenu(hpopup, ipos, MF_BYPOSITION); return S_OK; } else { return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); } } // // Helpers to banish STRRET's into the realm of darkness // STDAPI DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPTSTR psz, UINT cch) { *psz = 0; STRRET sr; HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr); if (SUCCEEDED(hr)) hr = StrRetToBuf(&sr, pidl, psz, cch); return hr; } STDAPI DisplayNameOfAsOLESTR(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPWSTR *ppsz) { *ppsz = NULL; STRRET sr; HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr); if (SUCCEEDED(hr)) hr = StrRetToStrW(&sr, pidl, ppsz); return hr; } // get the target pidl for a folder pidl. this deals with the case where a folder // is an alias to a real folder, Folder Shortcuts, etc. STDAPI SHGetTargetFolderIDList(LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidl) { *ppidl = NULL; // likely should ASSERT() that pidlFolder has SFGAO_FOLDER IShellLink *psl; HRESULT hr = SHGetUIObjectFromFullPIDL(pidlFolder, NULL, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { hr = psl->GetIDList(ppidl); psl->Release(); } // No its not a folder shortcut. Get the pidl normally. if (FAILED(hr)) hr = SHILClone(pidlFolder, ppidl); return hr; } // get the target folder for a folder pidl. this deals with the case where a folder // is an alias to a real folder, Folder Shortcuts, MyDocs, etc. STDAPI SHGetTargetFolderPathW(LPCITEMIDLIST pidlFolder, LPWSTR pszPath, UINT cchPath) { *pszPath = 0; LPITEMIDLIST pidlTarget; if (SUCCEEDED(SHGetTargetFolderIDList(pidlFolder, &pidlTarget))) { SHGetPathFromIDListW(pidlTarget, pszPath); // make sure it is a path ILFree(pidlTarget); } return *pszPath ? S_OK : E_FAIL; } STDAPI SHGetTargetFolderPathA(LPCITEMIDLIST pidlFolder, LPSTR pszPath, UINT cchPath) { *pszPath = 0; WCHAR szPath[MAX_PATH]; HRESULT hr = SHGetTargetFolderPathW(pidlFolder, szPath, ARRAYSIZE(szPath)); if (SUCCEEDED(hr)) SHAnsiToUnicode(pszPath, szPath, cchPath); return hr; } STDAPI SHBuildDisplayMachineName(LPCWSTR pszMachineName, LPCWSTR pszComment, LPWSTR pszDisplayName, DWORD cchDisplayName) { HRESULT hr = E_FAIL; if (pszComment && pszComment[0]) { // encorporate the comment into the display name LPCWSTR pszNoSlashes = SkipServerSlashes(pszMachineName); int i = wnsprintfW(pszDisplayName, cchDisplayName, L"%s (%s)", pszComment, pszNoSlashes); hr = (i < 0) ? E_FAIL : S_OK; } else { // Return failure here so netfldr can do smarter things to build a display name hr = E_FAIL; } return hr; } // create objects from registered under a key value, uses the per user per machine // reg services to do this. STDAPI CreateFromRegKey(LPCWSTR pszKey, LPCWSTR pszValue, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; WCHAR szCLSID[MAX_PATH]; DWORD cbSize = sizeof(szCLSID); if (SHRegGetUSValueW(pszKey, pszValue, NULL, szCLSID, &cbSize, FALSE, NULL, 0) == ERROR_SUCCESS) { CLSID clsid; if (GUIDFromString(szCLSID, &clsid)) { hr = SHCoCreateInstanceAC(clsid, NULL, CLSCTX_INPROC_SERVER, riid, ppv); } } return hr; } // // SHProcessMessagesUntilEvent: // // this executes message loop until an event or a timeout occurs // STDAPI_(DWORD) SHProcessMessagesUntilEventEx(HWND hwnd, HANDLE hEvent, DWORD dwTimeout, DWORD dwWakeMask) { DWORD dwEndTime = GetTickCount() + dwTimeout; LONG lWait = (LONG)dwTimeout; DWORD dwReturn; if (!hEvent && (dwTimeout == INFINITE)) { ASSERTMSG(FALSE, "SHProcessMessagesUntilEvent: caller passed a NULL hEvent and an INFINITE timeout!!"); return -1; } for (;;) { DWORD dwCount = hEvent ? 1 : 0; dwReturn = MsgWaitForMultipleObjects(dwCount, &hEvent, FALSE, lWait, dwWakeMask); // were we signalled or did we time out? if (dwReturn != (WAIT_OBJECT_0 + dwCount)) { break; } // we woke up because of messages. MSG msg; while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { ASSERT(msg.message != WM_QUIT); TranslateMessage(&msg); if (msg.message == WM_SETCURSOR) { SetCursor(LoadCursor(NULL, IDC_WAIT)); } else { DispatchMessage(&msg); } } // calculate new timeout value if (dwTimeout != INFINITE) { lWait = (LONG)dwEndTime - GetTickCount(); } } return dwReturn; } // deals with goofyness of IShellFolder::GetAttributesOf() including // in/out param issue // failures // goofy cast for 1 item case // masks off results to only return what you asked for STDAPI_(DWORD) SHGetAttributes(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwAttribs) { // like SHBindToObject, if psf is NULL, use absolute pidl LPCITEMIDLIST pidlChild; if (!psf) { SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); } else { psf->AddRef(); pidlChild = pidl; } DWORD dw = 0; if (psf) { dw = dwAttribs; dw = SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidlChild, &dw)) ? (dwAttribs & dw) : 0; if ((dw & SFGAO_FOLDER) && (dw & SFGAO_CANMONIKER) && !(dw & SFGAO_STORAGEANCESTOR) && (dwAttribs & SFGAO_STORAGEANCESTOR)) { if (OBJCOMPATF_NEEDSSTORAGEANCESTOR & SHGetObjectCompatFlags(psf, NULL)) { // switch SFGAO_CANMONIKER -> SFGAO_STORAGEANCESTOR dw |= SFGAO_STORAGEANCESTOR; dw &= ~SFGAO_CANMONIKER; } } } if (psf) { psf->Release(); } return dw; } //=========================================================================== // IDLARRAY stuff //=========================================================================== STDAPI_(HIDA) HIDA_Create(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl) { UINT offset = sizeof(CIDA) + sizeof(UINT) * cidl; UINT cbTotal = offset + ILGetSize(pidlFolder); for (UINT i = 0; icidl = cidl; for (i = 0, pidlNext = pidlFolder; ; pidlNext = apidl[i++]) { UINT cbSize = ILGetSize(pidlNext); pida->aoffset[i] = offset; CopyMemory(((LPBYTE)pida) + offset, pidlNext, cbSize); offset += cbSize; ASSERT(ILGetSize(HIDA_GetPIDLItem(pida,i-1)) == cbSize); if (i == cidl) break; } ASSERT(offset == cbTotal); } return hida; } STDAPI_(UINT) HIDA_GetCount(HIDA hida) { UINT count = 0; LPIDA pida = (LPIDA)GlobalLock(hida); if (pida) { count = pida->cidl; GlobalUnlock(hida); } return count; } STDAPI_(UINT) HIDA_GetIDList(HIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax) { LPIDA pida = (LPIDA)GlobalLock(hida); if (pida) { LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida); LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem(pida, i); UINT cbFolder = ILGetSize(pidlFolder) - sizeof(USHORT); UINT cbItem = ILGetSize(pidlItem); if (cbMax < cbFolder+cbItem) { if (pidlOut) pidlOut->mkid.cb = 0; } else { MoveMemory(pidlOut, pidlFolder, cbFolder); MoveMemory(((LPBYTE)pidlOut) + cbFolder, pidlItem, cbItem); } GlobalUnlock(hida); return cbFolder + cbItem; } return 0; } STDAPI_(BOOL) PathIsImage(LPCTSTR pszFile) { BOOL fPicture = FALSE; LPTSTR pszExt = PathFindExtension(pszFile); if (pszExt) { // there's no ASSOCSTR_PERCEIVED so pick it up from the registry. TCHAR szPerceivedType[MAX_PATH]; DWORD cb = ARRAYSIZE(szPerceivedType) * sizeof(TCHAR); if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, pszExt, TEXT("PerceivedType"), NULL, szPerceivedType, &cb)) { fPicture = (StrCmpI(szPerceivedType, TEXT("image")) == 0); } } return fPicture; } // helper function to create a stream or storage in a storage. HRESULT CreateStreamOrStorage(IStorage * pStorageParent, LPCTSTR pszName, REFIID riid, void **ppv) { DWORD grfModeCreated = STGM_READWRITE; HRESULT hr = E_INVALIDARG; if (IsEqualGUID(riid, IID_IStorage)) { IStorage * pStorageCreated; hr = pStorageParent->CreateStorage(pszName, grfModeCreated, 0, 0, &pStorageCreated); if (SUCCEEDED(hr)) { hr = pStorageParent->Commit(STGC_DEFAULT); *ppv = pStorageCreated; } } else if (IsEqualGUID(riid, IID_IStream)) { IStream * pStreamCreated; hr = pStorageParent->CreateStream(pszName, grfModeCreated, 0, 0, &pStreamCreated); if (SUCCEEDED(hr)) { hr = pStorageParent->Commit(STGC_DEFAULT); *ppv = pStreamCreated; } } return hr; } // same as PathMakeUniqueNameEx but it works on storages. // Note: LFN only! STDAPI StgMakeUniqueNameWithCount(IStorage *pStorageParent, LPCWSTR pszTemplate, int iMinLong, REFIID riid, void **ppv) { HRESULT hr = E_INVALIDARG; RIPMSG(pszTemplate && IS_VALID_STRING_PTR(pszTemplate, -1) && lstrlen(pszTemplate)<(MAX_PATH-6), "StgMakeUniqueNameWithCount: invalid pszTemplate"); if (pszTemplate && lstrlen(pszTemplate)<(MAX_PATH-6)) // -6 for " (999)" { WCHAR szBuffer[MAX_PATH]; WCHAR szFormat[MAX_PATH]; int cchStem; // Set up: // cchStem : length of pszTemplate we're going to use w/o wsprintf // szFormat : format string to wsprintf the number with, catenates on to pszTemplate[0..cchStem] // Has template already been uniquified? // LPWSTR pszRest = StrChr(pszTemplate, L'('); while (pszRest) { // First validate that this is the right one LPWSTR pszEndUniq = CharNext(pszRest); while (*pszEndUniq && *pszEndUniq >= L'0' && *pszEndUniq <= L'9') { pszEndUniq++; } if (*pszEndUniq == L')') break; // We have the right one! pszRest = StrChr(CharNext(pszRest), L'('); } if (!pszRest) { // if no (, then tack it on at the end. (but before the extension) // eg. New Link yields New Link (1) pszRest = PathFindExtension(pszTemplate); cchStem = (int)(pszRest - pszTemplate); wnsprintf(szFormat, ARRAYSIZE(szFormat), L" (%%d)%s", pszRest ? pszRest : L""); } else { // Template has been uniquified, remove uniquing digits // eg. New Link (1) yields New Link (2) // pszRest++; // step over the ( cchStem = (int) (pszRest - pszTemplate); while (*pszRest && *pszRest >= L'0' && *pszRest <= L'9') { pszRest++; } // we are guaranteed enough room because we don't include // the stuff before the # in this format wnsprintf(szFormat, ARRAYSIZE(szFormat), L"%%d%s", pszRest); } if (cchStem < ARRAYSIZE(szBuffer)) { // copy the fixed portion into the buffer // StrCpyN(szBuffer, pszTemplate, cchStem+1); // Iterate on the uniquifying szFormat portion until we find a unique name: // LPTSTR pszDigit = szBuffer + cchStem; hr = STG_E_FILEALREADYEXISTS; for (int i = iMinLong; (i < 1000) && (STG_E_FILEALREADYEXISTS == hr); i++) { wnsprintf(pszDigit, ARRAYSIZE(szBuffer) - cchStem, szFormat, i); // okay, we have the unique name, so create it in the storage. hr = CreateStreamOrStorage(pStorageParent, szBuffer, riid, ppv); } } else { hr = E_INVALIDARG; } } return hr; } STDAPI StgMakeUniqueName(IStorage *pStorageParent, LPCTSTR pszFileSpec, REFIID riid, void **ppv) { HRESULT hr = S_OK; TCHAR szTemp[MAX_PATH]; LPTSTR psz; LPTSTR pszNew; // try it without the ( if there's a space after it psz = StrChr(pszFileSpec, L'('); while (psz) { if (*(CharNext(psz)) == L')') break; psz = StrChr(CharNext(psz), L'('); } if (psz) { // We have the (). See if we have either x () y or x ().y in which case // we probably want to get rid of one of the blanks... int ichSkip = 2; LPTSTR pszT = CharPrev(pszFileSpec, psz); if (*pszT == L' ') { ichSkip = 3; psz = pszT; } StrCpyN(szTemp, pszFileSpec, ARRAYSIZE(szTemp)); SIZE_T cch = psz - pszFileSpec; pszNew = szTemp + cch; if (cch < ARRAYSIZE(szTemp)) { StrCpyN(pszNew, psz + ichSkip, ARRAYSIZE(szTemp) - (int)cch); } else { hr = E_FAIL; } } else { // 1taro registers its document with '/'. if (psz=StrChr(pszFileSpec, '/')) { LPTSTR pszT = CharNext(psz); pszNew = szTemp; StrCpyN(szTemp, pszFileSpec, ARRAYSIZE(szTemp)); SIZE_T cch = psz - pszFileSpec; pszNew = szTemp + cch; if (cch < ARRAYSIZE(szTemp)) { StrCpyN(pszNew, pszT, ARRAYSIZE(szTemp) - (int)cch); } else { hr = E_FAIL; } } else { if (lstrlen(pszFileSpec) < ARRAYSIZE(szTemp)) { StrCpyN(szTemp, pszFileSpec, ARRAYSIZE(szTemp)); } else { hr = E_FAIL; } } } if (SUCCEEDED(hr)) { hr = CreateStreamOrStorage(pStorageParent, szTemp, riid, ppv); } if (FAILED(hr)) { hr = StgMakeUniqueNameWithCount(pStorageParent, pszFileSpec, 2, riid, ppv); } return hr; } STDAPI SHInvokeCommandOnPidl(HWND hwnd, IUnknown* punk, LPCITEMIDLIST pidl, UINT uFlags, LPCSTR lpVerb) { IShellFolder* psf; LPCITEMIDLIST pidlChild; HRESULT hr = SHBindToFolderIDListParent(NULL, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { hr = SHInvokeCommandOnPidlArray(hwnd, punk, psf, &pidlChild, 1, uFlags, lpVerb); psf->Release(); } return hr; } STDAPI SHInvokeCommandOnPidlArray(HWND hwnd, IUnknown* punk, IShellFolder* psf, LPCITEMIDLIST *ppidlItem, UINT cItems, UINT uFlags, LPCSTR lpVerb) { IContextMenu *pcm; HRESULT hr = psf->GetUIObjectOf(hwnd, cItems, ppidlItem, IID_X_PPV_ARG(IContextMenu, 0, &pcm)); if (SUCCEEDED(hr) && pcm) { hr = SHInvokeCommandOnContextMenu(hwnd, punk, pcm, uFlags, lpVerb); pcm->Release(); } return hr; } STDAPI SHInvokeCommandOnDataObject(HWND hwnd, IUnknown* punk, IDataObject* pdtobj, UINT uFlags, LPCSTR pszVerb) { HRESULT hr = E_FAIL; STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { IShellFolder *psf; LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1); if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlParent, &psf)))) { LPCITEMIDLIST *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, pida->cidl * sizeof(LPCITEMIDLIST)); if (ppidl) { for (UINT i = 0; i < pida->cidl; i++) { ppidl[i] = IDA_GetIDListPtr(pida, i); } hr = SHInvokeCommandOnPidlArray(hwnd, punk, psf, ppidl, pida->cidl, uFlags, pszVerb); LocalFree(ppidl); } psf->Release(); } HIDA_ReleaseStgMedium(pida, &medium); } return hr; } STDAPI_(LPCITEMIDLIST) IDA_GetIDListPtr(LPIDA pida, UINT i) { LPCITEMIDLIST pidl = NULL; if (pida && ((i == (UINT)-1) || i < pida->cidl)) { pidl = HIDA_GetPIDLItem(pida, i); } return pidl; } STDAPI IUnknown_DragEnter(IUnknown* punk, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hr = E_FAIL; if (punk) { IDropTarget* pdt; hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect); pdt->Release(); } } if (FAILED(hr)) *pdwEffect = DROPEFFECT_NONE; return hr; } STDAPI IUnknown_DragOver(IUnknown* punk, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hr = E_FAIL; if (punk) { IDropTarget* pdt; hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = pdt->DragOver(grfKeyState, pt, pdwEffect); pdt->Release(); } } if (FAILED(hr)) *pdwEffect = DROPEFFECT_NONE; return hr; } STDAPI IUnknown_DragLeave(IUnknown* punk) { HRESULT hr = E_FAIL; if (punk) { IDropTarget* pdt; hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = pdt->DragLeave(); pdt->Release(); } } return hr; } STDAPI IUnknown_Drop(IUnknown* punk, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hr = E_FAIL; if (punk) { IDropTarget* pdt; hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = pdt->Drop(pdtobj, grfKeyState, pt, pdwEffect); pdt->Release(); } } if (FAILED(hr)) *pdwEffect = DROPEFFECT_NONE; return hr; } STDAPI_(BOOL) ShouldNavigateInIE(LPCWSTR pszUrl) { // Default to navigating in IE. The idea here is that this // changes the existing behavior the least. BOOL fResult = TRUE; // first, crack the URL WCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH]; DWORD cchScheme = ARRAYSIZE(szScheme); if (SUCCEEDED(UrlGetPartW(pszUrl, szScheme, &cchScheme, URL_PART_SCHEME, 0))) { // if it is an http:, https:, file:, or ftp: URL then look up the association // all other pluggable protocols go to IE if ((0 == StrCmpIW(szScheme, L"http")) || (0 == StrCmpIW(szScheme, L"ftp")) || (0 == StrCmpIW(szScheme, L"file")) || (0 == StrCmpIW(szScheme, L"https"))) { WCHAR szExecutable[MAX_PATH * 2]; DWORD cchExecutable = ARRAYSIZE(szExecutable); WCHAR szFile[MAX_PATH]; LPCWSTR pszQuery = szScheme; if (0 == StrCmpIW(szScheme, L"file")) { DWORD cchFile = ARRAYSIZE(szFile); if (SUCCEEDED(PathCreateFromUrl(pszUrl, szFile, &cchFile, 0))) { pszQuery = PathFindExtension(szFile); } } if (SUCCEEDED(AssocQueryStringW(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, pszQuery, NULL, szExecutable, &cchExecutable))) { if (!StrStrIW(szExecutable, L"iexplore")) { // IE isn't the default for the verb so we'll ShellExecute it. fResult = FALSE; } } } } return fResult; } STDAPI_(BOOL) IsDesktopFrame(IUnknown *punk) { IUnknown *punkDesktop; HRESULT hr = IUnknown_QueryService(punk, SID_SShellDesktop, SID_SShellDesktop, (void **)&punkDesktop); BOOL fResult; if (SUCCEEDED(hr)) { punkDesktop->Release(); fResult = TRUE; } else { fResult = FALSE; } return fResult; }