#include "shellprv.h" #pragma hdrstop #include "ids.h" #include "defview.h" #include "datautil.h" #include // base class for IObjectWithSite #include "idlcomm.h" // shlexec.c STDAPI_(BOOL) DoesAppWantUrl(LPCTSTR pszFullPathToApp); // drop target impl for .exe files class CExeDropTarget : public IDropTarget, IPersistFile, CObjectWithSite { public: // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IDropTarget STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragLeave(); STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); // IPersist STDMETHOD(GetClassID)(CLSID *pClassID); // IPersistFile STDMETHOD(IsDirty)(void); STDMETHOD(Load)(LPCOLESTR pszFileName, DWORD dwMode); STDMETHOD(Save)(LPCOLESTR pszFileName, BOOL fRemember); STDMETHOD(SaveCompleted)(LPCOLESTR pszFileName); STDMETHOD(GetCurFile)(LPOLESTR *ppszFileName); // IObjectWithSite // STDMETHOD(SetSite)(IUnknown *punkSite); // STDMETHOD(GetSite)(REFIID riid, void **ppvSite); CExeDropTarget(); private: ~CExeDropTarget(); void _FillSEIFromLinkSite(SHELLEXECUTEINFO *pei); void _CleanupSEIFromLinkSite(SHELLEXECUTEINFO *pei); LONG _cRef; DWORD _dwEffectLast; DWORD _grfKeyStateLast; TCHAR _szFile[MAX_PATH]; }; CExeDropTarget::CExeDropTarget() : _cRef(1) { } CExeDropTarget::~CExeDropTarget() { } STDMETHODIMP CExeDropTarget::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CExeDropTarget, IDropTarget), QITABENT(CExeDropTarget, IPersistFile), QITABENTMULTI(CExeDropTarget, IPersist, IPersistFile), QITABENT(CExeDropTarget, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CExeDropTarget::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CExeDropTarget::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CExeDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if ((S_OK == pdtobj->QueryGetData(&fmte)) || (S_OK == DataObj_GetShellURL(pdtobj, NULL, NULL))) { *pdwEffect &= (DROPEFFECT_COPY | DROPEFFECT_LINK); } else *pdwEffect = 0; _dwEffectLast = *pdwEffect; _grfKeyStateLast = grfKeyState; return S_OK; } STDMETHODIMP CExeDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { *pdwEffect = _dwEffectLast; _grfKeyStateLast = grfKeyState; return S_OK; } STDMETHODIMP CExeDropTarget::DragLeave() { return S_OK; } // // See if we were created from a shortcut. If so, then pull the exec // parameters from the shortcut. // void CExeDropTarget::_FillSEIFromLinkSite(SHELLEXECUTEINFO *pei) { ASSERT(pei->lpParameters == NULL); ASSERT(pei->lpDirectory == NULL); IShellLink *psl; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_LinkSite, IID_PPV_ARG(IShellLink, &psl)))) { TCHAR szBuf[MAX_PATH]; psl->GetShowCmd(&pei->nShow); // Hotkeys are annoying because IShellLink::GetHotkey uses a // WORD as the hotkey, but SHELLEXECUTEINFO uses a DWORD. WORD wHotkey; if (SUCCEEDED(psl->GetHotkey(&wHotkey))) { pei->dwHotKey = wHotkey; pei->fMask |= SEE_MASK_HOTKEY; } if (SUCCEEDED(psl->GetWorkingDirectory(szBuf, ARRAYSIZE(szBuf))) && szBuf[0]) { Str_SetPtr(const_cast(&pei->lpDirectory), szBuf); } if (SUCCEEDED(psl->GetArguments(szBuf, ARRAYSIZE(szBuf))) && szBuf[0]) { Str_SetPtr(const_cast(&pei->lpParameters), szBuf); } psl->Release(); } } void CExeDropTarget::_CleanupSEIFromLinkSite(SHELLEXECUTEINFO *pei) { Str_SetPtr(const_cast(&pei->lpDirectory), NULL); Str_SetPtr(const_cast(&pei->lpParameters), NULL); } BOOL GetAppDropTarget(LPCTSTR pszPath, CLSID *pclsid) { TCHAR sz[MAX_PATH]; // NOTE this assumes that this is a path to the exe // and not a command line PathToAppPathKey(pszPath, sz, ARRAYSIZE(sz)); TCHAR szClsid[64]; DWORD cb = sizeof(szClsid); return (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("DropTarget"), NULL, szClsid, &cb)) && GUIDFromString(szClsid, pclsid); } STDMETHODIMP CExeDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { DWORD dwEffectPerformed = 0; if (!(_grfKeyStateLast & MK_LBUTTON)) { HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_DROPONEXE); if (hmenu) { HWND hwnd; IUnknown_GetWindow(_punkSite, &hwnd); UINT idCmd = SHTrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, NULL); DestroyMenu(hmenu); if (idCmd != DDIDM_COPY) { *pdwEffect = 0; // canceled } } } if (*pdwEffect) { CLSID clsidDropTarget; if (GetAppDropTarget(_szFile, &clsidDropTarget)) { if (SUCCEEDED(SHSimulateDropOnClsid(clsidDropTarget, _punkSite, pdtobj))) { dwEffectPerformed = DROPEFFECT_COPY; // what we did } } else { SHELLEXECUTEINFO ei = { sizeof(ei), 0, NULL, NULL, _szFile, NULL, NULL, SW_SHOWNORMAL, NULL }; _FillSEIFromLinkSite(&ei); LPCTSTR pszLinkParams = ei.lpParameters; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; HRESULT hr = pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hr)) { TCHAR szPath[MAX_PATH]; int cchParam = ei.lpParameters ? lstrlen(ei.lpParameters) + 1 : 0; BOOL fLFNAware = App_IsLFNAware(_szFile); for (UINT i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++) { if (fLFNAware) PathQuoteSpaces(szPath); else GetShortPathName(szPath, szPath, ARRAYSIZE(szPath)); cchParam += lstrlen(szPath) + 2; // space and NULL } if (cchParam) { LPTSTR pszParam = (LPTSTR)LocalAlloc(LPTR, cchParam * sizeof(*pszParam)); if (pszParam) { // If the link had parameters, then put our filenames after // the parameters (with an intervening space) if (ei.lpParameters) { StrCpyN(pszParam, ei.lpParameters, cchParam); StrCatBuff(pszParam, c_szSpace, cchParam); } for (i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++) { if (fLFNAware) PathQuoteSpaces(szPath); else GetShortPathName(szPath, szPath, ARRAYSIZE(szPath)); if (i > 0) StrCatBuff(pszParam, c_szSpace, cchParam); StrCatBuff(pszParam, szPath, cchParam); } ei.lpParameters = pszParam; // all shellexec info comes from stuff thats in the dataobject or already on disk -- // no getting around it, and the args are quoted properly or put into short path names. ShellExecuteEx(&ei); LocalFree((HLOCAL)pszParam); dwEffectPerformed = DROPEFFECT_COPY; // what we did } } ReleaseStgMedium(&medium); } else { LPCSTR pszURL; if (SUCCEEDED(DataObj_GetShellURL(pdtobj, &medium, &pszURL))) { if (DoesAppWantUrl(_szFile)) { TCHAR szURL[INTERNET_MAX_URL_LENGTH]; SHAnsiToTChar(pszURL, szURL, ARRAYSIZE(szURL)); ei.lpParameters = szURL; // all shellexec info comes from stuff thats in the dataobject or already on disk ShellExecuteEx(&ei); dwEffectPerformed = DROPEFFECT_LINK; // what we did } ReleaseStgMediumHGLOBAL(NULL, &medium); } } // The process of building the ShellExecuteEx parameters may have // messed up the ei.lpParameters, so put the original back so the // cleanup function won't get confused. ei.lpParameters = pszLinkParams; _CleanupSEIFromLinkSite(&ei); } *pdwEffect = dwEffectPerformed; } return S_OK; } STDMETHODIMP CExeDropTarget::GetClassID(CLSID *pClassID) { *pClassID = CLSID_ExeDropTarget; return S_OK; } STDMETHODIMP CExeDropTarget::IsDirty(void) { return S_OK; // no } STDMETHODIMP CExeDropTarget::Load(LPCOLESTR pszFileName, DWORD dwMode) { SHUnicodeToTChar(pszFileName, _szFile, ARRAYSIZE(_szFile)); return S_OK; } STDMETHODIMP CExeDropTarget::Save(LPCOLESTR pszFileName, BOOL fRemember) { return S_OK; } STDMETHODIMP CExeDropTarget::SaveCompleted(LPCOLESTR pszFileName) { return S_OK; } STDMETHODIMP CExeDropTarget::GetCurFile(LPOLESTR *ppszFileName) { *ppszFileName = NULL; return E_NOTIMPL; } STDAPI CExeDropTarget_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CExeDropTarget* pdt = new CExeDropTarget(); if (pdt) { hr = pdt->QueryInterface(riid, ppv); pdt->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; }