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.

3259 lines
110 KiB

  1. #include "shellprv.h"
  2. #include <cowsite.h>
  3. #include "datautil.h"
  4. #include "ids.h"
  5. #include "defview.h"
  6. #include "_security.h"
  7. #include "shitemid.h"
  8. #include "idlcomm.h"
  9. #include "bitbuck.h"
  10. #include "bookmk.h"
  11. #include "filefldr.h"
  12. #include "brfcase.h"
  13. #include "copy.h"
  14. #include "filetbl.h"
  15. #define TF_DRAGDROP 0x04000000
  16. typedef struct
  17. {
  18. HWND hwnd;
  19. DWORD dwFlags;
  20. POINTL pt;
  21. CHAR szUrl[INTERNET_MAX_URL_LENGTH];
  22. } ADDTODESKTOP;
  23. DWORD CALLBACK AddToActiveDesktopThreadProc(void *pv)
  24. {
  25. ADDTODESKTOP* pToAD = (ADDTODESKTOP*)pv;
  26. CHAR szFilePath[MAX_PATH];
  27. DWORD cchFilePath = SIZECHARS(szFilePath);
  28. BOOL fAddComp = TRUE;
  29. if (SUCCEEDED(PathCreateFromUrlA(pToAD->szUrl, szFilePath, &cchFilePath, 0)))
  30. {
  31. TCHAR szPath[MAX_PATH];
  32. SHAnsiToTChar(szFilePath, szPath, ARRAYSIZE(szPath));
  33. // If the Url is in the Temp directory
  34. if (PathIsTemporary(szPath))
  35. {
  36. if (IDYES == ShellMessageBox(g_hinst, pToAD->hwnd, MAKEINTRESOURCE(IDS_REASONS_URLINTEMPDIR),
  37. MAKEINTRESOURCE(IDS_AD_NAME), MB_YESNO | MB_ICONQUESTION))
  38. {
  39. TCHAR szFilter[64], szTitle[64];
  40. TCHAR szFilename[MAX_PATH];
  41. LPTSTR psz;
  42. OPENFILENAME ofn = { 0 };
  43. LoadString(g_hinst, IDS_ALLFILESFILTER, szFilter, ARRAYSIZE(szFilter));
  44. LoadString(g_hinst, IDS_SAVEAS, szTitle, ARRAYSIZE(szTitle));
  45. psz = szFilter;
  46. //Strip out the # and make them Nulls for SaveAs Dialog
  47. while (*psz)
  48. {
  49. if (*psz == (WCHAR)('#'))
  50. *psz = (WCHAR)('\0');
  51. psz++;
  52. }
  53. lstrcpy(szFilename, PathFindFileName(szPath));
  54. ofn.lStructSize = sizeof(OPENFILENAME);
  55. ofn.hwndOwner = pToAD->hwnd;
  56. ofn.hInstance = g_hinst;
  57. ofn.lpstrFilter = szFilter;
  58. ofn.lpstrFile = szFilename;
  59. ofn.nMaxFile = ARRAYSIZE(szFilename);
  60. ofn.lpstrTitle = szTitle;
  61. ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
  62. if (GetSaveFileName(&ofn))
  63. {
  64. SHFILEOPSTRUCT sfo = { 0 };
  65. szPath[lstrlen(szPath) + 1] = 0;
  66. ofn.lpstrFile[lstrlen(ofn.lpstrFile) + 1] = 0;
  67. sfo.hwnd = pToAD->hwnd;
  68. sfo.wFunc = FO_COPY;
  69. sfo.pFrom = szPath;
  70. sfo.pTo = ofn.lpstrFile;
  71. cchFilePath = SIZECHARS(szPath);
  72. if (SHFileOperation(&sfo) == 0 &&
  73. SUCCEEDED(UrlCreateFromPath(szPath, szPath, &cchFilePath, 0)))
  74. {
  75. SHTCharToAnsi(szPath, pToAD->szUrl, ARRAYSIZE(pToAD->szUrl));
  76. }
  77. else
  78. fAddComp = FALSE;
  79. }
  80. else
  81. fAddComp = FALSE;
  82. }
  83. else
  84. fAddComp = FALSE;
  85. }
  86. }
  87. if (fAddComp)
  88. CreateDesktopComponents(pToAD->szUrl, NULL, pToAD->hwnd, pToAD->dwFlags, pToAD->pt.x, pToAD->pt.y);
  89. LocalFree((HLOCAL)pToAD);
  90. return 0;
  91. }
  92. typedef struct {
  93. DWORD dwDefEffect;
  94. IDataObject *pdtobj;
  95. POINTL pt;
  96. DWORD * pdwEffect;
  97. HKEY rghk[MAX_ASSOC_KEYS];
  98. DWORD ck;
  99. HMENU hmenu;
  100. UINT idCmd;
  101. DWORD grfKeyState;
  102. } FSDRAGDROPMENUPARAM;
  103. typedef struct
  104. {
  105. HMENU hMenu;
  106. UINT uCopyPos;
  107. UINT uMovePos;
  108. UINT uLinkPos;
  109. } FSMENUINFO;
  110. class CFSDropTarget : CObjectWithSite, public IDropTarget
  111. {
  112. public:
  113. // IUnknown
  114. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  115. STDMETHODIMP_(ULONG) AddRef(void);
  116. STDMETHODIMP_(ULONG) Release(void);
  117. // IDropTarget
  118. STDMETHODIMP DragEnter(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
  119. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
  120. STDMETHODIMP DragLeave();
  121. STDMETHODIMP Drop(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
  122. CFSDropTarget(CFSFolder *pFolder, HWND hwnd);
  123. protected:
  124. virtual ~CFSDropTarget();
  125. BOOL _IsBriefcaseTarget() { return IsEqualCLSID(_pFolder->_clsidBind, CLSID_BriefcaseFolder); };
  126. BOOL _IsDesktopFolder() { return _GetIDList() && ILIsEmpty(_GetIDList()); };
  127. HRESULT _FilterFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  128. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  129. HRESULT _FilterFileContentsOLEHack(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  130. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  131. HRESULT _FilterDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  132. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  133. HRESULT _FilterSneakernetBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  134. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  135. HRESULT _FilterBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  136. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  137. HRESULT _FilterHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  138. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  139. HRESULT _FilterHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  140. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  141. HRESULT _FilterDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  142. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  143. HRESULT _FilterDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  144. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  145. HRESULT _FilterOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  146. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  147. HRESULT _FilterOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  148. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  149. HRESULT _FilterOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  150. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
  151. DWORD _FilesystemAdjustedDefaultEffect(DWORD dwCurEffectAvail);
  152. HRESULT _GetPath(LPTSTR pszPath);
  153. LPCITEMIDLIST _GetIDList();
  154. DWORD _LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed);
  155. DWORD _GetDefaultEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect);
  156. DWORD _DetermineEffects(DWORD grfKeyState, DWORD *pdwEffectInOut, HMENU hmenu);
  157. DWORD _EffectFromFolder();
  158. typedef struct
  159. {
  160. CFSDropTarget *pThis;
  161. IStream *pstmDataObj;
  162. IStream *pstmFolderView;
  163. } DROPTHREADPARAMS;
  164. static void _FreeThreadParams(DROPTHREADPARAMS *pdtp);
  165. static DWORD CALLBACK _DoDropThreadProc(void *pv);
  166. void _DoDrop(IDataObject *pdtobj, IFolderView* pfv);
  167. static void _AddVerbs(DWORD* pdwEffects, DWORD dwEffectAvail, DWORD dwDefEffect,
  168. UINT idCopy, UINT idMove, UINT idLink,
  169. DWORD dwForceEffect, FSMENUINFO* pfsMenuInfo);
  170. void _FixUpDefaultItem(HMENU hmenu, DWORD dwDefEffect);
  171. HRESULT _DragDropMenu(FSDRAGDROPMENUPARAM *pddm);
  172. HRESULT _CreatePackage(IDataObject *pdtobj);
  173. HRESULT _CreateURLDeskComp(IDataObject *pdtobj, POINTL pt);
  174. HRESULT _CreateDeskCompImage(IDataObject *pdtobj, POINTL pt);
  175. void _GetStateFromSite();
  176. BOOL _IsFromSneakernetBriefcase();
  177. BOOL _IsFromSameBriefcase();
  178. void _MoveCopy(IDataObject *pdtobj, IFolderView* pfv, HDROP hDrop);
  179. void _MoveSelectIcons(IDataObject *pdtobj, IFolderView* pfv, void *hNameMap, LPCTSTR pszFiles, BOOL fMove, HDROP hDrop);
  180. LONG _cRef;
  181. CFSFolder *_pFolder;
  182. HWND _hwnd; // EVIL: used as a site and UI host
  183. UINT _idCmd;
  184. DWORD _grfKeyStateLast; // for previous DragOver/Enter
  185. IDataObject *_pdtobj; // used durring Dragover() and DoDrop(), don't use on background thread
  186. DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
  187. DWORD _dwEffect;
  188. DWORD _dwData; // DTID_*
  189. DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
  190. DWORD _dwEffectFolder; // folder desktop.ini preferred effect
  191. BOOL _fSameHwnd; // the drag source and target are the same folder
  192. BOOL _fDragDrop; //
  193. BOOL _fUseExactDropPoint; // Don't transform the drop point. The target knows exactly where it wants things.
  194. BOOL _fBkDropTarget;
  195. POINT _ptDrop;
  196. IFolderView* _pfv;
  197. typedef struct {
  198. FORMATETC fmte;
  199. HRESULT (CFSDropTarget::*pfnGetDragDropInfo)(
  200. IN FORMATETC* pfmte,
  201. IN DWORD grfKeyFlags,
  202. IN DWORD dwEffectsAvail,
  203. IN OUT DWORD* pdwEffectsUsed,
  204. OUT DWORD* pdwDefaultEffect,
  205. IN OUT FSMENUINFO* pfsMenuInfo);
  206. CLIPFORMAT *pcfInit;
  207. } _DATA_HANDLER;
  208. // HACKHACK: C++ doesn't let you initialize statics inside a class
  209. // definition, and also doesn't let you specify an empty
  210. // size (i.e., rg_data_handlers[]) inside a class definition
  211. // either, so we have to have this bogus NUM_DATA_HANDLERS
  212. // symbol that must manually be kept in sync.
  213. enum { NUM_DATA_HANDLERS = 16 };
  214. static _DATA_HANDLER rg_data_handlers[NUM_DATA_HANDLERS];
  215. static void _Init_rg_data_handlers();
  216. private:
  217. friend HRESULT CFSDropTarget_CreateInstance(CFSFolder* pFolder, HWND hwnd, IDropTarget** ppdt);
  218. };
  219. CFSDropTarget::CFSDropTarget(CFSFolder *pFolder, HWND hwnd) : _cRef(1), _hwnd(hwnd), _pFolder(pFolder), _dwEffectFolder(-1)
  220. {
  221. ASSERT(0 == _grfKeyStateLast);
  222. ASSERT(NULL == _pdtobj);
  223. ASSERT(0 == _dwEffectLastReturned);
  224. ASSERT(0 == _dwData);
  225. ASSERT(0 == _dwEffectPreferred);
  226. _pFolder->AddRef();
  227. }
  228. CFSDropTarget::~CFSDropTarget()
  229. {
  230. AssertMsg(_pdtobj == NULL, TEXT("didn't get matching DragLeave, fix that bug"));
  231. ATOMICRELEASE(_pdtobj);
  232. ATOMICRELEASE(_pfv);
  233. _pFolder->Release();
  234. }
  235. STDAPI CFSDropTarget_CreateInstance(CFSFolder* pFolder, HWND hwnd, IDropTarget** ppdt)
  236. {
  237. *ppdt = new CFSDropTarget(pFolder, hwnd);
  238. return *ppdt ? S_OK : E_OUTOFMEMORY;
  239. }
  240. HRESULT CFSDropTarget::QueryInterface(REFIID riid, void** ppvObj)
  241. {
  242. static const QITAB qit[] = {
  243. QITABENT(CFSDropTarget, IDropTarget),
  244. QITABENT(CFSDropTarget, IObjectWithSite),
  245. QITABENTMULTI2(CFSDropTarget, IID_IDropTargetWithDADSupport, IDropTarget),
  246. { 0 },
  247. };
  248. return QISearch(this, qit, riid, ppvObj);
  249. }
  250. STDMETHODIMP_(ULONG) CFSDropTarget::AddRef()
  251. {
  252. return InterlockedIncrement(&_cRef);
  253. }
  254. STDMETHODIMP_(ULONG) CFSDropTarget::Release()
  255. {
  256. if (InterlockedDecrement(&_cRef))
  257. return _cRef;
  258. delete this;
  259. return 0;
  260. }
  261. void CFSDropTarget::_FreeThreadParams(DROPTHREADPARAMS *pdtp)
  262. {
  263. pdtp->pThis->Release();
  264. ATOMICRELEASE(pdtp->pstmDataObj);
  265. ATOMICRELEASE(pdtp->pstmFolderView);
  266. LocalFree(pdtp);
  267. }
  268. // compute DTID_ bit flags from the data object to make format testing easier for
  269. // DragOver() and Drop() code
  270. STDAPI GetClipFormatFlags(IDataObject *pdtobj, DWORD *pdwData, DWORD *pdwEffectPreferred)
  271. {
  272. *pdwData = 0;
  273. *pdwEffectPreferred = 0;
  274. if (pdtobj)
  275. {
  276. IEnumFORMATETC *penum;
  277. if (SUCCEEDED(pdtobj->EnumFormatEtc(DATADIR_GET, &penum)))
  278. {
  279. FORMATETC fmte;
  280. ULONG celt;
  281. while (S_OK == penum->Next(1, &fmte, &celt))
  282. {
  283. if (fmte.cfFormat == CF_HDROP && (fmte.tymed & TYMED_HGLOBAL))
  284. *pdwData |= DTID_HDROP;
  285. if (fmte.cfFormat == g_cfHIDA && (fmte.tymed & TYMED_HGLOBAL))
  286. *pdwData |= DTID_HIDA;
  287. if (fmte.cfFormat == g_cfNetResource && (fmte.tymed & TYMED_HGLOBAL))
  288. *pdwData |= DTID_NETRES;
  289. if (fmte.cfFormat == g_cfEmbeddedObject && (fmte.tymed & TYMED_ISTORAGE))
  290. *pdwData |= DTID_EMBEDDEDOBJECT;
  291. if (fmte.cfFormat == g_cfFileContents && (fmte.tymed & (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)))
  292. *pdwData |= DTID_CONTENTS;
  293. if (fmte.cfFormat == g_cfFileGroupDescriptorA && (fmte.tymed & TYMED_HGLOBAL))
  294. *pdwData |= DTID_FDESCA;
  295. if (fmte.cfFormat == g_cfFileGroupDescriptorW && (fmte.tymed & TYMED_HGLOBAL))
  296. *pdwData |= DTID_FDESCW;
  297. if ((fmte.cfFormat == g_cfPreferredDropEffect) &&
  298. (fmte.tymed & TYMED_HGLOBAL) &&
  299. (DROPEFFECT_NONE != (*pdwEffectPreferred = DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE))))
  300. {
  301. *pdwData |= DTID_PREFERREDEFFECT;
  302. }
  303. #ifdef DEBUG
  304. TCHAR szFormat[MAX_PATH];
  305. if (GetClipboardFormatName(fmte.cfFormat, szFormat, ARRAYSIZE(szFormat)))
  306. {
  307. TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %s, tymed %d", szFormat, fmte.tymed);
  308. }
  309. else
  310. {
  311. TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %d, tymed %d", fmte.cfFormat, fmte.tymed);
  312. }
  313. #endif // DEBUG
  314. SHFree(fmte.ptd);
  315. }
  316. penum->Release();
  317. }
  318. //
  319. // HACK:
  320. // Win95 always did the GetData below which can be quite expensive if
  321. // the data is a directory structure on an ftp server etc.
  322. // dont check for FD_LINKUI if the data object has a preferred effect
  323. //
  324. if ((*pdwData & (DTID_PREFERREDEFFECT | DTID_CONTENTS)) == DTID_CONTENTS)
  325. {
  326. if (*pdwData & DTID_FDESCA)
  327. {
  328. FORMATETC fmteRead = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  329. STGMEDIUM medium = {0};
  330. if (S_OK == pdtobj->GetData(&fmteRead, &medium))
  331. {
  332. FILEGROUPDESCRIPTORA * pfgd = (FILEGROUPDESCRIPTORA *)GlobalLock(medium.hGlobal);
  333. if (pfgd)
  334. {
  335. if (pfgd->cItems >= 1)
  336. {
  337. if (pfgd->fgd[0].dwFlags & FD_LINKUI)
  338. *pdwData |= DTID_FD_LINKUI;
  339. }
  340. GlobalUnlock(medium.hGlobal);
  341. }
  342. ReleaseStgMedium(&medium);
  343. }
  344. }
  345. else if (*pdwData & DTID_FDESCW)
  346. {
  347. FORMATETC fmteRead = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  348. STGMEDIUM medium = {0};
  349. if (S_OK == pdtobj->GetData(&fmteRead, &medium))
  350. {
  351. FILEGROUPDESCRIPTORW * pfgd = (FILEGROUPDESCRIPTORW *)GlobalLock(medium.hGlobal);
  352. if (pfgd)
  353. {
  354. if (pfgd->cItems >= 1)
  355. {
  356. if (pfgd->fgd[0].dwFlags & FD_LINKUI)
  357. *pdwData |= DTID_FD_LINKUI;
  358. }
  359. GlobalUnlock(medium.hGlobal);
  360. }
  361. ReleaseStgMedium(&medium);
  362. }
  363. }
  364. }
  365. if (S_OK == OleQueryCreateFromData(pdtobj))
  366. *pdwData |= DTID_OLEOBJ;
  367. if (S_OK == OleQueryLinkFromData(pdtobj))
  368. *pdwData |= DTID_OLELINK;
  369. }
  370. return S_OK; // for now always succeeds
  371. }
  372. STDMETHODIMP CFSDropTarget::DragEnter(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  373. {
  374. ASSERT(NULL == _pdtobj); // req DragDrop protocol, someone forgot to call DragLeave
  375. // init our registerd data formats
  376. IDLData_InitializeClipboardFormats();
  377. _grfKeyStateLast = grfKeyState;
  378. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  379. GetClipFormatFlags(_pdtobj, &_dwData, &_dwEffectPreferred);
  380. *pdwEffect = _dwEffectLastReturned = _DetermineEffects(grfKeyState, pdwEffect, NULL);
  381. return S_OK;
  382. }
  383. STDMETHODIMP CFSDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  384. {
  385. if (_grfKeyStateLast != grfKeyState)
  386. {
  387. _grfKeyStateLast = grfKeyState;
  388. *pdwEffect = _dwEffectLastReturned = _DetermineEffects(grfKeyState, pdwEffect, NULL);
  389. }
  390. else
  391. {
  392. *pdwEffect = _dwEffectLastReturned;
  393. }
  394. return S_OK;
  395. }
  396. STDMETHODIMP CFSDropTarget::DragLeave()
  397. {
  398. IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  399. return S_OK;
  400. }
  401. // init data from our site that we will need in processing the drop
  402. void CFSDropTarget::_GetStateFromSite()
  403. {
  404. IShellFolderView* psfv;
  405. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
  406. {
  407. _fSameHwnd = S_OK == psfv->IsDropOnSource((IDropTarget*)this);
  408. _fDragDrop = S_OK == psfv->GetDropPoint(&_ptDrop);
  409. _fBkDropTarget = S_OK == psfv->IsBkDropTarget(NULL);
  410. psfv->QueryInterface(IID_PPV_ARG(IFolderView, &_pfv));
  411. psfv->Release();
  412. }
  413. }
  414. STDMETHODIMP CFSDropTarget::Drop(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  415. {
  416. // OLE may give us a different data object (fully marshalled)
  417. // from the one we've got on DragEnter (this is not the case on Win2k, this is a nop)
  418. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  419. _GetStateFromSite();
  420. // note, that on the drop the mouse buttons are not down so the grfKeyState
  421. // is not what we saw on the DragOver/DragEnter, thus we need to cache
  422. // the grfKeyState to detect left vs right drag
  423. //
  424. // ASSERT(this->grfKeyStateLast == grfKeyState);
  425. HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_TEMPLATEDD);
  426. DWORD dwDefEffect = _DetermineEffects(grfKeyState, pdwEffect, hmenu);
  427. if (DROPEFFECT_NONE == dwDefEffect)
  428. {
  429. *pdwEffect = DROPEFFECT_NONE;
  430. DAD_SetDragImage(NULL, NULL);
  431. IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  432. return S_OK;
  433. }
  434. TCHAR szPath[MAX_PATH];
  435. _GetPath(szPath);
  436. // this doesn't actually do the menu if (grfKeyState MK_LBUTTON)
  437. FSDRAGDROPMENUPARAM ddm;
  438. ddm.dwDefEffect = dwDefEffect;
  439. ddm.pdtobj = pdtobj;
  440. ddm.pt = pt;
  441. ddm.pdwEffect = pdwEffect;
  442. ddm.ck = SHGetAssocKeysForIDList(_GetIDList(), ddm.rghk, ARRAYSIZE(ddm.rghk));
  443. ddm.hmenu = hmenu;
  444. ddm.grfKeyState = grfKeyState;
  445. HRESULT hr = _DragDropMenu(&ddm);
  446. SHRegCloseKeys(ddm.rghk, ddm.ck);
  447. DestroyMenu(hmenu);
  448. if (hr == S_FALSE)
  449. {
  450. // let callers know where this is about to go
  451. // SHScrap cares because it needs to close the file so we can copy/move it
  452. DataObj_SetDropTarget(pdtobj, &CLSID_ShellFSFolder);
  453. switch (ddm.idCmd)
  454. {
  455. case DDIDM_CONTENTS_DESKCOMP:
  456. hr = CreateDesktopComponents(NULL, pdtobj, _hwnd, 0, ddm.pt.x, ddm.pt.y);
  457. break;
  458. case DDIDM_CONTENTS_DESKURL:
  459. hr = _CreateURLDeskComp(pdtobj, ddm.pt);
  460. break;
  461. case DDIDM_CONTENTS_DESKIMG:
  462. hr = _CreateDeskCompImage(pdtobj, ddm.pt);
  463. break;
  464. case DDIDM_CONTENTS_COPY:
  465. case DDIDM_CONTENTS_MOVE:
  466. case DDIDM_CONTENTS_LINK:
  467. hr = CFSFolder_AsyncCreateFileFromClip(_hwnd, szPath, pdtobj, pt, pdwEffect, _fBkDropTarget);
  468. break;
  469. case DDIDM_SCRAP_COPY:
  470. case DDIDM_SCRAP_MOVE:
  471. case DDIDM_DOCLINK:
  472. hr = SHCreateBookMark(_hwnd, szPath, pdtobj, pt, pdwEffect);
  473. break;
  474. case DDIDM_OBJECT_COPY:
  475. case DDIDM_OBJECT_MOVE:
  476. hr = _CreatePackage(pdtobj);
  477. if (E_UNEXPECTED == hr)
  478. {
  479. // _CreatePackage() can only expand certain types of packages
  480. // back into files. For example, it doesn't handle CMDLINK files.
  481. //
  482. // If _CreatePackage() didn't recognize the stream format, we fall
  483. // back to SHCreateBookMark(), which should create a scrap:
  484. hr = SHCreateBookMark(_hwnd, szPath, pdtobj, pt, pdwEffect);
  485. }
  486. break;
  487. case DDIDM_COPY:
  488. case DDIDM_SYNCCOPY:
  489. case DDIDM_SYNCCOPYTYPE:
  490. case DDIDM_MOVE:
  491. case DDIDM_LINK:
  492. _dwEffect = *pdwEffect;
  493. _idCmd = ddm.idCmd;
  494. if (DataObj_CanGoAsync(pdtobj) || DataObj_GoAsyncForCompat(pdtobj))
  495. {
  496. // create another thread to avoid blocking the source thread.
  497. DROPTHREADPARAMS *pdtp;
  498. hr = SHLocalAlloc(sizeof(*pdtp), &pdtp);
  499. if (SUCCEEDED(hr))
  500. {
  501. pdtp->pThis = this;
  502. pdtp->pThis->AddRef();
  503. CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &pdtp->pstmDataObj);
  504. CoMarshalInterThreadInterfaceInStream(IID_IFolderView, _pfv, &pdtp->pstmFolderView);
  505. if (SHCreateThread(_DoDropThreadProc, pdtp, CTF_COINIT, NULL))
  506. {
  507. hr = S_OK;
  508. }
  509. else
  510. {
  511. _FreeThreadParams(pdtp);
  512. hr = E_OUTOFMEMORY;
  513. }
  514. }
  515. }
  516. else
  517. {
  518. _DoDrop(pdtobj, _pfv); // synchronously
  519. }
  520. // in these CF_HDROP cases "Move" is always an optimized move, we delete the
  521. // source. make sure we don't return DROPEFFECT_MOVE so the source does not
  522. // try to do this too...
  523. // even if we have not done anything yet since we may have
  524. // kicked of a thread to do this
  525. DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect);
  526. if (DROPEFFECT_MOVE == *pdwEffect)
  527. *pdwEffect = DROPEFFECT_NONE;
  528. break;
  529. }
  530. }
  531. IUnknown_Set((IUnknown **)&_pdtobj, NULL); // don't use this any more
  532. if (FAILED(hr))
  533. *pdwEffect = DROPEFFECT_NONE;
  534. ASSERT(*pdwEffect==DROPEFFECT_COPY ||
  535. *pdwEffect==DROPEFFECT_LINK ||
  536. *pdwEffect==DROPEFFECT_MOVE ||
  537. *pdwEffect==DROPEFFECT_NONE);
  538. return hr;
  539. }
  540. void CFSDropTarget::_AddVerbs(DWORD* pdwEffects, DWORD dwEffectAvail, DWORD dwDefEffect,
  541. UINT idCopy, UINT idMove, UINT idLink,
  542. DWORD dwForceEffect, FSMENUINFO* pfsMenuInfo)
  543. {
  544. ASSERT(pdwEffects);
  545. MENUITEMINFO mii;
  546. TCHAR szCmd[MAX_PATH];
  547. if (NULL != pfsMenuInfo)
  548. {
  549. mii.cbSize = sizeof(mii);
  550. mii.dwTypeData = szCmd;
  551. mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
  552. mii.fType = MFT_STRING;
  553. }
  554. if ((DROPEFFECT_COPY == (DROPEFFECT_COPY & dwEffectAvail)) &&
  555. ((0 == (*pdwEffects & DROPEFFECT_COPY)) || (dwForceEffect & DROPEFFECT_COPY)))
  556. {
  557. ASSERT(0 != idCopy);
  558. if (NULL != pfsMenuInfo)
  559. {
  560. LoadString(HINST_THISDLL, idCopy + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
  561. mii.fState = MFS_ENABLED | ((DROPEFFECT_COPY == dwDefEffect) ? MFS_DEFAULT : 0);
  562. mii.wID = idCopy;
  563. mii.dwItemData = DROPEFFECT_COPY;
  564. InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uCopyPos, TRUE, &mii);
  565. pfsMenuInfo->uCopyPos++;
  566. pfsMenuInfo->uMovePos++;
  567. pfsMenuInfo->uLinkPos++;
  568. }
  569. }
  570. if ((DROPEFFECT_MOVE == (DROPEFFECT_MOVE & dwEffectAvail)) &&
  571. ((0 == (*pdwEffects & DROPEFFECT_MOVE)) || (dwForceEffect & DROPEFFECT_MOVE)))
  572. {
  573. ASSERT(0 != idMove);
  574. if (NULL != pfsMenuInfo)
  575. {
  576. LoadString(HINST_THISDLL, idMove + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
  577. mii.fState = MFS_ENABLED | ((DROPEFFECT_MOVE == dwDefEffect) ? MFS_DEFAULT : 0);
  578. mii.wID = idMove;
  579. mii.dwItemData = DROPEFFECT_MOVE;
  580. InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uMovePos, TRUE, &mii);
  581. pfsMenuInfo->uMovePos++;
  582. pfsMenuInfo->uLinkPos++;
  583. }
  584. }
  585. if ((DROPEFFECT_LINK == (DROPEFFECT_LINK & dwEffectAvail)) &&
  586. ((0 == (*pdwEffects & DROPEFFECT_LINK)) || (dwForceEffect & DROPEFFECT_LINK)))
  587. {
  588. ASSERT(0 != idLink);
  589. if (NULL != pfsMenuInfo)
  590. {
  591. LoadString(HINST_THISDLL, idLink + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
  592. mii.fState = MFS_ENABLED | ((DROPEFFECT_LINK == dwDefEffect) ? MFS_DEFAULT : 0);
  593. mii.wID = idLink;
  594. mii.dwItemData = DROPEFFECT_LINK;
  595. InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uLinkPos, TRUE, &mii);
  596. pfsMenuInfo->uLinkPos++;
  597. }
  598. }
  599. *pdwEffects |= dwEffectAvail;
  600. }
  601. // determine the default drop effect (move/copy/link) from the file type
  602. //
  603. // HKCR\.cda "DefaultDropEffect" = 4 // DROPEFFECT_LINK
  604. DWORD EffectFromFileType(IDataObject *pdtobj)
  605. {
  606. DWORD dwDefEffect = DROPEFFECT_NONE; // 0
  607. LPITEMIDLIST pidl;
  608. if (SUCCEEDED(PidlFromDataObject(pdtobj, &pidl)))
  609. {
  610. IQueryAssociations *pqa;
  611. if (SUCCEEDED(SHGetAssociations(pidl, (void **)&pqa)))
  612. {
  613. DWORD cb = sizeof(dwDefEffect);
  614. pqa->GetData(0, ASSOCDATA_VALUE, L"DefaultDropEffect", &dwDefEffect, &cb);
  615. pqa->Release();
  616. }
  617. ILFree(pidl);
  618. }
  619. return dwDefEffect;
  620. }
  621. // compute the default effect based on
  622. // the allowed effects
  623. // the keyboard state,
  624. // the preferred effect that might be in the data object
  625. // and previously computed default effect (if the above yields nothing)
  626. DWORD CFSDropTarget::_GetDefaultEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect)
  627. {
  628. DWORD dwDefEffect = 0;
  629. //
  630. // keyboard, (explicit user input) gets first crack
  631. //
  632. switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT))
  633. {
  634. case MK_CONTROL:
  635. dwDefEffect = DROPEFFECT_COPY;
  636. break;
  637. case MK_SHIFT:
  638. dwDefEffect = DROPEFFECT_MOVE;
  639. break;
  640. case MK_SHIFT | MK_CONTROL:
  641. case MK_ALT:
  642. dwDefEffect = DROPEFFECT_LINK;
  643. break;
  644. default: // no modifier keys case
  645. // if the data object contains a preferred drop effect, try to use it
  646. DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & dwAllEffectAvail;
  647. if (DROPEFFECT_NONE == dwPreferred)
  648. {
  649. dwPreferred = EffectFromFileType(_pdtobj) & dwAllEffectAvail;
  650. }
  651. if (dwPreferred)
  652. {
  653. if (dwPreferred & DROPEFFECT_MOVE)
  654. {
  655. dwDefEffect = DROPEFFECT_MOVE;
  656. }
  657. else if (dwPreferred & DROPEFFECT_COPY)
  658. {
  659. dwDefEffect = DROPEFFECT_COPY;
  660. }
  661. else if (dwPreferred & DROPEFFECT_LINK)
  662. {
  663. dwDefEffect = DROPEFFECT_LINK;
  664. }
  665. }
  666. else
  667. {
  668. dwDefEffect = dwOrigDefEffect;
  669. }
  670. break;
  671. }
  672. return dwDefEffect & dwCurEffectAvail;
  673. }
  674. HRESULT CFSDropTarget::_FilterDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  675. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  676. {
  677. ASSERT(pdwEffects);
  678. HRESULT hr = S_FALSE;
  679. if (!PolicyNoActiveDesktop() &&
  680. !SHRestricted(REST_NOADDDESKCOMP) &&
  681. _IsDesktopFolder())
  682. {
  683. hr = IsDeskCompHDrop(_pdtobj);
  684. if (S_OK == hr)
  685. {
  686. DWORD dwDefEffect = 0;
  687. DWORD dwEffectAdd = dwEffectsAvail & DROPEFFECT_LINK;
  688. if (pdwDefaultEffect)
  689. {
  690. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
  691. *pdwDefaultEffect = dwDefEffect;
  692. }
  693. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_CONTENTS_DESKCOMP,
  694. DROPEFFECT_LINK, // force add the DDIDM_CONTENTS_DESKCOMP verb
  695. pfsMenuInfo);
  696. }
  697. }
  698. return hr;
  699. }
  700. // see if a PIDL is scoped by a briefcaes
  701. BOOL IsBriefcaseOrChild(LPCITEMIDLIST pidlIn)
  702. {
  703. BOOL bRet = FALSE;
  704. LPITEMIDLIST pidl = ILClone(pidlIn);
  705. if (pidl)
  706. {
  707. do
  708. {
  709. CLSID clsid;
  710. if (SUCCEEDED(GetCLSIDFromIDList(pidl, &clsid)) &&
  711. IsEqualCLSID(clsid, CLSID_Briefcase))
  712. {
  713. bRet = TRUE; // it is a briefcase
  714. break;
  715. }
  716. } while (ILRemoveLastID(pidl));
  717. ILFree(pidl);
  718. }
  719. return bRet;
  720. }
  721. // returns true if the data object represents items in a sneakernet briefcase
  722. // (briefcase on removable media)
  723. BOOL CFSDropTarget::_IsFromSneakernetBriefcase()
  724. {
  725. BOOL bRet = FALSE; // assume no
  726. if (!_IsBriefcaseTarget())
  727. {
  728. STGMEDIUM medium = {0};
  729. LPIDA pida = DataObj_GetHIDA(_pdtobj, &medium);
  730. if (pida)
  731. {
  732. LPCITEMIDLIST pidlFolder = IDA_GetIDListPtr(pida, (UINT)-1);
  733. TCHAR szSource[MAX_PATH];
  734. if (SHGetPathFromIDList(pidlFolder, szSource))
  735. {
  736. // is source on removable device?
  737. if (!PathIsUNC(szSource) && IsRemovableDrive(DRIVEID(szSource)))
  738. {
  739. TCHAR szTarget[MAX_PATH];
  740. _GetPath(szTarget);
  741. // is the target fixed media?
  742. if (PathIsUNC(szTarget) || !IsRemovableDrive(DRIVEID(szTarget)))
  743. {
  744. bRet = IsBriefcaseOrChild(pidlFolder);
  745. }
  746. }
  747. }
  748. HIDA_ReleaseStgMedium(pida, &medium);
  749. }
  750. }
  751. return bRet;
  752. }
  753. // TRUE if any folders are in hdrop
  754. BOOL DroppingAnyFolders(HDROP hDrop)
  755. {
  756. TCHAR szPath[MAX_PATH];
  757. for (UINT i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++)
  758. {
  759. if (PathIsDirectory(szPath))
  760. return TRUE;
  761. }
  762. return FALSE;
  763. }
  764. // sneakernet case:
  765. // dragging a file/folder from a briefcase on removable media. we special case this
  766. // and use this as a chance to connect up this target folder with the content of the briefcase
  767. HRESULT CFSDropTarget::_FilterSneakernetBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  768. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  769. {
  770. ASSERT(pdwEffects);
  771. if (_IsFromSneakernetBriefcase())
  772. {
  773. // Yes; show the non-default briefcase cm
  774. STGMEDIUM medium = {0};
  775. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  776. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  777. {
  778. DWORD dwDefEffect = 0;
  779. DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail;
  780. if (pdwDefaultEffect)
  781. {
  782. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
  783. *pdwDefaultEffect = dwDefEffect;
  784. }
  785. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SYNCCOPY, 0, 0, 0, pfsMenuInfo);
  786. // Call _AddVerbs() again to force "Sync Copy of Type" as a 2nd DROPEFFECT_COPY verb:
  787. if ((DROPEFFECT_COPY & dwEffectsAvail) &&
  788. DroppingAnyFolders((HDROP)medium.hGlobal))
  789. {
  790. _AddVerbs(pdwEffects, DROPEFFECT_COPY, 0,
  791. DDIDM_SYNCCOPYTYPE, 0, 0, DROPEFFECT_COPY, pfsMenuInfo);
  792. }
  793. ReleaseStgMedium(&medium);
  794. }
  795. }
  796. return S_OK;
  797. }
  798. // returns true if the data object represents items from the same briefcase
  799. // as this drop target
  800. BOOL CFSDropTarget::_IsFromSameBriefcase()
  801. {
  802. BOOL bRet = FALSE;
  803. STGMEDIUM medium;
  804. FORMATETC fmteBrief = {g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  805. // Yes; are they from the same briefcase as the target?
  806. if (SUCCEEDED(_pdtobj->GetData(&fmteBrief, &medium)))
  807. {
  808. BriefObj *pbo = (BriefObj *)GlobalLock(medium.hGlobal);
  809. TCHAR szBriefPath[MAX_PATH], szPath[MAX_PATH];
  810. lstrcpy(szBriefPath, BOBriefcasePath(pbo));
  811. lstrcpy(szPath, BOFileList(pbo)); // first file in list
  812. TCHAR szPathTgt[MAX_PATH];
  813. _GetPath(szPathTgt);
  814. int cch = PathCommonPrefix(szPath, szPathTgt, NULL);
  815. bRet = (0 < cch) && (lstrlen(szBriefPath) <= cch);
  816. GlobalUnlock(medium.hGlobal);
  817. ReleaseStgMedium(&medium);
  818. }
  819. return bRet;
  820. }
  821. // briefcase drop target specific handling gets computed here
  822. HRESULT CFSDropTarget::_FilterBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  823. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  824. {
  825. if (_IsBriefcaseTarget() && !_IsFromSameBriefcase())
  826. {
  827. STGMEDIUM medium = {0};
  828. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  829. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  830. {
  831. DWORD dwDefEffect = DROPEFFECT_COPY;
  832. DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail;
  833. if (pdwDefaultEffect)
  834. {
  835. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
  836. *pdwDefaultEffect = dwDefEffect;
  837. }
  838. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SYNCCOPY, 0, 0, 0, pfsMenuInfo);
  839. // Call _AddVerbs() again to force "Sync Copy of Type" as a 2nd DROPEFFECT_COPY verb:
  840. if ((DROPEFFECT_COPY & dwEffectsAvail) &&
  841. DroppingAnyFolders((HDROP)medium.hGlobal))
  842. {
  843. _AddVerbs(pdwEffects, DROPEFFECT_COPY, 0,
  844. DDIDM_SYNCCOPYTYPE, 0, 0, DROPEFFECT_COPY, pfsMenuInfo);
  845. }
  846. ReleaseStgMedium(&medium);
  847. }
  848. }
  849. return S_OK;
  850. }
  851. HRESULT CFSDropTarget::_FilterHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  852. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  853. {
  854. ASSERT(pdwEffects);
  855. DWORD dwDefEffect = 0;
  856. DWORD dwEffectAdd = dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE);
  857. if (pdwDefaultEffect)
  858. {
  859. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, _FilesystemAdjustedDefaultEffect(dwEffectAdd));
  860. *pdwDefaultEffect = dwDefEffect;
  861. }
  862. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_COPY, DDIDM_MOVE, 0, 0, pfsMenuInfo);
  863. return S_OK;
  864. }
  865. HRESULT CFSDropTarget::_FilterFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  866. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  867. {
  868. ASSERT(pdwEffects);
  869. if ((_dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) ||
  870. (_dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW))
  871. {
  872. DWORD dwEffectAdd, dwSuggestedEffect;
  873. //
  874. // HACK: if there is a preferred drop effect and no HIDA
  875. // then just take the preferred effect as the available effects
  876. // this is because we didn't actually check the FD_LINKUI bit
  877. // back when we assembled dwData! (performance)
  878. //
  879. if ((_dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) == DTID_PREFERREDEFFECT)
  880. {
  881. dwEffectAdd = _dwEffectPreferred;
  882. dwSuggestedEffect = _dwEffectPreferred;
  883. }
  884. else if (_dwData & DTID_FD_LINKUI)
  885. {
  886. dwEffectAdd = DROPEFFECT_LINK;
  887. dwSuggestedEffect = DROPEFFECT_LINK;
  888. }
  889. else
  890. {
  891. dwEffectAdd = DROPEFFECT_COPY | DROPEFFECT_MOVE;
  892. dwSuggestedEffect = DROPEFFECT_COPY;
  893. }
  894. dwEffectAdd &= dwEffectsAvail;
  895. DWORD dwDefEffect = 0;
  896. if (pdwDefaultEffect)
  897. {
  898. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, dwSuggestedEffect);
  899. *pdwDefaultEffect = dwDefEffect;
  900. }
  901. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect,
  902. DDIDM_CONTENTS_COPY, DDIDM_CONTENTS_MOVE, DDIDM_CONTENTS_LINK,
  903. 0, pfsMenuInfo);
  904. }
  905. return S_OK;
  906. }
  907. //
  908. // Old versions of OLE have a bug where if two FORMATETCs use the same
  909. // CLIPFORMAT, then only the first one makes it to the IEnumFORMATETC,
  910. // even if the other parameters (such as DVASPECT) are different.
  911. //
  912. // This causes us problems because those other DVASPECTs might be useful.
  913. // So if we see a FileContents with the wrong DVASPECT, sniff at the
  914. // object to see if maybe it also contains a copy with the correct DVASPECT.
  915. //
  916. // This bug was fixed in 1996 on the NT side, but the Win9x side was
  917. // not fixed. The Win9x OLE team was disbanded before the fix could
  918. // be propagated. So we get to work around this OLE bug forever.
  919. //
  920. HRESULT CFSDropTarget::_FilterFileContentsOLEHack(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  921. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  922. {
  923. FORMATETC fmte = *pfmte;
  924. fmte.dwAspect = DVASPECT_CONTENT;
  925. //
  926. // Whoa, this test is so (intentionally) backwards it isn't funny.
  927. //
  928. // We want to see whether there is a DVASPECT_CONTENT available in
  929. // the real object. So we first ask the object if it has a
  930. // DVASPECT_CONTENT format already. If the answer is yes, then we
  931. // **skip** this FORMATETC, because it will be found (or has already
  932. // been found) by our big EnumFORMATETC loop.
  933. //
  934. // If the answer is NO, then maybe we're hitting an OLE bug.
  935. // (They cache the list of available formats, but the bug is that
  936. // their cache is broken.) Bypass the cache by actually getting the
  937. // data. If it works, then run with it. Otherwise, I guess OLE wasn't
  938. // kidding.
  939. //
  940. // Note that we do not GetData() unconditionally -- bad for perf.
  941. // Only call GetData() after all the easy tests have failed.
  942. //
  943. HRESULT hr = _pdtobj->QueryGetData(&fmte);
  944. if (hr == DV_E_FORMATETC)
  945. {
  946. // Maybe we are hitting the OLE bug. Try harder.
  947. STGMEDIUM stgm = {0};
  948. if (SUCCEEDED(_pdtobj->GetData(&fmte, &stgm)))
  949. {
  950. // Yup. OLE lied to us.
  951. ReleaseStgMedium(&stgm);
  952. hr = _FilterFileContents(&fmte, grfKeyFlags, dwEffectsAvail,
  953. pdwEffects, pdwDefaultEffect, pfsMenuInfo);
  954. }
  955. else
  956. {
  957. // Whaddya know, OLE was telling the truth. Do nothing with this
  958. // format.
  959. hr = S_OK;
  960. }
  961. }
  962. else
  963. {
  964. // Either QueryGetData() failed in some bizarre way
  965. // (in which case we ignore the problem) or the QueryGetData
  966. // succeeded, in which case we ignore this FORMATETC since
  967. // the big enumeration will find (or has already found) the
  968. // DVASPECT_CONTENT.
  969. hr = S_OK;
  970. }
  971. return hr;
  972. }
  973. HRESULT CFSDropTarget::_FilterHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  974. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  975. {
  976. ASSERT(pdwEffects);
  977. DWORD dwDefEffect = 0;
  978. DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
  979. // NOTE: we only add a HIDA default effect if HDROP isn't going to add a default
  980. // effect. This preserves shell behavior with file system data objects without
  981. // requiring us to change the enumerator order in CIDLDataObj. When we do change
  982. // the enumerator order, we can remove this special case:
  983. if (pdwDefaultEffect &&
  984. ((0 == (_dwData & DTID_HDROP)) ||
  985. (0 == _GetDefaultEffect(grfKeyFlags,
  986. dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE),
  987. dwEffectsAvail,
  988. _FilesystemAdjustedDefaultEffect(dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE))))))
  989. {
  990. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
  991. *pdwDefaultEffect = dwDefEffect;
  992. }
  993. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_LINK, 0, pfsMenuInfo);
  994. return S_OK;
  995. }
  996. // {F20DA720-C02F-11CE-927B-0800095AE340}
  997. const GUID CLSID_CPackage = {0xF20DA720L, 0xC02F, 0x11CE, 0x92, 0x7B, 0x08, 0x00, 0x09, 0x5A, 0xE3, 0x40};
  998. // old packager guid...
  999. // {0003000C-0000-0000-C000-000000000046}
  1000. const GUID CLSID_OldPackage = {0x0003000CL, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
  1001. HRESULT CFSDropTarget::_FilterOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  1002. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  1003. {
  1004. ASSERT(pdwEffects);
  1005. HRESULT hr = S_FALSE;
  1006. if (pdwDefaultEffect)
  1007. {
  1008. *pdwDefaultEffect = 0;
  1009. }
  1010. FORMATETC fmte = {g_cfObjectDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1011. STGMEDIUM medium = {0};
  1012. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  1013. {
  1014. // we've got an object descriptor
  1015. OBJECTDESCRIPTOR* pOD = (OBJECTDESCRIPTOR*) GlobalLock(medium.hGlobal);
  1016. if (pOD)
  1017. {
  1018. if (IsEqualCLSID(CLSID_OldPackage, pOD->clsid) ||
  1019. IsEqualCLSID(CLSID_CPackage, pOD->clsid))
  1020. {
  1021. // This is a package - proceed
  1022. DWORD dwDefEffect = 0;
  1023. DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail;
  1024. if (pdwDefaultEffect)
  1025. {
  1026. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
  1027. *pdwDefaultEffect = dwDefEffect;
  1028. }
  1029. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect,
  1030. DDIDM_OBJECT_COPY, DDIDM_OBJECT_MOVE, 0,
  1031. 0, pfsMenuInfo);
  1032. hr = S_OK;
  1033. }
  1034. GlobalUnlock(medium.hGlobal);
  1035. }
  1036. ReleaseStgMedium(&medium);
  1037. }
  1038. return hr;
  1039. }
  1040. // REARCHITECT:
  1041. // This code has lots of problems. We need to fix this the text time we touch this code
  1042. // outside of ship mode. TO FIX:
  1043. // 1. Use SHAnsiToUnicode(CP_UTF8, ) to convert pszHTML to unicode. This will allow international
  1044. // paths to work.
  1045. // 2. Obey the selected range.
  1046. // 3. Use MSHTML to get the image. You can have trident parse the HTML via IHTMLTxtRange::pasteHTML.
  1047. // MS HTML has a special collection of images. Ask for the first image in that collection, or
  1048. // the first image in that collection within the selected range. (#1 isn't needed with this)
  1049. BOOL ExtractImageURLFromCFHTML(IN LPSTR pszHTML, IN SIZE_T cbHTMLSize, OUT LPSTR szImg, IN DWORD dwSize)
  1050. {
  1051. BOOL fSucceeded = FALSE;
  1052. // To avoid going nuts, only look at the first 64K of the HTML.
  1053. // (Important on Win64 because StrCpyNA doesn't support more than 4GB.)
  1054. if (cbHTMLSize > 0xFFFF)
  1055. cbHTMLSize = 0xFFFF;
  1056. // NT #391669: pszHTML isn't terminated, so terminate it now.
  1057. LPSTR pszCopiedHTML = (LPSTR) LocalAlloc(LPTR, cbHTMLSize + 1);
  1058. if (pszCopiedHTML)
  1059. {
  1060. LPSTR szTemp;
  1061. DWORD dwLen = dwSize;
  1062. BOOL bRet = TRUE;
  1063. StrCpyNA(pszCopiedHTML, pszHTML, (int)(cbHTMLSize + 1));
  1064. //DANGER WILL ROBINSON:
  1065. // HTML is comming in as UFT-8 encoded. Neither Unicode or Ansi,
  1066. // We've got to do something.... I'm going to party on it as if it were
  1067. // Ansi. This code will choke on escape sequences.....
  1068. //Find the base URL
  1069. //Locate <!--StartFragment-->
  1070. //Read the <IMG SRC="
  1071. //From there to the "> should be the Image URL
  1072. //Determine if it's an absolute or relative URL
  1073. //If relative, append to BASE url. You may need to lop off from the
  1074. // last delimiter to the end of the string.
  1075. //Pull out the SourceURL
  1076. LPSTR szBase = StrStrIA(pszCopiedHTML,"SourceURL:"); // Point to the char after :
  1077. if (szBase)
  1078. {
  1079. szBase += sizeof("SourceURL:")-1;
  1080. //Since each line can be terminated by a CR, CR/LF or LF check each case...
  1081. szTemp = StrChrA(szBase,'\n');
  1082. if (!szTemp)
  1083. szTemp = StrChrA(szBase,'\r');
  1084. if (szTemp)
  1085. *szTemp = '\0';
  1086. szTemp++;
  1087. }
  1088. else
  1089. szTemp = pszCopiedHTML;
  1090. //Pull out the Img Src
  1091. LPSTR pszImgSrc = StrStrIA(szTemp,"IMG");
  1092. if (pszImgSrc != NULL)
  1093. {
  1094. pszImgSrc = StrStrIA(pszImgSrc,"SRC");
  1095. if (pszImgSrc != NULL)
  1096. {
  1097. LPSTR pszImgSrcOrig = pszImgSrc;
  1098. pszImgSrc = StrChrA(pszImgSrc,'\"');
  1099. if (pszImgSrc)
  1100. {
  1101. pszImgSrc++; // Skip over the quote at the beginning of the src path.
  1102. szTemp = StrChrA(pszImgSrc,'\"'); // Find the end of the path.
  1103. }
  1104. else
  1105. {
  1106. LPSTR pszTemp1;
  1107. LPSTR pszTemp2;
  1108. pszImgSrc = StrChrA(pszImgSrcOrig,'=');
  1109. pszImgSrc++; // Skip past the equals to the first char in the path.
  1110. // Someday we may need to handle spaces between '=' and the path.
  1111. pszTemp1 = StrChrA(pszImgSrc,' '); // Since the path doesn't have quotes around it, assume a space will terminate it.
  1112. pszTemp2 = StrChrA(pszImgSrc,'>'); // Since the path doesn't have quotes around it, assume a space will terminate it.
  1113. szTemp = pszTemp1; // Assume quote terminates path.
  1114. if (!pszTemp1)
  1115. szTemp = pszTemp2; // Use '>' if quote not found.
  1116. if (pszTemp1 && pszTemp2 && (pszTemp2 < pszTemp1))
  1117. szTemp = pszTemp2; // Change to having '>' terminate path if both exist and it comes first.
  1118. }
  1119. *szTemp = '\0'; // Terminate path.
  1120. //At this point, I've reduced the 2 important strings. Now see if I need to
  1121. //Join them.
  1122. //If this fails, then we don't have a full URL, Only a relative.
  1123. if (!UrlIsA(pszImgSrc,URLIS_URL) && szBase)
  1124. {
  1125. if (SUCCEEDED(UrlCombineA(szBase, pszImgSrc, szImg, &dwLen,0)))
  1126. fSucceeded = TRUE;
  1127. }
  1128. else
  1129. {
  1130. if (lstrlenA(pszImgSrc) <= (int)dwSize)
  1131. lstrcpyA(szImg, pszImgSrc);
  1132. fSucceeded = TRUE;
  1133. }
  1134. }
  1135. }
  1136. LocalFree(pszCopiedHTML);
  1137. }
  1138. return fSucceeded;
  1139. }
  1140. HRESULT CFSDropTarget::_FilterDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  1141. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  1142. {
  1143. ASSERT(pdwEffects);
  1144. HRESULT hr = S_FALSE;
  1145. if (pdwDefaultEffect)
  1146. {
  1147. *pdwDefaultEffect = 0;
  1148. }
  1149. if (!PolicyNoActiveDesktop() &&
  1150. !SHRestricted(REST_NOADDDESKCOMP) &&
  1151. _IsDesktopFolder())
  1152. {
  1153. FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1154. STGMEDIUM medium = {0};
  1155. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  1156. {
  1157. //DANGER WILL ROBINSON:
  1158. //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
  1159. // it as is it were ANSI. Find a way to escape the sequences...
  1160. CHAR *pszData = (CHAR*) GlobalLock(medium.hGlobal);
  1161. if (pszData)
  1162. {
  1163. CHAR szUrl[MAX_URL_STRING];
  1164. if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl)))
  1165. {
  1166. // The HTML contains an image tag - carry on...
  1167. DWORD dwDefEffect = 0;
  1168. DWORD dwEffectAdd = DROPEFFECT_LINK; // NOTE: ignoring dwEffectsAvail!
  1169. if (pdwDefaultEffect)
  1170. {
  1171. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd,
  1172. dwEffectsAvail | DROPEFFECT_LINK, DROPEFFECT_LINK);
  1173. *pdwDefaultEffect = dwDefEffect;
  1174. }
  1175. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect,
  1176. 0, 0, DDIDM_CONTENTS_DESKIMG,
  1177. 0, pfsMenuInfo);
  1178. hr = S_OK;
  1179. }
  1180. GlobalUnlock(medium.hGlobal);
  1181. }
  1182. ReleaseStgMedium(&medium);
  1183. }
  1184. }
  1185. return hr;
  1186. }
  1187. HRESULT CFSDropTarget::_FilterDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  1188. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  1189. {
  1190. ASSERT(pdwEffects);
  1191. HRESULT hr = S_FALSE;
  1192. if (pdwDefaultEffect)
  1193. {
  1194. *pdwDefaultEffect = 0;
  1195. }
  1196. if (!PolicyNoActiveDesktop() &&
  1197. !SHRestricted(REST_NOADDDESKCOMP) &&
  1198. _IsDesktopFolder())
  1199. {
  1200. FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1201. STGMEDIUM medium = {0};
  1202. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  1203. {
  1204. // DANGER WILL ROBINSON:
  1205. // HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
  1206. // it as is it were ANSI. Find a way to escape the sequences...
  1207. CHAR *pszData = (CHAR*) GlobalLock(medium.hGlobal);
  1208. if (pszData)
  1209. {
  1210. int nScheme = GetUrlSchemeA(pszData);
  1211. if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP))
  1212. {
  1213. // This is an internet scheme - carry on...
  1214. DWORD dwDefEffect = 0;
  1215. DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
  1216. if (pdwDefaultEffect)
  1217. {
  1218. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
  1219. *pdwDefaultEffect = dwDefEffect;
  1220. }
  1221. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect,
  1222. 0, 0, DDIDM_CONTENTS_DESKURL,
  1223. DROPEFFECT_LINK, // force add this verb
  1224. pfsMenuInfo);
  1225. hr = S_OK;
  1226. }
  1227. GlobalUnlock(medium.hGlobal);
  1228. }
  1229. ReleaseStgMedium(&medium);
  1230. }
  1231. }
  1232. return hr;
  1233. }
  1234. HRESULT CFSDropTarget::_FilterOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  1235. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  1236. {
  1237. ASSERT(pdwEffects);
  1238. HRESULT hr = S_FALSE;
  1239. if (_dwData & DTID_OLEOBJ)
  1240. {
  1241. DWORD dwDefEffect = 0;
  1242. DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail;
  1243. if (pdwDefaultEffect)
  1244. {
  1245. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
  1246. *pdwDefaultEffect = dwDefEffect;
  1247. }
  1248. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SCRAP_COPY, DDIDM_SCRAP_MOVE, 0, 0, pfsMenuInfo);
  1249. hr = S_OK;
  1250. }
  1251. return hr;
  1252. }
  1253. HRESULT CFSDropTarget::_FilterOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
  1254. DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
  1255. {
  1256. ASSERT(pdwEffects);
  1257. HRESULT hr = S_FALSE;
  1258. if (_dwData & DTID_OLELINK)
  1259. {
  1260. DWORD dwDefEffect = 0;
  1261. DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
  1262. if (pdwDefaultEffect)
  1263. {
  1264. dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
  1265. *pdwDefaultEffect = dwDefEffect;
  1266. }
  1267. _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_DOCLINK, 0, pfsMenuInfo);
  1268. hr = S_OK;
  1269. }
  1270. return hr;
  1271. }
  1272. HRESULT CFSDropTarget::_CreateURLDeskComp(IDataObject *pdtobj, POINTL pt)
  1273. {
  1274. // This code should only be entered if DDIDM_CONTENTS_DESKURL was added to the menu,
  1275. // and it has these checks:
  1276. ASSERT(!PolicyNoActiveDesktop() &&
  1277. !SHRestricted(REST_NOADDDESKCOMP) &&
  1278. _IsDesktopFolder());
  1279. STGMEDIUM medium = {0};
  1280. FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1281. HRESULT hr = pdtobj->GetData(&fmte, &medium);
  1282. if (SUCCEEDED(hr))
  1283. {
  1284. //DANGER WILL ROBINSON:
  1285. //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
  1286. // it as is it were ANSI. Find a way to escape the sequences...
  1287. CHAR *pszData = (CHAR*) GlobalLock(medium.hGlobal);
  1288. if (pszData)
  1289. {
  1290. int nScheme = GetUrlSchemeA(pszData);
  1291. if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP))
  1292. {
  1293. // This is an internet scheme - URL
  1294. hr = CreateDesktopComponents(pszData, NULL, _hwnd, DESKCOMP_URL, pt.x, pt.y);
  1295. }
  1296. GlobalUnlock(medium.hGlobal);
  1297. }
  1298. else
  1299. {
  1300. hr = E_FAIL;
  1301. }
  1302. ReleaseStgMedium(&medium);
  1303. }
  1304. return hr;
  1305. }
  1306. HRESULT CFSDropTarget::_CreateDeskCompImage(IDataObject *pdtobj, POINTL pt)
  1307. {
  1308. ASSERT(!PolicyNoActiveDesktop() &&
  1309. !SHRestricted(REST_NOADDDESKCOMP) &&
  1310. _IsDesktopFolder());
  1311. FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1312. STGMEDIUM medium = {0};
  1313. HRESULT hr = pdtobj->GetData(&fmte, &medium);
  1314. if (SUCCEEDED(hr))
  1315. {
  1316. //DANGER WILL ROBINSON:
  1317. //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
  1318. // it as is it were ANSI. Find a way to escape the sequences...
  1319. CHAR *pszData = (CHAR*) GlobalLock(medium.hGlobal);
  1320. if (pszData)
  1321. {
  1322. CHAR szUrl[MAX_URL_STRING];
  1323. if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl)))
  1324. {
  1325. // The HTML contains an image tag - carry on...
  1326. ADDTODESKTOP *pToAD;
  1327. hr = SHLocalAlloc(sizeof(*pToAD), &pToAD);
  1328. if (SUCCEEDED(hr))
  1329. {
  1330. pToAD->hwnd = _hwnd;
  1331. lstrcpyA(pToAD->szUrl, szUrl);
  1332. pToAD->dwFlags = DESKCOMP_IMAGE;
  1333. pToAD->pt = pt;
  1334. if (SHCreateThread(AddToActiveDesktopThreadProc, pToAD, CTF_COINIT, NULL))
  1335. {
  1336. hr = S_OK;
  1337. }
  1338. else
  1339. {
  1340. LocalFree(pToAD);
  1341. hr = E_OUTOFMEMORY;
  1342. }
  1343. }
  1344. }
  1345. else
  1346. {
  1347. hr = E_FAIL;
  1348. }
  1349. GlobalUnlock(medium.hGlobal);
  1350. }
  1351. else
  1352. hr = E_FAIL;
  1353. ReleaseStgMedium(&medium);
  1354. }
  1355. return hr;
  1356. }
  1357. //
  1358. // read byte by byte until we hit the null terminating char
  1359. // return: the number of bytes read
  1360. //
  1361. HRESULT StringReadFromStream(IStream* pstm, LPSTR pszBuf, UINT cchBuf)
  1362. {
  1363. UINT cch = 0;
  1364. do {
  1365. pstm->Read(pszBuf, sizeof(CHAR), NULL);
  1366. cch++;
  1367. } while (*pszBuf++ && cch <= cchBuf);
  1368. return cch;
  1369. }
  1370. HRESULT CopyStreamToFile(IStream* pstmSrc, LPCTSTR pszFile, ULONGLONG ullFileSize)
  1371. {
  1372. IStream *pstmFile;
  1373. HRESULT hr = SHCreateStreamOnFile(pszFile, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, &pstmFile);
  1374. if (SUCCEEDED(hr))
  1375. {
  1376. hr = CopyStreamUI(pstmSrc, pstmFile, NULL, ullFileSize);
  1377. pstmFile->Release();
  1378. }
  1379. return hr;
  1380. }
  1381. HRESULT CFSDropTarget::_CreatePackage(IDataObject *pdtobj)
  1382. {
  1383. ILockBytes* pLockBytes;
  1384. HRESULT hr = CreateILockBytesOnHGlobal(NULL, TRUE, &pLockBytes);
  1385. if (SUCCEEDED(hr))
  1386. {
  1387. STGMEDIUM medium;
  1388. medium.tymed = TYMED_ISTORAGE;
  1389. hr = StgCreateDocfileOnILockBytes(pLockBytes,
  1390. STGM_DIRECT | STGM_READWRITE | STGM_CREATE |
  1391. STGM_SHARE_EXCLUSIVE, 0, &medium.pstg);
  1392. if (SUCCEEDED(hr))
  1393. {
  1394. FORMATETC fmte = {g_cfEmbeddedObject, NULL, DVASPECT_CONTENT, -1, TYMED_ISTORAGE};
  1395. hr = pdtobj->GetDataHere(&fmte, &medium);
  1396. if (SUCCEEDED(hr))
  1397. {
  1398. IStream* pstm;
  1399. #ifdef DEBUG
  1400. STATSTG stat;
  1401. if (SUCCEEDED(medium.pstg->Stat(&stat, STATFLAG_NONAME)))
  1402. {
  1403. ASSERT(IsEqualCLSID(CLSID_OldPackage, stat.clsid) ||
  1404. IsEqualCLSID(CLSID_CPackage, stat.clsid));
  1405. }
  1406. #endif // DEBUG
  1407. #define PACKAGER_ICON 2
  1408. #define PACKAGER_CONTENTS L"\001Ole10Native"
  1409. #define PACKAGER_EMBED_TYPE 3
  1410. hr = medium.pstg->OpenStream(PACKAGER_CONTENTS, 0,
  1411. STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
  1412. 0, &pstm);
  1413. if (SUCCEEDED(hr))
  1414. {
  1415. DWORD dw;
  1416. WORD w;
  1417. CHAR szName[MAX_PATH];
  1418. CHAR szTemp[MAX_PATH];
  1419. if (SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // pkg size
  1420. SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // pkg appearance
  1421. (PACKAGER_ICON == w) &&
  1422. SUCCEEDED(StringReadFromStream(pstm, szName, ARRAYSIZE(szName))) &&
  1423. SUCCEEDED(StringReadFromStream(pstm, szTemp, ARRAYSIZE(szTemp))) && // icon path
  1424. SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // icon index
  1425. SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // panetype
  1426. (PACKAGER_EMBED_TYPE == w) &&
  1427. SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // filename size
  1428. SUCCEEDED(pstm->Read(szTemp, dw, NULL)) && // filename
  1429. SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL))) // get file size
  1430. {
  1431. // The rest of the stream is the file contents
  1432. TCHAR szPath[MAX_PATH], szBase[MAX_PATH], szDest[MAX_PATH];
  1433. _GetPath(szPath);
  1434. SHAnsiToTChar(szName, szBase, ARRAYSIZE(szBase));
  1435. PathAppend(szPath, szBase);
  1436. PathYetAnotherMakeUniqueName(szDest, szPath, NULL, szBase);
  1437. TraceMsg(TF_GENERAL, "CFSIDLDropTarget pkg: %s", szDest);
  1438. hr = CopyStreamToFile(pstm, szDest, dw);
  1439. if (SUCCEEDED(hr))
  1440. {
  1441. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szDest, NULL);
  1442. if (_fBkDropTarget && _hwnd)
  1443. {
  1444. PositionFileFromDrop(_hwnd, szDest, NULL);
  1445. }
  1446. }
  1447. }
  1448. else
  1449. {
  1450. hr = E_UNEXPECTED;
  1451. }
  1452. pstm->Release();
  1453. }
  1454. }
  1455. medium.pstg->Release();
  1456. }
  1457. pLockBytes->Release();
  1458. }
  1459. return hr;
  1460. }
  1461. HRESULT CFSDropTarget::_GetPath(LPTSTR pszPath)
  1462. {
  1463. return _pFolder->_GetPath(pszPath);
  1464. }
  1465. LPCITEMIDLIST CFSDropTarget::_GetIDList()
  1466. {
  1467. return _pFolder->_GetIDList();
  1468. }
  1469. DWORD CFSDropTarget::_EffectFromFolder()
  1470. {
  1471. if (-1 == _dwEffectFolder)
  1472. {
  1473. _dwEffectFolder = DROPEFFECT_NONE; // re-set to nothing (0)
  1474. TCHAR szPath[MAX_PATH];
  1475. // add a simple pathisroot check here to prevent it from hitting the disk (mostly floppy)
  1476. // when we want the dropeffect probe to be fast (sendto, hovering over drives in view).
  1477. // its not likely that we'll want to modify the root's drop effect, and this still allows
  1478. // dropeffect modification on floppy subfolders.
  1479. if (SUCCEEDED(_GetPath(szPath)) && !PathIsRoot(szPath) && PathAppend(szPath, TEXT("desktop.ini")))
  1480. {
  1481. _dwEffectFolder = GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("DefaultDropEffect"), 0, szPath);
  1482. }
  1483. }
  1484. return _dwEffectFolder;
  1485. }
  1486. BOOL AllRegisteredPrograms(HDROP hDrop)
  1487. {
  1488. TCHAR szPath[MAX_PATH];
  1489. for (UINT i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++)
  1490. {
  1491. if (!PathIsRegisteredProgram(szPath))
  1492. return FALSE;
  1493. }
  1494. return TRUE;
  1495. }
  1496. BOOL IsBriefcaseRoot(IDataObject *pdtobj)
  1497. {
  1498. BOOL bRet = FALSE;
  1499. STGMEDIUM medium;
  1500. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1501. if (pida)
  1502. {
  1503. // Is there a briefcase root in this pdtobj?
  1504. IShellFolder2 *psf;
  1505. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, IDA_GetIDListPtr(pida, (UINT)-1), &psf))))
  1506. {
  1507. for (UINT i = 0; i < pida->cidl; i++)
  1508. {
  1509. CLSID clsid;
  1510. bRet = SUCCEEDED(GetItemCLSID(psf, IDA_GetIDListPtr(pida, i), &clsid)) &&
  1511. IsEqualCLSID(clsid, CLSID_Briefcase);
  1512. if (bRet)
  1513. break;
  1514. }
  1515. psf->Release();
  1516. }
  1517. HIDA_ReleaseStgMedium(pida, &medium);
  1518. }
  1519. return bRet;
  1520. }
  1521. //
  1522. // the "default effect" defines what will be choosen out of the allowed effects
  1523. //
  1524. // If the data object does NOT contain HDROP -> "none"
  1525. // else if the source data object has a default drop effect folder list (maybe based on sub folderness)
  1526. // else if the source is root or registered progam -> "link"
  1527. // else if this is within a volume -> "move"
  1528. // else if this is a briefcase -> "move"
  1529. // else -> "copy"
  1530. //
  1531. DWORD CFSDropTarget::_FilesystemAdjustedDefaultEffect(DWORD dwCurEffectAvail)
  1532. {
  1533. DWORD dwDefEffect = DROPEFFECT_NONE;
  1534. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1535. STGMEDIUM medium = {0};
  1536. if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
  1537. {
  1538. TCHAR szPath[MAX_PATH];
  1539. DragQueryFile((HDROP) medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)); // focused item
  1540. // DROPEFFECTFOLDERLIST allows the source of the data
  1541. // to specify the desired drop effect for items under
  1542. // certain parts of the name space.
  1543. //
  1544. // cd-burning does this to avoid the default move/copy computation
  1545. // that would kick in for cross volume CD burning/staging area transfers
  1546. FORMATETC fmteDropFolders = {g_cfDropEffectFolderList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1547. STGMEDIUM mediumDropFolders = {0};
  1548. if (SUCCEEDED(_pdtobj->GetData(&fmteDropFolders, &mediumDropFolders)))
  1549. {
  1550. DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)GlobalLock(mediumDropFolders.hGlobal);
  1551. if (pdefl)
  1552. {
  1553. // get the default effect from the list -- in the staging area case this is DROPEFFECT_COPY
  1554. // so its a copy even if the staging area and source are on the same volume.
  1555. dwDefEffect = pdefl->dwDefaultDropEffect;
  1556. for (INT i = 0; i < pdefl->cFolders; i++)
  1557. {
  1558. // some folders are excluded, for example if you move a file from one part of the staging
  1559. // area to another we override (to DROPEFFECT_MOVE in this case).
  1560. if (PathIsEqualOrSubFolder(pdefl->aFolders[i].wszPath, szPath))
  1561. {
  1562. dwDefEffect = pdefl->aFolders[i].dwDropEffect;
  1563. break;
  1564. }
  1565. }
  1566. GlobalUnlock(pdefl);
  1567. }
  1568. ReleaseStgMedium(&mediumDropFolders);
  1569. }
  1570. if (DROPEFFECT_NONE == dwDefEffect)
  1571. {
  1572. dwDefEffect = _EffectFromFolder();
  1573. }
  1574. // If we didn't get a drop effect (==0) then lets fall back to the old checks
  1575. if (DROPEFFECT_NONE == dwDefEffect)
  1576. {
  1577. TCHAR szFolder[MAX_PATH];
  1578. _GetPath(szFolder);
  1579. // drive/UNC roots and installed programs get link
  1580. if (PathIsRoot(szPath) || AllRegisteredPrograms((HDROP)medium.hGlobal))
  1581. {
  1582. dwDefEffect = DROPEFFECT_LINK;
  1583. }
  1584. else if (PathIsSameRoot(szPath, szFolder))
  1585. {
  1586. dwDefEffect = DROPEFFECT_MOVE;
  1587. }
  1588. else if (IsBriefcaseRoot(_pdtobj))
  1589. {
  1590. // briefcase default to move even accross volumes
  1591. dwDefEffect = DROPEFFECT_MOVE;
  1592. }
  1593. else
  1594. {
  1595. dwDefEffect = DROPEFFECT_COPY;
  1596. }
  1597. }
  1598. ReleaseStgMedium(&medium);
  1599. }
  1600. else if (SUCCEEDED(_pdtobj->QueryGetData(&fmte)))
  1601. {
  1602. // but QueryGetData() succeeds!
  1603. // this means this data object has HDROP but can't
  1604. // provide it until it is dropped. Let's assume we are copying.
  1605. dwDefEffect = DROPEFFECT_COPY;
  1606. }
  1607. // Switch default verb if the dwCurEffectAvail hint suggests that we picked an
  1608. // unavailable effect (this code applies to MOVE and COPY only):
  1609. dwCurEffectAvail &= (DROPEFFECT_MOVE | DROPEFFECT_COPY);
  1610. if ((DROPEFFECT_MOVE == dwDefEffect) && (DROPEFFECT_COPY == dwCurEffectAvail))
  1611. {
  1612. // If we were going to return MOVE, and only COPY is available, return COPY:
  1613. dwDefEffect = DROPEFFECT_COPY;
  1614. }
  1615. else if ((DROPEFFECT_COPY == dwDefEffect) && (DROPEFFECT_MOVE == dwCurEffectAvail))
  1616. {
  1617. // If we were going to return COPY, and only MOVE is available, return MOVE:
  1618. dwDefEffect = DROPEFFECT_MOVE;
  1619. }
  1620. return dwDefEffect;
  1621. }
  1622. //
  1623. // make sure that the default effect is among the allowed effects
  1624. //
  1625. DWORD CFSDropTarget::_LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed)
  1626. {
  1627. if (dwDefEffect & dwEffectsAllowed)
  1628. return dwDefEffect;
  1629. if (dwEffectsAllowed & DROPEFFECT_COPY)
  1630. return DROPEFFECT_COPY;
  1631. if (dwEffectsAllowed & DROPEFFECT_MOVE)
  1632. return DROPEFFECT_MOVE;
  1633. if (dwEffectsAllowed & DROPEFFECT_LINK)
  1634. return DROPEFFECT_LINK;
  1635. return DROPEFFECT_NONE;
  1636. }
  1637. // Handy abbreviation
  1638. #define TYMED_ALLCONTENT (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)
  1639. // Use FSDH for registered clipboard formats (anything of the form g_cf*)
  1640. // Use _FSDH for predefined clipboard formats (like CF_HDROP or 0)
  1641. // Generate the _DATA_HANDLER array
  1642. #define FSDH(pfn, cf, dva, tymed) { { 0, NULL, dva, -1, tymed }, pfn, &cf }
  1643. #define _FSDH(pfn, cf, dva, tymed) { { (CLIPFORMAT)cf, NULL, dva, -1, tymed }, pfn, NULL }
  1644. // NOTE: the order is important (particularly for multiple entries with the same FORMATETC)
  1645. CFSDropTarget::_DATA_HANDLER
  1646. CFSDropTarget::rg_data_handlers[NUM_DATA_HANDLERS] = {
  1647. FSDH(_FilterFileContents, g_cfFileGroupDescriptorW, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1648. FSDH(_FilterFileContentsOLEHack, g_cfFileGroupDescriptorW, DVASPECT_LINK, TYMED_HGLOBAL),
  1649. FSDH(_FilterFileContents, g_cfFileGroupDescriptorA, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1650. FSDH(_FilterFileContentsOLEHack, g_cfFileGroupDescriptorA, DVASPECT_LINK, TYMED_HGLOBAL),
  1651. FSDH(_FilterFileContents, g_cfFileContents, DVASPECT_CONTENT, TYMED_ALLCONTENT),
  1652. FSDH(_FilterFileContentsOLEHack, g_cfFileContents, DVASPECT_LINK, TYMED_ALLCONTENT),
  1653. _FSDH(_FilterBriefcase, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1654. _FSDH(_FilterSneakernetBriefcase, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1655. _FSDH(_FilterHDROP, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1656. _FSDH(_FilterDeskCompHDROP, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1657. FSDH(_FilterHIDA, g_cfHIDA, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1658. FSDH(_FilterOlePackage, g_cfEmbeddedObject, DVASPECT_CONTENT, TYMED_ISTORAGE),
  1659. FSDH(_FilterDeskImage, g_cfHTML, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1660. FSDH(_FilterDeskComp, g_cfShellURL, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1661. _FSDH(_FilterOleObj, 0, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1662. _FSDH(_FilterOleLink, 0, DVASPECT_CONTENT, TYMED_HGLOBAL),
  1663. };
  1664. // Note that it's safe to race with another thread in this code
  1665. // since the function is idemponent. (Call it as many times as you
  1666. // like -- only the first time through actually does anything.)
  1667. void CFSDropTarget::_Init_rg_data_handlers()
  1668. {
  1669. for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
  1670. {
  1671. // If this assertion fires, then you have to change the value of
  1672. // NUM_DATA_HANDLERS to match the number of entries in the array
  1673. // definition.
  1674. ASSERT(rg_data_handlers[i].fmte.tymed);
  1675. if (rg_data_handlers[i].pcfInit)
  1676. {
  1677. rg_data_handlers[i].fmte.cfFormat = *rg_data_handlers[i].pcfInit;
  1678. }
  1679. }
  1680. }
  1681. //
  1682. // returns the default effect.
  1683. // also modifies *pdwEffectInOut to indicate "available" operations.
  1684. //
  1685. DWORD CFSDropTarget::_DetermineEffects(DWORD grfKeyState, DWORD *pdwEffectInOut, HMENU hmenu)
  1686. {
  1687. DWORD dwDefaultEffect = DROPEFFECT_NONE;
  1688. DWORD dwEffectsUsed = DROPEFFECT_NONE;
  1689. _Init_rg_data_handlers();
  1690. // Loop through formats, factoring in both the order of the enumerator and
  1691. // the order of our rg_data_handlers to determine the default effect
  1692. // (and possibly, to create the drop context menu)
  1693. FSMENUINFO fsmi = { hmenu, 0, 0, 0 };
  1694. IEnumFORMATETC *penum;
  1695. AssertMsg((NULL != _pdtobj), TEXT("CFSDropTarget::_DetermineEffects() _pdtobj is NULL but we need it. this=%#08lx"), this);
  1696. if (_pdtobj && SUCCEEDED(_pdtobj->EnumFormatEtc(DATADIR_GET, &penum)))
  1697. {
  1698. FORMATETC fmte;
  1699. ULONG celt;
  1700. while (penum->Next(1, &fmte, &celt) == S_OK)
  1701. {
  1702. for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
  1703. {
  1704. if (rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat &&
  1705. rg_data_handlers[i].fmte.dwAspect == fmte.dwAspect &&
  1706. (rg_data_handlers[i].fmte.tymed & fmte.tymed))
  1707. {
  1708. // keep passing dwDefaultEffect until someone computes one, this
  1709. // lets the first guy that figures out the default be the default
  1710. (this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
  1711. &fmte, grfKeyState, *pdwEffectInOut, &dwEffectsUsed,
  1712. (DROPEFFECT_NONE == dwDefaultEffect) ? &dwDefaultEffect : NULL,
  1713. hmenu ? &fsmi : NULL);
  1714. }
  1715. }
  1716. SHFree(fmte.ptd);
  1717. }
  1718. penum->Release();
  1719. }
  1720. // Loop through the rg_data_handlers that don't have an associated clipboard format last
  1721. for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
  1722. {
  1723. if (0 == rg_data_handlers[i].fmte.cfFormat)
  1724. {
  1725. // if default effect is still not computed continue to pass that
  1726. (this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
  1727. NULL, grfKeyState, *pdwEffectInOut, &dwEffectsUsed,
  1728. (DROPEFFECT_NONE == dwDefaultEffect) ? &dwDefaultEffect : NULL,
  1729. hmenu ? &fsmi : NULL);
  1730. }
  1731. }
  1732. *pdwEffectInOut &= dwEffectsUsed;
  1733. dwDefaultEffect = _LimitDefaultEffect(dwDefaultEffect, *pdwEffectInOut);
  1734. DebugMsg(TF_FSTREE, TEXT("CFSDT::GetDefaultEffect dwDef=%x, dwEffUsed=%x, *pdw=%x"),
  1735. dwDefaultEffect, dwEffectsUsed, *pdwEffectInOut);
  1736. return dwDefaultEffect; // this is what we want to do
  1737. }
  1738. // This is used to map command id's back to dropeffect's:
  1739. const struct {
  1740. UINT uID;
  1741. DWORD dwEffect;
  1742. } c_IDFSEffects[] = {
  1743. DDIDM_COPY, DROPEFFECT_COPY,
  1744. DDIDM_MOVE, DROPEFFECT_MOVE,
  1745. DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK,
  1746. DDIDM_LINK, DROPEFFECT_LINK,
  1747. DDIDM_SCRAP_COPY, DROPEFFECT_COPY,
  1748. DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE,
  1749. DDIDM_DOCLINK, DROPEFFECT_LINK,
  1750. DDIDM_CONTENTS_COPY, DROPEFFECT_COPY,
  1751. DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE,
  1752. DDIDM_CONTENTS_LINK, DROPEFFECT_LINK,
  1753. DDIDM_CONTENTS_DESKIMG, DROPEFFECT_LINK,
  1754. DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important)
  1755. DDIDM_SYNCCOPY, DROPEFFECT_COPY,
  1756. DDIDM_OBJECT_COPY, DROPEFFECT_COPY,
  1757. DDIDM_OBJECT_MOVE, DROPEFFECT_MOVE,
  1758. DDIDM_CONTENTS_DESKURL, DROPEFFECT_LINK,
  1759. };
  1760. void CFSDropTarget::_FixUpDefaultItem(HMENU hmenu, DWORD dwDefEffect)
  1761. {
  1762. // only do stuff if there is no default item already and we have a default effect
  1763. if ((GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1) && dwDefEffect)
  1764. {
  1765. for (int i = 0; i < GetMenuItemCount(hmenu); i++)
  1766. {
  1767. // for menu item matching default effect, make it the default.
  1768. MENUITEMINFO mii = { 0 };
  1769. mii.cbSize = sizeof(mii);
  1770. mii.fMask = MIIM_DATA | MIIM_STATE;
  1771. if (GetMenuItemInfo(hmenu, i, MF_BYPOSITION, &mii) && (mii.dwItemData == dwDefEffect))
  1772. {
  1773. mii.fState |= MFS_DEFAULT;
  1774. SetMenuItemInfo(hmenu, i, MF_BYPOSITION, &mii);
  1775. break;
  1776. }
  1777. }
  1778. }
  1779. }
  1780. HRESULT CFSDropTarget::_DragDropMenu(FSDRAGDROPMENUPARAM *pddm)
  1781. {
  1782. HRESULT hr = E_OUTOFMEMORY; // assume error
  1783. DWORD dwEffectOut = 0; // assume no-ope.
  1784. if (pddm->hmenu)
  1785. {
  1786. UINT idCmd;
  1787. UINT idCmdFirst = DDIDM_EXTFIRST;
  1788. HDXA hdxa = HDXA_Create();
  1789. HDCA hdca = DCA_Create();
  1790. if (hdxa && hdca)
  1791. {
  1792. // Enumerate the DD handlers and let them append menu items.
  1793. for (DWORD i = 0; i < pddm->ck; i++)
  1794. {
  1795. DCA_AddItemsFromKey(hdca, pddm->rghk[i], STRREG_SHEX_DDHANDLER);
  1796. }
  1797. idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, pddm->ck,
  1798. pddm->rghk, _GetIDList(), pddm->hmenu, 0,
  1799. DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca);
  1800. }
  1801. // modifier keys held down to force operations that are not permitted (for example
  1802. // alt to force a shortcut from the start menu, which does not have SFGAO_CANLINK)
  1803. // can result in no default items on the context menu. however in displaying the
  1804. // cursor overlay in this case we fall back to DROPEFFECT_COPY. a left drag then
  1805. // tries to invoke the default menu item (user thinks its copy) but theres no default.
  1806. // this function selects a default menu item to match the default effect if there
  1807. // is no default item already.
  1808. _FixUpDefaultItem(pddm->hmenu, pddm->dwDefEffect);
  1809. // If this dragging is caused by the left button, simply choose
  1810. // the default one, otherwise, pop up the context menu. If there
  1811. // is no key state info and the original effect is the same as the
  1812. // current effect, choose the default one, otherwise pop up the
  1813. // context menu.
  1814. if ((_grfKeyStateLast & MK_LBUTTON) ||
  1815. (!_grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect)))
  1816. {
  1817. idCmd = GetMenuDefaultItem(pddm->hmenu, MF_BYCOMMAND, 0);
  1818. // This one MUST be called here. Please read its comment block.
  1819. DAD_DragLeave();
  1820. if (_hwnd)
  1821. SetForegroundWindow(_hwnd);
  1822. }
  1823. else
  1824. {
  1825. // Note that SHTrackPopupMenu calls DAD_DragLeave().
  1826. idCmd = SHTrackPopupMenu(pddm->hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  1827. pddm->pt.x, pddm->pt.y, 0, _hwnd, NULL);
  1828. }
  1829. //
  1830. // We also need to call this here to release the dragged image.
  1831. //
  1832. DAD_SetDragImage(NULL, NULL);
  1833. //
  1834. // Check if the user selected one of add-in menu items.
  1835. //
  1836. if (idCmd == 0)
  1837. {
  1838. hr = S_OK; // Canceled by the user, return S_OK
  1839. }
  1840. else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST))
  1841. {
  1842. //
  1843. // Yes. Let the context menu handler process it.
  1844. //
  1845. CMINVOKECOMMANDINFOEX ici = {
  1846. sizeof(CMINVOKECOMMANDINFOEX),
  1847. 0L,
  1848. _hwnd,
  1849. (LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST),
  1850. NULL, NULL,
  1851. SW_NORMAL,
  1852. };
  1853. // record if the shift/control keys were down at the time of the drop
  1854. if (_grfKeyStateLast & MK_SHIFT)
  1855. {
  1856. ici.fMask |= CMIC_MASK_SHIFT_DOWN;
  1857. }
  1858. if (_grfKeyStateLast & MK_CONTROL)
  1859. {
  1860. ici.fMask |= CMIC_MASK_CONTROL_DOWN;
  1861. }
  1862. // We may not want to ignore the error code. (Can happen when you use the context menu
  1863. // to create new folders, but I don't know if that can happen here.).
  1864. HDXA_LetHandlerProcessCommandEx(hdxa, &ici, NULL);
  1865. hr = S_OK;
  1866. }
  1867. else
  1868. {
  1869. for (int nItem = 0; nItem < ARRAYSIZE(c_IDFSEffects); ++nItem)
  1870. {
  1871. if (idCmd == c_IDFSEffects[nItem].uID)
  1872. {
  1873. dwEffectOut = c_IDFSEffects[nItem].dwEffect;
  1874. break;
  1875. }
  1876. }
  1877. hr = S_FALSE;
  1878. }
  1879. if (hdca)
  1880. DCA_Destroy(hdca);
  1881. if (hdxa)
  1882. HDXA_Destroy(hdxa);
  1883. pddm->idCmd = idCmd;
  1884. }
  1885. *pddm->pdwEffect = dwEffectOut;
  1886. return hr;
  1887. }
  1888. void _MapName(void *hNameMap, LPTSTR pszPath)
  1889. {
  1890. if (hNameMap)
  1891. {
  1892. SHNAMEMAPPING *pNameMapping;
  1893. for (int i = 0; (pNameMapping = SHGetNameMappingPtr((HDSA)hNameMap, i)) != NULL; i++)
  1894. {
  1895. if (lstrcmpi(pszPath, pNameMapping->pszOldPath) == 0)
  1896. {
  1897. lstrcpy(pszPath, pNameMapping->pszNewPath);
  1898. break;
  1899. }
  1900. }
  1901. }
  1902. }
  1903. // convert double null list of files to array of pidls
  1904. int FileListToIDArray(LPCTSTR pszFiles, void *hNameMap, LPITEMIDLIST **pppidl)
  1905. {
  1906. int i = 0;
  1907. int nItems = CountFiles(pszFiles);
  1908. LPITEMIDLIST *ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, nItems * sizeof(*ppidl));
  1909. if (ppidl)
  1910. {
  1911. *pppidl = ppidl;
  1912. while (*pszFiles)
  1913. {
  1914. TCHAR szPath[MAX_PATH];
  1915. lstrcpy(szPath, pszFiles);
  1916. _MapName(hNameMap, szPath);
  1917. ppidl[i] = SHSimpleIDListFromPath(szPath);
  1918. pszFiles += lstrlen(pszFiles) + 1;
  1919. i++;
  1920. }
  1921. }
  1922. return i;
  1923. }
  1924. // move items to the new drop location
  1925. void CFSDropTarget::_MoveSelectIcons(IDataObject *pdtobj, IFolderView* pfv, void *hNameMap, LPCTSTR pszFiles, BOOL fMove, HDROP hDrop)
  1926. {
  1927. LPITEMIDLIST *ppidl = NULL;
  1928. int cidl;
  1929. if (pszFiles)
  1930. {
  1931. cidl = FileListToIDArray(pszFiles, hNameMap, &ppidl);
  1932. }
  1933. else
  1934. {
  1935. cidl = CreateMoveCopyList(hDrop, hNameMap, &ppidl);
  1936. }
  1937. if (ppidl)
  1938. {
  1939. if (pfv)
  1940. PositionItems(pfv, (LPCITEMIDLIST*)ppidl, cidl, pdtobj, fMove ? &_ptDrop : NULL);
  1941. FreeIDListArray(ppidl, cidl);
  1942. }
  1943. }
  1944. // this is the ILIsParent which matches up the desktop with the desktop directory.
  1945. BOOL AliasILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1946. {
  1947. LPITEMIDLIST pidlUse1 = SHLogILFromFSIL(pidl1);
  1948. if (pidlUse1)
  1949. pidl1 = pidlUse1;
  1950. LPITEMIDLIST pidlUse2 = SHLogILFromFSIL(pidl2);
  1951. if (pidlUse2)
  1952. pidl2 = pidlUse2;
  1953. BOOL fSame = ILIsParent(pidl1, pidl2, TRUE);
  1954. ILFree(pidlUse1); // NULL is OK here
  1955. ILFree(pidlUse2);
  1956. return fSame;
  1957. }
  1958. // in:
  1959. // pszDestDir destination dir for new file names
  1960. // pszDestSpecs double null list of destination specs
  1961. //
  1962. // returns:
  1963. // double null list of fully qualified destination file names to be freed
  1964. // with LocalFree()
  1965. //
  1966. LPTSTR RemapDestNamesW(LPCTSTR pszDestDir, LPCWSTR pszDestSpecs)
  1967. {
  1968. UINT cbDestSpec = lstrlen(pszDestDir) * sizeof(TCHAR) + sizeof(TCHAR);
  1969. LPCWSTR pszTemp;
  1970. UINT cbAlloc = sizeof(TCHAR); // for double NULL teriminaion of entire string
  1971. // compute length of buffer to aloc
  1972. for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenW(pszTemp) + 1)
  1973. {
  1974. // +1 for null teriminator
  1975. cbAlloc += cbDestSpec + lstrlenW(pszTemp) * sizeof(TCHAR) + sizeof(TCHAR);
  1976. }
  1977. LPTSTR pszRet = (LPTSTR)LocalAlloc(LPTR, cbAlloc);
  1978. if (pszRet)
  1979. {
  1980. LPTSTR pszDest = pszRet;
  1981. for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenW(pszTemp) + 1)
  1982. {
  1983. // PathCombine requires dest buffer of MAX_PATH size or it'll rip in call
  1984. // to PathCanonicalize (IsBadWritePtr)
  1985. TCHAR szTempDest[MAX_PATH];
  1986. PathCombine(szTempDest, pszDestDir, pszTemp);
  1987. lstrcpy(pszDest, szTempDest);
  1988. pszDest += lstrlen(pszDest) + 1;
  1989. ASSERT((UINT)((BYTE *)pszDest - (BYTE *)pszRet) < cbAlloc);
  1990. ASSERT(*pszDest == 0); // zero init alloc
  1991. }
  1992. ASSERT((LPTSTR)((BYTE *)pszRet + cbAlloc - sizeof(TCHAR)) >= pszDest);
  1993. ASSERT(*pszDest == 0); // zero init alloc
  1994. }
  1995. return pszRet;
  1996. }
  1997. LPTSTR RemapDestNamesA(LPCTSTR pszDestDir, LPCSTR pszDestSpecs)
  1998. {
  1999. UINT cbDestSpec = lstrlen(pszDestDir) * sizeof(TCHAR) + sizeof(TCHAR);
  2000. LPCSTR pszTemp;
  2001. LPTSTR pszRet;
  2002. UINT cbAlloc = sizeof(TCHAR); // for double NULL teriminaion of entire string
  2003. // compute length of buffer to aloc
  2004. for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenA(pszTemp) + 1)
  2005. {
  2006. // +1 for null teriminator
  2007. cbAlloc += cbDestSpec + lstrlenA(pszTemp) * sizeof(TCHAR) + sizeof(TCHAR);
  2008. }
  2009. pszRet = (LPTSTR)LocalAlloc(LPTR, cbAlloc);
  2010. if (pszRet)
  2011. {
  2012. LPTSTR pszDest = pszRet;
  2013. for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenA(pszTemp) + 1)
  2014. {
  2015. // PathCombine requires dest buffer of MAX_PATH size or it'll rip in call
  2016. // to PathCanonicalize (IsBadWritePtr)
  2017. TCHAR szTempDest[MAX_PATH];
  2018. WCHAR wszTemp[MAX_PATH];
  2019. SHAnsiToUnicode(pszTemp, wszTemp, ARRAYSIZE(wszTemp));
  2020. PathCombine(szTempDest, pszDestDir, wszTemp);
  2021. lstrcpy(pszDest, szTempDest);
  2022. pszDest += lstrlen(pszDest) + 1;
  2023. ASSERT((UINT)((BYTE *)pszDest - (BYTE *)pszRet) < cbAlloc);
  2024. ASSERT(*pszDest == 0); // zero init alloc
  2025. }
  2026. ASSERT((LPTSTR)((BYTE *)pszRet + cbAlloc - sizeof(TCHAR)) >= pszDest);
  2027. ASSERT(*pszDest == 0); // zero init alloc
  2028. }
  2029. return pszRet;
  2030. }
  2031. LPTSTR _GetDestNames(IDataObject *pdtobj, LPCTSTR pszPath)
  2032. {
  2033. LPTSTR pszDestNames = NULL;
  2034. STGMEDIUM medium;
  2035. FORMATETC fmte = {g_cfFileNameMapW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  2036. if (S_OK == pdtobj->GetData(&fmte, &medium))
  2037. {
  2038. pszDestNames = RemapDestNamesW(pszPath, (LPWSTR)GlobalLock(medium.hGlobal));
  2039. ReleaseStgMediumHGLOBAL(medium.hGlobal, &medium);
  2040. }
  2041. else
  2042. {
  2043. fmte.cfFormat = g_cfFileNameMapA;
  2044. if (S_OK == pdtobj->GetData(&fmte, &medium))
  2045. {
  2046. pszDestNames = RemapDestNamesA(pszPath, (LPSTR)GlobalLock(medium.hGlobal));
  2047. ReleaseStgMediumHGLOBAL(medium.hGlobal, &medium);
  2048. }
  2049. }
  2050. return pszDestNames;
  2051. }
  2052. BOOL _IsInSameFolder(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj)
  2053. {
  2054. BOOL bRet = FALSE;
  2055. STGMEDIUM medium = {0};
  2056. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  2057. if (pida)
  2058. {
  2059. for (UINT i = 0; i < pida->cidl; i++)
  2060. {
  2061. LPITEMIDLIST pidl = IDA_FullIDList(pida, i);
  2062. if (pidl)
  2063. {
  2064. // if we're doing keyboard cut/copy/paste
  2065. // to and from the same directories
  2066. // This is needed for common desktop support - BobDay/EricFlo
  2067. if (AliasILIsParent(pidlFolder, pidl))
  2068. {
  2069. bRet = TRUE;
  2070. }
  2071. ILFree(pidl);
  2072. }
  2073. }
  2074. HIDA_ReleaseStgMedium(pida, &medium);
  2075. }
  2076. return bRet;
  2077. }
  2078. LPCTSTR _RootSpecialCase(LPCTSTR pszFiles, LPTSTR pszSrc, LPTSTR pszDest)
  2079. {
  2080. if ((1 == CountFiles(pszFiles)) &&
  2081. PathIsRoot(pszFiles))
  2082. {
  2083. SHFILEINFO sfi;
  2084. // NOTE: don't use SHGFI_USEFILEATTRIBUTES because the simple IDList
  2085. // support for \\server\share produces the wrong name
  2086. if (SHGetFileInfo(pszFiles, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
  2087. {
  2088. if (!(PCS_FATAL & PathCleanupSpec(pszDest, sfi.szDisplayName)))
  2089. {
  2090. PathAppend(pszDest, sfi.szDisplayName); // sub dir name based on source root path
  2091. PathCombine(pszSrc, pszFiles, TEXT("*.*")); // all files on source
  2092. pszFiles = pszSrc;
  2093. }
  2094. }
  2095. }
  2096. return pszFiles;
  2097. }
  2098. void CFSDropTarget::_MoveCopy(IDataObject *pdtobj, IFolderView* pfv, HDROP hDrop)
  2099. {
  2100. #ifdef DEBUG
  2101. if (_hwnd == NULL)
  2102. {
  2103. TraceMsg(TF_GENERAL, "_MoveCopy() without an hwnd which will prevent displaying insert disk UI");
  2104. }
  2105. #endif // DEBUG
  2106. DRAGINFO di = { sizeof(di) };
  2107. if (DragQueryInfo(hDrop, &di))
  2108. {
  2109. TCHAR szDest[MAX_PATH] = {0}; // zero init for dbl null termination
  2110. _GetPath(szDest);
  2111. switch (_idCmd)
  2112. {
  2113. case DDIDM_MOVE:
  2114. if (_fSameHwnd)
  2115. {
  2116. _MoveSelectIcons(pdtobj, pfv, NULL, NULL, TRUE, hDrop);
  2117. break;
  2118. }
  2119. // fall through...
  2120. case DDIDM_COPY:
  2121. {
  2122. TCHAR szAltSource[MAX_PATH] = {0}; // zero init for dbl null termination
  2123. LPCTSTR pszSource = _RootSpecialCase(di.lpFileList, szAltSource, szDest);
  2124. SHFILEOPSTRUCT fo =
  2125. {
  2126. _hwnd,
  2127. (DDIDM_COPY == _idCmd) ? FO_COPY : FO_MOVE,
  2128. pszSource,
  2129. szDest,
  2130. FOF_WANTMAPPINGHANDLE | FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR
  2131. };
  2132. if (fo.wFunc == FO_MOVE && IsFolderSecurityModeOn())
  2133. {
  2134. fo.fFlags |= FOF_NOCOPYSECURITYATTRIBS;
  2135. }
  2136. // if they are in the same hwnd or to and from
  2137. // the same directory, turn on the automatic rename on collision flag
  2138. if (_fSameHwnd ||
  2139. ((DDIDM_COPY == _idCmd) && _IsInSameFolder(_GetIDList(), pdtobj)))
  2140. {
  2141. // do rename on collision for copy;
  2142. fo.fFlags |= FOF_RENAMEONCOLLISION;
  2143. }
  2144. // see if there is a rename mapping from recycle bin (or someone else)
  2145. LPTSTR pszDestNames = _GetDestNames(pdtobj, szDest);
  2146. if (pszDestNames)
  2147. {
  2148. fo.pTo = pszDestNames;
  2149. fo.fFlags |= FOF_MULTIDESTFILES;
  2150. fo.fFlags &= ~FOF_ALLOWUNDO; // HACK, this came from the recycle bin, don't allow undo
  2151. }
  2152. {
  2153. static UINT s_cfFileOpFlags = 0;
  2154. if (0 == s_cfFileOpFlags)
  2155. s_cfFileOpFlags = RegisterClipboardFormat(TEXT("FileOpFlags"));
  2156. fo.fFlags = (FILEOP_FLAGS)DataObj_GetDWORD(pdtobj, s_cfFileOpFlags, fo.fFlags);
  2157. }
  2158. // Check if there were any errors
  2159. if (SHFileOperation(&fo) == 0 && !fo.fAnyOperationsAborted)
  2160. {
  2161. if (_fBkDropTarget)
  2162. ShellFolderView_SetRedraw(_hwnd, 0);
  2163. SHChangeNotifyHandleEvents(); // force update now
  2164. if (_fBkDropTarget)
  2165. {
  2166. _MoveSelectIcons(pdtobj, pfv, fo.hNameMappings, pszDestNames, _fDragDrop, hDrop);
  2167. ShellFolderView_SetRedraw(_hwnd, TRUE);
  2168. }
  2169. }
  2170. if (fo.hNameMappings)
  2171. SHFreeNameMappings(fo.hNameMappings);
  2172. if (pszDestNames)
  2173. {
  2174. LocalFree((HLOCAL)pszDestNames);
  2175. // HACK, this usually comes from the bitbucket
  2176. // but in our shell, we don't handle the moves from the source
  2177. if (DDIDM_MOVE == _idCmd)
  2178. BBCheckRestoredFiles(pszSource);
  2179. }
  2180. }
  2181. break;
  2182. }
  2183. SHFree(di.lpFileList);
  2184. }
  2185. }
  2186. const UINT c_rgFolderShortcutTargets[] = {
  2187. CSIDL_STARTMENU,
  2188. CSIDL_COMMON_STARTMENU,
  2189. CSIDL_PROGRAMS,
  2190. CSIDL_COMMON_PROGRAMS,
  2191. CSIDL_NETHOOD,
  2192. };
  2193. BOOL _ShouldCreateFolderShortcut(LPCTSTR pszFolder)
  2194. {
  2195. return PathIsEqualOrSubFolderOf(pszFolder, c_rgFolderShortcutTargets, ARRAYSIZE(c_rgFolderShortcutTargets));
  2196. }
  2197. void CFSDropTarget::_DoDrop(IDataObject *pdtobj, IFolderView* pfv)
  2198. {
  2199. HRESULT hr = E_FAIL;
  2200. // Sleep(10 * 1000); // to debug async case
  2201. TCHAR szPath[MAX_PATH];
  2202. _GetPath(szPath);
  2203. SHCreateDirectory(NULL, szPath); // if this fails we catch it later
  2204. switch (_idCmd)
  2205. {
  2206. case DDIDM_SYNCCOPY:
  2207. case DDIDM_SYNCCOPYTYPE:
  2208. if (_IsBriefcaseTarget())
  2209. {
  2210. IBriefcaseStg *pbrfstg;
  2211. if (SUCCEEDED(CreateBrfStgFromPath(szPath, _hwnd, &pbrfstg)))
  2212. {
  2213. hr = pbrfstg->AddObject(pdtobj, NULL,
  2214. (DDIDM_SYNCCOPYTYPE == _idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT,
  2215. _hwnd);
  2216. pbrfstg->Release();
  2217. }
  2218. }
  2219. else
  2220. {
  2221. // Perform a sneakernet addition to the briefcase
  2222. STGMEDIUM medium;
  2223. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  2224. if (pida)
  2225. {
  2226. // Is there a briefcase root in this pdtobj?
  2227. IBriefcaseStg *pbrfstg;
  2228. if (SUCCEEDED(CreateBrfStgFromIDList(IDA_GetIDListPtr(pida, (UINT)-1), _hwnd, &pbrfstg)))
  2229. {
  2230. hr = pbrfstg->AddObject(pdtobj, szPath,
  2231. (DDIDM_SYNCCOPYTYPE == _idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT,
  2232. _hwnd);
  2233. pbrfstg->Release();
  2234. }
  2235. HIDA_ReleaseStgMedium(pida, &medium);
  2236. }
  2237. }
  2238. break;
  2239. case DDIDM_COPY:
  2240. case DDIDM_MOVE:
  2241. {
  2242. STGMEDIUM medium;
  2243. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  2244. hr = pdtobj->GetData(&fmte, &medium);
  2245. if (SUCCEEDED(hr))
  2246. {
  2247. _MoveCopy(pdtobj, pfv, (HDROP)medium.hGlobal);
  2248. ReleaseStgMedium(&medium);
  2249. }
  2250. }
  2251. break;
  2252. case DDIDM_LINK:
  2253. {
  2254. int i = 0;
  2255. LPITEMIDLIST *ppidl = NULL;
  2256. if (_fBkDropTarget)
  2257. {
  2258. i = DataObj_GetHIDACount(pdtobj);
  2259. ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, sizeof(*ppidl) * i);
  2260. }
  2261. // _grfKeyStateLast of 0 means this was a simulated drop
  2262. UINT uCreateFlags = _grfKeyStateLast && !(_dwEffectFolder & DROPEFFECT_LINK) ? SHCL_USETEMPLATE : 0;
  2263. if (_ShouldCreateFolderShortcut(szPath))
  2264. uCreateFlags |= SHCL_MAKEFOLDERSHORTCUT;
  2265. ShellFolderView_SetRedraw(_hwnd, FALSE);
  2266. // passing ppidl == NULL is correct in failure case
  2267. hr = SHCreateLinks(_hwnd, szPath, pdtobj, uCreateFlags, ppidl);
  2268. if (ppidl)
  2269. {
  2270. if (pfv)
  2271. PositionItems(pfv, (LPCITEMIDLIST*)ppidl, i, pdtobj, &_ptDrop);
  2272. FreeIDListArray(ppidl, i);
  2273. }
  2274. ShellFolderView_SetRedraw(_hwnd, TRUE);
  2275. }
  2276. break;
  2277. }
  2278. if (SUCCEEDED(hr) && _dwEffect)
  2279. {
  2280. DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, _dwEffect);
  2281. DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, _dwEffect);
  2282. }
  2283. SHChangeNotifyHandleEvents(); // force update now
  2284. }
  2285. DWORD CALLBACK CFSDropTarget::_DoDropThreadProc(void *pv)
  2286. {
  2287. DROPTHREADPARAMS *pdtp = (DROPTHREADPARAMS *)pv;
  2288. IDataObject *pdtobj;
  2289. if (SUCCEEDED(CoGetInterfaceAndReleaseStream(pdtp->pstmDataObj, IID_PPV_ARG(IDataObject, &pdtobj))))
  2290. {
  2291. IFolderView* pfv;
  2292. if (FAILED(CoGetInterfaceAndReleaseStream(pdtp->pstmFolderView, IID_PPV_ARG(IFolderView, &pfv))))
  2293. pfv = NULL;
  2294. pdtp->pThis->_DoDrop(pdtobj, pfv);
  2295. if (pfv)
  2296. pfv->Release();
  2297. pdtp->pstmFolderView = NULL; // stream now invalid; CoGetInterfaceAndReleaseStream already released it
  2298. pdtobj->Release();
  2299. }
  2300. pdtp->pstmDataObj = NULL; // stream now invalid; CoGetInterfaceAndReleaseStream already released it
  2301. _FreeThreadParams(pdtp);
  2302. CoFreeUnusedLibraries();
  2303. return 0;
  2304. }
  2305. // REARCHITECT: view and drop related helpers, these use the ugly old private defview messages
  2306. // we should replace the usage of this stuff with IShellFolderView programming
  2307. // create the pidl array that contains the destination file names. this is
  2308. // done by taking the source file names, and translating them through the
  2309. // name mapping returned by the copy engine.
  2310. //
  2311. //
  2312. // in:
  2313. // hDrop HDROP containing files recently moved/copied
  2314. // hNameMap used to translate names
  2315. //
  2316. // out:
  2317. // *pppidl id array of length return value
  2318. // # of items in pppida
  2319. //
  2320. // WARNING! You must use the provided HDROP. Do not attempt to ask the
  2321. // data object for a HDROP or HIDA or WS_FTP will break! They don't like
  2322. // it if you ask them for HDROP/HIDA, move the files to a new location
  2323. // (via the copy engine), and then ask them for HDROP/HIDA a second time.
  2324. // They notice that "Hey, those files I downloaded last time are gone!"
  2325. // and then get confused.
  2326. //
  2327. STDAPI_(int) CreateMoveCopyList(HDROP hDrop, void *hNameMap, LPITEMIDLIST **pppidl)
  2328. {
  2329. int nItems = 0;
  2330. if (hDrop)
  2331. {
  2332. nItems = DragQueryFile(hDrop, (UINT)-1, NULL, 0);
  2333. *pppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, nItems * sizeof(*pppidl));
  2334. if (*pppidl)
  2335. {
  2336. for (int i = nItems - 1; i >= 0; i--)
  2337. {
  2338. TCHAR szPath[MAX_PATH];
  2339. DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath));
  2340. _MapName(hNameMap, szPath);
  2341. (*pppidl)[i] = SHSimpleIDListFromPath(szPath);
  2342. }
  2343. }
  2344. }
  2345. return nItems;
  2346. }
  2347. // this is really not related to CFSFolder. it is generic over any view
  2348. // REARCHITECT: convert view hwnd programming to site pointer
  2349. STDAPI_(void) PositionFileFromDrop(HWND hwnd, LPCTSTR pszFile, DROPHISTORY *pdh)
  2350. {
  2351. LPITEMIDLIST pidl = SHSimpleIDListFromPath(pszFile);
  2352. if (pidl)
  2353. {
  2354. LPITEMIDLIST pidlNew = ILFindLastID(pidl);
  2355. HWND hwndView = ShellFolderViewWindow(hwnd);
  2356. SFM_SAP sap;
  2357. SHChangeNotifyHandleEvents();
  2358. // Fill in some easy SAP fields first.
  2359. sap.uSelectFlags = SVSI_SELECT;
  2360. sap.fMove = TRUE;
  2361. sap.pidl = pidlNew;
  2362. // Now compute the x,y coordinates.
  2363. // If we have a drop history, use it to determine the
  2364. // next point.
  2365. if (pdh)
  2366. {
  2367. // fill in the anchor point first...
  2368. if (!pdh->fInitialized)
  2369. {
  2370. ITEMSPACING is;
  2371. ShellFolderView_GetDropPoint(hwnd, &pdh->ptOrigin);
  2372. pdh->pt = pdh->ptOrigin; // Compute the first point.
  2373. // Compute the point deltas.
  2374. if (ShellFolderView_GetItemSpacing(hwnd, &is))
  2375. {
  2376. pdh->cxItem = is.cxSmall;
  2377. pdh->cyItem = is.cySmall;
  2378. pdh->xDiv = is.cxLarge;
  2379. pdh->yDiv = is.cyLarge;
  2380. pdh->xMul = is.cxSmall;
  2381. pdh->yMul = is.cySmall;
  2382. }
  2383. else
  2384. {
  2385. pdh->cxItem = g_cxIcon;
  2386. pdh->cyItem = g_cyIcon;
  2387. pdh->xDiv = pdh->yDiv = pdh->xMul = pdh->yMul = 1;
  2388. }
  2389. // First point gets special flags.
  2390. sap.uSelectFlags |= SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
  2391. pdh->fInitialized = TRUE; // We be initialized.
  2392. }
  2393. // if we have no list of offsets, then just inc by icon size..
  2394. else if ( !pdh->pptOffset )
  2395. {
  2396. // Simple computation of the next point.
  2397. pdh->pt.x += pdh->cxItem;
  2398. pdh->pt.y += pdh->cyItem;
  2399. }
  2400. // do this after the above stuff so that we always get our position relative to the anchor
  2401. // point, if we use the anchor point as the first one things get screwy...
  2402. if (pdh->pptOffset)
  2403. {
  2404. // Transform the old offset to our coordinates.
  2405. pdh->pt.x = ((pdh->pptOffset[pdh->iItem].x * pdh->xMul) / pdh->xDiv) + pdh->ptOrigin.x;
  2406. pdh->pt.y = ((pdh->pptOffset[pdh->iItem].y * pdh->yMul) / pdh->yDiv) + pdh->ptOrigin.y;
  2407. }
  2408. sap.pt = pdh->pt; // Copy the next point from the drop history.
  2409. }
  2410. else
  2411. {
  2412. // Preinitialize this puppy in case the folder view doesn't
  2413. // know what the drop point is (e.g., if it didn't come from
  2414. // a drag/drop but rather from a paste or a ChangeNotify.)
  2415. sap.pt.x = 0x7FFFFFFF; // "don't know"
  2416. sap.pt.y = 0x7FFFFFFF;
  2417. // Get the drop point, conveniently already in
  2418. // defview's screen coordinates.
  2419. //
  2420. // pdv->bDropAnchor should be TRUE at this point,
  2421. // see DefView's GetDropPoint() for details.
  2422. ShellFolderView_GetDropPoint(hwnd, &sap.pt);
  2423. // Only point gets special flags.
  2424. sap.uSelectFlags |= SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
  2425. }
  2426. SendMessage(hwndView, SVM_SELECTANDPOSITIONITEM, 1, (LPARAM)&sap);
  2427. ILFree(pidl);
  2428. }
  2429. }
  2430. //
  2431. // Class used to scale and position items for drag and drops. Handles
  2432. // scaling between different sized views.
  2433. //
  2434. //
  2435. // Bug 165413 (edwardp 8/16/00) Convert IShellFolderView usage in CItemPositioning to IFolderView
  2436. //
  2437. class CItemPositioning
  2438. {
  2439. // Methods
  2440. public:
  2441. CItemPositioning(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ppt);
  2442. void DragSetPoints(void);
  2443. void DropPositionItems(void);
  2444. private:
  2445. typedef enum
  2446. {
  2447. DPIWP_AUTOARRANGE,
  2448. DPIWP_DATAOBJ,
  2449. } DPIWP;
  2450. BOOL _DragShouldPositionItems(void);
  2451. BOOL _DragGetPoints(POINT* apts);
  2452. void _DragPositionPoints(POINT* apts);
  2453. void _DragScalePoints(POINT* apts);
  2454. POINT* _DropGetPoints(DPIWP dpiwp, STGMEDIUM* pMediam);
  2455. void _DropFreePoints(DPIWP dpiwp, POINT* apts, STGMEDIUM* pmedium);
  2456. void _DropPositionPoints(POINT* apts);
  2457. void _DropScalePoints(POINT* apts);
  2458. void _DropPositionItemsWithPoints(DPIWP dpiwp);
  2459. void _DropPositionItems(POINT* apts);
  2460. void _ScalePoints(POINT* apts, POINT ptFrom, POINT ptTo);
  2461. POINT* _SkipAnchorPoint(POINT* apts);
  2462. // Data
  2463. private:
  2464. IFolderView* _pfv;
  2465. LPCITEMIDLIST* _apidl;
  2466. UINT _cidl;
  2467. IDataObject* _pdtobj;
  2468. POINT* _ppt;
  2469. };
  2470. CItemPositioning::CItemPositioning(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ppt)
  2471. {
  2472. ASSERT(pifv);
  2473. ASSERT(apidl);
  2474. ASSERT(cidl);
  2475. ASSERT(pdtobj);
  2476. _pfv = pifv; // No need to addref as long as CPostionItems is only used locally.
  2477. _apidl = apidl;
  2478. _cidl = cidl;
  2479. _pdtobj = pdtobj; // No need to addref as long as CPostionItems is only used locally.
  2480. _ppt = ppt;
  2481. }
  2482. void CItemPositioning::DragSetPoints(void)
  2483. {
  2484. if (_DragShouldPositionItems())
  2485. {
  2486. POINT* apts = (POINT*) GlobalAlloc(GPTR, sizeof(POINT) * (_cidl + 1));
  2487. if (apts)
  2488. {
  2489. if (_DragGetPoints(apts))
  2490. {
  2491. _DragPositionPoints(_SkipAnchorPoint(apts));
  2492. _DragScalePoints(_SkipAnchorPoint(apts));
  2493. if (FAILED(DataObj_SetGlobal(_pdtobj, g_cfOFFSETS, apts)))
  2494. GlobalFree((HGLOBAL)apts);
  2495. }
  2496. else
  2497. {
  2498. GlobalFree((HGLOBAL)apts);
  2499. }
  2500. }
  2501. }
  2502. }
  2503. BOOL CItemPositioning::_DragShouldPositionItems()
  2504. {
  2505. // Don't position multiple items if they come from a view that doesn't allow
  2506. // positioning. The position information is not likely to be usefull in this
  2507. // case.
  2508. // Always position single items so they show up at the drop point.
  2509. // Don't bother with position data for 100 or more items.
  2510. return ((S_OK == _pfv->GetSpacing(NULL)) || 1 == _cidl) && _cidl < 100;
  2511. }
  2512. BOOL CItemPositioning::_DragGetPoints(POINT* apts)
  2513. {
  2514. BOOL fRet = TRUE;
  2515. // The first point is the anchor.
  2516. apts[0] = *_ppt;
  2517. for (UINT i = 0; i < _cidl; i++)
  2518. {
  2519. if (FAILED(_pfv->GetItemPosition(_apidl[i], &apts[i + 1])))
  2520. {
  2521. if (1 == _cidl)
  2522. {
  2523. apts[i + 1].x = _ppt->x;
  2524. apts[i + 1].y = _ppt->y;
  2525. }
  2526. else
  2527. {
  2528. fRet = FALSE;
  2529. }
  2530. }
  2531. }
  2532. return fRet;
  2533. }
  2534. void CItemPositioning::_DragPositionPoints(POINT* apts)
  2535. {
  2536. for (UINT i = 0; i < _cidl; i++)
  2537. {
  2538. apts[i].x -= _ppt->x;
  2539. apts[i].y -= _ppt->y;
  2540. }
  2541. }
  2542. void CItemPositioning::_DragScalePoints(POINT* apts)
  2543. {
  2544. POINT ptFrom;
  2545. POINT ptTo;
  2546. _pfv->GetSpacing(&ptFrom);
  2547. _pfv->GetDefaultSpacing(&ptTo);
  2548. if (ptFrom.x != ptTo.x || ptFrom.y != ptTo.y)
  2549. _ScalePoints(apts, ptFrom, ptTo);
  2550. }
  2551. void CItemPositioning::DropPositionItems(void)
  2552. {
  2553. if (S_OK == _pfv->GetAutoArrange())
  2554. {
  2555. _DropPositionItemsWithPoints(DPIWP_AUTOARRANGE);
  2556. }
  2557. else if (S_OK == _pfv->GetSpacing(NULL) && _ppt)
  2558. {
  2559. _DropPositionItemsWithPoints(DPIWP_DATAOBJ);
  2560. }
  2561. else
  2562. {
  2563. _DropPositionItems(NULL);
  2564. }
  2565. }
  2566. void CItemPositioning::_DropPositionItemsWithPoints(DPIWP dpiwp)
  2567. {
  2568. STGMEDIUM medium;
  2569. POINT* apts = _DropGetPoints(dpiwp, &medium);
  2570. if (apts)
  2571. {
  2572. if (DPIWP_DATAOBJ == dpiwp)
  2573. {
  2574. _DropScalePoints(_SkipAnchorPoint(apts));
  2575. _DropPositionPoints(_SkipAnchorPoint(apts));
  2576. }
  2577. _DropPositionItems(_SkipAnchorPoint(apts));
  2578. _DropFreePoints(dpiwp, apts, &medium);
  2579. }
  2580. else if (_ppt)
  2581. {
  2582. POINT *ppts;
  2583. ppts = (POINT *)LocalAlloc(LPTR, _cidl * sizeof(POINT));
  2584. if (ppts)
  2585. {
  2586. POINT pt;
  2587. _pfv->GetDefaultSpacing(&pt);
  2588. for (UINT i = 0; i < _cidl; i++)
  2589. {
  2590. ppts[i].x = (-g_cxIcon / 2) + (i * pt.x);
  2591. ppts[i].y = (-g_cyIcon / 2) + (i * pt.y);
  2592. }
  2593. _DropScalePoints(ppts);
  2594. _DropPositionPoints(ppts);
  2595. _DropPositionItems(ppts);
  2596. LocalFree(ppts);
  2597. }
  2598. else
  2599. {
  2600. _DropPositionItems(NULL);
  2601. }
  2602. }
  2603. else
  2604. {
  2605. _DropPositionItems(NULL);
  2606. }
  2607. }
  2608. void CItemPositioning::_DropPositionItems(POINT* apts)
  2609. {
  2610. // Drop the first item with special selection flags.
  2611. LPCITEMIDLIST pidl = ILFindLastID(_apidl[0]);
  2612. _pfv->SelectAndPositionItems(1, &pidl, apts, SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED);
  2613. // Drop the rest of the items.
  2614. if (_cidl > 1)
  2615. {
  2616. LPCITEMIDLIST* apidl = (LPCITEMIDLIST*)LocalAlloc(GPTR, sizeof(LPCITEMIDLIST) * (_cidl - 1));
  2617. if (apidl)
  2618. {
  2619. for (UINT i = 1; i < _cidl; i++)
  2620. apidl[i - 1] = ILFindLastID(_apidl[i]);
  2621. _pfv->SelectAndPositionItems(_cidl - 1, apidl, (apts) ? &apts[1] : NULL, SVSI_SELECT);
  2622. LocalFree(apidl);
  2623. }
  2624. }
  2625. }
  2626. POINT* CItemPositioning::_DropGetPoints(DPIWP dpiwp, STGMEDIUM* pmedium)
  2627. {
  2628. POINT* pptRet = NULL;
  2629. if (DPIWP_DATAOBJ == dpiwp)
  2630. {
  2631. FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  2632. if (SUCCEEDED(_pdtobj->GetData(&fmte, pmedium)))
  2633. {
  2634. if (pmedium->hGlobal)
  2635. {
  2636. POINT *pptSrc;
  2637. pptSrc = (POINT *)GlobalLock(pmedium->hGlobal);
  2638. if (pptSrc)
  2639. {
  2640. pptRet = (POINT*)LocalAlloc(GPTR, (_cidl + 1) * sizeof(POINT));
  2641. if (pptRet)
  2642. {
  2643. for (UINT i = 0; i <= _cidl; i++)
  2644. {
  2645. pptRet[i] = pptSrc[i];
  2646. }
  2647. }
  2648. GlobalUnlock(pptSrc);
  2649. }
  2650. }
  2651. ReleaseStgMedium(pmedium);
  2652. }
  2653. }
  2654. else if (DPIWP_AUTOARRANGE == dpiwp)
  2655. {
  2656. if (_ppt)
  2657. {
  2658. pptRet = (POINT*)LocalAlloc(GPTR, (_cidl + 1) * sizeof(POINT));
  2659. if (pptRet)
  2660. {
  2661. // skip first point to simulate data object use of first point
  2662. for (UINT i = 1; i <= _cidl; i++)
  2663. {
  2664. pptRet[i] = *_ppt;
  2665. }
  2666. }
  2667. }
  2668. }
  2669. return pptRet;
  2670. }
  2671. void CItemPositioning::_DropFreePoints(DPIWP dpiwp, POINT* apts, STGMEDIUM* pmedium)
  2672. {
  2673. LocalFree(apts);
  2674. }
  2675. void CItemPositioning::_DropScalePoints(POINT* apts)
  2676. {
  2677. POINT ptFrom;
  2678. POINT ptTo;
  2679. _pfv->GetDefaultSpacing(&ptFrom);
  2680. _pfv->GetSpacing(&ptTo);
  2681. if (ptFrom.x != ptTo.x || ptFrom.y != ptTo.y)
  2682. _ScalePoints(apts, ptFrom, ptTo);
  2683. }
  2684. void CItemPositioning::_DropPositionPoints(POINT* apts)
  2685. {
  2686. for (UINT i = 0; i < _cidl; i++)
  2687. {
  2688. apts[i].x += _ppt->x;
  2689. apts[i].y += _ppt->y;
  2690. }
  2691. }
  2692. void CItemPositioning::_ScalePoints(POINT* apts, POINT ptFrom, POINT ptTo)
  2693. {
  2694. for (UINT i = 0; i < _cidl; i++)
  2695. {
  2696. apts[i].x = MulDiv(apts[i].x, ptTo.x, ptFrom.x);
  2697. apts[i].y = MulDiv(apts[i].y, ptTo.y, ptFrom.y);
  2698. }
  2699. }
  2700. POINT* CItemPositioning::_SkipAnchorPoint(POINT* apts)
  2701. {
  2702. return &apts[1];
  2703. }
  2704. STDAPI_(void) SetPositionItemsPoints(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrag)
  2705. {
  2706. CItemPositioning cpi(pifv, apidl, cidl, pdtobj, ptDrag);
  2707. cpi.DragSetPoints();
  2708. }
  2709. STDAPI_(void) PositionItems(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrop)
  2710. {
  2711. CItemPositioning cip(pifv, apidl, cidl, pdtobj, ptDrop);
  2712. cip.DropPositionItems();
  2713. }
  2714. //
  2715. // Don't use PositionItems_DontUse. Instead convert to PositionItems.
  2716. // PositionItems_DontUse will be removed.
  2717. //
  2718. // Bug#163533 (edwardp 8/15/00) Remove this code.
  2719. STDAPI_(void) PositionItems_DontUse(HWND hwndOwner, UINT cidl, const LPITEMIDLIST *ppidl, IDataObject *pdtobj, POINT *pptOrigin, BOOL fMove, BOOL fUseExactOrigin)
  2720. {
  2721. if (!ppidl || !IsWindow(hwndOwner))
  2722. return;
  2723. SFM_SAP *psap = (SFM_SAP *)GlobalAlloc(GPTR, sizeof(SFM_SAP) * cidl);
  2724. if (psap)
  2725. {
  2726. UINT i, cxItem, cyItem;
  2727. int xMul, yMul, xDiv, yDiv;
  2728. STGMEDIUM medium;
  2729. POINT *pptItems = NULL;
  2730. POINT pt;
  2731. ITEMSPACING is;
  2732. // select those objects;
  2733. // this had better not fail
  2734. HWND hwnd = ShellFolderViewWindow(hwndOwner);
  2735. if (fMove)
  2736. {
  2737. FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  2738. if (SUCCEEDED(pdtobj->GetData(&fmte, &medium)) &&
  2739. medium.hGlobal)
  2740. {
  2741. pptItems = (POINT *)GlobalLock(medium.hGlobal);
  2742. pptItems++; // The first point is the anchor
  2743. }
  2744. else
  2745. {
  2746. // By default, drop at (-g_cxIcon/2, -g_cyIcon/2), and increase
  2747. // x and y by icon dimension for each icon
  2748. pt.x = ((-3 * g_cxIcon) / 2) + pptOrigin->x;
  2749. pt.y = ((-3 * g_cyIcon) / 2) + pptOrigin->y;
  2750. medium.hGlobal = NULL;
  2751. }
  2752. if (ShellFolderView_GetItemSpacing(hwndOwner, &is))
  2753. {
  2754. xDiv = is.cxLarge;
  2755. yDiv = is.cyLarge;
  2756. xMul = is.cxSmall;
  2757. yMul = is.cySmall;
  2758. cxItem = is.cxSmall;
  2759. cyItem = is.cySmall;
  2760. }
  2761. else
  2762. {
  2763. xDiv = yDiv = xMul = yMul = 1;
  2764. cxItem = g_cxIcon;
  2765. cyItem = g_cyIcon;
  2766. }
  2767. }
  2768. for (i = 0; i < cidl; i++)
  2769. {
  2770. if (ppidl[i])
  2771. {
  2772. psap[i].pidl = ILFindLastID(ppidl[i]);
  2773. psap[i].fMove = fMove;
  2774. if (fMove)
  2775. {
  2776. if (fUseExactOrigin)
  2777. {
  2778. psap[i].pt = *pptOrigin;
  2779. }
  2780. else
  2781. {
  2782. if (pptItems)
  2783. {
  2784. psap[i].pt.x = ((pptItems[i].x * xMul) / xDiv) + pptOrigin->x;
  2785. psap[i].pt.y = ((pptItems[i].y * yMul) / yDiv) + pptOrigin->y;
  2786. }
  2787. else
  2788. {
  2789. pt.x += cxItem;
  2790. pt.y += cyItem;
  2791. psap[i].pt = pt;
  2792. }
  2793. }
  2794. }
  2795. // do regular selection from all of the rest of the items
  2796. psap[i].uSelectFlags = SVSI_SELECT;
  2797. }
  2798. }
  2799. // do this special one for the first only
  2800. psap[0].uSelectFlags = SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
  2801. SendMessage(hwnd, SVM_SELECTANDPOSITIONITEM, cidl, (LPARAM)psap);
  2802. if (fMove && medium.hGlobal)
  2803. ReleaseStgMediumHGLOBAL(NULL, &medium);
  2804. GlobalFree(psap);
  2805. }
  2806. }