// need IProgressDialog to be marshaled, def in .idl file (cleanup needed here) // get CLSID_MediaDevMgr set to "Both" // issues: // can't drop on open folder, Insert() throws an exception // Rio600 Next throws exception on cidl > 1 // Storage2::GetStroage() fails for Rio600, does SP need to implement this? #include "pch.h" #include #include #include // IDelegateFolder #include "thisdll.h" #include #include #include #include #include #include #include "shdup.h" #include "ids.h" #include #pragma hdrstop #include #include #include #include // get CLSID/IID defs #include #define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid } #define IsEqualSCID(a, b) (((a).pid == (b).pid) && IsEqualIID((a).fmtid, (b).fmtid) ) DEFINE_SCID(SCID_SIZE, PSGUID_STORAGE, PID_STG_SIZE); DEFINE_SCID(SCID_DESCRIPTIONID, PSGUID_SHELLDETAILS, PID_DESCRIPTIONID); DEFINE_SCID(SCID_CAPACITY, PSGUID_VOLUME, PID_VOLUME_CAPACITY); DEFINE_SCID(SCID_FREESPACE, PSGUID_VOLUME, PID_VOLUME_FREE); DEFINE_SCID(SCID_DisplayProperties, PSGUID_WEBVIEW, PID_DISPLAY_PROPERTIES); DEFINE_SCID(SCID_TYPE, PSGUID_STORAGE, PID_STG_STORAGETYPE); DEFINE_SCID(SCID_NAME, PSGUID_STORAGE, PID_STG_NAME); DEFINE_SCID(SCID_WRITETIME, PSGUID_STORAGE, PID_STG_WRITETIME); DEFINE_SCID(SCID_AUDIO_Duration, PSGUID_AUDIO, PIDASI_TIMELENGTH); //100ns units, not milliseconds. VT_UI8, not VT_UI4 DEFINE_SCID(SCID_AUDIO_Bitrate, PSGUID_AUDIO, PIDASI_AVG_DATA_RATE); // bits per second EXTERN_C const BYTE abPVK[4096]; EXTERN_C const BYTE abCert[100]; #pragma pack(1) typedef struct // typedef struct { // { // these need to line up ----------------------- WORD cbSize; // WORD cbSize; // Size of entire item ID WORD wOuter; // WORD wOuter; // Private data owned by the outer folder WORD cbInner; // WORD cbInner; // Size of delegate's data // --------------------------------------------- DWORD dwMagic; // BYTE rgb[1]; // Inner folder's data, DWORD dwAttributes; // } DELEGATEITEMID; ULARGE_INTEGER cbTotal; ULARGE_INTEGER cbFree; union { FILETIME ftModified; ULARGE_INTEGER ulModified; }; WCHAR szName[1]; // variable size } MEDIADEVITEM; #pragma pack() typedef UNALIGNED MEDIADEVITEM * LPMEDIADEVITEM; typedef const UNALIGNED MEDIADEVITEM * LPCMEDIADEVITEM; #define MEDIADEVITEM_MAGIC 0x08311978 class CMediaDeviceFolder; class CMediaDeviceEnum; class CMediaDeviceDropTarget; class CMediaDeviceFolder : public IShellFolder2, IPersistFolder2, IShellFolderViewCB, IDelegateFolder, IContextMenuCB { public: CMediaDeviceFolder(CMediaDeviceFolder *pParent, IWMDMStorage *pstg); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IPersist STDMETHODIMP GetClassID(CLSID *pClassID); // IPersistFolder STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IPersistFolder2 STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl); // IShellFolder STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes); STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { return BindToObject(pidl, pbc, riid, ppv); }; STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppvOut); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppvOut); STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName); STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid) { return E_NOTIMPL; }; STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum) { return E_NOTIMPL; }; STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; }; STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; } STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv); STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails); STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid); // IShellFolderViewCB STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam); // IDelegateFolder STDMETHODIMP SetItemAlloc(IMalloc *pmalloc); // IContextMenuCB STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); // public for friends LPCITEMIDLIST GetIDList() { return _pidl; } private: ~CMediaDeviceFolder(); HRESULT _CreateIDList(LPCTSTR pszName, MEDIADEVITEM **ppmditem, BOOL *pfFolder); HRESULT _IDListForDevice(IWMDMDevice *pdev, LPITEMIDLIST *ppidl, BOOL *pfFolder); HRESULT _IDListForStorage(IWMDMStorage *pstg, LPITEMIDLIST *ppidl, BOOL *pfFolder); ULONG _GetAttributesOf(LPCMEDIADEVITEM pmdi, ULONG rgfIn); HRESULT _CreateExtractIcon(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv); static HRESULT CALLBACK _ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); HRESULT _DeleteItems(HWND hwnd, IDataObject *pdtobj); HRESULT _ChangeNotifyItem(LONG lEvent, LPCITEMIDLIST pidl); HRESULT _ChangeNotifyObj(LONG lEvent, IUnknown *punkStg); // folder view callback handlers HRESULT _OnBackgroundEnum(DWORD pv); HRESULT _OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents); HRESULT _OnViewMode(DWORD pv, FOLDERVIEWMODE *pvm); LPCMEDIADEVITEM _IsValid(LPCITEMIDLIST pidl); DWORD _IsFolder(LPCMEDIADEVITEM pmditem); BOOL _FileInfo(LPCMEDIADEVITEM pmdi, DWORD dwFlags, SHFILEINFO *psfi); LPCTSTR _DisplayName(LPCMEDIADEVITEM pmdi, LPTSTR pszName, UINT cch); LPCTSTR _Name(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch); LPCTSTR _RawName(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch); LPCTSTR _Type(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch); HRESULT _GetMediaDevMgr(REFIID riid, void **ppv); HRESULT _Authenticate(IUnknown *punk); HRESULT _EnumDevices(IWMDMEnumDevice **ppEnumDevices); HRESULT _FindDeviceByName(LPCWSTR pszName, IWMDMDevice **ppdevice); HRESULT _StorageByName(LPCWSTR pszName, REFIID riid, void **ppv); HRESULT _StroageFromItem(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv); HRESULT _StorageForDevice(IWMDMDevice *pdev, REFIID riid, void **ppv); HRESULT _Storage(REFIID riid, void **ppv); HRESULT _EnumStorage(IWMDMEnumStorage **ppEnumStorage); HRESULT _DeviceFromItem(LPCMEDIADEVITEM pmdi, IWMDMDevice **ppdev); BOOL _IsRoot(); void *_Alloc(SIZE_T cb); BOOL _ShouldShowDevice(IWMDMDevice *pdev); friend CMediaDeviceEnum; friend CMediaDeviceDropTarget; LONG _cRef; IMalloc *_pmalloc; LPITEMIDLIST _pidl; // full idlist of this folder BOOL _bRoot; // is this the root of the name space IGlobalInterfaceTable *_pgit; DWORD _dwStorageCookie; // GIT token for IWMDMStorage DWORD _dwDevMgrCookie; // GIT token for media device manager CSecureChannelClient *_pSAC; // this must outlive any objects that used this BOOL _fDelegate; }; class CMediaDeviceEnum : public IEnumIDList { public: CMediaDeviceEnum(CMediaDeviceFolder* prf, DWORD grfFlags); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IEnumIDList STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; }; STDMETHODIMP Reset() { _iIndex = 0; return S_OK; }; STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; }; private: ~CMediaDeviceEnum(); HRESULT _Init(); BOOL _FilterItem(BOOL bFolder); LONG _cRef; CDPA _dpaItems; int _iIndex; CMediaDeviceFolder *_pmdf; DWORD _grfFlags; }; class CMediaDeviceDropTarget : public IDropTarget, INamespaceWalkCB, IWMDMProgress2 { public: CMediaDeviceDropTarget(CMediaDeviceFolder *pFolder, HWND hwnd); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void ** ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IDropTarget STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragLeave(); STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); // IWMDMProgress STDMETHODIMP Begin(DWORD dwEstimatedTicks); STDMETHODIMP Progress(DWORD dwTranspiredTicks); STDMETHODIMP End(); // IWMDMProgress2 STDMETHODIMP End2(HRESULT hrCompletionCode); // INamespaceWalkCB STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel) { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; } private: ~CMediaDeviceDropTarget(); DWORD _GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState); static DWORD CALLBACK _DoCopyThreadProc(void *pv); HRESULT _DoCopy(IDataObject *pdtobj); LONG _cRef; CMediaDeviceFolder *_pmdf; HWND _hwnd; // EVIL: used as a site and UI host IDataObject *_pdtobj; // used durring DragOver() and DoDrop(), don't use on background thread UINT _idCmd; DWORD _grfKeyStateLast; // for previous DragOver/Enter DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT IProgressDialog *_ppd; ULONGLONG _ulProgressTotal; ULONGLONG _ulProgressCurrent; ULONGLONG _ulThisFile; IUnknown *_punkFTM; // make our callback interface calls direct }; CMediaDeviceFolder::CMediaDeviceFolder(CMediaDeviceFolder *pParent, IWMDMStorage *pstg) : _cRef(1), _pidl(NULL), _dwStorageCookie(-1), _dwDevMgrCookie(-1) { _bRoot = (NULL == pstg); if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IGlobalInterfaceTable, &_pgit)))) { if (pstg) { _pgit->RegisterInterfaceInGlobal(pstg, IID_IWMDMStorage, &_dwStorageCookie); } } DllAddRef(); } CMediaDeviceFolder::~CMediaDeviceFolder() { ILFree(_pidl); #ifdef _X86_ if (_pSAC) delete _pSAC; #endif if (_pgit) { if (-1 != _dwStorageCookie) _pgit->RevokeInterfaceFromGlobal(_dwStorageCookie); if (-1 != _dwDevMgrCookie) _pgit->RevokeInterfaceFromGlobal(_dwDevMgrCookie); _pgit->Release(); } ATOMICRELEASE(_pmalloc); DllRelease(); } HRESULT CMediaDeviceFolder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CMediaDeviceFolder, IShellFolder, IShellFolder2), QITABENT (CMediaDeviceFolder, IShellFolder2), QITABENTMULTI(CMediaDeviceFolder, IPersist, IPersistFolder2), QITABENTMULTI(CMediaDeviceFolder, IPersistFolder, IPersistFolder2), QITABENT (CMediaDeviceFolder, IPersistFolder2), QITABENT (CMediaDeviceFolder, IShellFolderViewCB), QITABENT (CMediaDeviceFolder, IDelegateFolder), QITABENT (CMediaDeviceFolder, IContextMenuCB), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CMediaDeviceFolder::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CMediaDeviceFolder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDAPI CMediaDeviceFolder_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { HRESULT hr; CMediaDeviceFolder *pmdf = new CMediaDeviceFolder(NULL, NULL); if (pmdf) { *ppunk = SAFECAST(pmdf, IShellFolder2 *); hr = S_OK; } else { *ppunk = NULL; hr = E_OUTOFMEMORY; } return hr; } HRESULT CMediaDeviceFolder::_Authenticate(IUnknown *punk) { #ifndef _X86_ HRESULT hr = E_FAIL; #else IComponentAuthenticate *pAuth; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IComponentAuthenticate, &pAuth)); if (SUCCEEDED(hr)) { if (NULL == _pSAC) _pSAC = new CSecureChannelClient; if (_pSAC) { hr = _pSAC->SetCertificate(SAC_CERT_V1, (BYTE *)abCert, sizeof(abCert), (BYTE *)abPVK, sizeof(abPVK)); if (SUCCEEDED(hr)) { _pSAC->SetInterface(pAuth); hr = _pSAC->Authenticate(SAC_PROTOCOL_V1); } } else hr = E_OUTOFMEMORY; pAuth->Release(); } #endif return hr; } HRESULT CMediaDeviceFolder::_GetMediaDevMgr(REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; if (_IsRoot() && _pgit) { if (-1 == _dwDevMgrCookie) { IWMDeviceManager *pdevmgr; if (SUCCEEDED(CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IWMDeviceManager, &pdevmgr)))) { _Authenticate(pdevmgr); _pgit->RegisterInterfaceInGlobal(pdevmgr, IID_IWMDeviceManager, &_dwDevMgrCookie); pdevmgr->Release(); } } if (-1 != _dwDevMgrCookie) hr = _pgit->GetInterfaceFromGlobal(_dwDevMgrCookie, riid, ppv); } return hr; } HRESULT CMediaDeviceFolder::_EnumDevices(IWMDMEnumDevice **ppEnumDevices) { *ppEnumDevices = NULL; IWMDeviceManager *pdevmgr; HRESULT hr = _GetMediaDevMgr(IID_PPV_ARG(IWMDeviceManager, &pdevmgr)); if (SUCCEEDED(hr)) { hr = pdevmgr->EnumDevices(ppEnumDevices); pdevmgr->Release(); } return hr; } // test to see if this is the root of the media devices name space (where the devices are) BOOL CMediaDeviceFolder::_IsRoot() { return _bRoot; } HRESULT CMediaDeviceFolder::_Storage(REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_NOINTERFACE; if (-1 != _dwStorageCookie) { ASSERT(!_IsRoot()); hr = _pgit->GetInterfaceFromGlobal(_dwStorageCookie, riid, ppv); } return hr; } HRESULT CMediaDeviceFolder::_EnumStorage(IWMDMEnumStorage **ppEnumStorage) { *ppEnumStorage = NULL; IWMDMStorage *pStorage; HRESULT hr = _Storage(IID_PPV_ARG(IWMDMStorage, &pStorage)); if (SUCCEEDED(hr)) { hr = pStorage->EnumStorage(ppEnumStorage); pStorage->Release(); } return hr; } HRESULT CMediaDeviceFolder::_FindDeviceByName(LPCWSTR pszName, IWMDMDevice **ppdevice) { IWMDMEnumDevice *pEnumDevice; HRESULT hr = _EnumDevices(&pEnumDevice); if (SUCCEEDED(hr)) { hr = E_FAIL; ULONG ulFetched; IWMDMDevice *pdev; while (S_OK == pEnumDevice->Next(1, &pdev, &ulFetched)) { WCHAR szName[MAX_PATH]; if (SUCCEEDED(pdev->GetName(szName, ARRAYSIZE(szName))) && (0 == StrCmpIC(pszName, szName))) { *ppdevice = pdev; hr = S_OK; break; } pdev->Release(); } pEnumDevice->Release(); } return hr; } HRESULT CMediaDeviceFolder::_StorageByName(LPCWSTR pszName, REFIID riid, void **ppv) { IWMDMStorage2 *pstg; HRESULT hr = _Storage(IID_PPV_ARG(IWMDMStorage2, &pstg)); if (SUCCEEDED(hr)) { IWMDMStorage *pstgFound; hr = pstg->GetStorage((LPWSTR)pszName, &pstgFound); if (SUCCEEDED(hr)) { hr = pstgFound->QueryInterface(riid, ppv); pstgFound->Release(); } else if (E_NOINTERFACE == hr) { // Rio600 fails on this now. I asked jhelin to look into this IWMDMEnumStorage *penum; if (SUCCEEDED(pstg->EnumStorage(&penum))) { ULONG ulFetched; while (S_OK == penum->Next(1, &pstgFound, &ulFetched)) { WCHAR szName[MAX_PATH]; if (SUCCEEDED(pstgFound->GetName(szName, ARRAYSIZE(szName))) && (0 == StrCmpI(pszName, szName))) { hr = pstgFound->QueryInterface(riid, ppv); } pstgFound->Release(); if (SUCCEEDED(hr)) break; } penum->Release(); } } pstg->Release(); } return hr; } LPCMEDIADEVITEM CMediaDeviceFolder::_IsValid(LPCITEMIDLIST pidl) { if (pidl && ((LPCMEDIADEVITEM)pidl)->dwMagic == MEDIADEVITEM_MAGIC) return (LPCMEDIADEVITEM)pidl; return NULL; } DWORD CMediaDeviceFolder::_IsFolder(LPCMEDIADEVITEM pmditem) { return pmditem->dwAttributes & WMDM_FILE_ATTR_FOLDER ? FILE_ATTRIBUTE_DIRECTORY : 0; } // helper to support being run as a delegate or a stand alone folder // // the cbInner is the size of the data needed by the delegate. we need to compute // the full size of the pidl for the allocation and init that we the outer folder data void *CMediaDeviceFolder::_Alloc(SIZE_T cbInner) { DELEGATEITEMID *pidl; if (_pmalloc) { pidl = (DELEGATEITEMID *)_pmalloc->Alloc(cbInner); } else { SIZE_T cbAlloc = sizeof(DELEGATEITEMID) - sizeof(pidl->rgb[0]) + // header cbInner + // inner sizeof(WORD); // trailing null (pidl terminator) pidl = (DELEGATEITEMID *)SHAlloc(cbAlloc); if (pidl) { ZeroMemory(pidl, cbAlloc); // make it all empty pidl->cbSize = (WORD)cbAlloc - sizeof(WORD); pidl->cbInner = (WORD)cbInner; } } return (void *)pidl; } HRESULT CMediaDeviceFolder::_CreateIDList(LPCTSTR pszName, MEDIADEVITEM **ppmditem, BOOL *pfFolder) { HRESULT hr; UINT cbInner = sizeof(MEDIADEVITEM) - (sizeof(DELEGATEITEMID) - 1) + (sizeof(WCHAR) * lstrlen(pszName)); // null is accounted for in szName[1] *ppmditem = (MEDIADEVITEM *)_Alloc(cbInner); if (*ppmditem) { (*ppmditem)->dwMagic = MEDIADEVITEM_MAGIC; StrCpyW((*ppmditem)->szName, pszName); if (pfFolder) *pfFolder = _IsFolder(*ppmditem); hr = S_OK; } else hr = E_OUTOFMEMORY; return hr; } // Creates an item identifier list for the objects in the namespace HRESULT CMediaDeviceFolder::_IDListForDevice(IWMDMDevice *pdev, LPITEMIDLIST *ppidl, BOOL *pfFolder) { WCHAR szName[MAX_PATH]; HRESULT hr = pdev->GetName(szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr)) { // WCHAR szTemp[128]; // pdev->GetManufacturer(szTemp, ARRAYSIZE(szTemp)); MEDIADEVITEM *pmditem; hr = _CreateIDList(szName, &pmditem, pfFolder); if (SUCCEEDED(hr)) { IWMDMStorage *pstg; if (SUCCEEDED(_StorageForDevice(pdev, IID_PPV_ARG(IWMDMStorage, &pstg)))) { IWMDMStorageGlobals *pstgGlobals; if (SUCCEEDED(pstg->GetStorageGlobals(&pstgGlobals))) { pstgGlobals->GetTotalSize(&pmditem->cbTotal.LowPart, &pmditem->cbTotal.HighPart); pstgGlobals->GetTotalFree(&pmditem->cbFree.LowPart, &pmditem->cbFree.HighPart); pstgGlobals->Release(); } pstg->Release(); } pmditem->dwAttributes = WMDM_FILE_ATTR_FOLDER | WMDM_STORAGE_ATTR_HAS_FOLDERS; // pdev->GetType(&pmditem->dwType); // this is an expensive call, hits the disk *ppidl = (LPITEMIDLIST)pmditem; } } return hr; } void WMDMDateTimeToSystemTime(const WMDMDATETIME *pWMDM, FILETIME *pft) { SYSTEMTIME st = {0}; st.wYear = pWMDM->wYear; st.wMonth = pWMDM->wMonth; st.wDay = pWMDM->wDay; st.wHour = pWMDM->wHour; st.wMinute = pWMDM->wMinute; st.wSecond = pWMDM->wSecond; SystemTimeToFileTime(&st, pft); } HRESULT CMediaDeviceFolder::_IDListForStorage(IWMDMStorage *pstg, LPITEMIDLIST *ppidl, BOOL *pfFolder) { WCHAR szName[MAX_PATH]; HRESULT hr = pstg->GetName(szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr)) { MEDIADEVITEM *pmditem; hr = _CreateIDList(szName, &pmditem, pfFolder); if (SUCCEEDED(hr)) { _WAVEFORMATEX fmt; pstg->GetAttributes(&pmditem->dwAttributes, &fmt); WMDMDATETIME datetime; if (SUCCEEDED(pstg->GetDate(&datetime))) { WMDMDateTimeToSystemTime(&datetime, &pmditem->ftModified); } if (!(pmditem->dwAttributes & WMDM_FILE_ATTR_FOLDER)) { pstg->GetSize(&pmditem->cbTotal.LowPart, &pmditem->cbTotal.HighPart); } *ppidl = (LPITEMIDLIST)pmditem; } } return hr; } // IPersist STDMETHODIMP CMediaDeviceFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_MediaDeviceFolder; return S_OK; } // IPersistFolder STDMETHODIMP CMediaDeviceFolder::Initialize(LPCITEMIDLIST pidl) { ILFree(_pidl); return SHILClone(pidl, &_pidl); } // IPersistFolder2 HRESULT CMediaDeviceFolder::GetCurFolder(LPITEMIDLIST *ppidl) { if (_pidl) return SHILClone(_pidl, ppidl); *ppidl = NULL; return S_FALSE; } // IShellFolder(2) HRESULT CMediaDeviceFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes) { HRESULT hr; if (!pszName || !ppidl) return E_INVALIDARG; *ppidl = NULL; #if 1 hr = E_NOTIMPL; #else TCHAR szName[MAX_PATH]; hr = _NextSegment((LPCWSTR*)&pszName, szName, ARRAYSIZE(szName), TRUE); if (SUCCEEDED(hr)) { hr = _IDListForDevice(szName, ppidl, NULL); if (SUCCEEDED(hr) && pszName) { IShellFolder *psf; hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { ULONG chEaten; LPITEMIDLIST pidlNext; hr = psf->ParseDisplayName(hwnd, pbc, pszName, &chEaten, &pidlNext, pdwAttributes); if (SUCCEEDED(hr)) hr = SHILAppend(pidlNext, ppidl); psf->Release(); } } else if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes) { GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes); } } #endif // clean up if the parse failed. if (FAILED(hr)) { ILFree(*ppidl); *ppidl = NULL; } return hr; } HRESULT CMediaDeviceFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum) { HRESULT hr; CMediaDeviceEnum *penum = new CMediaDeviceEnum(this, grfFlags); if (penum) { hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); penum->Release(); } else { hr = E_OUTOFMEMORY; *ppenum = NULL; } return hr; } STDAPI_(LPITEMIDLIST) ILCombineParentAndFirst(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlNext) { ULONG cbParent = ILGetSize(pidlParent); ULONG cbRest = (ULONG)((ULONG_PTR)pidlNext - (ULONG_PTR)pidl); LPITEMIDLIST pidlNew = _ILCreate(cbParent + cbRest); if (pidlNew) { cbParent -= sizeof(pidlParent->mkid.cb); memcpy(pidlNew, pidlParent, cbParent); memcpy((BYTE *)pidlNew + cbParent, pidl, cbRest); ASSERT(_ILSkip(pidlNew, cbParent + cbRest)->mkid.cb == 0); } return pidlNew; } HRESULT CMediaDeviceFolder::_StorageForDevice(IWMDMDevice *pdev, REFIID riid, void **ppv) { *ppv = NULL; IWMDMEnumStorage *penum; HRESULT hr = pdev->EnumStorage(&penum); if (SUCCEEDED(hr)) { ULONG ulFetched; IWMDMStorage *pstg; if (S_OK == penum->Next(1, &pstg, &ulFetched)) { hr= pstg->QueryInterface(riid, ppv); pstg->Release(); } else { hr = E_NOINTERFACE; } penum->Release(); } return hr; } HRESULT CMediaDeviceFolder::_StroageFromItem(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv) { WCHAR szName[MAX_PATH]; _Name(pmdi, szName, ARRAYSIZE(szName)); *ppv = NULL; HRESULT hr; if (_IsRoot()) { // root case IWMDMDevice *pdev; hr = _FindDeviceByName(szName, &pdev); if (SUCCEEDED(hr)) { hr = _StorageForDevice(pdev, riid, ppv); pdev->Release(); } } else { hr = _StorageByName(szName, riid, ppv); } return hr; } HRESULT CMediaDeviceFolder::_ChangeNotifyItem(LONG lEvent, LPCITEMIDLIST pidl) { ITEMIDLIST *pidlFull; HRESULT hr = SHILCombine(_pidl, pidl, &pidlFull); if (SUCCEEDED(hr)) { SHChangeNotify(lEvent, 0, (LPCTSTR)pidlFull, NULL); ILFree(pidlFull); } return hr; } HRESULT CMediaDeviceFolder::_ChangeNotifyObj(LONG lEvent, IUnknown *punkStg) { IWMDMStorage *pstg; HRESULT hr = punkStg->QueryInterface(IID_PPV_ARG(IWMDMStorage, &pstg)); if (SUCCEEDED(hr)) { ITEMIDLIST *pidl; BOOL bFolder; hr = _IDListForStorage(pstg, &pidl, &bFolder); if (SUCCEEDED(hr)) { hr = _ChangeNotifyItem(lEvent, pidl); ILFree(pidl); } pstg->Release(); } return hr; } HRESULT CMediaDeviceFolder::_DeviceFromItem(LPCMEDIADEVITEM pmdi, IWMDMDevice **ppdev) { *ppdev = NULL; TCHAR szName[MAX_PATH]; return _IsRoot() ? _FindDeviceByName(_RawName(pmdi, szName, ARRAYSIZE(szName)), ppdev) : E_NOINTERFACE; } HRESULT CMediaDeviceFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_NOINTERFACE; LPCMEDIADEVITEM pmdi = _IsValid(pidl); if (pmdi && _IsFolder(pmdi)) { if (IID_IShellFolder == riid || IID_IShellFolder2 == riid) { IWMDMStorage *pstg; hr = _StroageFromItem(pmdi, IID_PPV_ARG(IWMDMStorage, &pstg)); if (SUCCEEDED(hr)) { LPCITEMIDLIST pidlNext = _ILNext(pidl); LPITEMIDLIST pidlSubFolder = ILCombineParentAndFirst(_pidl, pidl, pidlNext); if (pidlSubFolder) { CMediaDeviceFolder *pmdf = new CMediaDeviceFolder(this, pstg); if (pmdf) { hr = pmdf->Initialize(pidlSubFolder); if (SUCCEEDED(hr)) { // if there's nothing left in the pidl, get the interface on this one. if (ILIsEmpty(pidlNext)) hr = pmdf->QueryInterface(riid, ppv); else { // otherwise, hand the rest of it off to the new shellfolder. hr = pmdf->BindToObject(pidlNext, pbc, riid, ppv); } } pmdf->Release(); } else hr = E_OUTOFMEMORY; ILFree(pidlSubFolder); } else hr = E_OUTOFMEMORY; pstg->Release(); } } } else hr = E_FAIL; ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && (NULL == *ppv))); // Assert hr is consistent w/out param. return hr; } enum { DEV_COL_NAME = 0, DEV_COL_SIZE, DEV_COL_TYPE, DEV_COL_MODIFIED, }; HRESULT CMediaDeviceFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPCMEDIADEVITEM pmdi1 = _IsValid(pidl1); LPCMEDIADEVITEM pmdi2 = _IsValid(pidl2); int nCmp = 0; if (!pmdi1 || !pmdi2) return E_INVALIDARG; // folders always sort to the top of the list, if either of these items // are folders then compare on the folderness if (_IsFolder(pmdi1) || _IsFolder(pmdi2)) { if (_IsFolder(pmdi1) && !_IsFolder(pmdi2)) nCmp = -1; else if (!_IsFolder(pmdi1) && _IsFolder(pmdi2)) nCmp = 1; } // if they match (or are not folders, then lets compare based on the column ID. if (nCmp == 0) { switch (lParam & SHCIDS_COLUMNMASK) { case DEV_COL_NAME: // caught later on break; case DEV_COL_SIZE: if (pmdi1->cbTotal.QuadPart < pmdi2->cbTotal.QuadPart) nCmp = -1; else if (pmdi1->cbTotal.QuadPart > pmdi2->cbTotal.QuadPart) nCmp = 1; break; case DEV_COL_TYPE: { TCHAR szType1[MAX_PATH], szType2[MAX_PATH]; _Type(pmdi1, szType1, ARRAYSIZE(szType1)); _Type(pmdi2, szType2, ARRAYSIZE(szType2)); nCmp = StrCmp(szType1, szType2); break; } case DEV_COL_MODIFIED: if (pmdi1->ulModified.QuadPart < pmdi2->ulModified.QuadPart) nCmp = -1; else if (pmdi1->ulModified.QuadPart > pmdi2->ulModified.QuadPart) nCmp = 1; break; } if (nCmp == 0) { TCHAR szName1[MAX_PATH], szName2[MAX_PATH]; nCmp = StrCmpLogicalW(_Name(pmdi1, szName1, ARRAYSIZE(szName1)), _Name(pmdi2, szName2, ARRAYSIZE(szName2))); } } return ResultFromShort(nCmp); } HRESULT CMediaDeviceFolder::_OnBackgroundEnum(DWORD pv) { return S_OK; // do enum on backgound always } HRESULT CMediaDeviceFolder::_OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents) { *ppidl = _pidl; // evil alais *plEvents = SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \ SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \ SHCNE_MKDIR | SHCNE_RMDIR; return S_OK; } HRESULT CMediaDeviceFolder::_OnViewMode(DWORD pv, FOLDERVIEWMODE *pvm) { *pvm = FVM_TILE; return S_OK; } // IShellFolderViewCB STDMETHODIMP CMediaDeviceFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = E_FAIL; switch (uMsg) { HANDLE_MSG(0, SFVM_BACKGROUNDENUM, _OnBackgroundEnum); HANDLE_MSG(0, SFVM_GETNOTIFY, _OnGetNotify); HANDLE_MSG(0, SFVM_DEFVIEWMODE, _OnViewMode); } return hr; } HRESULT CMediaDeviceFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { HRESULT hr = E_NOINTERFACE; *ppv = NULL; if (IsEqualIID(riid, IID_IShellView)) { SFV_CREATE sSFV = { 0 }; sSFV.cbSize = sizeof(sSFV); sSFV.psfvcb = SAFECAST(this, IShellFolderViewCB *); sSFV.pshf = SAFECAST(this, IShellFolder *); hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv); } else if (IsEqualIID(riid, IID_IContextMenu)) { HKEY hkNoFiles = NULL; RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles); hr = CDefFolderMenu_Create2(_pidl, hwnd, 0, NULL, this, NULL, 1, &hkNoFiles, (IContextMenu **)ppv); if (hkNoFiles) RegCloseKey(hkNoFiles); } else if (IsEqualIID(riid, IID_IDropTarget) && !_IsRoot()) { CMediaDeviceDropTarget *psdt = new CMediaDeviceDropTarget(this, hwnd); if (psdt) { hr = psdt->QueryInterface(riid, ppv); psdt->Release(); } else hr = E_OUTOFMEMORY; } return hr; } DWORD WMDMAttributesSFGAO(DWORD dwWMDMAttributes) { DWORD dwShell = SFGAO_DROPTARGET; // defaults if (WMDM_FILE_ATTR_FOLDER & dwWMDMAttributes) dwShell |= SFGAO_FOLDER; if (WMDM_FILE_ATTR_LINK & dwWMDMAttributes) dwShell |= SFGAO_LINK; if (WMDM_FILE_ATTR_CANDELETE & dwWMDMAttributes) dwShell |= SFGAO_CANDELETE; if (WMDM_FILE_ATTR_CANMOVE & dwWMDMAttributes) dwShell |= SFGAO_CANMOVE; if (WMDM_FILE_ATTR_CANRENAME & dwWMDMAttributes) dwShell |= SFGAO_CANRENAME; if (WMDM_FILE_ATTR_HIDDEN & dwWMDMAttributes) dwShell |= SFGAO_HIDDEN; if (WMDM_FILE_ATTR_READONLY & dwWMDMAttributes) dwShell |= SFGAO_READONLY; if (WMDM_STORAGE_ATTR_HAS_FOLDERS & dwWMDMAttributes) dwShell |= SFGAO_HASSUBFOLDER; return dwShell; } ULONG CMediaDeviceFolder::_GetAttributesOf(LPCMEDIADEVITEM pmdi, ULONG rgfIn) { ULONG lResult = rgfIn & WMDMAttributesSFGAO(pmdi->dwAttributes); if (_IsRoot()) { lResult |= SFGAO_CANLINK | SFGAO_REMOVABLE; } return lResult; } BOOL IsSelf(UINT cidl, LPCITEMIDLIST *apidl) { return cidl == 0 || (cidl == 1 && (apidl == NULL || apidl[0] == NULL || ILIsEmpty(apidl[0]))); } HRESULT CMediaDeviceFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut) { UINT rgfOut = *prgfInOut; // return attributes of the namespace root? if (IsSelf(cidl, apidl)) { rgfOut &= SFGAO_FOLDER | SFGAO_LINK | SFGAO_DROPTARGET | SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANLINK | SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_HASSUBFOLDER; } else { for (UINT i = 0; i < cidl; i++) { LPCMEDIADEVITEM pmdi = _IsValid(apidl[i]); if (pmdi) rgfOut &= _GetAttributesOf(pmdi, *prgfInOut); } } *prgfInOut = rgfOut; return S_OK; } HRESULT _CreateProgress(HWND hwnd, IProgressDialog **pppd) { HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IProgressDialog, pppd)); if (SUCCEEDED(hr)) { (*pppd)->StartProgressDialog(hwnd, NULL, PROGDLG_AUTOTIME, NULL); } return hr; } HRESULT CMediaDeviceFolder::_DeleteItems(HWND hwnd, IDataObject *pdtobj) { IProgressDialog *ppd; HRESULT hr = _CreateProgress(hwnd, &ppd); if (SUCCEEDED(hr)) { ppd->SetTitle(L"Deleting Files..."); STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { for (UINT i = 0; i < pida->cidl; i++) { LPCMEDIADEVITEM pmdi = _IsValid(HIDA_GetPIDLItem(pida, i)); if (pmdi) { TCHAR szName[MAX_PATH]; ppd->SetLine(2, _DisplayName(pmdi, szName, ARRAYSIZE(szName)), FALSE, NULL); ppd->SetProgress64(i, pida->cidl); IWMDMStorageControl *pstgCtrl; hr = _StroageFromItem(pmdi, IID_PPV_ARG(IWMDMStorageControl, &pstgCtrl)); if (SUCCEEDED(hr)) { hr = pstgCtrl->Delete(WMDM_MODE_BLOCK, NULL); if (SUCCEEDED(hr)) { _ChangeNotifyItem(SHCNE_DELETE, (LPCITEMIDLIST)pmdi); } pstgCtrl->Release(); ppd->SetProgress64(i + 1, pida->cidl); if (ppd->HasUserCancelled()) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); break; } } } // free space changed on device, notify // SHChangeNotify(SHCNE_UPDATEITEM, 0, (LPCTSTR)_pidl, NULL); } HIDA_ReleaseStgMedium(pida, &medium); } ppd->StopProgressDialog(); ppd->Release(); } return hr; } HRESULT CMediaDeviceFolder::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch (uMsg) { case DFM_MERGECONTEXTMENU: break; case DFM_INVOKECOMMANDEX: { DFMICS *pdfmics = (DFMICS *)lParam; switch (wParam) { case DFM_CMD_DELETE: hr = _DeleteItems(hwnd, pdtobj); break; case DFM_CMD_PROPERTIES: break; default: // This is common menu items, use the default code. hr = S_FALSE; break; } break; } default: hr = E_NOTIMPL; break; } return hr; } HRESULT CALLBACK CMediaDeviceFolder::_ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { IContextMenuCB *pcmcb; HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IContextMenuCB, &pcmcb)); if (SUCCEEDED(hr)) { hr = pcmcb->CallBack(psf, hwnd, pdtobj, uMsg, wParam, lParam); pcmcb->Release(); } return hr; } class CExtractConstIcon : public IExtractIconW { public: CExtractConstIcon(LPCTSTR psz, UINT uID); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IExtractIconW STDMETHODIMP GetIconLocation(UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int* piIndex, UINT* pwFlags); STDMETHODIMP Extract(LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize); private: LONG _cRef; TCHAR _szPath[MAX_PATH]; UINT _uID; }; CExtractConstIcon::CExtractConstIcon(LPCTSTR psz, UINT uID) : _cRef(1), _uID(uID) { StrCpyN(_szPath, psz, ARRAYSIZE(_szPath)); } HRESULT CExtractConstIcon::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CExtractConstIcon, IExtractIconW), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CExtractConstIcon::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CExtractConstIcon::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDMETHODIMP CExtractConstIcon::GetIconLocation(UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) { StrCpyN(pszIconFile, _szPath, cchMax); *piIndex = _uID; *pwFlags = GIL_PERINSTANCE; return S_OK; } STDMETHODIMP CExtractConstIcon::Extract(LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize) { return S_FALSE; // do the extract for me } HRESULT CMediaDeviceFolder::_CreateExtractIcon(LPCMEDIADEVITEM pmdi, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; if (_IsRoot()) { #define IDI_AUDIOPLAYER 299 CExtractConstIcon *pei = new CExtractConstIcon(TEXT("shell32.dll"), -IDI_AUDIOPLAYER); if (pei) { hr = pei->QueryInterface(riid, ppv); pei->Release(); } } else { WCHAR szName[MAX_PATH]; hr = SHCreateFileExtractIconW(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), riid, ppv); } return hr; } HRESULT CMediaDeviceFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, void **ppv) { HRESULT hr = E_INVALIDARG; LPCMEDIADEVITEM pmdi = cidl ? _IsValid(apidl[0]) : NULL; if (pmdi && (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))) { hr = _CreateExtractIcon(pmdi, riid, ppv); } else if (IsEqualIID(riid, IID_IDataObject) && cidl) { hr = CIDLData_CreateFromIDArray(_pidl, cidl, apidl, (IDataObject**) ppv); } else if (IsEqualIID(riid, IID_IContextMenu) && pmdi) { // get the association for these files and lets attempt to // build the context menu for the selection. IQueryAssociations *pqa; hr = GetUIObjectOf(hwnd, 1, apidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { HKEY ahk[3]; DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk)); hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, this, _ItemsMenuCB, cKeys, ahk, (IContextMenu **)ppv); SHRegCloseKeys(ahk, cKeys); pqa->Release(); } } else if (IsEqualIID(riid, IID_IQueryAssociations) && pmdi) { TCHAR szName[MAX_PATH]; hr = SHAssocCreateForFile(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), NULL, riid, ppv); } else if (IsEqualIID(riid, IID_IDropTarget) && pmdi && _IsFolder(pmdi)) { // If a directory is selected in the view, the drop is going to a folder, // so we need to bind to that folder and ask it to create a drop target IShellFolder *psf; hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = psf->CreateViewObject(hwnd, IID_IDropTarget, ppv); psf->Release(); } } return hr; } BOOL CMediaDeviceFolder::_FileInfo(LPCMEDIADEVITEM pmdi, DWORD dwFlags, SHFILEINFO *psfi) { TCHAR szName[MAX_PATH]; ASSERT(!(dwFlags & SHGFI_SYSICONINDEX)); ZeroMemory(psfi, sizeof(*psfi)); return (BOOL)SHGetFileInfo(_Name(pmdi, szName, ARRAYSIZE(szName)), _IsFolder(pmdi), psfi, sizeof(*psfi), SHGFI_USEFILEATTRIBUTES | dwFlags); } LPCTSTR CMediaDeviceFolder::_RawName(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch) { ualstrcpyn(psz, pmdi->szName, cch); return psz; } LPCTSTR CMediaDeviceFolder::_Name(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch) { ualstrcpyn(psz, pmdi->szName, cch); return PathFindFileName(psz); // some devices use path components in the names of the items } LPCTSTR CMediaDeviceFolder::_DisplayName(LPCMEDIADEVITEM pmdi, LPTSTR pszResult, UINT cch) { SHFILEINFO sfi; if (_FileInfo(pmdi, SHGFI_DISPLAYNAME, &sfi)) { StrCpyN(pszResult, sfi.szDisplayName, cch); } else { *pszResult = 0; } return pszResult; } HRESULT CMediaDeviceFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet) { HRESULT hr; LPCMEDIADEVITEM pmdi = _IsValid(pidl); if (pmdi) { TCHAR szTemp[MAX_PATH]; if (dwFlags & SHGDN_FORPARSING) { if (dwFlags & SHGDN_INFOLDER) { _RawName(pmdi, szTemp, ARRAYSIZE(szTemp)); // relative parse name } else { SHGetNameAndFlags(_pidl, dwFlags, szTemp, ARRAYSIZE(szTemp), NULL); TCHAR szName[MAX_PATH]; PathAppend(szTemp, _RawName(pmdi, szName, ARRAYSIZE(szName))); } } else { _DisplayName(pmdi, szTemp, ARRAYSIZE(szTemp)); } hr = StringToStrRetW(szTemp, pStrRet); } else hr = E_INVALIDARG; return hr; } HRESULT CMediaDeviceFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut) { return E_NOTIMPL; } STDMETHODIMP CMediaDeviceFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr = E_FAIL; LPCMEDIADEVITEM pmdi = _IsValid(pidl); if (pmdi) { if (_IsRoot()) { if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID)) { SHDESCRIPTIONID did = {0}; did.dwDescriptionId = SHDID_COMPUTER_AUDIO; hr = InitVariantFromBuffer(pv, &did, sizeof(did)); } else if (IsEqualSCID(*pscid, SCID_CAPACITY)) { pv->vt = VT_UI8; pv->ullVal = pmdi->cbTotal.QuadPart; hr = S_OK; } else if (IsEqualSCID(*pscid, SCID_FREESPACE)) { pv->vt = VT_UI8; pv->ullVal = pmdi->cbFree.QuadPart; hr = S_OK; } else if (IsEqualSCID(*pscid, SCID_DisplayProperties)) { hr = InitVariantFromStr(pv, TEXT("prop:Name;Type;FreeSpace;Capacity")); } } else { if (IsEqualSCID(*pscid, SCID_TYPE)) { TCHAR sz[64]; hr = InitVariantFromStr(pv, _Type(pmdi, sz, ARRAYSIZE(sz))); } else if (IsEqualSCID(*pscid, SCID_SIZE)) { pv->vt = VT_UI8; pv->ullVal = pmdi->cbTotal.QuadPart; hr = S_OK; } } } return hr; } LPCTSTR CMediaDeviceFolder::_Type(LPCMEDIADEVITEM pmdi, LPTSTR psz, UINT cch) { *psz = 0; SHFILEINFO sfi; if (_FileInfo(pmdi, SHGFI_TYPENAME, &sfi)) StrCpyN(psz, sfi.szTypeName, cch); return psz; } static const struct { UINT iTitle; UINT cchCol; UINT iFmt; const SHCOLUMNID *pscid; } c_aMediaDeviceColumns[] = { {IDS_NAME_COL, 40, LVCFMT_LEFT, &SCID_NAME}, {IDS_SIZE_COL, 10, LVCFMT_RIGHT, &SCID_SIZE}, {IDS_TYPE_COL, 30, LVCFMT_LEFT, &SCID_TYPE}, {IDS_MODIFIED_COL, 20, LVCFMT_LEFT, &SCID_WRITETIME}, }; HRESULT CMediaDeviceFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail) { if (iColumn >= ARRAYSIZE(c_aMediaDeviceColumns)) return E_NOTIMPL; HRESULT hr = S_OK; TCHAR szTemp[MAX_PATH]; szTemp[0] = 0; if (NULL == pidl) { pDetail->fmt = c_aMediaDeviceColumns[iColumn].iFmt; pDetail->cxChar = c_aMediaDeviceColumns[iColumn].cchCol; LoadString(m_hInst, c_aMediaDeviceColumns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp)); } else { LPCMEDIADEVITEM pmdi = _IsValid(pidl); if (pmdi) { // return the property to the caller that is being requested, this is based on the // list of columns we gave out when the view was created. switch (iColumn) { case DEV_COL_NAME: _DisplayName(pmdi, szTemp, ARRAYSIZE(szTemp)); break; case DEV_COL_SIZE: if (!_IsFolder(pmdi)) { ULARGE_INTEGER ullSize = pmdi->cbTotal; StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp)); } break; case DEV_COL_TYPE: _Type(pmdi, szTemp, ARRAYSIZE(szTemp)); break; case DEV_COL_MODIFIED: SHFormatDateTime(&pmdi->ftModified, NULL, szTemp, ARRAYSIZE(szTemp)); break; } } } return StringToStrRetW(szTemp, &(pDetail->str)); } STDMETHODIMP CMediaDeviceFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) { HRESULT hr = E_INVALIDARG; ZeroMemory(pscid, sizeof(*pscid)); if (!_IsRoot()) { if (iColumn < ARRAYSIZE(c_aMediaDeviceColumns)) { *pscid = *c_aMediaDeviceColumns[iColumn].pscid; hr = S_OK; } } return hr; } // IDelegateFolder HRESULT CMediaDeviceFolder::SetItemAlloc(IMalloc *pmalloc) { _fDelegate = TRUE; IUnknown_Set((IUnknown**)&_pmalloc, pmalloc); return S_OK; } CMediaDeviceEnum::CMediaDeviceEnum(CMediaDeviceFolder *pmdf, DWORD grfFlags) : _cRef(1), _grfFlags(grfFlags), _iIndex(0) { _pmdf = pmdf; _pmdf->AddRef(); // force full enum on this thread. avoid problem where calls made on the // marshaled thread fail due to authentication _Init(); DllAddRef(); } int CALLBACK _FreeItems(ITEMIDLIST *pidl, IShellFolder *psf) { ILFree(pidl); return 1; } CMediaDeviceEnum::~CMediaDeviceEnum() { if ((HDPA)_dpaItems) _dpaItems.DestroyCallbackEx(_FreeItems, (IShellFolder *)NULL); _pmdf->Release(); DllRelease(); } HRESULT CMediaDeviceEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CMediaDeviceEnum, IEnumIDList), // IID_IEnumIDList { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CMediaDeviceEnum::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CMediaDeviceEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } BOOL CMediaDeviceEnum::_FilterItem(BOOL bFolder) { if (bFolder) { if (!(_grfFlags & SHCONTF_FOLDERS)) { return TRUE; } } else if (!(_grfFlags & SHCONTF_NONFOLDERS)) { return TRUE; } return FALSE; } BOOL CMediaDeviceFolder::_ShouldShowDevice(IWMDMDevice *pdev) { BOOL bShow = TRUE; if (_fDelegate) { DWORD dwPowerSource, dwPercentRemaining; pdev->GetPowerSource(&dwPowerSource, &dwPercentRemaining); // heuristic to detect file system driver supported devices. // if it runs on batteries assume it is not a file system device bShow = (WMDM_POWER_CAP_BATTERY & dwPowerSource); } return bShow; } HRESULT CMediaDeviceEnum::_Init() { if ((HDPA)_dpaItems) return S_OK; HRESULT hr = _dpaItems.Create(10) ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { if (_pmdf->_IsRoot()) { IWMDMEnumDevice *pEnumDevice; hr = _pmdf->_EnumDevices(&pEnumDevice); if (SUCCEEDED(hr)) { IWMDMDevice *pdev; ULONG ulFetched; while (S_OK == pEnumDevice->Next(1, &pdev, &ulFetched)) { if (_pmdf->_ShouldShowDevice(pdev)) { ITEMIDLIST *pidl; BOOL bFolder; hr = _pmdf->_IDListForDevice(pdev, &pidl, &bFolder); if (SUCCEEDED(hr)) { if (_FilterItem(bFolder) || (-1 == _dpaItems.AppendPtr(pidl))) { ILFree(pidl); } } } pdev->Release(); } pEnumDevice->Release(); } } else { IWMDMEnumStorage *penum; hr = _pmdf->_EnumStorage(&penum); if (SUCCEEDED(hr)) { ULONG ulFetched; IWMDMStorage *rgpstg[1]; // 1 is to WORKAROUND Rio600 faults on > 1 while (SUCCEEDED(penum->Next(ARRAYSIZE(rgpstg), rgpstg, &ulFetched))) { for (UINT i = 0; i < ulFetched; i++) { ITEMIDLIST *pidl; BOOL bFolder; hr = _pmdf->_IDListForStorage(rgpstg[i], &pidl, &bFolder); if (SUCCEEDED(hr)) { if (_FilterItem(bFolder) || (-1 == _dpaItems.AppendPtr(pidl))) { ILFree(pidl); } } rgpstg[i]->Release(); } if (ulFetched != ARRAYSIZE(rgpstg)) break; } penum->Release(); } } } return hr; } HRESULT CMediaDeviceEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) { HRESULT hr = _Init(); if (SUCCEEDED(hr)) { hr = S_FALSE; if (_iIndex < _dpaItems.GetPtrCount()) { hr = SHILClone(_dpaItems.GetPtr(_iIndex++), rgelt); } if (pceltFetched) *pceltFetched = (hr == S_OK) ? 1 : 0; } return hr; } CMediaDeviceDropTarget::CMediaDeviceDropTarget(CMediaDeviceFolder *pmdf, HWND hwnd) : _cRef(1), _pmdf(pmdf), _hwnd(hwnd), _grfKeyStateLast(-1) { _pmdf->AddRef(); // use the FTM to make the call back interface calls unmarshaled CoCreateFreeThreadedMarshaler(SAFECAST(this, IWMDMProgress2 *), &_punkFTM); DllAddRef(); } CMediaDeviceDropTarget::~CMediaDeviceDropTarget() { DragLeave(); _pmdf->Release(); if (_punkFTM) _punkFTM->Release(); DllRelease(); } STDMETHODIMP_(ULONG) CMediaDeviceDropTarget::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CMediaDeviceDropTarget::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CMediaDeviceDropTarget::QueryInterface(REFIID riid, void** ppv) { static const QITAB qit[] = { QITABENT(CMediaDeviceDropTarget, IDropTarget), QITABENTMULTI(CMediaDeviceDropTarget, IWMDMProgress, IWMDMProgress2), QITABENT (CMediaDeviceDropTarget, IWMDMProgress2), QITABENT(CMediaDeviceDropTarget, INamespaceWalkCB), { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppv); if (FAILED(hr) && (IID_IMarshal == riid) && _punkFTM) hr = _punkFTM->QueryInterface(riid, ppv); return hr; } CLIPFORMAT g_cfPreferredDropEffect = 0; // IMPLEMENT CLIPFORMAT g_cfLogicalPerformedDropEffect = 0; CLIPFORMAT g_cfPerformedDropEffect = 0; #define POPUP_NONDEFAULTDD 1 #define DDIDM_COPY 2 #define DDIDM_MOVE 3 #define DDIDM_LINK 4 DWORD CMediaDeviceDropTarget::_GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState) { DWORD dwEffectReturned = DROPEFFECT_NONE; switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT)) { case MK_CONTROL: dwEffectReturned = DROPEFFECT_COPY; break; case MK_SHIFT: dwEffectReturned = DROPEFFECT_MOVE; break; case MK_SHIFT | MK_CONTROL: dwEffectReturned = DROPEFFECT_LINK; break; case MK_ALT: dwEffectReturned = DROPEFFECT_LINK; break; default: { // no modifier keys: // if the data object contains a preferred drop effect, try to use it DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & *pdwEffect; if (dwPreferred) { if (dwPreferred & DROPEFFECT_MOVE) { dwEffectReturned = DROPEFFECT_MOVE; } else if (dwPreferred & DROPEFFECT_COPY) { dwEffectReturned = DROPEFFECT_COPY; } else if (dwPreferred & DROPEFFECT_LINK) { dwEffectReturned = DROPEFFECT_LINK; } } else { dwEffectReturned = DROPEFFECT_COPY; } } break; } return dwEffectReturned; } STDMETHODIMP CMediaDeviceDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { IUnknown_Set((IUnknown **)&_pdtobj, pdtobj); _grfKeyStateLast = grfKeyState; if (pdwEffect) *pdwEffect = _dwEffectLastReturned = _GetDropEffect(pdwEffect, grfKeyState); return S_OK; } STDMETHODIMP CMediaDeviceDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { // has the key state changed? if not then lets return the previously cached // version, otherwise recompute. if (_grfKeyStateLast == grfKeyState) { if (*pdwEffect) *pdwEffect = _dwEffectLastReturned; } else if (*pdwEffect) { *pdwEffect = _GetDropEffect(pdwEffect, grfKeyState); } _dwEffectLastReturned = *pdwEffect; _grfKeyStateLast = grfKeyState; return S_OK; } STDMETHODIMP CMediaDeviceDropTarget::DragLeave() { ATOMICRELEASE(_pdtobj); return S_OK; } class COPY_THREAD_DATA { public: COPY_THREAD_DATA(CMediaDeviceDropTarget *pmddt) : _pmddt(pmddt), _pstm(NULL) { _pmddt->AddRef(); } ~COPY_THREAD_DATA() { _pmddt->Release(); if (_pstm) _pstm->Release(); } HRESULT Marshal(IDataObject *pdtobj) { return CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &_pstm); } HRESULT UnMarshal(IDataObject **ppdtobj) { HRESULT hr = CoGetInterfaceAndReleaseStream(_pstm, IID_PPV_ARG(IDataObject, ppdtobj)); _pstm = NULL; return hr; } CMediaDeviceDropTarget *_pmddt; private: IStream *_pstm; // IStream for marshaling the IDataObject }; STDMETHODIMP CMediaDeviceDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_NONE; // incase of failure // determine the type of operation to performed, if the right button is down // then lets display the menu, otherwise base it on the drop effect UINT idCmd = 0; // Choice from drop popup menu #if 0 if (!(_grfKeyStateLast & MK_LBUTTON)) { HMENU hMenu = SHLoadPopupMenu(m_hInst, POPUP_NONDEFAULTDD); if (!hMenu) { DragLeave(); return E_FAIL; } SetMenuDefaultItem(hMenu, POPUP_NONDEFAULTDD, FALSE); idCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN, pt.x, pt.y, 0, _hwnd, NULL); DestroyMenu(hMenu); } else #endif { switch (_GetDropEffect(pdwEffect, grfKeyState)) { case DROPEFFECT_COPY: idCmd = DDIDM_COPY; break; case DROPEFFECT_MOVE: idCmd = DDIDM_MOVE; break; } } // now perform the operation, based on the command ID we have. HRESULT hr = E_FAIL; switch (idCmd) { case DDIDM_COPY: COPY_THREAD_DATA *pctd = new COPY_THREAD_DATA(this); if (pctd) { if (SUCCEEDED(pctd->Marshal(pdtobj))) { if (!SHCreateThread(_DoCopyThreadProc, pctd, CTF_COINIT, NULL)) { delete pctd; } } } break; } #if 0 // success so lets populate the new changes to the effect if (SUCCEEDED(hr) && *pdwEffect) { DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect); DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, *pdwEffect); } #endif DragLeave(); return hr; } DWORD CMediaDeviceDropTarget::_DoCopyThreadProc(void *pv) { COPY_THREAD_DATA *pctd = (COPY_THREAD_DATA *)pv; IDataObject *pdtobj; HRESULT hr = pctd->UnMarshal(&pdtobj); if (SUCCEEDED(hr)) { pctd->_pmddt->_DoCopy(pdtobj); pdtobj->Release(); } delete pctd; return 0; } HRESULT CMediaDeviceDropTarget::_DoCopy(IDataObject *pdtobj) { IProgressDialog *ppd; HRESULT hr = _CreateProgress(_hwnd, &ppd); if (SUCCEEDED(hr)) { ppd->SetTitle(L"Copying Files..."); IWMDMStorageControl *pstgCtrl; hr = _pmdf->_Storage(IID_PPV_ARG(IWMDMStorageControl, &pstgCtrl)); if (SUCCEEDED(hr)) { INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { hr = pnsw->Walk(pdtobj, 0, 4, SAFECAST(this, INamespaceWalkCB *)); if (SUCCEEDED(hr)) { UINT cItems; LPITEMIDLIST *ppidls; hr = pnsw->GetIDArrayResult(&cItems, &ppidls); if (SUCCEEDED(hr)) { for (UINT i = 0; SUCCEEDED(hr) && (i < cItems); i++) { TCHAR szPath[MAX_PATH]; if (SHGetPathFromIDList(ppidls[i], szPath)) { ppd->SetLine(2, PathFindFileName(szPath), FALSE, NULL); ppd->SetProgress64(_ulProgressCurrent, _ulProgressTotal); _ppd = ppd; // used by the callback interface IWMDMStorage *pstgNew; hr = pstgCtrl->Insert(WMDM_MODE_BLOCK | WMDM_CONTENT_FILE, szPath, NULL, SAFECAST(this, IWMDMProgress2 *), &pstgNew); if (SUCCEEDED(hr)) { _pmdf->_ChangeNotifyObj(SHCNE_CREATE, pstgNew); pstgNew->Release(); ppd->SetProgress64(_ulProgressCurrent, _ulProgressTotal); if (ppd->HasUserCancelled()) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } } } } FreeIDListArray(ppidls, cItems); // free space changed on device, notify // SHChangeNotify(SHCNE_UPDATEITEM, 0, (LPCTSTR)_pmdf->GetIDList(), NULL); } } pnsw->Release(); } pstgCtrl->Release(); } ppd->StopProgressDialog(); ppd->Release(); } return S_OK; } // IWMDMProgress // since we use the FTM we get called here on an MTA thread STDMETHODIMP CMediaDeviceDropTarget::Begin(DWORD dwEstimatedTicks) { _ulThisFile = dwEstimatedTicks; return S_OK; // dwEstimatedTicks size in bytes of file } STDMETHODIMP CMediaDeviceDropTarget::Progress(DWORD dwTranspiredTicks) { _ppd->SetProgress64(_ulProgressCurrent + dwTranspiredTicks, _ulProgressTotal); if (_ulThisFile == dwTranspiredTicks) _ulProgressCurrent += _ulThisFile; // end of this file, advance total return _ppd->HasUserCancelled() ? HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK; } STDMETHODIMP CMediaDeviceDropTarget::End() { return S_OK; } // IWMDMProgress2 STDMETHODIMP CMediaDeviceDropTarget::End2(HRESULT hrCompletionCode) { return S_OK; } // INamespaceWalkCB STDMETHODIMP CMediaDeviceDropTarget::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl) { IShellFolder2 *psf2; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { ULONGLONG ul; if (SUCCEEDED(GetLongProperty(psf2, pidl, &SCID_SIZE, &ul))) { // TCHAR szName[MAX_PATH]; // DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName)); _ulProgressTotal += ul; } psf2->Release(); } return S_OK; } STDMETHODIMP CMediaDeviceDropTarget::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; } STDMETHODIMP CMediaDeviceDropTarget::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; }