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.

2151 lines
63 KiB

  1. // need IProgressDialog to be marshaled, def in .idl file (cleanup needed here)
  2. // get CLSID_MediaDevMgr set to "Both"
  3. // issues:
  4. // can't drop on open folder, Insert() throws an exception
  5. // Rio600 Next throws exception on cidl > 1
  6. // Storage2::GetStroage() fails for Rio600, does SP need to implement this?
  7. #include "pch.h"
  8. #include <shlobj.h>
  9. #include <shsemip.h>
  10. #include <shlobjp.h> // IDelegateFolder
  11. #include "thisdll.h"
  12. #include <windowsx.h>
  13. #include <varutil.h>
  14. #include <dobjutil.h>
  15. #include <malloc.h>
  16. #include <dpa.h>
  17. #include <shdguid.h>
  18. #include "shdup.h"
  19. #include "ids.h"
  20. #include <ccstock2.h>
  21. #pragma hdrstop
  22. #include <wmsdk.h>
  23. #include <mswmdm.h>
  24. #include <scclient.h>
  25. #include <mswmdm_i.c> // get CLSID/IID defs
  26. #include <ntquery.h>
  27. #define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
  28. #define IsEqualSCID(a, b) (((a).pid == (b).pid) && IsEqualIID((a).fmtid, (b).fmtid) )
  29. DEFINE_SCID(SCID_SIZE, PSGUID_STORAGE, PID_STG_SIZE);
  30. DEFINE_SCID(SCID_DESCRIPTIONID, PSGUID_SHELLDETAILS, PID_DESCRIPTIONID);
  31. DEFINE_SCID(SCID_CAPACITY, PSGUID_VOLUME, PID_VOLUME_CAPACITY);
  32. DEFINE_SCID(SCID_FREESPACE, PSGUID_VOLUME, PID_VOLUME_FREE);
  33. DEFINE_SCID(SCID_DisplayProperties, PSGUID_WEBVIEW, PID_DISPLAY_PROPERTIES);
  34. DEFINE_SCID(SCID_TYPE, PSGUID_STORAGE, PID_STG_STORAGETYPE);
  35. DEFINE_SCID(SCID_NAME, PSGUID_STORAGE, PID_STG_NAME);
  36. DEFINE_SCID(SCID_WRITETIME, PSGUID_STORAGE, PID_STG_WRITETIME);
  37. DEFINE_SCID(SCID_AUDIO_Duration, PSGUID_AUDIO, PIDASI_TIMELENGTH); //100ns units, not milliseconds. VT_UI8, not VT_UI4
  38. DEFINE_SCID(SCID_AUDIO_Bitrate, PSGUID_AUDIO, PIDASI_AVG_DATA_RATE); // bits per second
  39. EXTERN_C const BYTE abPVK[4096];
  40. EXTERN_C const BYTE abCert[100];
  41. #pragma pack(1)
  42. typedef struct // typedef struct
  43. { // {
  44. // these need to line up -----------------------
  45. WORD cbSize; // WORD cbSize; // Size of entire item ID
  46. WORD wOuter; // WORD wOuter; // Private data owned by the outer folder
  47. WORD cbInner; // WORD cbInner; // Size of delegate's data
  48. // ---------------------------------------------
  49. DWORD dwMagic; // BYTE rgb[1]; // Inner folder's data,
  50. DWORD dwAttributes; // } DELEGATEITEMID;
  51. ULARGE_INTEGER cbTotal;
  52. ULARGE_INTEGER cbFree;
  53. union
  54. {
  55. FILETIME ftModified;
  56. ULARGE_INTEGER ulModified;
  57. };
  58. WCHAR szName[1]; // variable size
  59. } MEDIADEVITEM;
  60. #pragma pack()
  61. typedef UNALIGNED MEDIADEVITEM * LPMEDIADEVITEM;
  62. typedef const UNALIGNED MEDIADEVITEM * LPCMEDIADEVITEM;
  63. #define MEDIADEVITEM_MAGIC 0x08311978
  64. class CMediaDeviceFolder;
  65. class CMediaDeviceEnum;
  66. class CMediaDeviceDropTarget;
  67. class CMediaDeviceFolder : public IShellFolder2, IPersistFolder2, IShellFolderViewCB, IDelegateFolder, IContextMenuCB
  68. {
  69. public:
  70. CMediaDeviceFolder(CMediaDeviceFolder *pParent, IWMDMStorage *pstg);
  71. // IUnknown
  72. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  73. STDMETHODIMP_(ULONG) AddRef(void);
  74. STDMETHODIMP_(ULONG) Release(void);
  75. // IPersist
  76. STDMETHODIMP GetClassID(CLSID *pClassID);
  77. // IPersistFolder
  78. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  79. // IPersistFolder2
  80. STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
  81. // IShellFolder
  82. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  83. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
  84. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
  85. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  86. { return BindToObject(pidl, pbc, riid, ppv); };
  87. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  88. STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppvOut);
  89. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  90. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppvOut);
  91. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
  92. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut);
  93. // IShellFolder2
  94. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid)
  95. { return E_NOTIMPL; };
  96. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
  97. { return E_NOTIMPL; };
  98. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  99. { return E_NOTIMPL; };
  100. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  101. { return E_NOTIMPL; }
  102. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  103. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  104. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid);
  105. // IShellFolderViewCB
  106. STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
  107. // IDelegateFolder
  108. STDMETHODIMP SetItemAlloc(IMalloc *pmalloc);
  109. // IContextMenuCB
  110. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  111. // public for friends
  112. LPCITEMIDLIST GetIDList() { return _pidl; }
  113. private:
  114. ~CMediaDeviceFolder();
  115. HRESULT _CreateIDList(LPCTSTR pszName, MEDIADEVITEM **ppmditem, BOOL *pfFolder);
  116. HRESULT _IDListForDevice(IWMDMDevice *pdev, LPITEMIDLIST *ppidl, BOOL *pfFolder);
  117. HRESULT _IDListForStorage(IWMDMStorage *pstg, LPITEMIDLIST *ppidl, BOOL *pfFolder);
  118. ULONG _GetAttributesOf(LPCMEDIADEVITEM pmdi, ULONG rgfIn);
  119. HRESULT _CreateExtractIcon(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv);
  120. static HRESULT CALLBACK _ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  121. HRESULT _DeleteItems(HWND hwnd, IDataObject *pdtobj);
  122. HRESULT _ChangeNotifyItem(LONG lEvent, LPCITEMIDLIST pidl);
  123. HRESULT _ChangeNotifyObj(LONG lEvent, IUnknown *punkStg);
  124. // folder view callback handlers
  125. HRESULT _OnBackgroundEnum(DWORD pv);
  126. HRESULT _OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents);
  127. HRESULT _OnViewMode(DWORD pv, FOLDERVIEWMODE *pvm);
  128. LPCMEDIADEVITEM _IsValid(LPCITEMIDLIST pidl);
  129. DWORD _IsFolder(LPCMEDIADEVITEM pmditem);
  130. BOOL _FileInfo(LPCMEDIADEVITEM pmdi, DWORD dwFlags, SHFILEINFO *psfi);
  131. LPCTSTR _DisplayName(LPCMEDIADEVITEM pmdi, LPTSTR pszName, UINT cch);
  132. LPCTSTR _Name(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch);
  133. LPCTSTR _RawName(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch);
  134. LPCTSTR _Type(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch);
  135. HRESULT _GetMediaDevMgr(REFIID riid, void **ppv);
  136. HRESULT _Authenticate(IUnknown *punk);
  137. HRESULT _EnumDevices(IWMDMEnumDevice **ppEnumDevices);
  138. HRESULT _FindDeviceByName(LPCWSTR pszName, IWMDMDevice **ppdevice);
  139. HRESULT _StorageByName(LPCWSTR pszName, REFIID riid, void **ppv);
  140. HRESULT _StroageFromItem(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv);
  141. HRESULT _StorageForDevice(IWMDMDevice *pdev, REFIID riid, void **ppv);
  142. HRESULT _Storage(REFIID riid, void **ppv);
  143. HRESULT _EnumStorage(IWMDMEnumStorage **ppEnumStorage);
  144. HRESULT _DeviceFromItem(LPCMEDIADEVITEM pmdi, IWMDMDevice **ppdev);
  145. BOOL _IsRoot();
  146. void *_Alloc(SIZE_T cb);
  147. BOOL _ShouldShowDevice(IWMDMDevice *pdev);
  148. friend CMediaDeviceEnum;
  149. friend CMediaDeviceDropTarget;
  150. LONG _cRef;
  151. IMalloc *_pmalloc;
  152. LPITEMIDLIST _pidl; // full idlist of this folder
  153. BOOL _bRoot; // is this the root of the name space
  154. IGlobalInterfaceTable *_pgit;
  155. DWORD _dwStorageCookie; // GIT token for IWMDMStorage
  156. DWORD _dwDevMgrCookie; // GIT token for media device manager
  157. CSecureChannelClient *_pSAC; // this must outlive any objects that used this
  158. BOOL _fDelegate;
  159. };
  160. class CMediaDeviceEnum : public IEnumIDList
  161. {
  162. public:
  163. CMediaDeviceEnum(CMediaDeviceFolder* prf, DWORD grfFlags);
  164. // IUnknown
  165. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  166. STDMETHODIMP_(ULONG) AddRef();
  167. STDMETHODIMP_(ULONG) Release();
  168. // IEnumIDList
  169. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  170. STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; };
  171. STDMETHODIMP Reset() { _iIndex = 0; return S_OK; };
  172. STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; };
  173. private:
  174. ~CMediaDeviceEnum();
  175. HRESULT _Init();
  176. BOOL _FilterItem(BOOL bFolder);
  177. LONG _cRef;
  178. CDPA<ITEMIDLIST> _dpaItems;
  179. int _iIndex;
  180. CMediaDeviceFolder *_pmdf;
  181. DWORD _grfFlags;
  182. };
  183. class CMediaDeviceDropTarget : public IDropTarget, INamespaceWalkCB, IWMDMProgress2
  184. {
  185. public:
  186. CMediaDeviceDropTarget(CMediaDeviceFolder *pFolder, HWND hwnd);
  187. // IUnknown
  188. STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
  189. STDMETHODIMP_(ULONG) AddRef(void);
  190. STDMETHODIMP_(ULONG) Release(void);
  191. // IDropTarget
  192. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  193. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  194. STDMETHODIMP DragLeave();
  195. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  196. // IWMDMProgress
  197. STDMETHODIMP Begin(DWORD dwEstimatedTicks);
  198. STDMETHODIMP Progress(DWORD dwTranspiredTicks);
  199. STDMETHODIMP End();
  200. // IWMDMProgress2
  201. STDMETHODIMP End2(HRESULT hrCompletionCode);
  202. // INamespaceWalkCB
  203. STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl);
  204. STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl);
  205. STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl);
  206. STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel)
  207. { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
  208. private:
  209. ~CMediaDeviceDropTarget();
  210. DWORD _GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState);
  211. static DWORD CALLBACK _DoCopyThreadProc(void *pv);
  212. HRESULT _DoCopy(IDataObject *pdtobj);
  213. LONG _cRef;
  214. CMediaDeviceFolder *_pmdf;
  215. HWND _hwnd; // EVIL: used as a site and UI host
  216. IDataObject *_pdtobj; // used durring DragOver() and DoDrop(), don't use on background thread
  217. UINT _idCmd;
  218. DWORD _grfKeyStateLast; // for previous DragOver/Enter
  219. DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
  220. DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
  221. IProgressDialog *_ppd;
  222. ULONGLONG _ulProgressTotal;
  223. ULONGLONG _ulProgressCurrent;
  224. ULONGLONG _ulThisFile;
  225. IUnknown *_punkFTM; // make our callback interface calls direct
  226. };
  227. CMediaDeviceFolder::CMediaDeviceFolder(CMediaDeviceFolder *pParent, IWMDMStorage *pstg) :
  228. _cRef(1), _pidl(NULL), _dwStorageCookie(-1), _dwDevMgrCookie(-1)
  229. {
  230. _bRoot = (NULL == pstg);
  231. if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IGlobalInterfaceTable, &_pgit))))
  232. {
  233. if (pstg)
  234. {
  235. _pgit->RegisterInterfaceInGlobal(pstg, IID_IWMDMStorage, &_dwStorageCookie);
  236. }
  237. }
  238. DllAddRef();
  239. }
  240. CMediaDeviceFolder::~CMediaDeviceFolder()
  241. {
  242. ILFree(_pidl);
  243. #ifdef _X86_
  244. if (_pSAC)
  245. delete _pSAC;
  246. #endif
  247. if (_pgit)
  248. {
  249. if (-1 != _dwStorageCookie)
  250. _pgit->RevokeInterfaceFromGlobal(_dwStorageCookie);
  251. if (-1 != _dwDevMgrCookie)
  252. _pgit->RevokeInterfaceFromGlobal(_dwDevMgrCookie);
  253. _pgit->Release();
  254. }
  255. ATOMICRELEASE(_pmalloc);
  256. DllRelease();
  257. }
  258. HRESULT CMediaDeviceFolder::QueryInterface(REFIID riid, void **ppv)
  259. {
  260. static const QITAB qit[] =
  261. {
  262. QITABENTMULTI(CMediaDeviceFolder, IShellFolder, IShellFolder2),
  263. QITABENT (CMediaDeviceFolder, IShellFolder2),
  264. QITABENTMULTI(CMediaDeviceFolder, IPersist, IPersistFolder2),
  265. QITABENTMULTI(CMediaDeviceFolder, IPersistFolder, IPersistFolder2),
  266. QITABENT (CMediaDeviceFolder, IPersistFolder2),
  267. QITABENT (CMediaDeviceFolder, IShellFolderViewCB),
  268. QITABENT (CMediaDeviceFolder, IDelegateFolder),
  269. QITABENT (CMediaDeviceFolder, IContextMenuCB),
  270. { 0 },
  271. };
  272. return QISearch(this, qit, riid, ppv);
  273. }
  274. STDMETHODIMP_(ULONG) CMediaDeviceFolder::AddRef()
  275. {
  276. return InterlockedIncrement(&_cRef);
  277. }
  278. STDMETHODIMP_(ULONG) CMediaDeviceFolder::Release()
  279. {
  280. if (InterlockedDecrement(&_cRef))
  281. return _cRef;
  282. delete this;
  283. return 0;
  284. }
  285. STDAPI CMediaDeviceFolder_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  286. {
  287. HRESULT hr;
  288. CMediaDeviceFolder *pmdf = new CMediaDeviceFolder(NULL, NULL);
  289. if (pmdf)
  290. {
  291. *ppunk = SAFECAST(pmdf, IShellFolder2 *);
  292. hr = S_OK;
  293. }
  294. else
  295. {
  296. *ppunk = NULL;
  297. hr = E_OUTOFMEMORY;
  298. }
  299. return hr;
  300. }
  301. HRESULT CMediaDeviceFolder::_Authenticate(IUnknown *punk)
  302. {
  303. #ifndef _X86_
  304. HRESULT hr = E_FAIL;
  305. #else
  306. IComponentAuthenticate *pAuth;
  307. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IComponentAuthenticate, &pAuth));
  308. if (SUCCEEDED(hr))
  309. {
  310. if (NULL == _pSAC)
  311. _pSAC = new CSecureChannelClient;
  312. if (_pSAC)
  313. {
  314. hr = _pSAC->SetCertificate(SAC_CERT_V1, (BYTE *)abCert, sizeof(abCert), (BYTE *)abPVK, sizeof(abPVK));
  315. if (SUCCEEDED(hr))
  316. {
  317. _pSAC->SetInterface(pAuth);
  318. hr = _pSAC->Authenticate(SAC_PROTOCOL_V1);
  319. }
  320. }
  321. else
  322. hr = E_OUTOFMEMORY;
  323. pAuth->Release();
  324. }
  325. #endif
  326. return hr;
  327. }
  328. HRESULT CMediaDeviceFolder::_GetMediaDevMgr(REFIID riid, void **ppv)
  329. {
  330. *ppv = NULL;
  331. HRESULT hr = E_FAIL;
  332. if (_IsRoot() && _pgit)
  333. {
  334. if (-1 == _dwDevMgrCookie)
  335. {
  336. IWMDeviceManager *pdevmgr;
  337. if (SUCCEEDED(CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IWMDeviceManager, &pdevmgr))))
  338. {
  339. _Authenticate(pdevmgr);
  340. _pgit->RegisterInterfaceInGlobal(pdevmgr, IID_IWMDeviceManager, &_dwDevMgrCookie);
  341. pdevmgr->Release();
  342. }
  343. }
  344. if (-1 != _dwDevMgrCookie)
  345. hr = _pgit->GetInterfaceFromGlobal(_dwDevMgrCookie, riid, ppv);
  346. }
  347. return hr;
  348. }
  349. HRESULT CMediaDeviceFolder::_EnumDevices(IWMDMEnumDevice **ppEnumDevices)
  350. {
  351. *ppEnumDevices = NULL;
  352. IWMDeviceManager *pdevmgr;
  353. HRESULT hr = _GetMediaDevMgr(IID_PPV_ARG(IWMDeviceManager, &pdevmgr));
  354. if (SUCCEEDED(hr))
  355. {
  356. hr = pdevmgr->EnumDevices(ppEnumDevices);
  357. pdevmgr->Release();
  358. }
  359. return hr;
  360. }
  361. // test to see if this is the root of the media devices name space (where the devices are)
  362. BOOL CMediaDeviceFolder::_IsRoot()
  363. {
  364. return _bRoot;
  365. }
  366. HRESULT CMediaDeviceFolder::_Storage(REFIID riid, void **ppv)
  367. {
  368. *ppv = NULL;
  369. HRESULT hr = E_NOINTERFACE;
  370. if (-1 != _dwStorageCookie)
  371. {
  372. ASSERT(!_IsRoot());
  373. hr = _pgit->GetInterfaceFromGlobal(_dwStorageCookie, riid, ppv);
  374. }
  375. return hr;
  376. }
  377. HRESULT CMediaDeviceFolder::_EnumStorage(IWMDMEnumStorage **ppEnumStorage)
  378. {
  379. *ppEnumStorage = NULL;
  380. IWMDMStorage *pStorage;
  381. HRESULT hr = _Storage(IID_PPV_ARG(IWMDMStorage, &pStorage));
  382. if (SUCCEEDED(hr))
  383. {
  384. hr = pStorage->EnumStorage(ppEnumStorage);
  385. pStorage->Release();
  386. }
  387. return hr;
  388. }
  389. HRESULT CMediaDeviceFolder::_FindDeviceByName(LPCWSTR pszName, IWMDMDevice **ppdevice)
  390. {
  391. IWMDMEnumDevice *pEnumDevice;
  392. HRESULT hr = _EnumDevices(&pEnumDevice);
  393. if (SUCCEEDED(hr))
  394. {
  395. hr = E_FAIL;
  396. ULONG ulFetched;
  397. IWMDMDevice *pdev;
  398. while (S_OK == pEnumDevice->Next(1, &pdev, &ulFetched))
  399. {
  400. WCHAR szName[MAX_PATH];
  401. if (SUCCEEDED(pdev->GetName(szName, ARRAYSIZE(szName))) &&
  402. (0 == StrCmpIC(pszName, szName)))
  403. {
  404. *ppdevice = pdev;
  405. hr = S_OK;
  406. break;
  407. }
  408. pdev->Release();
  409. }
  410. pEnumDevice->Release();
  411. }
  412. return hr;
  413. }
  414. HRESULT CMediaDeviceFolder::_StorageByName(LPCWSTR pszName, REFIID riid, void **ppv)
  415. {
  416. IWMDMStorage2 *pstg;
  417. HRESULT hr = _Storage(IID_PPV_ARG(IWMDMStorage2, &pstg));
  418. if (SUCCEEDED(hr))
  419. {
  420. IWMDMStorage *pstgFound;
  421. hr = pstg->GetStorage((LPWSTR)pszName, &pstgFound);
  422. if (SUCCEEDED(hr))
  423. {
  424. hr = pstgFound->QueryInterface(riid, ppv);
  425. pstgFound->Release();
  426. }
  427. else if (E_NOINTERFACE == hr)
  428. {
  429. // Rio600 fails on this now. I asked jhelin to look into this
  430. IWMDMEnumStorage *penum;
  431. if (SUCCEEDED(pstg->EnumStorage(&penum)))
  432. {
  433. ULONG ulFetched;
  434. while (S_OK == penum->Next(1, &pstgFound, &ulFetched))
  435. {
  436. WCHAR szName[MAX_PATH];
  437. if (SUCCEEDED(pstgFound->GetName(szName, ARRAYSIZE(szName))) &&
  438. (0 == StrCmpI(pszName, szName)))
  439. {
  440. hr = pstgFound->QueryInterface(riid, ppv);
  441. }
  442. pstgFound->Release();
  443. if (SUCCEEDED(hr))
  444. break;
  445. }
  446. penum->Release();
  447. }
  448. }
  449. pstg->Release();
  450. }
  451. return hr;
  452. }
  453. LPCMEDIADEVITEM CMediaDeviceFolder::_IsValid(LPCITEMIDLIST pidl)
  454. {
  455. if (pidl && ((LPCMEDIADEVITEM)pidl)->dwMagic == MEDIADEVITEM_MAGIC)
  456. return (LPCMEDIADEVITEM)pidl;
  457. return NULL;
  458. }
  459. DWORD CMediaDeviceFolder::_IsFolder(LPCMEDIADEVITEM pmditem)
  460. {
  461. return pmditem->dwAttributes & WMDM_FILE_ATTR_FOLDER ? FILE_ATTRIBUTE_DIRECTORY : 0;
  462. }
  463. // helper to support being run as a delegate or a stand alone folder
  464. //
  465. // the cbInner is the size of the data needed by the delegate. we need to compute
  466. // the full size of the pidl for the allocation and init that we the outer folder data
  467. void *CMediaDeviceFolder::_Alloc(SIZE_T cbInner)
  468. {
  469. DELEGATEITEMID *pidl;
  470. if (_pmalloc)
  471. {
  472. pidl = (DELEGATEITEMID *)_pmalloc->Alloc(cbInner);
  473. }
  474. else
  475. {
  476. SIZE_T cbAlloc =
  477. sizeof(DELEGATEITEMID) - sizeof(pidl->rgb[0]) + // header
  478. cbInner + // inner
  479. sizeof(WORD); // trailing null (pidl terminator)
  480. pidl = (DELEGATEITEMID *)SHAlloc(cbAlloc);
  481. if (pidl)
  482. {
  483. ZeroMemory(pidl, cbAlloc); // make it all empty
  484. pidl->cbSize = (WORD)cbAlloc - sizeof(WORD);
  485. pidl->cbInner = (WORD)cbInner;
  486. }
  487. }
  488. return (void *)pidl;
  489. }
  490. HRESULT CMediaDeviceFolder::_CreateIDList(LPCTSTR pszName, MEDIADEVITEM **ppmditem, BOOL *pfFolder)
  491. {
  492. HRESULT hr;
  493. UINT cbInner = sizeof(MEDIADEVITEM) - (sizeof(DELEGATEITEMID) - 1) +
  494. (sizeof(WCHAR) * lstrlen(pszName)); // null is accounted for in szName[1]
  495. *ppmditem = (MEDIADEVITEM *)_Alloc(cbInner);
  496. if (*ppmditem)
  497. {
  498. (*ppmditem)->dwMagic = MEDIADEVITEM_MAGIC;
  499. StrCpyW((*ppmditem)->szName, pszName);
  500. if (pfFolder)
  501. *pfFolder = _IsFolder(*ppmditem);
  502. hr = S_OK;
  503. }
  504. else
  505. hr = E_OUTOFMEMORY;
  506. return hr;
  507. }
  508. // Creates an item identifier list for the objects in the namespace
  509. HRESULT CMediaDeviceFolder::_IDListForDevice(IWMDMDevice *pdev, LPITEMIDLIST *ppidl, BOOL *pfFolder)
  510. {
  511. WCHAR szName[MAX_PATH];
  512. HRESULT hr = pdev->GetName(szName, ARRAYSIZE(szName));
  513. if (SUCCEEDED(hr))
  514. {
  515. // WCHAR szTemp[128];
  516. // pdev->GetManufacturer(szTemp, ARRAYSIZE(szTemp));
  517. MEDIADEVITEM *pmditem;
  518. hr = _CreateIDList(szName, &pmditem, pfFolder);
  519. if (SUCCEEDED(hr))
  520. {
  521. IWMDMStorage *pstg;
  522. if (SUCCEEDED(_StorageForDevice(pdev, IID_PPV_ARG(IWMDMStorage, &pstg))))
  523. {
  524. IWMDMStorageGlobals *pstgGlobals;
  525. if (SUCCEEDED(pstg->GetStorageGlobals(&pstgGlobals)))
  526. {
  527. pstgGlobals->GetTotalSize(&pmditem->cbTotal.LowPart, &pmditem->cbTotal.HighPart);
  528. pstgGlobals->GetTotalFree(&pmditem->cbFree.LowPart, &pmditem->cbFree.HighPart);
  529. pstgGlobals->Release();
  530. }
  531. pstg->Release();
  532. }
  533. pmditem->dwAttributes = WMDM_FILE_ATTR_FOLDER | WMDM_STORAGE_ATTR_HAS_FOLDERS;
  534. // pdev->GetType(&pmditem->dwType); // this is an expensive call, hits the disk
  535. *ppidl = (LPITEMIDLIST)pmditem;
  536. }
  537. }
  538. return hr;
  539. }
  540. void WMDMDateTimeToSystemTime(const WMDMDATETIME *pWMDM, FILETIME *pft)
  541. {
  542. SYSTEMTIME st = {0};
  543. st.wYear = pWMDM->wYear;
  544. st.wMonth = pWMDM->wMonth;
  545. st.wDay = pWMDM->wDay;
  546. st.wHour = pWMDM->wHour;
  547. st.wMinute = pWMDM->wMinute;
  548. st.wSecond = pWMDM->wSecond;
  549. SystemTimeToFileTime(&st, pft);
  550. }
  551. HRESULT CMediaDeviceFolder::_IDListForStorage(IWMDMStorage *pstg, LPITEMIDLIST *ppidl, BOOL *pfFolder)
  552. {
  553. WCHAR szName[MAX_PATH];
  554. HRESULT hr = pstg->GetName(szName, ARRAYSIZE(szName));
  555. if (SUCCEEDED(hr))
  556. {
  557. MEDIADEVITEM *pmditem;
  558. hr = _CreateIDList(szName, &pmditem, pfFolder);
  559. if (SUCCEEDED(hr))
  560. {
  561. _WAVEFORMATEX fmt;
  562. pstg->GetAttributes(&pmditem->dwAttributes, &fmt);
  563. WMDMDATETIME datetime;
  564. if (SUCCEEDED(pstg->GetDate(&datetime)))
  565. {
  566. WMDMDateTimeToSystemTime(&datetime, &pmditem->ftModified);
  567. }
  568. if (!(pmditem->dwAttributes & WMDM_FILE_ATTR_FOLDER))
  569. {
  570. pstg->GetSize(&pmditem->cbTotal.LowPart, &pmditem->cbTotal.HighPart);
  571. }
  572. *ppidl = (LPITEMIDLIST)pmditem;
  573. }
  574. }
  575. return hr;
  576. }
  577. // IPersist
  578. STDMETHODIMP CMediaDeviceFolder::GetClassID(CLSID *pClassID)
  579. {
  580. *pClassID = CLSID_MediaDeviceFolder;
  581. return S_OK;
  582. }
  583. // IPersistFolder
  584. STDMETHODIMP CMediaDeviceFolder::Initialize(LPCITEMIDLIST pidl)
  585. {
  586. ILFree(_pidl);
  587. return SHILClone(pidl, &_pidl);
  588. }
  589. // IPersistFolder2
  590. HRESULT CMediaDeviceFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  591. {
  592. if (_pidl)
  593. return SHILClone(_pidl, ppidl);
  594. *ppidl = NULL;
  595. return S_FALSE;
  596. }
  597. // IShellFolder(2)
  598. HRESULT CMediaDeviceFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  599. {
  600. HRESULT hr;
  601. if (!pszName || !ppidl)
  602. return E_INVALIDARG;
  603. *ppidl = NULL;
  604. #if 1
  605. hr = E_NOTIMPL;
  606. #else
  607. TCHAR szName[MAX_PATH];
  608. hr = _NextSegment((LPCWSTR*)&pszName, szName, ARRAYSIZE(szName), TRUE);
  609. if (SUCCEEDED(hr))
  610. {
  611. hr = _IDListForDevice(szName, ppidl, NULL);
  612. if (SUCCEEDED(hr) && pszName)
  613. {
  614. IShellFolder *psf;
  615. hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
  616. if (SUCCEEDED(hr))
  617. {
  618. ULONG chEaten;
  619. LPITEMIDLIST pidlNext;
  620. hr = psf->ParseDisplayName(hwnd, pbc, pszName, &chEaten, &pidlNext, pdwAttributes);
  621. if (SUCCEEDED(hr))
  622. hr = SHILAppend(pidlNext, ppidl);
  623. psf->Release();
  624. }
  625. }
  626. else if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
  627. {
  628. GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
  629. }
  630. }
  631. #endif
  632. // clean up if the parse failed.
  633. if (FAILED(hr))
  634. {
  635. ILFree(*ppidl);
  636. *ppidl = NULL;
  637. }
  638. return hr;
  639. }
  640. HRESULT CMediaDeviceFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  641. {
  642. HRESULT hr;
  643. CMediaDeviceEnum *penum = new CMediaDeviceEnum(this, grfFlags);
  644. if (penum)
  645. {
  646. hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  647. penum->Release();
  648. }
  649. else
  650. {
  651. hr = E_OUTOFMEMORY;
  652. *ppenum = NULL;
  653. }
  654. return hr;
  655. }
  656. STDAPI_(LPITEMIDLIST) ILCombineParentAndFirst(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlNext)
  657. {
  658. ULONG cbParent = ILGetSize(pidlParent);
  659. ULONG cbRest = (ULONG)((ULONG_PTR)pidlNext - (ULONG_PTR)pidl);
  660. LPITEMIDLIST pidlNew = _ILCreate(cbParent + cbRest);
  661. if (pidlNew)
  662. {
  663. cbParent -= sizeof(pidlParent->mkid.cb);
  664. memcpy(pidlNew, pidlParent, cbParent);
  665. memcpy((BYTE *)pidlNew + cbParent, pidl, cbRest);
  666. ASSERT(_ILSkip(pidlNew, cbParent + cbRest)->mkid.cb == 0);
  667. }
  668. return pidlNew;
  669. }
  670. HRESULT CMediaDeviceFolder::_StorageForDevice(IWMDMDevice *pdev, REFIID riid, void **ppv)
  671. {
  672. *ppv = NULL;
  673. IWMDMEnumStorage *penum;
  674. HRESULT hr = pdev->EnumStorage(&penum);
  675. if (SUCCEEDED(hr))
  676. {
  677. ULONG ulFetched;
  678. IWMDMStorage *pstg;
  679. if (S_OK == penum->Next(1, &pstg, &ulFetched))
  680. {
  681. hr= pstg->QueryInterface(riid, ppv);
  682. pstg->Release();
  683. }
  684. else
  685. {
  686. hr = E_NOINTERFACE;
  687. }
  688. penum->Release();
  689. }
  690. return hr;
  691. }
  692. HRESULT CMediaDeviceFolder::_StroageFromItem(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv)
  693. {
  694. WCHAR szName[MAX_PATH];
  695. _Name(pmdi, szName, ARRAYSIZE(szName));
  696. *ppv = NULL;
  697. HRESULT hr;
  698. if (_IsRoot())
  699. {
  700. // root case
  701. IWMDMDevice *pdev;
  702. hr = _FindDeviceByName(szName, &pdev);
  703. if (SUCCEEDED(hr))
  704. {
  705. hr = _StorageForDevice(pdev, riid, ppv);
  706. pdev->Release();
  707. }
  708. }
  709. else
  710. {
  711. hr = _StorageByName(szName, riid, ppv);
  712. }
  713. return hr;
  714. }
  715. HRESULT CMediaDeviceFolder::_ChangeNotifyItem(LONG lEvent, LPCITEMIDLIST pidl)
  716. {
  717. ITEMIDLIST *pidlFull;
  718. HRESULT hr = SHILCombine(_pidl, pidl, &pidlFull);
  719. if (SUCCEEDED(hr))
  720. {
  721. SHChangeNotify(lEvent, 0, (LPCTSTR)pidlFull, NULL);
  722. ILFree(pidlFull);
  723. }
  724. return hr;
  725. }
  726. HRESULT CMediaDeviceFolder::_ChangeNotifyObj(LONG lEvent, IUnknown *punkStg)
  727. {
  728. IWMDMStorage *pstg;
  729. HRESULT hr = punkStg->QueryInterface(IID_PPV_ARG(IWMDMStorage, &pstg));
  730. if (SUCCEEDED(hr))
  731. {
  732. ITEMIDLIST *pidl;
  733. BOOL bFolder;
  734. hr = _IDListForStorage(pstg, &pidl, &bFolder);
  735. if (SUCCEEDED(hr))
  736. {
  737. hr = _ChangeNotifyItem(lEvent, pidl);
  738. ILFree(pidl);
  739. }
  740. pstg->Release();
  741. }
  742. return hr;
  743. }
  744. HRESULT CMediaDeviceFolder::_DeviceFromItem(LPCMEDIADEVITEM pmdi, IWMDMDevice **ppdev)
  745. {
  746. *ppdev = NULL;
  747. TCHAR szName[MAX_PATH];
  748. return _IsRoot() ? _FindDeviceByName(_RawName(pmdi, szName, ARRAYSIZE(szName)), ppdev) : E_NOINTERFACE;
  749. }
  750. HRESULT CMediaDeviceFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  751. {
  752. *ppv = NULL;
  753. HRESULT hr = E_NOINTERFACE;
  754. LPCMEDIADEVITEM pmdi = _IsValid(pidl);
  755. if (pmdi && _IsFolder(pmdi))
  756. {
  757. if (IID_IShellFolder == riid ||
  758. IID_IShellFolder2 == riid)
  759. {
  760. IWMDMStorage *pstg;
  761. hr = _StroageFromItem(pmdi, IID_PPV_ARG(IWMDMStorage, &pstg));
  762. if (SUCCEEDED(hr))
  763. {
  764. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  765. LPITEMIDLIST pidlSubFolder = ILCombineParentAndFirst(_pidl, pidl, pidlNext);
  766. if (pidlSubFolder)
  767. {
  768. CMediaDeviceFolder *pmdf = new CMediaDeviceFolder(this, pstg);
  769. if (pmdf)
  770. {
  771. hr = pmdf->Initialize(pidlSubFolder);
  772. if (SUCCEEDED(hr))
  773. {
  774. // if there's nothing left in the pidl, get the interface on this one.
  775. if (ILIsEmpty(pidlNext))
  776. hr = pmdf->QueryInterface(riid, ppv);
  777. else
  778. {
  779. // otherwise, hand the rest of it off to the new shellfolder.
  780. hr = pmdf->BindToObject(pidlNext, pbc, riid, ppv);
  781. }
  782. }
  783. pmdf->Release();
  784. }
  785. else
  786. hr = E_OUTOFMEMORY;
  787. ILFree(pidlSubFolder);
  788. }
  789. else
  790. hr = E_OUTOFMEMORY;
  791. pstg->Release();
  792. }
  793. }
  794. }
  795. else
  796. hr = E_FAIL;
  797. ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && (NULL == *ppv))); // Assert hr is consistent w/out param.
  798. return hr;
  799. }
  800. enum
  801. {
  802. DEV_COL_NAME = 0,
  803. DEV_COL_SIZE,
  804. DEV_COL_TYPE,
  805. DEV_COL_MODIFIED,
  806. };
  807. HRESULT CMediaDeviceFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  808. {
  809. LPCMEDIADEVITEM pmdi1 = _IsValid(pidl1);
  810. LPCMEDIADEVITEM pmdi2 = _IsValid(pidl2);
  811. int nCmp = 0;
  812. if (!pmdi1 || !pmdi2)
  813. return E_INVALIDARG;
  814. // folders always sort to the top of the list, if either of these items
  815. // are folders then compare on the folderness
  816. if (_IsFolder(pmdi1) || _IsFolder(pmdi2))
  817. {
  818. if (_IsFolder(pmdi1) && !_IsFolder(pmdi2))
  819. nCmp = -1;
  820. else if (!_IsFolder(pmdi1) && _IsFolder(pmdi2))
  821. nCmp = 1;
  822. }
  823. // if they match (or are not folders, then lets compare based on the column ID.
  824. if (nCmp == 0)
  825. {
  826. switch (lParam & SHCIDS_COLUMNMASK)
  827. {
  828. case DEV_COL_NAME: // caught later on
  829. break;
  830. case DEV_COL_SIZE:
  831. if (pmdi1->cbTotal.QuadPart < pmdi2->cbTotal.QuadPart)
  832. nCmp = -1;
  833. else if (pmdi1->cbTotal.QuadPart > pmdi2->cbTotal.QuadPart)
  834. nCmp = 1;
  835. break;
  836. case DEV_COL_TYPE:
  837. {
  838. TCHAR szType1[MAX_PATH], szType2[MAX_PATH];
  839. _Type(pmdi1, szType1, ARRAYSIZE(szType1));
  840. _Type(pmdi2, szType2, ARRAYSIZE(szType2));
  841. nCmp = StrCmp(szType1, szType2);
  842. break;
  843. }
  844. case DEV_COL_MODIFIED:
  845. if (pmdi1->ulModified.QuadPart < pmdi2->ulModified.QuadPart)
  846. nCmp = -1;
  847. else if (pmdi1->ulModified.QuadPart > pmdi2->ulModified.QuadPart)
  848. nCmp = 1;
  849. break;
  850. }
  851. if (nCmp == 0)
  852. {
  853. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  854. nCmp = StrCmpLogicalW(_Name(pmdi1, szName1, ARRAYSIZE(szName1)), _Name(pmdi2, szName2, ARRAYSIZE(szName2)));
  855. }
  856. }
  857. return ResultFromShort(nCmp);
  858. }
  859. HRESULT CMediaDeviceFolder::_OnBackgroundEnum(DWORD pv)
  860. {
  861. return S_OK; // do enum on backgound always
  862. }
  863. HRESULT CMediaDeviceFolder::_OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents)
  864. {
  865. *ppidl = _pidl; // evil alais
  866. *plEvents = SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \
  867. SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \
  868. SHCNE_MKDIR | SHCNE_RMDIR;
  869. return S_OK;
  870. }
  871. HRESULT CMediaDeviceFolder::_OnViewMode(DWORD pv, FOLDERVIEWMODE *pvm)
  872. {
  873. *pvm = FVM_TILE;
  874. return S_OK;
  875. }
  876. // IShellFolderViewCB
  877. STDMETHODIMP CMediaDeviceFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
  878. {
  879. HRESULT hr = E_FAIL;
  880. switch (uMsg)
  881. {
  882. HANDLE_MSG(0, SFVM_BACKGROUNDENUM, _OnBackgroundEnum);
  883. HANDLE_MSG(0, SFVM_GETNOTIFY, _OnGetNotify);
  884. HANDLE_MSG(0, SFVM_DEFVIEWMODE, _OnViewMode);
  885. }
  886. return hr;
  887. }
  888. HRESULT CMediaDeviceFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  889. {
  890. HRESULT hr = E_NOINTERFACE;
  891. *ppv = NULL;
  892. if (IsEqualIID(riid, IID_IShellView))
  893. {
  894. SFV_CREATE sSFV = { 0 };
  895. sSFV.cbSize = sizeof(sSFV);
  896. sSFV.psfvcb = SAFECAST(this, IShellFolderViewCB *);
  897. sSFV.pshf = SAFECAST(this, IShellFolder *);
  898. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  899. }
  900. else if (IsEqualIID(riid, IID_IContextMenu))
  901. {
  902. HKEY hkNoFiles = NULL;
  903. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles);
  904. hr = CDefFolderMenu_Create2(_pidl, hwnd, 0, NULL, this, NULL,
  905. 1, &hkNoFiles, (IContextMenu **)ppv);
  906. if (hkNoFiles)
  907. RegCloseKey(hkNoFiles);
  908. }
  909. else if (IsEqualIID(riid, IID_IDropTarget) && !_IsRoot())
  910. {
  911. CMediaDeviceDropTarget *psdt = new CMediaDeviceDropTarget(this, hwnd);
  912. if (psdt)
  913. {
  914. hr = psdt->QueryInterface(riid, ppv);
  915. psdt->Release();
  916. }
  917. else
  918. hr = E_OUTOFMEMORY;
  919. }
  920. return hr;
  921. }
  922. DWORD WMDMAttributesSFGAO(DWORD dwWMDMAttributes)
  923. {
  924. DWORD dwShell = SFGAO_DROPTARGET; // defaults
  925. if (WMDM_FILE_ATTR_FOLDER & dwWMDMAttributes)
  926. dwShell |= SFGAO_FOLDER;
  927. if (WMDM_FILE_ATTR_LINK & dwWMDMAttributes)
  928. dwShell |= SFGAO_LINK;
  929. if (WMDM_FILE_ATTR_CANDELETE & dwWMDMAttributes)
  930. dwShell |= SFGAO_CANDELETE;
  931. if (WMDM_FILE_ATTR_CANMOVE & dwWMDMAttributes)
  932. dwShell |= SFGAO_CANMOVE;
  933. if (WMDM_FILE_ATTR_CANRENAME & dwWMDMAttributes)
  934. dwShell |= SFGAO_CANRENAME;
  935. if (WMDM_FILE_ATTR_HIDDEN & dwWMDMAttributes)
  936. dwShell |= SFGAO_HIDDEN;
  937. if (WMDM_FILE_ATTR_READONLY & dwWMDMAttributes)
  938. dwShell |= SFGAO_READONLY;
  939. if (WMDM_STORAGE_ATTR_HAS_FOLDERS & dwWMDMAttributes)
  940. dwShell |= SFGAO_HASSUBFOLDER;
  941. return dwShell;
  942. }
  943. ULONG CMediaDeviceFolder::_GetAttributesOf(LPCMEDIADEVITEM pmdi, ULONG rgfIn)
  944. {
  945. ULONG lResult = rgfIn & WMDMAttributesSFGAO(pmdi->dwAttributes);
  946. if (_IsRoot())
  947. {
  948. lResult |= SFGAO_CANLINK | SFGAO_REMOVABLE;
  949. }
  950. return lResult;
  951. }
  952. BOOL IsSelf(UINT cidl, LPCITEMIDLIST *apidl)
  953. {
  954. return cidl == 0 || (cidl == 1 && (apidl == NULL || apidl[0] == NULL || ILIsEmpty(apidl[0])));
  955. }
  956. HRESULT CMediaDeviceFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  957. {
  958. UINT rgfOut = *prgfInOut;
  959. // return attributes of the namespace root?
  960. if (IsSelf(cidl, apidl))
  961. {
  962. rgfOut &= SFGAO_FOLDER |
  963. SFGAO_LINK | SFGAO_DROPTARGET |
  964. SFGAO_CANRENAME | SFGAO_CANDELETE |
  965. SFGAO_CANLINK | SFGAO_CANCOPY |
  966. SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
  967. }
  968. else
  969. {
  970. for (UINT i = 0; i < cidl; i++)
  971. {
  972. LPCMEDIADEVITEM pmdi = _IsValid(apidl[i]);
  973. if (pmdi)
  974. rgfOut &= _GetAttributesOf(pmdi, *prgfInOut);
  975. }
  976. }
  977. *prgfInOut = rgfOut;
  978. return S_OK;
  979. }
  980. HRESULT _CreateProgress(HWND hwnd, IProgressDialog **pppd)
  981. {
  982. HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IProgressDialog, pppd));
  983. if (SUCCEEDED(hr))
  984. {
  985. (*pppd)->StartProgressDialog(hwnd, NULL, PROGDLG_AUTOTIME, NULL);
  986. }
  987. return hr;
  988. }
  989. HRESULT CMediaDeviceFolder::_DeleteItems(HWND hwnd, IDataObject *pdtobj)
  990. {
  991. IProgressDialog *ppd;
  992. HRESULT hr = _CreateProgress(hwnd, &ppd);
  993. if (SUCCEEDED(hr))
  994. {
  995. ppd->SetTitle(L"Deleting Files...");
  996. STGMEDIUM medium;
  997. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  998. if (pida)
  999. {
  1000. for (UINT i = 0; i < pida->cidl; i++)
  1001. {
  1002. LPCMEDIADEVITEM pmdi = _IsValid(HIDA_GetPIDLItem(pida, i));
  1003. if (pmdi)
  1004. {
  1005. TCHAR szName[MAX_PATH];
  1006. ppd->SetLine(2, _DisplayName(pmdi, szName, ARRAYSIZE(szName)), FALSE, NULL);
  1007. ppd->SetProgress64(i, pida->cidl);
  1008. IWMDMStorageControl *pstgCtrl;
  1009. hr = _StroageFromItem(pmdi, IID_PPV_ARG(IWMDMStorageControl, &pstgCtrl));
  1010. if (SUCCEEDED(hr))
  1011. {
  1012. hr = pstgCtrl->Delete(WMDM_MODE_BLOCK, NULL);
  1013. if (SUCCEEDED(hr))
  1014. {
  1015. _ChangeNotifyItem(SHCNE_DELETE, (LPCITEMIDLIST)pmdi);
  1016. }
  1017. pstgCtrl->Release();
  1018. ppd->SetProgress64(i + 1, pida->cidl);
  1019. if (ppd->HasUserCancelled())
  1020. {
  1021. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  1022. break;
  1023. }
  1024. }
  1025. }
  1026. // free space changed on device, notify
  1027. // SHChangeNotify(SHCNE_UPDATEITEM, 0, (LPCTSTR)_pidl, NULL);
  1028. }
  1029. HIDA_ReleaseStgMedium(pida, &medium);
  1030. }
  1031. ppd->StopProgressDialog();
  1032. ppd->Release();
  1033. }
  1034. return hr;
  1035. }
  1036. HRESULT CMediaDeviceFolder::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1037. {
  1038. HRESULT hr = S_OK;
  1039. switch (uMsg)
  1040. {
  1041. case DFM_MERGECONTEXTMENU:
  1042. break;
  1043. case DFM_INVOKECOMMANDEX:
  1044. {
  1045. DFMICS *pdfmics = (DFMICS *)lParam;
  1046. switch (wParam)
  1047. {
  1048. case DFM_CMD_DELETE:
  1049. hr = _DeleteItems(hwnd, pdtobj);
  1050. break;
  1051. case DFM_CMD_PROPERTIES:
  1052. break;
  1053. default:
  1054. // This is common menu items, use the default code.
  1055. hr = S_FALSE;
  1056. break;
  1057. }
  1058. break;
  1059. }
  1060. default:
  1061. hr = E_NOTIMPL;
  1062. break;
  1063. }
  1064. return hr;
  1065. }
  1066. HRESULT CALLBACK CMediaDeviceFolder::_ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1067. {
  1068. IContextMenuCB *pcmcb;
  1069. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IContextMenuCB, &pcmcb));
  1070. if (SUCCEEDED(hr))
  1071. {
  1072. hr = pcmcb->CallBack(psf, hwnd, pdtobj, uMsg, wParam, lParam);
  1073. pcmcb->Release();
  1074. }
  1075. return hr;
  1076. }
  1077. class CExtractConstIcon : public IExtractIconW
  1078. {
  1079. public:
  1080. CExtractConstIcon(LPCTSTR psz, UINT uID);
  1081. // IUnknown
  1082. STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
  1083. STDMETHODIMP_(ULONG) AddRef();
  1084. STDMETHODIMP_(ULONG) Release();
  1085. // IExtractIconW
  1086. STDMETHODIMP GetIconLocation(UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int* piIndex, UINT* pwFlags);
  1087. STDMETHODIMP Extract(LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize);
  1088. private:
  1089. LONG _cRef;
  1090. TCHAR _szPath[MAX_PATH];
  1091. UINT _uID;
  1092. };
  1093. CExtractConstIcon::CExtractConstIcon(LPCTSTR psz, UINT uID) : _cRef(1), _uID(uID)
  1094. {
  1095. StrCpyN(_szPath, psz, ARRAYSIZE(_szPath));
  1096. }
  1097. HRESULT CExtractConstIcon::QueryInterface(REFIID riid, void **ppv)
  1098. {
  1099. static const QITAB qit[] =
  1100. {
  1101. QITABENT(CExtractConstIcon, IExtractIconW),
  1102. { 0 },
  1103. };
  1104. return QISearch(this, qit, riid, ppv);
  1105. }
  1106. STDMETHODIMP_(ULONG) CExtractConstIcon::AddRef()
  1107. {
  1108. return InterlockedIncrement(&_cRef);
  1109. }
  1110. STDMETHODIMP_(ULONG) CExtractConstIcon::Release()
  1111. {
  1112. if (InterlockedDecrement(&_cRef))
  1113. return _cRef;
  1114. delete this;
  1115. return 0;
  1116. }
  1117. STDMETHODIMP CExtractConstIcon::GetIconLocation(UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
  1118. {
  1119. StrCpyN(pszIconFile, _szPath, cchMax);
  1120. *piIndex = _uID;
  1121. *pwFlags = GIL_PERINSTANCE;
  1122. return S_OK;
  1123. }
  1124. STDMETHODIMP CExtractConstIcon::Extract(LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
  1125. {
  1126. return S_FALSE; // do the extract for me
  1127. }
  1128. HRESULT CMediaDeviceFolder::_CreateExtractIcon(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv)
  1129. {
  1130. *ppv = NULL;
  1131. HRESULT hr = E_FAIL;
  1132. if (_IsRoot())
  1133. {
  1134. #define IDI_AUDIOPLAYER 299
  1135. CExtractConstIcon *pei = new CExtractConstIcon(TEXT("shell32.dll"), -IDI_AUDIOPLAYER);
  1136. if (pei)
  1137. {
  1138. hr = pei->QueryInterface(riid, ppv);
  1139. pei->Release();
  1140. }
  1141. }
  1142. else
  1143. {
  1144. WCHAR szName[MAX_PATH];
  1145. hr = SHCreateFileExtractIconW(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), riid, ppv);
  1146. }
  1147. return hr;
  1148. }
  1149. HRESULT CMediaDeviceFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  1150. REFIID riid, UINT *prgfInOut, void **ppv)
  1151. {
  1152. HRESULT hr = E_INVALIDARG;
  1153. LPCMEDIADEVITEM pmdi = cidl ? _IsValid(apidl[0]) : NULL;
  1154. if (pmdi &&
  1155. (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
  1156. {
  1157. hr = _CreateExtractIcon(pmdi, riid, ppv);
  1158. }
  1159. else if (IsEqualIID(riid, IID_IDataObject) && cidl)
  1160. {
  1161. hr = CIDLData_CreateFromIDArray(_pidl, cidl, apidl, (IDataObject**) ppv);
  1162. }
  1163. else if (IsEqualIID(riid, IID_IContextMenu) && pmdi)
  1164. {
  1165. // get the association for these files and lets attempt to
  1166. // build the context menu for the selection.
  1167. IQueryAssociations *pqa;
  1168. hr = GetUIObjectOf(hwnd, 1, apidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
  1169. if (SUCCEEDED(hr))
  1170. {
  1171. HKEY ahk[3];
  1172. DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk));
  1173. hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl,
  1174. this, _ItemsMenuCB, cKeys, ahk,
  1175. (IContextMenu **)ppv);
  1176. SHRegCloseKeys(ahk, cKeys);
  1177. pqa->Release();
  1178. }
  1179. }
  1180. else if (IsEqualIID(riid, IID_IQueryAssociations) && pmdi)
  1181. {
  1182. TCHAR szName[MAX_PATH];
  1183. hr = SHAssocCreateForFile(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), NULL, riid, ppv);
  1184. }
  1185. else if (IsEqualIID(riid, IID_IDropTarget) && pmdi && _IsFolder(pmdi))
  1186. {
  1187. // If a directory is selected in the view, the drop is going to a folder,
  1188. // so we need to bind to that folder and ask it to create a drop target
  1189. IShellFolder *psf;
  1190. hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
  1191. if (SUCCEEDED(hr))
  1192. {
  1193. hr = psf->CreateViewObject(hwnd, IID_IDropTarget, ppv);
  1194. psf->Release();
  1195. }
  1196. }
  1197. return hr;
  1198. }
  1199. BOOL CMediaDeviceFolder::_FileInfo(LPCMEDIADEVITEM pmdi, DWORD dwFlags, SHFILEINFO *psfi)
  1200. {
  1201. TCHAR szName[MAX_PATH];
  1202. ASSERT(!(dwFlags & SHGFI_SYSICONINDEX));
  1203. ZeroMemory(psfi, sizeof(*psfi));
  1204. return (BOOL)SHGetFileInfo(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), psfi, sizeof(*psfi), SHGFI_USEFILEATTRIBUTES | dwFlags);
  1205. }
  1206. LPCTSTR CMediaDeviceFolder::_RawName(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch)
  1207. {
  1208. ualstrcpyn(psz, pmdi->szName, cch);
  1209. return psz;
  1210. }
  1211. LPCTSTR CMediaDeviceFolder::_Name(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch)
  1212. {
  1213. ualstrcpyn(psz, pmdi->szName, cch);
  1214. return PathFindFileName(psz); // some devices use path components in the names of the items
  1215. }
  1216. LPCTSTR CMediaDeviceFolder::_DisplayName(LPCMEDIADEVITEM pmdi, LPTSTR pszResult, UINT cch)
  1217. {
  1218. SHFILEINFO sfi;
  1219. if (_FileInfo(pmdi, SHGFI_DISPLAYNAME, &sfi))
  1220. {
  1221. StrCpyN(pszResult, sfi.szDisplayName, cch);
  1222. }
  1223. else
  1224. {
  1225. *pszResult = 0;
  1226. }
  1227. return pszResult;
  1228. }
  1229. HRESULT CMediaDeviceFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet)
  1230. {
  1231. HRESULT hr;
  1232. LPCMEDIADEVITEM pmdi = _IsValid(pidl);
  1233. if (pmdi)
  1234. {
  1235. TCHAR szTemp[MAX_PATH];
  1236. if (dwFlags & SHGDN_FORPARSING)
  1237. {
  1238. if (dwFlags & SHGDN_INFOLDER)
  1239. {
  1240. _RawName(pmdi, szTemp, ARRAYSIZE(szTemp)); // relative parse name
  1241. }
  1242. else
  1243. {
  1244. SHGetNameAndFlags(_pidl, dwFlags, szTemp, ARRAYSIZE(szTemp), NULL);
  1245. TCHAR szName[MAX_PATH];
  1246. PathAppend(szTemp, _RawName(pmdi, szName, ARRAYSIZE(szName)));
  1247. }
  1248. }
  1249. else
  1250. {
  1251. _DisplayName(pmdi, szTemp, ARRAYSIZE(szTemp));
  1252. }
  1253. hr = StringToStrRetW(szTemp, pStrRet);
  1254. }
  1255. else
  1256. hr = E_INVALIDARG;
  1257. return hr;
  1258. }
  1259. HRESULT CMediaDeviceFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut)
  1260. {
  1261. return E_NOTIMPL;
  1262. }
  1263. STDMETHODIMP CMediaDeviceFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  1264. {
  1265. HRESULT hr = E_FAIL;
  1266. LPCMEDIADEVITEM pmdi = _IsValid(pidl);
  1267. if (pmdi)
  1268. {
  1269. if (_IsRoot())
  1270. {
  1271. if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
  1272. {
  1273. SHDESCRIPTIONID did = {0};
  1274. did.dwDescriptionId = SHDID_COMPUTER_AUDIO;
  1275. hr = InitVariantFromBuffer(pv, &did, sizeof(did));
  1276. }
  1277. else if (IsEqualSCID(*pscid, SCID_CAPACITY))
  1278. {
  1279. pv->vt = VT_UI8;
  1280. pv->ullVal = pmdi->cbTotal.QuadPart;
  1281. hr = S_OK;
  1282. }
  1283. else if (IsEqualSCID(*pscid, SCID_FREESPACE))
  1284. {
  1285. pv->vt = VT_UI8;
  1286. pv->ullVal = pmdi->cbFree.QuadPart;
  1287. hr = S_OK;
  1288. }
  1289. else if (IsEqualSCID(*pscid, SCID_DisplayProperties))
  1290. {
  1291. hr = InitVariantFromStr(pv, TEXT("prop:Name;Type;FreeSpace;Capacity"));
  1292. }
  1293. }
  1294. else
  1295. {
  1296. if (IsEqualSCID(*pscid, SCID_TYPE))
  1297. {
  1298. TCHAR sz[64];
  1299. hr = InitVariantFromStr(pv, _Type(pmdi, sz, ARRAYSIZE(sz)));
  1300. }
  1301. else if (IsEqualSCID(*pscid, SCID_SIZE))
  1302. {
  1303. pv->vt = VT_UI8;
  1304. pv->ullVal = pmdi->cbTotal.QuadPart;
  1305. hr = S_OK;
  1306. }
  1307. }
  1308. }
  1309. return hr;
  1310. }
  1311. LPCTSTR CMediaDeviceFolder::_Type(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch)
  1312. {
  1313. *psz = 0;
  1314. SHFILEINFO sfi;
  1315. if (_FileInfo(pmdi, SHGFI_TYPENAME, &sfi))
  1316. StrCpyN(psz, sfi.szTypeName, cch);
  1317. return psz;
  1318. }
  1319. static const struct
  1320. {
  1321. UINT iTitle;
  1322. UINT cchCol;
  1323. UINT iFmt;
  1324. const SHCOLUMNID *pscid;
  1325. }
  1326. c_aMediaDeviceColumns[] =
  1327. {
  1328. {IDS_NAME_COL, 40, LVCFMT_LEFT, &SCID_NAME},
  1329. {IDS_SIZE_COL, 10, LVCFMT_RIGHT, &SCID_SIZE},
  1330. {IDS_TYPE_COL, 30, LVCFMT_LEFT, &SCID_TYPE},
  1331. {IDS_MODIFIED_COL, 20, LVCFMT_LEFT, &SCID_WRITETIME},
  1332. };
  1333. HRESULT CMediaDeviceFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail)
  1334. {
  1335. if (iColumn >= ARRAYSIZE(c_aMediaDeviceColumns))
  1336. return E_NOTIMPL;
  1337. HRESULT hr = S_OK;
  1338. TCHAR szTemp[MAX_PATH];
  1339. szTemp[0] = 0;
  1340. if (NULL == pidl)
  1341. {
  1342. pDetail->fmt = c_aMediaDeviceColumns[iColumn].iFmt;
  1343. pDetail->cxChar = c_aMediaDeviceColumns[iColumn].cchCol;
  1344. LoadString(m_hInst, c_aMediaDeviceColumns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
  1345. }
  1346. else
  1347. {
  1348. LPCMEDIADEVITEM pmdi = _IsValid(pidl);
  1349. if (pmdi)
  1350. {
  1351. // return the property to the caller that is being requested, this is based on the
  1352. // list of columns we gave out when the view was created.
  1353. switch (iColumn)
  1354. {
  1355. case DEV_COL_NAME:
  1356. _DisplayName(pmdi, szTemp, ARRAYSIZE(szTemp));
  1357. break;
  1358. case DEV_COL_SIZE:
  1359. if (!_IsFolder(pmdi))
  1360. {
  1361. ULARGE_INTEGER ullSize = pmdi->cbTotal;
  1362. StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp));
  1363. }
  1364. break;
  1365. case DEV_COL_TYPE:
  1366. _Type(pmdi, szTemp, ARRAYSIZE(szTemp));
  1367. break;
  1368. case DEV_COL_MODIFIED:
  1369. SHFormatDateTime(&pmdi->ftModified, NULL, szTemp, ARRAYSIZE(szTemp));
  1370. break;
  1371. }
  1372. }
  1373. }
  1374. return StringToStrRetW(szTemp, &(pDetail->str));
  1375. }
  1376. STDMETHODIMP CMediaDeviceFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  1377. {
  1378. HRESULT hr = E_INVALIDARG;
  1379. ZeroMemory(pscid, sizeof(*pscid));
  1380. if (!_IsRoot())
  1381. {
  1382. if (iColumn < ARRAYSIZE(c_aMediaDeviceColumns))
  1383. {
  1384. *pscid = *c_aMediaDeviceColumns[iColumn].pscid;
  1385. hr = S_OK;
  1386. }
  1387. }
  1388. return hr;
  1389. }
  1390. // IDelegateFolder
  1391. HRESULT CMediaDeviceFolder::SetItemAlloc(IMalloc *pmalloc)
  1392. {
  1393. _fDelegate = TRUE;
  1394. IUnknown_Set((IUnknown**)&_pmalloc, pmalloc);
  1395. return S_OK;
  1396. }
  1397. CMediaDeviceEnum::CMediaDeviceEnum(CMediaDeviceFolder *pmdf, DWORD grfFlags) : _cRef(1), _grfFlags(grfFlags), _iIndex(0)
  1398. {
  1399. _pmdf = pmdf;
  1400. _pmdf->AddRef();
  1401. // force full enum on this thread. avoid problem where calls made on the
  1402. // marshaled thread fail due to authentication
  1403. _Init();
  1404. DllAddRef();
  1405. }
  1406. int CALLBACK _FreeItems(ITEMIDLIST *pidl, IShellFolder *psf)
  1407. {
  1408. ILFree(pidl);
  1409. return 1;
  1410. }
  1411. CMediaDeviceEnum::~CMediaDeviceEnum()
  1412. {
  1413. if ((HDPA)_dpaItems)
  1414. _dpaItems.DestroyCallbackEx(_FreeItems, (IShellFolder *)NULL);
  1415. _pmdf->Release();
  1416. DllRelease();
  1417. }
  1418. HRESULT CMediaDeviceEnum::QueryInterface(REFIID riid, void **ppv)
  1419. {
  1420. static const QITAB qit[] =
  1421. {
  1422. QITABENT(CMediaDeviceEnum, IEnumIDList), // IID_IEnumIDList
  1423. { 0 },
  1424. };
  1425. return QISearch(this, qit, riid, ppv);
  1426. }
  1427. STDMETHODIMP_(ULONG) CMediaDeviceEnum::AddRef()
  1428. {
  1429. return InterlockedIncrement(&_cRef);
  1430. }
  1431. STDMETHODIMP_(ULONG) CMediaDeviceEnum::Release()
  1432. {
  1433. if (InterlockedDecrement(&_cRef))
  1434. return _cRef;
  1435. delete this;
  1436. return 0;
  1437. }
  1438. BOOL CMediaDeviceEnum::_FilterItem(BOOL bFolder)
  1439. {
  1440. if (bFolder)
  1441. {
  1442. if (!(_grfFlags & SHCONTF_FOLDERS))
  1443. {
  1444. return TRUE;
  1445. }
  1446. }
  1447. else if (!(_grfFlags & SHCONTF_NONFOLDERS))
  1448. {
  1449. return TRUE;
  1450. }
  1451. return FALSE;
  1452. }
  1453. BOOL CMediaDeviceFolder::_ShouldShowDevice(IWMDMDevice *pdev)
  1454. {
  1455. BOOL bShow = TRUE;
  1456. if (_fDelegate)
  1457. {
  1458. DWORD dwPowerSource, dwPercentRemaining;
  1459. pdev->GetPowerSource(&dwPowerSource, &dwPercentRemaining);
  1460. // heuristic to detect file system driver supported devices.
  1461. // if it runs on batteries assume it is not a file system device
  1462. bShow = (WMDM_POWER_CAP_BATTERY & dwPowerSource);
  1463. }
  1464. return bShow;
  1465. }
  1466. HRESULT CMediaDeviceEnum::_Init()
  1467. {
  1468. if ((HDPA)_dpaItems)
  1469. return S_OK;
  1470. HRESULT hr = _dpaItems.Create(10) ? S_OK : E_OUTOFMEMORY;
  1471. if (SUCCEEDED(hr))
  1472. {
  1473. if (_pmdf->_IsRoot())
  1474. {
  1475. IWMDMEnumDevice *pEnumDevice;
  1476. hr = _pmdf->_EnumDevices(&pEnumDevice);
  1477. if (SUCCEEDED(hr))
  1478. {
  1479. IWMDMDevice *pdev;
  1480. ULONG ulFetched;
  1481. while (S_OK == pEnumDevice->Next(1, &pdev, &ulFetched))
  1482. {
  1483. if (_pmdf->_ShouldShowDevice(pdev))
  1484. {
  1485. ITEMIDLIST *pidl;
  1486. BOOL bFolder;
  1487. hr = _pmdf->_IDListForDevice(pdev, &pidl, &bFolder);
  1488. if (SUCCEEDED(hr))
  1489. {
  1490. if (_FilterItem(bFolder) ||
  1491. (-1 == _dpaItems.AppendPtr(pidl)))
  1492. {
  1493. ILFree(pidl);
  1494. }
  1495. }
  1496. }
  1497. pdev->Release();
  1498. }
  1499. pEnumDevice->Release();
  1500. }
  1501. }
  1502. else
  1503. {
  1504. IWMDMEnumStorage *penum;
  1505. hr = _pmdf->_EnumStorage(&penum);
  1506. if (SUCCEEDED(hr))
  1507. {
  1508. ULONG ulFetched;
  1509. IWMDMStorage *rgpstg[1]; // 1 is to WORKAROUND Rio600 faults on > 1
  1510. while (SUCCEEDED(penum->Next(ARRAYSIZE(rgpstg), rgpstg, &ulFetched)))
  1511. {
  1512. for (UINT i = 0; i < ulFetched; i++)
  1513. {
  1514. ITEMIDLIST *pidl;
  1515. BOOL bFolder;
  1516. hr = _pmdf->_IDListForStorage(rgpstg[i], &pidl, &bFolder);
  1517. if (SUCCEEDED(hr))
  1518. {
  1519. if (_FilterItem(bFolder) ||
  1520. (-1 == _dpaItems.AppendPtr(pidl)))
  1521. {
  1522. ILFree(pidl);
  1523. }
  1524. }
  1525. rgpstg[i]->Release();
  1526. }
  1527. if (ulFetched != ARRAYSIZE(rgpstg))
  1528. break;
  1529. }
  1530. penum->Release();
  1531. }
  1532. }
  1533. }
  1534. return hr;
  1535. }
  1536. HRESULT CMediaDeviceEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1537. {
  1538. HRESULT hr = _Init();
  1539. if (SUCCEEDED(hr))
  1540. {
  1541. hr = S_FALSE;
  1542. if (_iIndex < _dpaItems.GetPtrCount())
  1543. {
  1544. hr = SHILClone(_dpaItems.GetPtr(_iIndex++), rgelt);
  1545. }
  1546. if (pceltFetched)
  1547. *pceltFetched = (hr == S_OK) ? 1 : 0;
  1548. }
  1549. return hr;
  1550. }
  1551. CMediaDeviceDropTarget::CMediaDeviceDropTarget(CMediaDeviceFolder *pmdf, HWND hwnd) :
  1552. _cRef(1), _pmdf(pmdf), _hwnd(hwnd), _grfKeyStateLast(-1)
  1553. {
  1554. _pmdf->AddRef();
  1555. // use the FTM to make the call back interface calls unmarshaled
  1556. CoCreateFreeThreadedMarshaler(SAFECAST(this, IWMDMProgress2 *), &_punkFTM);
  1557. DllAddRef();
  1558. }
  1559. CMediaDeviceDropTarget::~CMediaDeviceDropTarget()
  1560. {
  1561. DragLeave();
  1562. _pmdf->Release();
  1563. if (_punkFTM)
  1564. _punkFTM->Release();
  1565. DllRelease();
  1566. }
  1567. STDMETHODIMP_(ULONG) CMediaDeviceDropTarget::AddRef()
  1568. {
  1569. return InterlockedIncrement(&_cRef);
  1570. }
  1571. STDMETHODIMP_(ULONG) CMediaDeviceDropTarget::Release()
  1572. {
  1573. if (InterlockedDecrement(&_cRef))
  1574. return _cRef;
  1575. delete this;
  1576. return 0;
  1577. }
  1578. HRESULT CMediaDeviceDropTarget::QueryInterface(REFIID riid, void** ppv)
  1579. {
  1580. static const QITAB qit[] = {
  1581. QITABENT(CMediaDeviceDropTarget, IDropTarget),
  1582. QITABENTMULTI(CMediaDeviceDropTarget, IWMDMProgress, IWMDMProgress2),
  1583. QITABENT (CMediaDeviceDropTarget, IWMDMProgress2),
  1584. QITABENT(CMediaDeviceDropTarget, INamespaceWalkCB),
  1585. { 0 },
  1586. };
  1587. HRESULT hr = QISearch(this, qit, riid, ppv);
  1588. if (FAILED(hr) && (IID_IMarshal == riid) && _punkFTM)
  1589. hr = _punkFTM->QueryInterface(riid, ppv);
  1590. return hr;
  1591. }
  1592. CLIPFORMAT g_cfPreferredDropEffect = 0; // IMPLEMENT
  1593. CLIPFORMAT g_cfLogicalPerformedDropEffect = 0;
  1594. CLIPFORMAT g_cfPerformedDropEffect = 0;
  1595. #define POPUP_NONDEFAULTDD 1
  1596. #define DDIDM_COPY 2
  1597. #define DDIDM_MOVE 3
  1598. #define DDIDM_LINK 4
  1599. DWORD CMediaDeviceDropTarget::_GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState)
  1600. {
  1601. DWORD dwEffectReturned = DROPEFFECT_NONE;
  1602. switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT))
  1603. {
  1604. case MK_CONTROL: dwEffectReturned = DROPEFFECT_COPY; break;
  1605. case MK_SHIFT: dwEffectReturned = DROPEFFECT_MOVE; break;
  1606. case MK_SHIFT | MK_CONTROL: dwEffectReturned = DROPEFFECT_LINK; break;
  1607. case MK_ALT: dwEffectReturned = DROPEFFECT_LINK; break;
  1608. default:
  1609. {
  1610. // no modifier keys:
  1611. // if the data object contains a preferred drop effect, try to use it
  1612. DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & *pdwEffect;
  1613. if (dwPreferred)
  1614. {
  1615. if (dwPreferred & DROPEFFECT_MOVE)
  1616. {
  1617. dwEffectReturned = DROPEFFECT_MOVE;
  1618. }
  1619. else if (dwPreferred & DROPEFFECT_COPY)
  1620. {
  1621. dwEffectReturned = DROPEFFECT_COPY;
  1622. }
  1623. else if (dwPreferred & DROPEFFECT_LINK)
  1624. {
  1625. dwEffectReturned = DROPEFFECT_LINK;
  1626. }
  1627. }
  1628. else
  1629. {
  1630. dwEffectReturned = DROPEFFECT_COPY;
  1631. }
  1632. }
  1633. break;
  1634. }
  1635. return dwEffectReturned;
  1636. }
  1637. STDMETHODIMP CMediaDeviceDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1638. {
  1639. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  1640. _grfKeyStateLast = grfKeyState;
  1641. if (pdwEffect)
  1642. *pdwEffect = _dwEffectLastReturned = _GetDropEffect(pdwEffect, grfKeyState);
  1643. return S_OK;
  1644. }
  1645. STDMETHODIMP CMediaDeviceDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1646. {
  1647. // has the key state changed? if not then lets return the previously cached
  1648. // version, otherwise recompute.
  1649. if (_grfKeyStateLast == grfKeyState)
  1650. {
  1651. if (*pdwEffect)
  1652. *pdwEffect = _dwEffectLastReturned;
  1653. }
  1654. else if (*pdwEffect)
  1655. {
  1656. *pdwEffect = _GetDropEffect(pdwEffect, grfKeyState);
  1657. }
  1658. _dwEffectLastReturned = *pdwEffect;
  1659. _grfKeyStateLast = grfKeyState;
  1660. return S_OK;
  1661. }
  1662. STDMETHODIMP CMediaDeviceDropTarget::DragLeave()
  1663. {
  1664. ATOMICRELEASE(_pdtobj);
  1665. return S_OK;
  1666. }
  1667. class COPY_THREAD_DATA
  1668. {
  1669. public:
  1670. COPY_THREAD_DATA(CMediaDeviceDropTarget *pmddt) : _pmddt(pmddt), _pstm(NULL)
  1671. {
  1672. _pmddt->AddRef();
  1673. }
  1674. ~COPY_THREAD_DATA()
  1675. {
  1676. _pmddt->Release();
  1677. if (_pstm)
  1678. _pstm->Release();
  1679. }
  1680. HRESULT Marshal(IDataObject *pdtobj)
  1681. {
  1682. return CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &_pstm);
  1683. }
  1684. HRESULT UnMarshal(IDataObject **ppdtobj)
  1685. {
  1686. HRESULT hr = CoGetInterfaceAndReleaseStream(_pstm, IID_PPV_ARG(IDataObject, ppdtobj));
  1687. _pstm = NULL;
  1688. return hr;
  1689. }
  1690. CMediaDeviceDropTarget *_pmddt;
  1691. private:
  1692. IStream *_pstm; // IStream for marshaling the IDataObject
  1693. };
  1694. STDMETHODIMP CMediaDeviceDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1695. {
  1696. *pdwEffect = DROPEFFECT_NONE; // incase of failure
  1697. // determine the type of operation to performed, if the right button is down
  1698. // then lets display the menu, otherwise base it on the drop effect
  1699. UINT idCmd = 0; // Choice from drop popup menu
  1700. #if 0
  1701. if (!(_grfKeyStateLast & MK_LBUTTON))
  1702. {
  1703. HMENU hMenu = SHLoadPopupMenu(m_hInst, POPUP_NONDEFAULTDD);
  1704. if (!hMenu)
  1705. {
  1706. DragLeave();
  1707. return E_FAIL;
  1708. }
  1709. SetMenuDefaultItem(hMenu, POPUP_NONDEFAULTDD, FALSE);
  1710. idCmd = TrackPopupMenu(hMenu,
  1711. TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
  1712. pt.x, pt.y, 0, _hwnd, NULL);
  1713. DestroyMenu(hMenu);
  1714. }
  1715. else
  1716. #endif
  1717. {
  1718. switch (_GetDropEffect(pdwEffect, grfKeyState))
  1719. {
  1720. case DROPEFFECT_COPY: idCmd = DDIDM_COPY; break;
  1721. case DROPEFFECT_MOVE: idCmd = DDIDM_MOVE; break;
  1722. }
  1723. }
  1724. // now perform the operation, based on the command ID we have.
  1725. HRESULT hr = E_FAIL;
  1726. switch (idCmd)
  1727. {
  1728. case DDIDM_COPY:
  1729. COPY_THREAD_DATA *pctd = new COPY_THREAD_DATA(this);
  1730. if (pctd)
  1731. {
  1732. if (SUCCEEDED(pctd->Marshal(pdtobj)))
  1733. {
  1734. if (!SHCreateThread(_DoCopyThreadProc, pctd, CTF_COINIT, NULL))
  1735. {
  1736. delete pctd;
  1737. }
  1738. }
  1739. }
  1740. break;
  1741. }
  1742. #if 0
  1743. // success so lets populate the new changes to the effect
  1744. if (SUCCEEDED(hr) && *pdwEffect)
  1745. {
  1746. DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect);
  1747. DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, *pdwEffect);
  1748. }
  1749. #endif
  1750. DragLeave();
  1751. return hr;
  1752. }
  1753. DWORD CMediaDeviceDropTarget::_DoCopyThreadProc(void *pv)
  1754. {
  1755. COPY_THREAD_DATA *pctd = (COPY_THREAD_DATA *)pv;
  1756. IDataObject *pdtobj;
  1757. HRESULT hr = pctd->UnMarshal(&pdtobj);
  1758. if (SUCCEEDED(hr))
  1759. {
  1760. pctd->_pmddt->_DoCopy(pdtobj);
  1761. pdtobj->Release();
  1762. }
  1763. delete pctd;
  1764. return 0;
  1765. }
  1766. HRESULT CMediaDeviceDropTarget::_DoCopy(IDataObject *pdtobj)
  1767. {
  1768. IProgressDialog *ppd;
  1769. HRESULT hr = _CreateProgress(_hwnd, &ppd);
  1770. if (SUCCEEDED(hr))
  1771. {
  1772. ppd->SetTitle(L"Copying Files...");
  1773. IWMDMStorageControl *pstgCtrl;
  1774. hr = _pmdf->_Storage(IID_PPV_ARG(IWMDMStorageControl, &pstgCtrl));
  1775. if (SUCCEEDED(hr))
  1776. {
  1777. INamespaceWalk *pnsw;
  1778. hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
  1779. if (SUCCEEDED(hr))
  1780. {
  1781. hr = pnsw->Walk(pdtobj, 0, 4, SAFECAST(this, INamespaceWalkCB *));
  1782. if (SUCCEEDED(hr))
  1783. {
  1784. UINT cItems;
  1785. LPITEMIDLIST *ppidls;
  1786. hr = pnsw->GetIDArrayResult(&cItems, &ppidls);
  1787. if (SUCCEEDED(hr))
  1788. {
  1789. for (UINT i = 0; SUCCEEDED(hr) && (i < cItems); i++)
  1790. {
  1791. TCHAR szPath[MAX_PATH];
  1792. if (SHGetPathFromIDList(ppidls[i], szPath))
  1793. {
  1794. ppd->SetLine(2, PathFindFileName(szPath), FALSE, NULL);
  1795. ppd->SetProgress64(_ulProgressCurrent, _ulProgressTotal);
  1796. _ppd = ppd; // used by the callback interface
  1797. IWMDMStorage *pstgNew;
  1798. hr = pstgCtrl->Insert(WMDM_MODE_BLOCK | WMDM_CONTENT_FILE,
  1799. szPath, NULL, SAFECAST(this, IWMDMProgress2 *), &pstgNew);
  1800. if (SUCCEEDED(hr))
  1801. {
  1802. _pmdf->_ChangeNotifyObj(SHCNE_CREATE, pstgNew);
  1803. pstgNew->Release();
  1804. ppd->SetProgress64(_ulProgressCurrent, _ulProgressTotal);
  1805. if (ppd->HasUserCancelled())
  1806. {
  1807. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  1808. }
  1809. }
  1810. }
  1811. }
  1812. FreeIDListArray(ppidls, cItems);
  1813. // free space changed on device, notify
  1814. // SHChangeNotify(SHCNE_UPDATEITEM, 0, (LPCTSTR)_pmdf->GetIDList(), NULL);
  1815. }
  1816. }
  1817. pnsw->Release();
  1818. }
  1819. pstgCtrl->Release();
  1820. }
  1821. ppd->StopProgressDialog();
  1822. ppd->Release();
  1823. }
  1824. return S_OK;
  1825. }
  1826. // IWMDMProgress
  1827. // since we use the FTM we get called here on an MTA thread
  1828. STDMETHODIMP CMediaDeviceDropTarget::Begin(DWORD dwEstimatedTicks)
  1829. {
  1830. _ulThisFile = dwEstimatedTicks;
  1831. return S_OK; // dwEstimatedTicks size in bytes of file
  1832. }
  1833. STDMETHODIMP CMediaDeviceDropTarget::Progress(DWORD dwTranspiredTicks)
  1834. {
  1835. _ppd->SetProgress64(_ulProgressCurrent + dwTranspiredTicks, _ulProgressTotal);
  1836. if (_ulThisFile == dwTranspiredTicks)
  1837. _ulProgressCurrent += _ulThisFile; // end of this file, advance total
  1838. return _ppd->HasUserCancelled() ? HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK;
  1839. }
  1840. STDMETHODIMP CMediaDeviceDropTarget::End()
  1841. {
  1842. return S_OK;
  1843. }
  1844. // IWMDMProgress2
  1845. STDMETHODIMP CMediaDeviceDropTarget::End2(HRESULT hrCompletionCode)
  1846. {
  1847. return S_OK;
  1848. }
  1849. // INamespaceWalkCB
  1850. STDMETHODIMP CMediaDeviceDropTarget::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
  1851. {
  1852. IShellFolder2 *psf2;
  1853. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  1854. {
  1855. ULONGLONG ul;
  1856. if (SUCCEEDED(GetLongProperty(psf2, pidl, &SCID_SIZE, &ul)))
  1857. {
  1858. // TCHAR szName[MAX_PATH];
  1859. // DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1860. _ulProgressTotal += ul;
  1861. }
  1862. psf2->Release();
  1863. }
  1864. return S_OK;
  1865. }
  1866. STDMETHODIMP CMediaDeviceDropTarget::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  1867. {
  1868. return S_OK;
  1869. }
  1870. STDMETHODIMP CMediaDeviceDropTarget::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  1871. {
  1872. return S_OK;
  1873. }