2152 lines
63 KiB
C++
2152 lines
63 KiB
C++
// 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 <shlobj.h>
|
|
#include <shsemip.h>
|
|
#include <shlobjp.h> // IDelegateFolder
|
|
#include "thisdll.h"
|
|
#include <windowsx.h>
|
|
#include <varutil.h>
|
|
#include <dobjutil.h>
|
|
#include <malloc.h>
|
|
#include <dpa.h>
|
|
#include <shdguid.h>
|
|
#include "shdup.h"
|
|
#include "ids.h"
|
|
#include <ccstock2.h>
|
|
#pragma hdrstop
|
|
#include <wmsdk.h>
|
|
#include <mswmdm.h>
|
|
#include <scclient.h>
|
|
#include <mswmdm_i.c> // get CLSID/IID defs
|
|
|
|
#include <ntquery.h>
|
|
|
|
#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<ITEMIDLIST> _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;
|
|
}
|
|
|