#include "shellprv.h" //#include "mkhelp.h" #include "urlmon.h" #include "ids.h" class CBSCLocalCopyHelper : public IBindStatusCallback, public IAuthenticate { public: CBSCLocalCopyHelper(IBindCtx *pbc, BOOL fWebfolders); // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef () ; STDMETHODIMP_(ULONG) Release (); // *** IAuthenticate *** virtual STDMETHODIMP Authenticate( HWND *phwnd, LPWSTR *pszUsername, LPWSTR *pszPassword); // *** IBindStatusCallback *** virtual STDMETHODIMP OnStartBinding( /* [in] */ DWORD grfBSCOption, /* [in] */ IBinding *pib); virtual STDMETHODIMP GetPriority( /* [out] */ LONG *pnPriority); virtual STDMETHODIMP OnLowResource( /* [in] */ DWORD reserved); virtual STDMETHODIMP OnProgress( /* [in] */ ULONG ulProgress, /* [in] */ ULONG ulProgressMax, /* [in] */ ULONG ulStatusCode, /* [in] */ LPCWSTR szStatusText); virtual STDMETHODIMP OnStopBinding( /* [in] */ HRESULT hresult, /* [in] */ LPCWSTR szError); virtual STDMETHODIMP GetBindInfo( /* [out] */ DWORD *grfBINDINFOF, /* [unique][out][in] */ BINDINFO *pbindinfo); virtual STDMETHODIMP OnDataAvailable( /* [in] */ DWORD grfBSCF, /* [in] */ DWORD dwSize, /* [in] */ FORMATETC *pformatetc, /* [in] */ STGMEDIUM *pstgmed); virtual STDMETHODIMP OnObjectAvailable( /* [in] */ REFIID riid, /* [iid_is][in] */ IUnknown *punk); protected: ~CBSCLocalCopyHelper(); long _cRef; IBinding *_pib; IProgressDialog *_pdlg; HWND _hwnd; BOOL _fRosebudMagic; }; CBSCLocalCopyHelper::CBSCLocalCopyHelper(IBindCtx *pbc, BOOL fWebfolders) : _cRef(1) , _fRosebudMagic(fWebfolders) { // we should use the pbc to // get our simpler uiprogress // interface. but for now // we will do nothing } CBSCLocalCopyHelper::~CBSCLocalCopyHelper() { ATOMICRELEASE(_pib); ATOMICRELEASE(_pdlg); // NOTE dont need to release _ppstm because we dont own it } STDMETHODIMP CBSCLocalCopyHelper::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CBSCLocalCopyHelper, IBindStatusCallback), QITABENT(CBSCLocalCopyHelper, IAuthenticate), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CBSCLocalCopyHelper::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CBSCLocalCopyHelper::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDMETHODIMP CBSCLocalCopyHelper::Authenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword) { if (ppszUsername) *ppszUsername = NULL; if (ppszPassword) *ppszPassword = NULL; *phwnd = GetLastActivePopup(_hwnd); return *phwnd ? S_OK : E_FAIL; } STDMETHODIMP CBSCLocalCopyHelper::OnStartBinding(DWORD dwReserved,IBinding *pib) { ATOMICRELEASE(_pib); if (pib) { pib->AddRef(); _pib = pib; } if (_pdlg) { WCHAR sz[MAX_PATH]; // we are starting out here _pdlg->Timer(PDTIMER_RESET, NULL); _pdlg->SetProgress(0, 0); LoadStringW(HINST_THISDLL, IDS_ACCESSINGMONIKER, sz, ARRAYSIZE(sz)); _pdlg->SetLine(1, sz, FALSE, NULL); } return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::GetPriority(LONG *pnPriority) { if (pnPriority) { // we are a blocking UI thread *pnPriority = THREAD_PRIORITY_ABOVE_NORMAL; } return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::OnLowResource(DWORD reserved) { return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pszStatusText) { HRESULT hr = S_OK; // handle UI udpates if (_pdlg) { if (_pdlg->HasUserCancelled()) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } if (ulProgressMax) { _pdlg->SetProgress(ulProgress, ulProgressMax); } if (pszStatusText) _pdlg->SetLine(1, pszStatusText, FALSE, NULL); } return hr; } STDMETHODIMP CBSCLocalCopyHelper::OnStopBinding(HRESULT hresult, LPCWSTR szError) { // handle something ATOMICRELEASE(_pib); return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::GetBindInfo(DWORD *grfBINDINFOF, BINDINFO *pbindinfo) { if (_fRosebudMagic && pbindinfo) { // this is the magic number that says its ok for URLMON to use DAV/rosebud/webfolders. // we dont need this during download and in fact if we // set it, we may not be able to retrieve the resource. // we coudl do some kind of check on the moniker to verify the clsid // comes from URLMON. right now this is how office handles // all of its requests so we do too. pbindinfo->dwOptions = 1; } if (grfBINDINFOF) { *grfBINDINFOF = BINDF_GETFROMCACHE_IF_NET_FAIL | BINDF_GETNEWESTVERSION; } return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) { #if 0 // we might need this, but right now we are sync // so we get the stream back in the BindToStorage() if (grfBSCF & BSCF_LASTDATANOTIFICATION && pformatetc && pformatetc->tymed == TYMED_ISTREAM && pstgmed && pstgmed->pstm) { if (_ppstm) { pstgmed->pstm->AddRef(); *_ppstm = pstgmed->pstm; } } #endif return S_OK; } STDMETHODIMP CBSCLocalCopyHelper::OnObjectAvailable(REFIID riid, IUnknown *punk) { return E_UNEXPECTED; } HRESULT _CreateUrlmonBindCtx(IBindCtx *pbcIn, BOOL fWebfolders, IBindCtx **ppbc, IBindStatusCallback **ppbsc) { IBindCtx *pbc; HRESULT hr = CreateBindCtx(0, &pbc); *ppbc = NULL; *ppbsc = NULL; if (SUCCEEDED(hr)) { IBindStatusCallback *pbsc = (IBindStatusCallback *) new CBSCLocalCopyHelper(pbcIn, fWebfolders); if (pbsc) { // maybe we should attach it to the existing // pbc, but for now we will create a new one. hr = RegisterBindStatusCallback(pbc, pbsc, NULL, 0); if (SUCCEEDED(hr)) { BIND_OPTS bo = {0}; bo.cbStruct = SIZEOF(bo); bo.grfMode = BindCtx_GetMode(pbcIn, STGM_READ); // // on webfolders, (and possibly other URLMON // monikers), if you are attempting to create a // writable stream you also need to pass STGM_CREATE // even if the file you are writing to already exists. // if (bo.grfMode & (STGM_WRITE | STGM_READWRITE)) bo.grfMode |= STGM_CREATE; hr = pbc->SetBindOptions(&bo); } } else hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) { *ppbc = pbc; *ppbsc = pbsc; } else { pbc->Release(); if (pbsc) pbsc->Release(); } } return hr; } static const GUID CLSID_WEBFOLDERS = // {BDEADF00-C265-11D0-BCED-00A0C90AB50F} { 0xBDEADF00, 0xC265, 0x11D0, { 0xBC, 0xED, 0x00, 0xA0, 0xC9, 0x0A, 0xB5, 0x0F} }; BOOL _IsWebfolders(IShellItem *psi) { BOOL fRet = FALSE; IShellItem *psiParent; HRESULT hr = psi->GetParent(&psiParent); if (SUCCEEDED(hr)) { IShellFolder *psf; SFGAOF flags = SFGAO_LINK; if (SUCCEEDED(psiParent->GetAttributes(flags, &flags)) && (flags & SFGAO_LINK)) { // this is a folder shortcut that needs derefing IShellItem *psiTarget; hr = psiParent->BindToHandler(NULL, BHID_LinkTargetItem, IID_PPV_ARG(IShellItem, &psiTarget)); if (SUCCEEDED(hr)) { // switcheroo psiParent->Release(); psiParent = psiTarget; } } if (SUCCEEDED(hr)) { hr = psiParent->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { CLSID clsid; if (SUCCEEDED(IUnknown_GetClassID(psf, &clsid))) fRet = IsEqualGUID(clsid, CLSID_WEBFOLDERS); psf->Release(); } } psiParent->Release(); } return fRet; } HRESULT _CreateStorageHelper(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) { IMoniker *pmk; HRESULT hr = psi->BindToHandler(pbc, BHID_SFObject, IID_PPV_ARG(IMoniker, &pmk)); if (SUCCEEDED(hr)) { IBindCtx *pbcMk; IBindStatusCallback *pbsc; hr = _CreateUrlmonBindCtx(pbc, _IsWebfolders(psi), &pbcMk, &pbsc); if (SUCCEEDED(hr)) { hr = pmk->BindToStorage(pbcMk, NULL, riid, ppv); // urlmon + ftp url can cause this. remove when 3140245 is fixed if (SUCCEEDED(hr) && NULL == *ppv) hr = E_FAIL; RevokeBindStatusCallback(pbcMk, pbsc); pbcMk->Release(); pbsc->Release(); } } return hr; } #if 0 // not needed right now LPCWSTR CLocalCopyHelper::_GetTitle(void) { if (!*_szTitle) { WCHAR sz[MAX_PATH]; DWORD cch = ARRAYSIZE(_szTitle); GetModuleFileNameW(NULL, sz, ARRAYSIZE(sz)); if (FAILED(AssocQueryStringW(ASSOCF_INIT_BYEXENAME | ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, sz, NULL, _szTitle, &cch))) StrCpyNW(_szTitle, PathFindFileNameW(sz), ARRAYSIZE(_szTitle)); } return _szTitle; } void CLocalCopyHelper::_InitUI(MKHELPF flags, IProgressDialog **ppdlg, HWND *phwnd, LPCWSTR pszTitle) { *ppdlg = NULL; if (flags & MKHELPF_NOUI) { *phwnd = NULL; return; } if (!(flags & MKHELPF_NOPROGRESSUI) && *phwnd) { IProgressDialog *pdlg; if(SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IProgressDialog, &pdlg)))) { if (!pszTitle) pszTitle = _GetTitle(); pdlg->SetTitle(pszTitle); pdlg->SetAnimation(g_hinst, IDA_FILECOPY); pdlg->SetLine(2, _pszName, TRUE, NULL); if (SUCCEEDED(pdlg->StartProgressDialog(*phwnd, NULL, PROGDLG_MODAL | PROGDLG_AUTOTIME, NULL))) *ppdlg = pdlg; else pdlg->Release(); } } } #endif EXTERN_C WINSHELLAPI HRESULT STDAPICALLTYPE SHCopyMonikerToTemp(IMoniker *pmk, LPCWSTR pszIn, LPWSTR pszOut, int cchOut) { // REMOVE this as soon as ComDlg32 is updated return E_NOTIMPL; }