Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
10 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "ids.h"
  4. #include "defview.h"
  5. #include "datautil.h"
  6. #include <cowsite.h> // base class for IObjectWithSite
  7. #include "idlcomm.h"
  8. // shlexec.c
  9. STDAPI_(BOOL) DoesAppWantUrl(LPCTSTR pszFullPathToApp);
  10. // drop target impl for .exe files
  11. class CExeDropTarget : public IDropTarget, IPersistFile, CObjectWithSite
  12. {
  13. public:
  14. // IUnknown
  15. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  16. STDMETHOD_(ULONG, AddRef)();
  17. STDMETHOD_(ULONG, Release)();
  18. // IDropTarget
  19. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  20. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  21. STDMETHODIMP DragLeave();
  22. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  23. // IPersist
  24. STDMETHOD(GetClassID)(CLSID *pClassID);
  25. // IPersistFile
  26. STDMETHOD(IsDirty)(void);
  27. STDMETHOD(Load)(LPCOLESTR pszFileName, DWORD dwMode);
  28. STDMETHOD(Save)(LPCOLESTR pszFileName, BOOL fRemember);
  29. STDMETHOD(SaveCompleted)(LPCOLESTR pszFileName);
  30. STDMETHOD(GetCurFile)(LPOLESTR *ppszFileName);
  31. // IObjectWithSite
  32. // STDMETHOD(SetSite)(IUnknown *punkSite);
  33. // STDMETHOD(GetSite)(REFIID riid, void **ppvSite);
  34. CExeDropTarget();
  35. private:
  36. ~CExeDropTarget();
  37. void _FillSEIFromLinkSite(SHELLEXECUTEINFO *pei);
  38. void _CleanupSEIFromLinkSite(SHELLEXECUTEINFO *pei);
  39. LONG _cRef;
  40. DWORD _dwEffectLast;
  41. DWORD _grfKeyStateLast;
  42. TCHAR _szFile[MAX_PATH];
  43. };
  44. CExeDropTarget::CExeDropTarget() : _cRef(1)
  45. {
  46. }
  47. CExeDropTarget::~CExeDropTarget()
  48. {
  49. }
  50. STDMETHODIMP CExeDropTarget::QueryInterface(REFIID riid, void **ppv)
  51. {
  52. static const QITAB qit[] = {
  53. QITABENT(CExeDropTarget, IDropTarget),
  54. QITABENT(CExeDropTarget, IPersistFile),
  55. QITABENTMULTI(CExeDropTarget, IPersist, IPersistFile),
  56. QITABENT(CExeDropTarget, IObjectWithSite), // IID_IObjectWithSite
  57. { 0 },
  58. };
  59. return QISearch(this, qit, riid, ppv);
  60. }
  61. STDMETHODIMP_(ULONG) CExeDropTarget::AddRef()
  62. {
  63. return InterlockedIncrement(&_cRef);
  64. }
  65. STDMETHODIMP_(ULONG) CExeDropTarget::Release()
  66. {
  67. if (InterlockedDecrement(&_cRef))
  68. return _cRef;
  69. delete this;
  70. return 0;
  71. }
  72. STDMETHODIMP CExeDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  73. {
  74. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  75. if ((S_OK == pdtobj->QueryGetData(&fmte)) ||
  76. (S_OK == DataObj_GetShellURL(pdtobj, NULL, NULL)))
  77. {
  78. *pdwEffect &= (DROPEFFECT_COPY | DROPEFFECT_LINK);
  79. }
  80. else
  81. *pdwEffect = 0;
  82. _dwEffectLast = *pdwEffect;
  83. _grfKeyStateLast = grfKeyState;
  84. return S_OK;
  85. }
  86. STDMETHODIMP CExeDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  87. {
  88. *pdwEffect = _dwEffectLast;
  89. _grfKeyStateLast = grfKeyState;
  90. return S_OK;
  91. }
  92. STDMETHODIMP CExeDropTarget::DragLeave()
  93. {
  94. return S_OK;
  95. }
  96. //
  97. // See if we were created from a shortcut. If so, then pull the exec
  98. // parameters from the shortcut.
  99. //
  100. void CExeDropTarget::_FillSEIFromLinkSite(SHELLEXECUTEINFO *pei)
  101. {
  102. ASSERT(pei->lpParameters == NULL);
  103. ASSERT(pei->lpDirectory == NULL);
  104. IShellLink *psl;
  105. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_LinkSite, IID_IShellLink, (void **)&psl)))
  106. {
  107. TCHAR szBuf[MAX_PATH];
  108. psl->GetShowCmd(&pei->nShow);
  109. // Hotkeys are annoying because IShellLink::GetHotkey uses a
  110. // WORD as the hotkey, but SHELLEXECUTEINFO uses a DWORD.
  111. WORD wHotkey;
  112. if (SUCCEEDED(psl->GetHotkey(&wHotkey)))
  113. {
  114. pei->dwHotKey = wHotkey;
  115. pei->fMask |= SEE_MASK_HOTKEY;
  116. }
  117. if (SUCCEEDED(psl->GetWorkingDirectory(szBuf, ARRAYSIZE(szBuf))) &&
  118. szBuf[0])
  119. {
  120. Str_SetPtr(const_cast<LPTSTR *>(&pei->lpDirectory), szBuf);
  121. }
  122. if (SUCCEEDED(psl->GetArguments(szBuf, ARRAYSIZE(szBuf))) &&
  123. szBuf[0])
  124. {
  125. Str_SetPtr(const_cast<LPTSTR *>(&pei->lpParameters), szBuf);
  126. }
  127. psl->Release();
  128. }
  129. }
  130. void CExeDropTarget::_CleanupSEIFromLinkSite(SHELLEXECUTEINFO *pei)
  131. {
  132. Str_SetPtr(const_cast<LPTSTR *>(&pei->lpDirectory), NULL);
  133. Str_SetPtr(const_cast<LPTSTR *>(&pei->lpParameters), NULL);
  134. }
  135. BOOL GetAppDropTarget(LPCTSTR pszPath, CLSID *pclsid)
  136. {
  137. TCHAR sz[MAX_PATH];
  138. // NOTE this assumes that this is a path to the exe
  139. // and not a command line
  140. PathToAppPathKey(pszPath, sz, ARRAYSIZE(sz));
  141. TCHAR szClsid[64];
  142. DWORD cb = sizeof(szClsid);
  143. return (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("DropTarget"), NULL, szClsid, &cb)) &&
  144. GUIDFromString(szClsid, pclsid);
  145. }
  146. STDMETHODIMP CExeDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  147. {
  148. DWORD dwEffectPerformed = 0;
  149. if (!(_grfKeyStateLast & MK_LBUTTON))
  150. {
  151. HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_DROPONEXE);
  152. if (hmenu)
  153. {
  154. HWND hwnd;
  155. IUnknown_GetWindow(_punkSite, &hwnd);
  156. UINT idCmd = SHTrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  157. pt.x, pt.y, 0, hwnd, NULL);
  158. DestroyMenu(hmenu);
  159. if (idCmd != DDIDM_COPY)
  160. {
  161. *pdwEffect = 0; // canceled
  162. }
  163. }
  164. }
  165. if (*pdwEffect)
  166. {
  167. CLSID clsidDropTarget;
  168. if (GetAppDropTarget(_szFile, &clsidDropTarget))
  169. {
  170. if (SUCCEEDED(SHSimulateDropOnClsid(clsidDropTarget, _punkSite, pdtobj)))
  171. {
  172. dwEffectPerformed = DROPEFFECT_COPY; // what we did
  173. }
  174. }
  175. else
  176. {
  177. SHELLEXECUTEINFO ei = {
  178. sizeof(ei),
  179. 0, NULL, NULL, _szFile, NULL, NULL, SW_SHOWNORMAL, NULL
  180. };
  181. _FillSEIFromLinkSite(&ei);
  182. LPCTSTR pszLinkParams = ei.lpParameters;
  183. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  184. STGMEDIUM medium;
  185. HRESULT hr = pdtobj->GetData(&fmte, &medium);
  186. if (SUCCEEDED(hr))
  187. {
  188. TCHAR szPath[MAX_PATH];
  189. int cchParam = ei.lpParameters ? lstrlen(ei.lpParameters) + 1 : 0;
  190. BOOL fLFNAware = App_IsLFNAware(_szFile);
  191. for (UINT i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
  192. {
  193. if (fLFNAware)
  194. PathQuoteSpaces(szPath);
  195. else
  196. GetShortPathName(szPath, szPath, ARRAYSIZE(szPath));
  197. cchParam += lstrlen(szPath) + 2; // space and NULL
  198. }
  199. if (cchParam)
  200. {
  201. LPTSTR pszParam = (LPTSTR)LocalAlloc(LPTR, cchParam * sizeof(*pszParam));
  202. if (pszParam)
  203. {
  204. // If the link had parameters, then put our filenames after
  205. // the parameters (with an intervening space)
  206. if (ei.lpParameters)
  207. {
  208. lstrcpyn(pszParam, ei.lpParameters, cchParam);
  209. lstrcat(pszParam, c_szSpace);
  210. }
  211. for (i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
  212. {
  213. if (fLFNAware)
  214. PathQuoteSpaces(szPath);
  215. else
  216. GetShortPathName(szPath, szPath, ARRAYSIZE(szPath));
  217. if (i > 0)
  218. lstrcat(pszParam, c_szSpace);
  219. lstrcat(pszParam, szPath);
  220. }
  221. ei.lpParameters = pszParam;
  222. ShellExecuteEx(&ei);
  223. LocalFree((HLOCAL)pszParam);
  224. dwEffectPerformed = DROPEFFECT_COPY; // what we did
  225. }
  226. }
  227. ReleaseStgMedium(&medium);
  228. }
  229. else
  230. {
  231. LPCSTR pszURL;
  232. if (SUCCEEDED(DataObj_GetShellURL(pdtobj, &medium, &pszURL)))
  233. {
  234. if (DoesAppWantUrl(_szFile))
  235. {
  236. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  237. SHAnsiToTChar(pszURL, szURL, ARRAYSIZE(szURL));
  238. ei.lpParameters = szURL;
  239. ShellExecuteEx(&ei);
  240. dwEffectPerformed = DROPEFFECT_LINK; // what we did
  241. }
  242. ReleaseStgMediumHGLOBAL(NULL, &medium);
  243. }
  244. }
  245. // The process of building the ShellExecuteEx parameters may have
  246. // messed up the ei.lpParameters, so put the original back so the
  247. // cleanup function won't get confused.
  248. ei.lpParameters = pszLinkParams;
  249. _CleanupSEIFromLinkSite(&ei);
  250. }
  251. *pdwEffect = dwEffectPerformed;
  252. }
  253. return S_OK;
  254. }
  255. STDMETHODIMP CExeDropTarget::GetClassID(CLSID *pClassID)
  256. {
  257. *pClassID = CLSID_ExeDropTarget;
  258. return S_OK;
  259. }
  260. STDMETHODIMP CExeDropTarget::IsDirty(void)
  261. {
  262. return S_OK; // no
  263. }
  264. STDMETHODIMP CExeDropTarget::Load(LPCOLESTR pszFileName, DWORD dwMode)
  265. {
  266. SHUnicodeToTChar(pszFileName, _szFile, ARRAYSIZE(_szFile));
  267. return S_OK;
  268. }
  269. STDMETHODIMP CExeDropTarget::Save(LPCOLESTR pszFileName, BOOL fRemember)
  270. {
  271. return S_OK;
  272. }
  273. STDMETHODIMP CExeDropTarget::SaveCompleted(LPCOLESTR pszFileName)
  274. {
  275. return S_OK;
  276. }
  277. STDMETHODIMP CExeDropTarget::GetCurFile(LPOLESTR *ppszFileName)
  278. {
  279. *ppszFileName = NULL;
  280. return E_NOTIMPL;
  281. }
  282. STDAPI CExeDropTarget_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  283. {
  284. HRESULT hr;
  285. CExeDropTarget* pdt = new CExeDropTarget();
  286. if (pdt)
  287. {
  288. hr = pdt->QueryInterface(riid, ppv);
  289. pdt->Release();
  290. }
  291. else
  292. {
  293. *ppv = NULL;
  294. hr = E_OUTOFMEMORY;
  295. }
  296. return hr;
  297. }