windows-nt/Source/XPSP1/NT/shell/ext/audiodevice/wmfldr.cpp
2020-09-26 16:20:57 +08:00

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;
}