Leaked source code of windows server 2003
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.

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