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

1875 lines
54 KiB

  1. #include "shellprv.h"
  2. #include <caggunk.h>
  3. #include "ids.h"
  4. #include "datautil.h"
  5. #include "idlcomm.h"
  6. #include "idldata.h"
  7. #include "views.h"
  8. #include "stgutil.h"
  9. #pragma hdrstop
  10. typedef struct
  11. {
  12. WORD wSize;
  13. DWORD dwMagic;
  14. DWORD dwType;
  15. ULARGE_INTEGER cbFileSize;
  16. union
  17. {
  18. FILETIME ftModified;
  19. ULARGE_INTEGER ulModified;
  20. };
  21. WCHAR szName[MAX_PATH];
  22. WORD wZero;
  23. } STGITEM;
  24. typedef UNALIGNED STGITEM * LPSTGITEM;
  25. typedef const UNALIGNED STGITEM * LPCSTGITEM;
  26. // this sets stgfldr pidls apart from others
  27. #define STGITEM_MAGIC 0x08311978
  28. static const struct
  29. {
  30. UINT iTitle;
  31. UINT cchCol;
  32. UINT iFmt;
  33. }
  34. g_aStgColumns[] =
  35. {
  36. {IDS_NAME_COL, 20, LVCFMT_LEFT},
  37. {IDS_SIZE_COL, 10, LVCFMT_RIGHT},
  38. {IDS_TYPE_COL, 20, LVCFMT_LEFT},
  39. {IDS_MODIFIED_COL, 20, LVCFMT_LEFT},
  40. };
  41. enum
  42. {
  43. STG_COL_NAME,
  44. STG_COL_SIZE,
  45. STG_COL_TYPE,
  46. STG_COL_MODIFIED,
  47. };
  48. // folder object
  49. class CStgFolder;
  50. class CStgEnum;
  51. class CStgDropTarget;
  52. STDAPI CStgEnum_CreateInstance(CStgFolder *pstgf, DWORD grfFlags, IEnumIDList **ppenum);
  53. STDAPI CStgDropTarget_CreateInstance(CStgFolder *pstgf, HWND hwnd, IDropTarget **ppdt);
  54. STDAPI CStgFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
  55. class CStgFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder3, IShellFolderViewCB, IStorage, IPersistStorage
  56. {
  57. public:
  58. CStgFolder(IUnknown *punkAgg, CStgFolder *pstgfParent);
  59. ~CStgFolder();
  60. // IUnknown
  61. STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return CAggregatedUnknown::QueryInterface(riid,ppv);};
  62. STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef();};
  63. STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release();};
  64. // IPersist
  65. STDMETHODIMP GetClassID(CLSID *pClassID)
  66. { *pClassID = CLSID_StgFolder; return S_OK; }
  67. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  68. // IPersistFolder2
  69. STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
  70. // IPersistFolder3
  71. STDMETHODIMP GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *ppfti)
  72. { return E_NOTIMPL; }
  73. STDMETHODIMP InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti);
  74. // IShellFolder
  75. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  76. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  77. { return CStgEnum_CreateInstance(this, grfFlags, ppenumIDList); };
  78. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  79. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  80. { return BindToObject(pidl, pbc, riid, ppv); };
  81. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  82. STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv);
  83. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  84. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
  85. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
  86. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut);
  87. // IShellFolder2
  88. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid)
  89. { return E_NOTIMPL; };
  90. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
  91. { return E_NOTIMPL; };
  92. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  93. { return E_NOTIMPL; };
  94. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  95. { return E_NOTIMPL; }
  96. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  97. { return E_NOTIMPL; };
  98. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  99. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  100. { return E_NOTIMPL; };
  101. // IShellFolderViewCB
  102. STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
  103. // IStorage
  104. STDMETHODIMP Commit(DWORD grfCommitFlags);
  105. STDMETHODIMP Revert();
  106. STDMETHODIMP SetClass(REFCLSID clsid);
  107. STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask);
  108. STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag);
  109. STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum);
  110. STDMETHODIMP OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
  111. STDMETHODIMP OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
  112. STDMETHODIMP DestroyElement(LPCWSTR pszRel);
  113. STDMETHODIMP RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName);
  114. STDMETHODIMP SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime);
  115. STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest);
  116. STDMETHODIMP MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags);
  117. STDMETHODIMP CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm);
  118. STDMETHODIMP CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg);
  119. // IPersistStorage
  120. STDMETHODIMP IsDirty(void);
  121. STDMETHODIMP InitNew(IStorage *pStg);
  122. STDMETHODIMP Load(IStorage *pStg);
  123. STDMETHODIMP Save(IStorage *pStgSave, BOOL fSameAsLoad);
  124. STDMETHODIMP SaveCompleted(IStorage *pStgNew);
  125. STDMETHODIMP HandsOffStorage(void);
  126. private:
  127. LONG _cRef;
  128. CStgFolder *_pstgfParent;
  129. IStorage *_pstg;
  130. IStorage *_pstgLoad;
  131. LPITEMIDLIST _pidl;
  132. DWORD _dwMode;
  133. virtual HRESULT v_InternalQueryInterface(REFIID riid, void **ppv);
  134. HRESULT _BindToStorageObject(LPCITEMIDLIST pidl, DWORD grfMode, IStorage **ppstg);
  135. BOOL _OkayWithCurrentMode(DWORD grfMode);
  136. HRESULT _EnsureStorage(DWORD grfMode);
  137. void _CloseStorage();
  138. HRESULT _InitNewStgFolder(CStgFolder *pstgf, DWORD grfMode, LPCITEMIDLIST pidlNew);
  139. HRESULT _AllocIDList(STATSTG stat, LPITEMIDLIST *ppidl, BOOL *pfFolder);
  140. HRESULT _AllocIDList(LPCTSTR pszName, LPITEMIDLIST *ppidl, BOOL *pfFolder);
  141. HRESULT _SetShortcutStorage(IStorage *pstgLink);
  142. HRESULT _GetTypeOf(LPCSTGITEM pistg, LPTSTR pszBuffer, INT cchBuffer);
  143. ULONG _GetAttributesOf(LPCSTGITEM pistg, ULONG rgfIn);
  144. BOOL _ShowExtension();
  145. static HRESULT CALLBACK _ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  146. // folder view callback handlers
  147. HRESULT _OnBackgroundEnum(DWORD pv)
  148. { return S_OK; };
  149. HRESULT _OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents);
  150. LPCSTGITEM _IsStgItem(LPCITEMIDLIST pidl);
  151. DWORD _IsFolder(LPCSTGITEM psitem);
  152. friend CStgEnum;
  153. friend CStgDropTarget;
  154. friend HRESULT CStgFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
  155. };
  156. // Construction and IUnknown for folder root
  157. CStgFolder::CStgFolder(IUnknown *punkAgg, CStgFolder *pstgfParent) : CAggregatedUnknown(punkAgg),
  158. _dwMode(STGM_READ), _cRef(1)
  159. {
  160. ASSERT(NULL == _pidl);
  161. ASSERT(NULL == _pstg);
  162. ASSERT(NULL == _pstgLoad);
  163. _pstgfParent = pstgfParent;
  164. if (_pstgfParent)
  165. _pstgfParent->AddRef();
  166. DllAddRef();
  167. }
  168. CStgFolder::~CStgFolder()
  169. {
  170. ILFree(_pidl);
  171. if (_pstg)
  172. _pstg->Commit(STGC_DEFAULT);
  173. ATOMICRELEASE(_pstg);
  174. ATOMICRELEASE(_pstgfParent);
  175. ATOMICRELEASE(_pstgLoad);
  176. DllRelease();
  177. }
  178. HRESULT CStgFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
  179. {
  180. static const QITAB qit[] =
  181. {
  182. QITABENTMULTI(CStgFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  183. QITABENT (CStgFolder, IShellFolder2), // IID_IShellFolder2
  184. QITABENTMULTI(CStgFolder, IPersist, IPersistFolder3), // IID_IPersist
  185. QITABENTMULTI(CStgFolder, IPersistFolder, IPersistFolder3), // IID_IPersistFolder
  186. QITABENTMULTI(CStgFolder, IPersistFolder2, IPersistFolder3), // IID_IPersistFolder2
  187. QITABENT (CStgFolder, IPersistStorage), // IID_IPersistStorage
  188. QITABENT (CStgFolder, IPersistFolder3), // IID_IPersistFolder3
  189. QITABENT (CStgFolder, IShellFolderViewCB), // IID_IShellFolderViewCB
  190. QITABENT (CStgFolder, IStorage), // IID_IStorage
  191. { 0 },
  192. };
  193. if (IsEqualIID(CLSID_StgFolder, riid))
  194. {
  195. *ppv = this; // not ref counted
  196. return S_OK;
  197. }
  198. return QISearch(this, qit, riid, ppv);
  199. }
  200. STDAPI CStgFolder_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
  201. {
  202. CStgFolder *pstgf = new CStgFolder(punkOuter, NULL);
  203. if (!pstgf)
  204. return E_OUTOFMEMORY;
  205. HRESULT hr = pstgf->_GetInner()->QueryInterface(riid, ppv);
  206. pstgf->_GetInner()->Release();
  207. return hr;
  208. }
  209. LPCSTGITEM CStgFolder::_IsStgItem(LPCITEMIDLIST pidl)
  210. {
  211. if (pidl && ((LPCSTGITEM) pidl)->dwMagic == STGITEM_MAGIC)
  212. return (LPCSTGITEM)pidl;
  213. return NULL;
  214. }
  215. // BOOL, but returns FILE_ATTRIBUTE_DIRECTORY for convience
  216. DWORD CStgFolder::_IsFolder(LPCSTGITEM pistg)
  217. {
  218. return pistg->dwType == STGTY_STORAGE ? FILE_ATTRIBUTE_DIRECTORY : 0;
  219. }
  220. // Creates an item identifier list for the objects in the namespace
  221. HRESULT CStgFolder::_AllocIDList(STATSTG stat, LPITEMIDLIST *ppidl, BOOL *pfFolder)
  222. {
  223. // Note the terminating NULL is already in the sizeof(STGITEM)
  224. STGITEM sitem = {0};
  225. UINT uNameLen = lstrlen(stat.pwcsName);
  226. if (uNameLen >= MAX_PATH)
  227. {
  228. return E_INVALIDARG;
  229. }
  230. sitem.wSize = (WORD)(FIELD_OFFSET(STGITEM, szName[uNameLen + 1]));
  231. sitem.dwMagic = STGITEM_MAGIC;
  232. sitem.dwType = stat.type;
  233. sitem.cbFileSize = stat.cbSize;
  234. sitem.ftModified = stat.mtime;
  235. lstrcpyn(sitem.szName, stat.pwcsName, uNameLen + 1);
  236. if (pfFolder)
  237. {
  238. *pfFolder = _IsFolder(&sitem);
  239. }
  240. return SHILClone((LPCITEMIDLIST)&sitem, ppidl);
  241. }
  242. HRESULT CStgFolder::_AllocIDList(LPCTSTR pszName, LPITEMIDLIST *ppidl, BOOL *pfFolder)
  243. {
  244. // given a name, look it up in the current storage object we have and
  245. // compute the STATSTG which we can then build an IDLIST from.
  246. DWORD grfMode = STGM_READ;
  247. HRESULT hr = _EnsureStorage(grfMode);
  248. if (SUCCEEDED(hr))
  249. {
  250. STATSTG stat;
  251. // is it a stream or a storage?
  252. IStream *pstrm;
  253. hr = _pstg->OpenStream(pszName, NULL, grfMode, 0, &pstrm);
  254. if (SUCCEEDED(hr))
  255. {
  256. hr = pstrm->Stat(&stat, STATFLAG_DEFAULT);
  257. pstrm->Release();
  258. }
  259. else
  260. {
  261. IStorage *pstg;
  262. hr = _pstg->OpenStorage(pszName, NULL, grfMode, NULL, 0, &pstg);
  263. if (SUCCEEDED(hr))
  264. {
  265. hr = pstg->Stat(&stat, STATFLAG_DEFAULT);
  266. pstg->Release();
  267. }
  268. }
  269. // if that worked then lets allocate the object,
  270. // nb: release the name returned in the STATSTG
  271. if (SUCCEEDED(hr))
  272. {
  273. hr = _AllocIDList(stat, ppidl, pfFolder);
  274. CoTaskMemFree(stat.pwcsName);
  275. }
  276. }
  277. return hr;
  278. }
  279. void CStgFolder::_CloseStorage()
  280. {
  281. if (_pstg)
  282. {
  283. _pstg->Commit(STGC_DEFAULT);
  284. ATOMICRELEASE(_pstg);
  285. }
  286. }
  287. HRESULT CStgFolder::_BindToStorageObject(LPCITEMIDLIST pidl, DWORD grfMode, IStorage **ppstg)
  288. {
  289. IBindCtx *pbc;
  290. HRESULT hr = SHCreateSkipBindCtx(NULL, &pbc); // NULL to mean we skip all CLSIDs
  291. if (SUCCEEDED(hr))
  292. {
  293. BIND_OPTS bo = {sizeof(bo)};
  294. hr = pbc->GetBindOptions(&bo);
  295. if (SUCCEEDED(hr))
  296. {
  297. bo.grfMode = grfMode;
  298. hr = pbc->SetBindOptions(&bo);
  299. if (SUCCEEDED(hr))
  300. {
  301. hr = SHBindToObjectEx(NULL, pidl, pbc, IID_PPV_ARG(IStorage, ppstg));
  302. }
  303. }
  304. pbc->Release();
  305. }
  306. return hr;
  307. }
  308. HRESULT CStgFolder::_SetShortcutStorage(IStorage *pstgLink)
  309. {
  310. #if 1
  311. return CShortcutStorage_CreateInstance(pstgLink, IID_PPV_ARG(IStorage, &_pstg));
  312. #else
  313. IUnknown_Set((IUnknown **)&_pstg, (IUnknown *)pstgLink);
  314. return S_OK;
  315. #endif
  316. }
  317. BOOL CStgFolder::_OkayWithCurrentMode(DWORD grfMode)
  318. {
  319. DWORD dwNewBits = grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
  320. DWORD dwOldBits = _dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
  321. return (dwOldBits == STGM_READWRITE) || (dwOldBits == dwNewBits);
  322. }
  323. HRESULT CStgFolder::_EnsureStorage(DWORD grfMode)
  324. {
  325. // if we have a storage and its mode encompasses the grfMode we need then we
  326. // can skip the whole thing.
  327. HRESULT hr = S_OK;
  328. if (!_pstg || !_OkayWithCurrentMode(grfMode))
  329. {
  330. _dwMode = grfMode;
  331. _CloseStorage();
  332. if (_pstgfParent)
  333. {
  334. hr = _pstgfParent->_EnsureStorage(grfMode);
  335. if (SUCCEEDED(hr))
  336. {
  337. LPCWSTR pwszName;
  338. LPCSTGITEM pit = _IsStgItem(ILFindLastID(_pidl));
  339. WSTR_ALIGNED_STACK_COPY(&pwszName, pit->szName);
  340. hr = _pstgfParent->_pstg->OpenStorage(pwszName, NULL, grfMode, NULL, 0, &_pstg);
  341. }
  342. }
  343. else if (_pstgLoad)
  344. {
  345. hr = _pstgLoad->QueryInterface(IID_PPV_ARG(IStorage, &_pstg));
  346. }
  347. else
  348. {
  349. IStorage *pstgLink;
  350. hr = _BindToStorageObject(_pidl, grfMode, &pstgLink);
  351. if (hr == STG_E_FILENOTFOUND)
  352. hr = _BindToStorageObject(_pidl, grfMode | STGM_CREATE, &pstgLink);
  353. if (SUCCEEDED(hr))
  354. {
  355. hr = _SetShortcutStorage(pstgLink);
  356. pstgLink->Release();
  357. }
  358. }
  359. }
  360. return hr;
  361. }
  362. BOOL CStgFolder::_ShowExtension()
  363. {
  364. SHELLSTATE ss;
  365. SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE);
  366. return ss.fShowExtensions;
  367. }
  368. HRESULT CStgFolder::_GetTypeOf(LPCSTGITEM pistg, LPTSTR pszBuffer, INT cchBuffer)
  369. {
  370. *pszBuffer = TEXT('\0'); // null out the return buffer
  371. LPCWSTR pwszName;
  372. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  373. LPTSTR pszExt = PathFindExtension(pwszName);
  374. if (pszExt)
  375. {
  376. StrCpyN(pszBuffer, pszExt, cchBuffer);
  377. }
  378. return S_OK;
  379. }
  380. CStgFolder* _GetStgFolder(IShellFolder *psf)
  381. {
  382. CStgFolder *pstgf;
  383. return SUCCEEDED(psf->QueryInterface(CLSID_StgFolder, (void**)&pstgf)) ? pstgf : NULL;
  384. }
  385. // IPersist / IPersistFolder etc
  386. STDMETHODIMP CStgFolder::Initialize(LPCITEMIDLIST pidl)
  387. {
  388. ILFree(_pidl);
  389. return SHILClone(pidl, &_pidl);
  390. }
  391. HRESULT CStgFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  392. {
  393. if (_pidl)
  394. return SHILClone(_pidl, ppidl);
  395. *ppidl = NULL;
  396. return S_FALSE;
  397. }
  398. HRESULT CStgFolder::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti)
  399. {
  400. ASSERTMSG(_pstg == NULL, "shouldn't initialize again if we already have a storage");
  401. HRESULT hr = Initialize(pidlRoot);
  402. if (SUCCEEDED(hr) && pbc)
  403. {
  404. // we don't care if these don't succeed so don't propagate the hr here
  405. BIND_OPTS bo = {sizeof(bo)};
  406. if (SUCCEEDED(pbc->GetBindOptions(&bo)))
  407. {
  408. _dwMode = bo.grfMode;
  409. }
  410. IUnknown *punk;
  411. if (SUCCEEDED(pbc->GetObjectParam(STGSTR_STGTOBIND, &punk)))
  412. {
  413. IStorage *pstgLink;
  414. hr = punk->QueryInterface(IID_PPV_ARG(IStorage, &pstgLink));
  415. if (SUCCEEDED(hr))
  416. {
  417. hr = _SetShortcutStorage(pstgLink);
  418. if (SUCCEEDED(hr))
  419. {
  420. STATSTG stat;
  421. hr = _pstg->Stat(&stat, STATFLAG_NONAME);
  422. if (SUCCEEDED(hr))
  423. {
  424. // we want to know what mode we're opened in, so we will only
  425. // have to re-open if necessary
  426. _dwMode = stat.grfMode;
  427. }
  428. }
  429. pstgLink->Release();
  430. }
  431. punk->Release();
  432. }
  433. }
  434. return hr;
  435. }
  436. // IShellFolder(2)
  437. HRESULT CStgFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  438. {
  439. HRESULT hr;
  440. if (!ppidl)
  441. return E_INVALIDARG;
  442. *ppidl = NULL;
  443. if (!pszName)
  444. return E_INVALIDARG;
  445. TCHAR szName[MAX_PATH];
  446. hr = _NextSegment((LPCWSTR*)&pszName, szName, ARRAYSIZE(szName), TRUE);
  447. if (SUCCEEDED(hr))
  448. {
  449. hr = _AllocIDList(szName, ppidl, NULL);
  450. if (SUCCEEDED(hr) && pszName)
  451. {
  452. IShellFolder *psf;
  453. hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
  454. if (SUCCEEDED(hr))
  455. {
  456. ULONG chEaten;
  457. LPITEMIDLIST pidlNext;
  458. hr = psf->ParseDisplayName(hwnd, pbc, pszName, &chEaten, &pidlNext, pdwAttributes);
  459. if (SUCCEEDED(hr))
  460. hr = SHILAppend(pidlNext, ppidl);
  461. psf->Release();
  462. }
  463. }
  464. else if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
  465. {
  466. GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
  467. }
  468. }
  469. // clean up if the parse failed.
  470. if (FAILED(hr))
  471. {
  472. ILFree(*ppidl);
  473. *ppidl = NULL;
  474. }
  475. return hr;
  476. }
  477. HRESULT CStgFolder::_InitNewStgFolder(CStgFolder *pstgf, DWORD grfMode, LPCITEMIDLIST pidlNew)
  478. {
  479. HRESULT hr = pstgf->Initialize(pidlNew);
  480. if (SUCCEEDED(hr))
  481. {
  482. pstgf->_dwMode = grfMode;
  483. }
  484. return hr;
  485. }
  486. HRESULT CStgFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  487. {
  488. *ppv = NULL;
  489. LPCSTGITEM pistg = _IsStgItem(pidl);
  490. if (!pistg)
  491. return E_FAIL;
  492. DWORD grfMode = BindCtx_GetMode(pbc, STGM_READ);
  493. HRESULT hr;
  494. if (IsEqualIID(riid, IID_IStream))
  495. {
  496. // they are requesting a stream on the current
  497. // item, therefore lets return it to them.
  498. hr = _EnsureStorage(grfMode);
  499. if (SUCCEEDED(hr))
  500. {
  501. LPCWSTR pwszName;
  502. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  503. IStream *pstrm;
  504. hr = _pstg->OpenStream(pwszName, NULL, grfMode, 0, &pstrm);
  505. if (SUCCEEDED(hr))
  506. {
  507. hr = pstrm->QueryInterface(riid, ppv);
  508. pstrm->Release();
  509. }
  510. }
  511. }
  512. else
  513. {
  514. // its not an IStream request, so lets bind to the shell folder
  515. // and get the interface that the caller requested.
  516. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  517. LPITEMIDLIST pidlSubFolder = ILCombineParentAndFirst(_pidl, pidl, pidlNext);
  518. if (pidlSubFolder)
  519. {
  520. CStgFolder *pstgf = new CStgFolder(NULL, this);
  521. if (pstgf)
  522. {
  523. hr = _InitNewStgFolder(pstgf, grfMode, pidlSubFolder);
  524. if (SUCCEEDED(hr))
  525. {
  526. // if there's nothing left in the pidl, get the interface on this one.
  527. if (ILIsEmpty(pidlNext))
  528. hr = pstgf->QueryInterface(riid, ppv);
  529. else
  530. {
  531. // otherwise, hand the rest of it off to the new shellfolder.
  532. hr = pstgf->BindToObject(pidlNext, pbc, riid, ppv);
  533. }
  534. }
  535. pstgf->Release();
  536. }
  537. else
  538. hr = E_OUTOFMEMORY;
  539. ILFree(pidlSubFolder);
  540. }
  541. else
  542. hr = E_OUTOFMEMORY;
  543. }
  544. ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && (NULL == *ppv))); // Assert hr is consistent w/out param.
  545. return hr;
  546. }
  547. HRESULT CStgFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  548. {
  549. LPCSTGITEM pistg1 = _IsStgItem(pidl1);
  550. LPCSTGITEM pistg2 = _IsStgItem(pidl2);
  551. INT nCmp = 0;
  552. if (!pistg1 || !pistg2)
  553. return E_INVALIDARG;
  554. // folders always sort to the top of the list, if either of these items
  555. // are folders then compare on the folderness
  556. if (_IsFolder(pistg1) || _IsFolder(pistg2))
  557. {
  558. if (_IsFolder(pistg1) && !_IsFolder(pistg2))
  559. nCmp = -1;
  560. else if (!_IsFolder(pistg1) && _IsFolder(pistg2))
  561. nCmp = 1;
  562. }
  563. // if they match (or are not folders, then lets compare based on the column ID.
  564. if (nCmp == 0)
  565. {
  566. switch (lParam & SHCIDS_COLUMNMASK)
  567. {
  568. case STG_COL_NAME: // caught later on
  569. break;
  570. case STG_COL_SIZE:
  571. {
  572. if (pistg1->cbFileSize.QuadPart < pistg2->cbFileSize.QuadPart)
  573. nCmp = -1;
  574. else if (pistg1->cbFileSize.QuadPart > pistg2->cbFileSize.QuadPart)
  575. nCmp = 1;
  576. break;
  577. }
  578. case STG_COL_TYPE:
  579. {
  580. TCHAR szType1[MAX_PATH], szType2[MAX_PATH];
  581. _GetTypeOf(pistg1, szType1, ARRAYSIZE(szType1));
  582. _GetTypeOf(pistg2, szType2, ARRAYSIZE(szType2));
  583. nCmp = StrCmpI(szType1, szType2);
  584. break;
  585. }
  586. case STG_COL_MODIFIED:
  587. {
  588. if (pistg1->ulModified.QuadPart < pistg2->ulModified.QuadPart)
  589. nCmp = -1;
  590. else if (pistg1->ulModified.QuadPart > pistg2->ulModified.QuadPart)
  591. nCmp = 1;
  592. break;
  593. }
  594. }
  595. if (nCmp == 0)
  596. {
  597. nCmp = ualstrcmpi(pistg1->szName, pistg2->szName);
  598. }
  599. }
  600. return ResultFromShort(nCmp);
  601. }
  602. HRESULT CStgFolder::_OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents)
  603. {
  604. *ppidl = _pidl;
  605. *plEvents = SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \
  606. SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \
  607. SHCNE_MKDIR | SHCNE_RMDIR;
  608. return S_OK;
  609. }
  610. STDMETHODIMP CStgFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
  611. {
  612. HRESULT hr = E_FAIL;
  613. switch (uMsg)
  614. {
  615. HANDLE_MSG(0, SFVM_BACKGROUNDENUM, _OnBackgroundEnum);
  616. HANDLE_MSG(0, SFVM_GETNOTIFY, _OnGetNotify);
  617. }
  618. return hr;
  619. }
  620. HRESULT CStgFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  621. {
  622. HRESULT hr = E_NOINTERFACE;
  623. *ppv = NULL;
  624. if (IsEqualIID(riid, IID_IShellView))
  625. {
  626. SFV_CREATE sSFV = { 0 };
  627. sSFV.cbSize = sizeof(sSFV);
  628. sSFV.psfvcb = this;
  629. sSFV.pshf = this;
  630. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  631. }
  632. else if (IsEqualIID(riid, IID_IContextMenu))
  633. {
  634. HKEY hkNoFiles = NULL;
  635. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles);
  636. hr = CDefFolderMenu_Create2(_pidl, hwnd,
  637. 0, NULL,
  638. this, NULL,
  639. 1, &hkNoFiles,
  640. (IContextMenu **)ppv);
  641. if (hkNoFiles)
  642. RegCloseKey(hkNoFiles);
  643. }
  644. else if (IsEqualIID(riid, IID_IDropTarget))
  645. {
  646. hr = CStgDropTarget_CreateInstance(this, hwnd, (IDropTarget **)ppv);
  647. }
  648. return hr;
  649. }
  650. ULONG CStgFolder::_GetAttributesOf(LPCSTGITEM pistg, ULONG rgfIn)
  651. {
  652. ULONG dwResult = rgfIn & (SFGAO_CANCOPY | SFGAO_CANDELETE |
  653. SFGAO_CANLINK | SFGAO_CANMOVE |
  654. SFGAO_CANRENAME | SFGAO_HASPROPSHEET |
  655. SFGAO_DROPTARGET);
  656. // if the items is a folder then lets check to see if it has sub folders etc...
  657. if (pistg && _IsFolder(pistg) && (rgfIn & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER)))
  658. {
  659. dwResult |= rgfIn & SFGAO_FOLDER;
  660. if (rgfIn & SFGAO_HASSUBFOLDER)
  661. {
  662. IShellFolder *psf = NULL;
  663. if (SUCCEEDED(BindToObject((LPITEMIDLIST)pistg, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  664. {
  665. IEnumIDList * pei;
  666. if (S_OK == psf->EnumObjects(NULL, SHCONTF_FOLDERS, &pei))
  667. {
  668. LPITEMIDLIST pidl;
  669. if (S_OK == pei->Next(1, &pidl, NULL))
  670. {
  671. dwResult |= rgfIn & SFGAO_HASSUBFOLDER;
  672. ILFree(pidl);
  673. }
  674. pei->Release();
  675. }
  676. psf->Release();
  677. }
  678. }
  679. }
  680. return dwResult;
  681. }
  682. HRESULT CStgFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  683. {
  684. UINT rgfOut = *prgfInOut;
  685. // return attributes of the namespace root?
  686. if ( !cidl || !apidl )
  687. {
  688. rgfOut &= SFGAO_FOLDER | SFGAO_FILESYSTEM |
  689. SFGAO_LINK | SFGAO_DROPTARGET |
  690. SFGAO_CANRENAME | SFGAO_CANDELETE |
  691. SFGAO_CANLINK | SFGAO_CANCOPY |
  692. SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
  693. }
  694. else
  695. {
  696. for (UINT i = 0; i < cidl; i++)
  697. rgfOut &= _GetAttributesOf(_IsStgItem(apidl[i]), *prgfInOut);
  698. }
  699. *prgfInOut = rgfOut;
  700. return S_OK;
  701. }
  702. HRESULT CALLBACK CStgFolder::_ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  703. {
  704. HRESULT hr = S_OK;
  705. switch (uMsg)
  706. {
  707. case DFM_MERGECONTEXTMENU:
  708. break;
  709. case DFM_INVOKECOMMANDEX:
  710. {
  711. DFMICS *pdfmics = (DFMICS *)lParam;
  712. switch (wParam)
  713. {
  714. case DFM_CMD_DELETE:
  715. hr = StgDeleteUsingDataObject(hwnd, pdfmics->fMask, pdtobj);
  716. break;
  717. case DFM_CMD_PROPERTIES:
  718. break;
  719. default:
  720. // This is common menu items, use the default code.
  721. hr = S_FALSE;
  722. break;
  723. }
  724. break;
  725. }
  726. default:
  727. hr = E_NOTIMPL;
  728. break;
  729. }
  730. return hr;
  731. }
  732. HRESULT CStgFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  733. REFIID riid, UINT *prgfInOut, void **ppv)
  734. {
  735. HRESULT hr = E_INVALIDARG;
  736. LPCSTGITEM pistg = cidl ? _IsStgItem(apidl[0]) : NULL;
  737. if (pistg &&
  738. (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
  739. {
  740. LPCWSTR pwszName;
  741. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  742. hr = SHCreateFileExtractIconW(pwszName, _IsFolder(pistg), riid, ppv);
  743. }
  744. else if (IsEqualIID(riid, IID_IDataObject) && cidl)
  745. {
  746. hr = CIDLData_CreateInstance(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
  747. }
  748. #if 0
  749. else if (IsEqualIID(riid, IID_IContextMenu) && pistg)
  750. {
  751. // get the association for these files and lets attempt to
  752. // build the context menu for the selection.
  753. IQueryAssociations *pqa;
  754. hr = GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pistg, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
  755. if (SUCCEEDED(hr))
  756. {
  757. HKEY ahk[3];
  758. // this is broken for docfiles (shell\ext\stgfldr's keys work though)
  759. // maybe because GetClassFile punts when it's not fs?
  760. DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk));
  761. hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl,
  762. this, _ItemsMenuCB, cKeys, ahk,
  763. (IContextMenu **)ppv);
  764. SHRegCloseKeys(ahk, cKeys);
  765. pqa->Release();
  766. }
  767. }
  768. else if (IsEqualIID(riid, IID_IQueryAssociations) && pistg)
  769. {
  770. // need to create a valid Assoc obj here
  771. }
  772. #endif
  773. else if (IsEqualIID(riid, IID_IDropTarget) && pistg)
  774. {
  775. // If a directory is selected in the view, the drop is going to a folder,
  776. // so we need to bind to that folder and ask it to create a drop target
  777. if (_IsFolder(pistg))
  778. {
  779. IShellFolder *psf;
  780. hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
  781. if (SUCCEEDED(hr))
  782. {
  783. hr = psf->CreateViewObject(hwnd, IID_IDropTarget, ppv);
  784. psf->Release();
  785. }
  786. }
  787. else
  788. {
  789. hr = CreateViewObject(hwnd, IID_IDropTarget, ppv);
  790. }
  791. }
  792. return hr;
  793. }
  794. HRESULT CStgFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet)
  795. {
  796. HRESULT hr = E_INVALIDARG;
  797. LPCSTGITEM pistg = _IsStgItem(pidl);
  798. if (pistg)
  799. {
  800. LPCWSTR pwszName;
  801. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  802. if (dwFlags & SHGDN_FORPARSING)
  803. {
  804. if (dwFlags & SHGDN_INFOLDER)
  805. {
  806. hr = StringToStrRet(pwszName, pStrRet); // relative name
  807. }
  808. else
  809. {
  810. // compute the for parsing name based on the path to the storage object
  811. // and then the in folder name of the object
  812. TCHAR szTemp[MAX_PATH];
  813. SHGetNameAndFlags(_pidl, dwFlags, szTemp, ARRAYSIZE(szTemp), NULL);
  814. PathAppend(szTemp, pwszName);
  815. hr = StringToStrRet(szTemp, pStrRet);
  816. }
  817. }
  818. else
  819. {
  820. SHFILEINFO sfi;
  821. if (SHGetFileInfo(pwszName, _IsFolder(pistg), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
  822. hr = StringToStrRet(sfi.szDisplayName, pStrRet);
  823. else
  824. hr = E_FAIL;
  825. }
  826. }
  827. return hr;
  828. }
  829. HRESULT CStgFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut)
  830. {
  831. LPCSTGITEM pistg = _IsStgItem(pidl);
  832. if (!pistg)
  833. return E_INVALIDARG;
  834. HRESULT hr = _EnsureStorage(STGM_READWRITE);
  835. if (SUCCEEDED(hr))
  836. {
  837. LPCWSTR pwszName;
  838. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  839. // format up the new name before we attempt to perform the rename
  840. TCHAR szNewName[MAX_PATH];
  841. StrCpyN(szNewName, pszName, ARRAYSIZE(szNewName));
  842. if (!_ShowExtension())
  843. {
  844. StrCatBuff(szNewName, PathFindExtension(pwszName), ARRAYSIZE(szNewName));
  845. }
  846. hr = _pstg->RenameElement(pwszName, szNewName);
  847. if (SUCCEEDED(hr))
  848. {
  849. hr = _pstg->Commit(STGC_DEFAULT);
  850. }
  851. // if that was successful lets return the
  852. // new IDLIST we generated.
  853. if (SUCCEEDED(hr) && ppidlOut)
  854. hr = _AllocIDList(szNewName, ppidlOut, NULL);
  855. }
  856. return hr;
  857. }
  858. HRESULT CStgFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail)
  859. {
  860. HRESULT hr = S_OK;
  861. TCHAR szTemp[MAX_PATH];
  862. // is this a valid column?
  863. if (iColumn >= ARRAYSIZE(g_aStgColumns))
  864. return E_NOTIMPL;
  865. pDetail->str.uType = STRRET_CSTR;
  866. pDetail->str.cStr[0] = 0;
  867. LPCSTGITEM pistg = _IsStgItem(pidl);
  868. if (!pistg)
  869. {
  870. // when the IDLIST is not a storage item then we return the column information
  871. // back to the caller.
  872. pDetail->fmt = g_aStgColumns[iColumn].iFmt;
  873. pDetail->cxChar = g_aStgColumns[iColumn].cchCol;
  874. LoadString(HINST_THISDLL, g_aStgColumns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
  875. hr = StringToStrRet(szTemp, &(pDetail->str));
  876. }
  877. else
  878. {
  879. // return the property to the caller that is being requested, this is based on the
  880. // list of columns we gave out when the view was created.
  881. LPCWSTR pwszName;
  882. WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
  883. switch (iColumn)
  884. {
  885. case STG_COL_NAME:
  886. hr = StringToStrRet(pwszName, &(pDetail->str));
  887. break;
  888. case STG_COL_SIZE:
  889. if (!_IsFolder(pistg))
  890. {
  891. ULARGE_INTEGER ullSize = pistg->cbFileSize;
  892. StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp));
  893. hr = StringToStrRet(szTemp, &(pDetail->str));
  894. }
  895. break;
  896. case STG_COL_TYPE:
  897. {
  898. SHFILEINFO sfi;
  899. if (SHGetFileInfo(pwszName, _IsFolder(pistg), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME))
  900. hr = StringToStrRet(sfi.szTypeName, &(pDetail->str));
  901. break;
  902. }
  903. case STG_COL_MODIFIED:
  904. SHFormatDateTime(&pistg->ftModified, NULL, szTemp, ARRAYSIZE(szTemp));
  905. hr = StringToStrRet(szTemp, &(pDetail->str));
  906. break;
  907. }
  908. }
  909. return hr;
  910. }
  911. // IStorage
  912. HRESULT CStgFolder::Commit(DWORD grfCommitFlags)
  913. {
  914. HRESULT hr = _EnsureStorage(_dwMode);
  915. if (SUCCEEDED(hr))
  916. {
  917. ASSERTMSG(_pstg != NULL, "no _pstg in Commit");
  918. hr = _pstg->Commit(grfCommitFlags);
  919. }
  920. return hr;
  921. }
  922. HRESULT CStgFolder::Revert()
  923. {
  924. HRESULT hr = _EnsureStorage(_dwMode);
  925. if (SUCCEEDED(hr))
  926. {
  927. ASSERTMSG(_pstg != NULL, "no _pstg in Revert");
  928. hr = _pstg->Revert();
  929. }
  930. return hr;
  931. }
  932. HRESULT CStgFolder::SetClass(REFCLSID clsid)
  933. {
  934. HRESULT hr = _EnsureStorage(_dwMode);
  935. if (SUCCEEDED(hr))
  936. {
  937. ASSERTMSG(_pstg != NULL, "no _pstg in SetClass");
  938. hr = _pstg->SetClass(clsid);
  939. }
  940. return hr;
  941. }
  942. HRESULT CStgFolder::SetStateBits(DWORD grfStateBits, DWORD grfMask)
  943. {
  944. HRESULT hr = _EnsureStorage(_dwMode);
  945. if (SUCCEEDED(hr))
  946. {
  947. ASSERTMSG(_pstg != NULL, "no _pstg in SetStateBits");
  948. hr = _pstg->SetStateBits(grfStateBits, grfMask);
  949. }
  950. return hr;
  951. }
  952. HRESULT CStgFolder::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  953. {
  954. HRESULT hr = _EnsureStorage(_dwMode);
  955. if (SUCCEEDED(hr))
  956. {
  957. ASSERTMSG(_pstg != NULL, "no _pstg in Stat");
  958. hr = _pstg->Stat(pstatstg, grfStatFlag);
  959. }
  960. return hr;
  961. }
  962. HRESULT CStgFolder::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum)
  963. {
  964. HRESULT hr = _EnsureStorage(_dwMode);
  965. if (SUCCEEDED(hr))
  966. {
  967. ASSERTMSG(_pstg != NULL, "no _pstg in EnumElements");
  968. hr = _pstg->EnumElements(reserved1, reserved2, reserved3, ppenum);
  969. }
  970. return hr;
  971. }
  972. HRESULT CStgFolder::OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm)
  973. {
  974. HRESULT hr = _EnsureStorage(grfMode);
  975. if (SUCCEEDED(hr))
  976. {
  977. ASSERTMSG(_pstg != NULL, "no _pstg in OpenStream");
  978. hr = _pstg->OpenStream(pszRel, reserved1, grfMode, reserved2, ppstm);
  979. }
  980. return hr;
  981. }
  982. HRESULT CStgFolder::OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg)
  983. {
  984. HRESULT hr = _EnsureStorage(grfMode);
  985. if (SUCCEEDED(hr))
  986. {
  987. ASSERTMSG(_pstg != NULL, "no _pstg in OpenStorage");
  988. hr = _pstg->OpenStorage(pszRel, pstgPriority, grfMode, snbExclude, reserved, ppstg);
  989. }
  990. return hr;
  991. }
  992. HRESULT CStgFolder::DestroyElement(LPCWSTR pszRel)
  993. {
  994. HRESULT hr = _EnsureStorage(_dwMode);
  995. if (SUCCEEDED(hr))
  996. {
  997. ASSERTMSG(_pstg != NULL, "no _pstg in DestroyElement");
  998. LPITEMIDLIST pidl;
  999. BOOL fFolder;
  1000. hr = _AllocIDList(pszRel, &pidl, &fFolder);
  1001. if (SUCCEEDED(hr))
  1002. {
  1003. LPITEMIDLIST pidlAbs;
  1004. hr = SHILCombine(_pidl, pidl, &pidlAbs);
  1005. if (SUCCEEDED(hr))
  1006. {
  1007. hr = _pstg->DestroyElement(pszRel);
  1008. if (SUCCEEDED(hr))
  1009. SHChangeNotify(fFolder ? SHCNE_RMDIR : SHCNE_DELETE, SHCNF_IDLIST, pidlAbs, NULL);
  1010. ILFree(pidlAbs);
  1011. }
  1012. ILFree(pidl);
  1013. }
  1014. }
  1015. return hr;
  1016. }
  1017. HRESULT CStgFolder::RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName)
  1018. {
  1019. HRESULT hr = _EnsureStorage(_dwMode);
  1020. if (SUCCEEDED(hr))
  1021. {
  1022. ASSERTMSG(_pstg != NULL, "no _pstg in RenameElement");
  1023. LPITEMIDLIST pidlOld;
  1024. BOOL fFolder;
  1025. hr = _AllocIDList(pwcsOldName, &pidlOld, &fFolder);
  1026. if (SUCCEEDED(hr))
  1027. {
  1028. LPITEMIDLIST pidlAbsOld;
  1029. hr = SHILCombine(_pidl, pidlOld, &pidlAbsOld);
  1030. if (SUCCEEDED(hr))
  1031. {
  1032. hr = _pstg->RenameElement(pwcsOldName, pwcsNewName);
  1033. if (SUCCEEDED(hr))
  1034. {
  1035. LPITEMIDLIST pidlNew;
  1036. hr = _AllocIDList(pwcsNewName, &pidlNew, NULL);
  1037. if (SUCCEEDED(hr))
  1038. {
  1039. LPITEMIDLIST pidlAbsNew;
  1040. hr = SHILCombine(_pidl, pidlNew, &pidlAbsNew);
  1041. if (SUCCEEDED(hr))
  1042. {
  1043. SHChangeNotify(fFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlAbsOld, pidlAbsNew);
  1044. ILFree(pidlAbsNew);
  1045. }
  1046. ILFree(pidlNew);
  1047. }
  1048. }
  1049. ILFree(pidlAbsOld);
  1050. }
  1051. ILFree(pidlOld);
  1052. }
  1053. }
  1054. return hr;
  1055. }
  1056. HRESULT CStgFolder::SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime)
  1057. {
  1058. HRESULT hr = _EnsureStorage(_dwMode);
  1059. if (SUCCEEDED(hr))
  1060. {
  1061. ASSERTMSG(_pstg != NULL, "no _pstg in SetElementTimes");
  1062. hr = _pstg->SetElementTimes(pszRel, pctime, patime, pmtime);
  1063. if (SUCCEEDED(hr))
  1064. {
  1065. LPITEMIDLIST pidl;
  1066. BOOL fFolder;
  1067. hr = _AllocIDList(pszRel, &pidl, &fFolder);
  1068. if (SUCCEEDED(hr))
  1069. {
  1070. LPITEMIDLIST pidlAbs;
  1071. hr = SHILCombine(_pidl, pidl, &pidlAbs);
  1072. if (SUCCEEDED(hr))
  1073. {
  1074. SHChangeNotify(fFolder ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM, SHCNF_IDLIST, pidlAbs, NULL);
  1075. ILFree(pidlAbs);
  1076. }
  1077. ILFree(pidl);
  1078. }
  1079. }
  1080. }
  1081. return hr;
  1082. }
  1083. HRESULT CStgFolder::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest)
  1084. {
  1085. HRESULT hr = _EnsureStorage(_dwMode);
  1086. if (SUCCEEDED(hr))
  1087. {
  1088. ASSERTMSG(_pstg != NULL, "no _pstg in CopyTo");
  1089. hr = _pstg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest);
  1090. }
  1091. return hr;
  1092. }
  1093. HRESULT CStgFolder::MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags)
  1094. {
  1095. HRESULT hr = _EnsureStorage(_dwMode);
  1096. if (SUCCEEDED(hr))
  1097. {
  1098. ASSERTMSG(_pstg != NULL, "no _pstg in MoveElementTo");
  1099. hr = _pstg->MoveElementTo(pszRel, pstgDest, pwcsNewName, grfFlags);
  1100. }
  1101. return hr;
  1102. }
  1103. HRESULT CStgFolder::CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm)
  1104. {
  1105. HRESULT hr = _EnsureStorage(grfMode);
  1106. if (SUCCEEDED(hr))
  1107. {
  1108. ASSERTMSG(_pstg != NULL, "no _pstg in CreateStream");
  1109. hr = _pstg->CreateStream(pwcsName, grfMode, res1, res2, ppstm);
  1110. if (SUCCEEDED(hr))
  1111. hr = _pstg->Commit(STGC_DEFAULT);
  1112. if (SUCCEEDED(hr))
  1113. {
  1114. LPITEMIDLIST pidl;
  1115. hr = _AllocIDList(pwcsName, &pidl, NULL);
  1116. if (SUCCEEDED(hr))
  1117. {
  1118. LPITEMIDLIST pidlAbs;
  1119. hr = SHILCombine(_pidl, pidl, &pidlAbs);
  1120. if (SUCCEEDED(hr))
  1121. {
  1122. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pidlAbs, NULL);
  1123. ILFree(pidlAbs);
  1124. }
  1125. ILFree(pidl);
  1126. }
  1127. }
  1128. }
  1129. return hr;
  1130. }
  1131. HRESULT CStgFolder::CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg)
  1132. {
  1133. HRESULT hr = _EnsureStorage(grfMode);
  1134. if (SUCCEEDED(hr))
  1135. {
  1136. ASSERTMSG(_pstg != NULL, "no _pstg in CreateStorage");
  1137. hr = _pstg->CreateStorage(pwcsName, grfMode, res1, res2, ppstg);
  1138. if (SUCCEEDED(hr))
  1139. hr = _pstg->Commit(STGC_DEFAULT);
  1140. if (SUCCEEDED(hr))
  1141. {
  1142. LPITEMIDLIST pidl;
  1143. hr = _AllocIDList(pwcsName, &pidl, NULL);
  1144. if (SUCCEEDED(hr))
  1145. {
  1146. LPITEMIDLIST pidlAbs;
  1147. hr = SHILCombine(_pidl, pidl, &pidlAbs);
  1148. if (SUCCEEDED(hr))
  1149. {
  1150. SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbs, NULL);
  1151. ILFree(pidlAbs);
  1152. }
  1153. ILFree(pidl);
  1154. }
  1155. }
  1156. }
  1157. return hr;
  1158. }
  1159. // IPersistStorage
  1160. HRESULT CStgFolder::IsDirty(void)
  1161. {
  1162. return E_NOTIMPL;
  1163. }
  1164. HRESULT CStgFolder::InitNew(IStorage *pStg)
  1165. {
  1166. return E_NOTIMPL;
  1167. }
  1168. HRESULT CStgFolder::Load(IStorage *pStg)
  1169. {
  1170. IUnknown_Set((IUnknown **)&_pstgLoad, (IUnknown *)pStg);
  1171. return S_OK;
  1172. }
  1173. HRESULT CStgFolder::Save(IStorage *pStgSave, BOOL fSameAsLoad)
  1174. {
  1175. return E_NOTIMPL;
  1176. }
  1177. HRESULT CStgFolder::SaveCompleted(IStorage *pStgNew)
  1178. {
  1179. return E_NOTIMPL;
  1180. }
  1181. HRESULT CStgFolder::HandsOffStorage(void)
  1182. {
  1183. return E_NOTIMPL;
  1184. }
  1185. // Enumerator object for storages
  1186. class CStgEnum : public IEnumIDList
  1187. {
  1188. public:
  1189. CStgEnum(CStgFolder* prf, DWORD grfFlags);
  1190. ~CStgEnum();
  1191. // IUnknown
  1192. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  1193. STDMETHODIMP_(ULONG) AddRef();
  1194. STDMETHODIMP_(ULONG) Release();
  1195. // IEnumIDList
  1196. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  1197. STDMETHODIMP Skip(ULONG celt)
  1198. { return E_NOTIMPL; };
  1199. STDMETHODIMP Reset()
  1200. { ATOMICRELEASE(_pEnum); return S_OK; };
  1201. STDMETHODIMP Clone(IEnumIDList **ppenum)
  1202. { return E_NOTIMPL; };
  1203. private:
  1204. LONG _cRef;
  1205. CStgFolder* _pstgf;
  1206. DWORD _grfFlags;
  1207. IEnumSTATSTG *_pEnum;
  1208. };
  1209. STDAPI CStgEnum_CreateInstance(CStgFolder *pstgf, DWORD grfFlags, IEnumIDList **ppenum)
  1210. {
  1211. CStgEnum *penum = new CStgEnum(pstgf, grfFlags);
  1212. if (!penum)
  1213. return E_OUTOFMEMORY;
  1214. HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  1215. penum->Release();
  1216. return hr;
  1217. }
  1218. CStgEnum::CStgEnum(CStgFolder *pstgf, DWORD grfFlags) :
  1219. _cRef(1), _pstgf(pstgf), _grfFlags(grfFlags)
  1220. {
  1221. _pstgf->AddRef();
  1222. }
  1223. CStgEnum::~CStgEnum()
  1224. {
  1225. ATOMICRELEASE(_pEnum);
  1226. _pstgf->Release();
  1227. }
  1228. STDMETHODIMP_(ULONG) CStgEnum::AddRef()
  1229. {
  1230. return InterlockedIncrement(&_cRef);
  1231. }
  1232. STDMETHODIMP_(ULONG) CStgEnum::Release()
  1233. {
  1234. if (InterlockedDecrement(&_cRef))
  1235. return _cRef;
  1236. delete this;
  1237. return 0;
  1238. }
  1239. HRESULT CStgEnum::QueryInterface(REFIID riid, void **ppv)
  1240. {
  1241. static const QITAB qit[] =
  1242. {
  1243. QITABENT(CStgEnum, IEnumIDList), // IID_IEnumIDList
  1244. { 0 },
  1245. };
  1246. return QISearch(this, qit, riid, ppv);
  1247. }
  1248. HRESULT CStgEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1249. {
  1250. HRESULT hr = S_OK;
  1251. // do we have an enumerator, if not then lets get one from the
  1252. // storage object
  1253. if (!_pEnum)
  1254. {
  1255. // we need to reopen docfile storages to get a new enumerator
  1256. // because they keep giving us a stale one.
  1257. // if we don't close to get a new enumerator, when we delete a file
  1258. // the SHCNE_UPDATEDIR wont work because we'll spit out the same
  1259. // pidls that should have been deleted.
  1260. // can we get around this?
  1261. _pstgf->_CloseStorage();
  1262. hr = _pstgf->_EnsureStorage(STGM_READ);
  1263. if (SUCCEEDED(hr))
  1264. hr = _pstgf->_pstg->EnumElements(0, NULL, 0, &_pEnum);
  1265. }
  1266. // now return items, if all is good and we have stuff to pass back.
  1267. for (UINT cItems = 0; (cItems != celt) && SUCCEEDED(hr) && (hr != S_FALSE) ; )
  1268. {
  1269. STATSTG statstg = { 0 };
  1270. hr = _pEnum->Next(1, &statstg, NULL);
  1271. if (SUCCEEDED(hr) && (hr != S_FALSE))
  1272. {
  1273. BOOL fFolder;
  1274. LPITEMIDLIST pidl;
  1275. hr = _pstgf->_AllocIDList(statstg, &pidl, &fFolder);
  1276. CoTaskMemFree(statstg.pwcsName);
  1277. if (SUCCEEDED(hr))
  1278. {
  1279. if (fFolder)
  1280. {
  1281. if (!(_grfFlags & SHCONTF_FOLDERS))
  1282. {
  1283. ILFree(pidl);
  1284. continue;
  1285. }
  1286. }
  1287. else if (!(_grfFlags & SHCONTF_NONFOLDERS))
  1288. {
  1289. ILFree(pidl);
  1290. continue;
  1291. }
  1292. rgelt[cItems++] = pidl; // return the idlist
  1293. }
  1294. }
  1295. }
  1296. if (hr == S_FALSE)
  1297. ATOMICRELEASE(_pEnum);
  1298. if (pceltFetched)
  1299. *pceltFetched = cItems;
  1300. return hr;
  1301. }
  1302. // Drop target object
  1303. class CStgDropTarget : public IDropTarget
  1304. {
  1305. public:
  1306. CStgDropTarget(CStgFolder *pstgf, HWND hwnd);
  1307. ~CStgDropTarget();
  1308. // IUnknown
  1309. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  1310. STDMETHODIMP_(ULONG) AddRef(void);
  1311. STDMETHODIMP_(ULONG) Release(void);
  1312. // IDropTarget
  1313. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  1314. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  1315. STDMETHODIMP DragLeave();
  1316. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  1317. private:
  1318. LONG _cRef;
  1319. CStgFolder *_pstgf;
  1320. HWND _hwnd; // EVIL: used as a site and UI host
  1321. IDataObject *_pdtobj; // used durring DragOver() and DoDrop(), don't use on background thread
  1322. UINT _idCmd;
  1323. DWORD _grfKeyStateLast; // for previous DragOver/Enter
  1324. DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
  1325. DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
  1326. DWORD _GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState);
  1327. HRESULT _Transfer(IDataObject *pdtobj, UINT uiCmd);
  1328. };
  1329. CStgDropTarget::CStgDropTarget(CStgFolder *pstgf, HWND hwnd) :
  1330. _cRef(1),
  1331. _pstgf(pstgf),
  1332. _hwnd(hwnd),
  1333. _grfKeyStateLast(-1)
  1334. {
  1335. _pstgf->AddRef();
  1336. }
  1337. CStgDropTarget::~CStgDropTarget()
  1338. {
  1339. DragLeave();
  1340. ATOMICRELEASE(_pstgf);
  1341. }
  1342. STDMETHODIMP_(ULONG) CStgDropTarget::AddRef()
  1343. {
  1344. return InterlockedIncrement(&_cRef);
  1345. }
  1346. STDMETHODIMP_(ULONG) CStgDropTarget::Release()
  1347. {
  1348. if (InterlockedDecrement(&_cRef))
  1349. return _cRef;
  1350. delete this;
  1351. return 0;
  1352. }
  1353. HRESULT CStgDropTarget::QueryInterface(REFIID riid, void**ppv)
  1354. {
  1355. static const QITAB qit[] = {
  1356. QITABENT(CStgDropTarget, IDropTarget),
  1357. { 0 },
  1358. };
  1359. return QISearch(this, qit, riid,ppv);
  1360. }
  1361. STDAPI CStgDropTarget_CreateInstance(CStgFolder *pstgf, HWND hwnd, IDropTarget **ppdt)
  1362. {
  1363. CStgDropTarget *psdt = new CStgDropTarget(pstgf, hwnd);
  1364. if (!psdt)
  1365. return E_OUTOFMEMORY;
  1366. HRESULT hr = psdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt));
  1367. psdt->Release();
  1368. return hr;
  1369. }
  1370. #if 0
  1371. HRESULT StorageFromDataObj(IDataObject *pdtobj, IShellFolder **ppsf, IStorage **ppstg)
  1372. {
  1373. *ppsf = NULL;
  1374. *ppstg = NULL;
  1375. HRESULT hr = E_FAIL;
  1376. STGMEDIUM medium = {0};
  1377. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1378. if (pida)
  1379. {
  1380. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, IDA_GetIDListPtr(pida, (UINT)-1), ppsf))))
  1381. {
  1382. // for (UINT i = 0; i < pida->cidl; i++)
  1383. for (UINT i = 0; i < 1; i++)
  1384. {
  1385. hr = (*ppsf)->BindToObject(IDA_GetIDListPtr(pida, i), NULL, IID_PPV_ARG(IStorage, ppstg));
  1386. if (FAILED(hr))
  1387. {
  1388. (*ppsf)->Release();
  1389. *ppsf = NULL;
  1390. }
  1391. }
  1392. }
  1393. HIDA_ReleaseStgMedium(pida, &medium);
  1394. }
  1395. return hr;
  1396. }
  1397. #endif
  1398. HRESULT CStgDropTarget::_Transfer(IDataObject *pdtobj, UINT uiCmd)
  1399. {
  1400. #if 0
  1401. IShellFolder *psf;
  1402. IStorage *pstg;
  1403. HRESULT hr = StorageFromDataObj(pdtobj, &psf, &pstg);
  1404. if (SUCCEEDED(hr))
  1405. {
  1406. DWORD grfModeCreated = STGM_READWRITE;
  1407. HRESULT hr = _pstgf->_EnsureStorage(grfModeCreated);
  1408. if (SUCCEEDED(hr))
  1409. {
  1410. hr = pstg->CopyTo(0, NULL, 0, _pstgf->_pstg);
  1411. if (SUCCEEDED(hr))
  1412. {
  1413. hr = _pstgf->_pstg->Commit(STGC_DEFAULT);
  1414. }
  1415. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST|SHCNF_FLUSH|SHCNF_FLUSHNOWAIT, _pstgf->_pidl, NULL);
  1416. }
  1417. pstg->Release();
  1418. psf->Release();
  1419. }
  1420. #else
  1421. HRESULT hr;
  1422. STGMEDIUM medium = {0};
  1423. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1424. if (pida)
  1425. {
  1426. IShellFolder *psf;
  1427. hr = SHBindToObjectEx(NULL, IDA_GetIDListPtr(pida, (UINT)-1), NULL, IID_PPV_ARG(IShellFolder, &psf));
  1428. if (SUCCEEDED(hr))
  1429. {
  1430. IStorage *pstgSrc;
  1431. hr = psf->QueryInterface(IID_PPV_ARG(IStorage, &pstgSrc));
  1432. if (SUCCEEDED(hr))
  1433. {
  1434. hr = _pstgf->_EnsureStorage(STGM_READWRITE);
  1435. if (SUCCEEDED(hr))
  1436. {
  1437. for (UINT i = 0; i < pida->cidl; i++)
  1438. {
  1439. WCHAR szName[MAX_PATH];
  1440. hr = DisplayNameOf(psf, IDA_GetIDListPtr(pida, i), SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1441. if (SUCCEEDED(hr))
  1442. {
  1443. DWORD grfFlags = (uiCmd == DDIDM_COPY) ? STGMOVE_COPY : STGMOVE_MOVE;
  1444. hr = pstgSrc->MoveElementTo(szName, _pstgf->_pstg, szName, grfFlags);
  1445. if (SUCCEEDED(hr))
  1446. hr = _pstgf->_pstg->Commit(STGC_DEFAULT);
  1447. }
  1448. }
  1449. }
  1450. pstgSrc->Release();
  1451. }
  1452. psf->Release();
  1453. }
  1454. HIDA_ReleaseStgMedium(pida, &medium);
  1455. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST|SHCNF_FLUSH|SHCNF_FLUSHNOWAIT, _pstgf->_pidl, NULL);
  1456. }
  1457. else
  1458. hr = E_FAIL;
  1459. #endif
  1460. return hr;
  1461. }
  1462. DWORD CStgDropTarget::_GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState)
  1463. {
  1464. DWORD dwEffectReturned = DROPEFFECT_NONE;
  1465. switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT))
  1466. {
  1467. case MK_CONTROL: dwEffectReturned = DROPEFFECT_COPY; break;
  1468. case MK_SHIFT: dwEffectReturned = DROPEFFECT_MOVE; break;
  1469. case MK_SHIFT | MK_CONTROL: dwEffectReturned = DROPEFFECT_LINK; break;
  1470. case MK_ALT: dwEffectReturned = DROPEFFECT_LINK; break;
  1471. default:
  1472. {
  1473. // no modifier keys:
  1474. // if the data object contains a preferred drop effect, try to use it
  1475. DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & *pdwEffect;
  1476. if (dwPreferred)
  1477. {
  1478. if (dwPreferred & DROPEFFECT_MOVE)
  1479. {
  1480. dwEffectReturned = DROPEFFECT_MOVE;
  1481. }
  1482. else if (dwPreferred & DROPEFFECT_COPY)
  1483. {
  1484. dwEffectReturned = DROPEFFECT_COPY;
  1485. }
  1486. else if (dwPreferred & DROPEFFECT_LINK)
  1487. {
  1488. dwEffectReturned = DROPEFFECT_LINK;
  1489. }
  1490. }
  1491. else
  1492. {
  1493. dwEffectReturned = DROPEFFECT_COPY;
  1494. }
  1495. }
  1496. break;
  1497. }
  1498. return dwEffectReturned;
  1499. }
  1500. STDMETHODIMP CStgDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1501. {
  1502. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  1503. _grfKeyStateLast = grfKeyState;
  1504. if (pdwEffect)
  1505. *pdwEffect = _dwEffectLastReturned = _GetDropEffect(pdwEffect, grfKeyState);
  1506. return S_OK;
  1507. }
  1508. STDMETHODIMP CStgDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1509. {
  1510. // has the key state changed? if not then lets return the previously cached
  1511. // version, otherwise recompute.
  1512. if (_grfKeyStateLast == grfKeyState)
  1513. {
  1514. if (*pdwEffect)
  1515. *pdwEffect = _dwEffectLastReturned;
  1516. }
  1517. else if (*pdwEffect)
  1518. {
  1519. *pdwEffect = _GetDropEffect(pdwEffect, grfKeyState);
  1520. }
  1521. _dwEffectLastReturned = *pdwEffect;
  1522. _grfKeyStateLast = grfKeyState;
  1523. return S_OK;
  1524. }
  1525. STDMETHODIMP CStgDropTarget::DragLeave()
  1526. {
  1527. ATOMICRELEASE(_pdtobj);
  1528. return S_OK;
  1529. }
  1530. STDMETHODIMP CStgDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1531. {
  1532. *pdwEffect = DROPEFFECT_NONE; // incase of failure
  1533. // determine the type of operation to performed, if the right button is down
  1534. // then lets display the menu, otherwise base it on the drop effect
  1535. UINT idCmd = 0; // Choice from drop popup menu
  1536. if (!(_grfKeyStateLast & MK_LBUTTON))
  1537. {
  1538. HMENU hMenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_NONDEFAULTDD);
  1539. if (!hMenu)
  1540. {
  1541. DragLeave();
  1542. return E_FAIL;
  1543. }
  1544. SetMenuDefaultItem(hMenu, POPUP_NONDEFAULTDD, FALSE);
  1545. idCmd = TrackPopupMenu(hMenu,
  1546. TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
  1547. pt.x, pt.y, 0, _hwnd, NULL);
  1548. DestroyMenu(hMenu);
  1549. }
  1550. else
  1551. {
  1552. switch (_GetDropEffect(pdwEffect, grfKeyState))
  1553. {
  1554. case DROPEFFECT_COPY: idCmd = DDIDM_COPY; break;
  1555. case DROPEFFECT_MOVE: idCmd = DDIDM_MOVE; break;
  1556. case DROPEFFECT_LINK: idCmd = DDIDM_LINK; break;
  1557. }
  1558. }
  1559. // now perform the operation, based on the command ID we have.
  1560. HRESULT hr = E_FAIL;
  1561. switch (idCmd)
  1562. {
  1563. case DDIDM_COPY:
  1564. case DDIDM_MOVE:
  1565. hr = _Transfer(pdtobj, idCmd);
  1566. if (SUCCEEDED(hr))
  1567. *pdwEffect = (idCmd == DDIDM_COPY) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
  1568. else
  1569. *pdwEffect = 0;
  1570. break;
  1571. case DDIDM_LINK:
  1572. {
  1573. WCHAR wzPath[MAX_PATH];
  1574. SHGetNameAndFlags(_pstgf->_pidl, SHGDN_FORPARSING, wzPath, ARRAYSIZE(wzPath), NULL);
  1575. hr = SHCreateLinks(_hwnd, wzPath, pdtobj, 0, NULL);
  1576. break;
  1577. }
  1578. }
  1579. // success so lets populate the new changes to the effect
  1580. if (SUCCEEDED(hr) && *pdwEffect)
  1581. {
  1582. DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect);
  1583. DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, *pdwEffect);
  1584. }
  1585. DragLeave();
  1586. return hr;
  1587. }