|
|
#include "shellprv.h"
#pragma hdrstop
#include "ids.h"
#include "defview.h"
#include "datautil.h"
#include <cowsite.h> // 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<LPTSTR *>(&pei->lpDirectory), szBuf); }
if (SUCCEEDED(psl->GetArguments(szBuf, ARRAYSIZE(szBuf))) && szBuf[0]) { Str_SetPtr(const_cast<LPTSTR *>(&pei->lpParameters), szBuf); }
psl->Release(); }
}
void CExeDropTarget::_CleanupSEIFromLinkSite(SHELLEXECUTEINFO *pei) { Str_SetPtr(const_cast<LPTSTR *>(&pei->lpDirectory), NULL); Str_SetPtr(const_cast<LPTSTR *>(&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; }
|