WindowsXP-SP1/shell/shell32/wirelessfldr.cpp

1323 lines
39 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include "idldata.h"
#include "datautil.h"
#include "ids.h"
#include <obex.h>
#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 dwType; // } DELEGATEITEMID;
DWORD dwAttributes;
ULARGE_INTEGER cbTotal;
ULARGE_INTEGER cbFree;
union
{
FILETIME ftModified;
ULARGE_INTEGER ulModified;
};
WCHAR szName[1]; // variable size
} WIRELESSITEM;
#pragma pack()
typedef UNALIGNED WIRELESSITEM * LPWIRELESSITEM;
typedef const UNALIGNED WIRELESSITEM * LPCWIRELESSITEM;
#define WIRELESSITEM_MAGIC 0x98765432
class CWirelessDeviceFolder;
class CWirelessDeviceEnum;
class CWirelessDeviceDropTarget;
class CWirelessDeviceFolder : public IShellFolder2, IPersistFolder2, IShellFolderViewCB, IDelegateFolder
{
public:
CWirelessDeviceFolder();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID);
// IPersistFolder
STDMETHOD(Initialize)(LPCITEMIDLIST pidl);
// IPersistFolder2
STDMETHOD(GetCurFolder)(LPITEMIDLIST *ppidl);
// IShellFolder
STDMETHOD(ParseDisplayName)(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
STDMETHOD(EnumObjects)(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{ return BindToObject(pidl, pbc, riid, ppv); };
STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
STDMETHOD(CreateViewObject)(HWND hwndOwner, REFIID riid, void **ppvOut);
STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
STDMETHOD(GetUIObjectOf)(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppvOut);
STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
STDMETHOD(SetNameOf)(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut);
// IShellFolder2
STDMETHOD(GetDefaultSearchGUID)(GUID *pGuid)
{ return E_NOTIMPL; };
STDMETHOD(EnumSearches)(IEnumExtraSearch **ppenum)
{ return E_NOTIMPL; };
STDMETHOD(GetDefaultColumn)(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
{ return E_NOTIMPL; };
STDMETHOD(GetDefaultColumnState)(UINT iColumn, DWORD *pbState)
{ return E_NOTIMPL; }
STDMETHOD(GetDetailsEx)(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{ return E_NOTIMPL; };
STDMETHOD(GetDetailsOf)(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
STDMETHOD(MapColumnToSCID)(UINT iColumn, SHCOLUMNID *pscid)
{ return E_NOTIMPL; };
// IShellFolderViewCB
STDMETHOD(MessageSFVCB)(UINT uMsg, WPARAM wParam, LPARAM lParam);
// IDelegateFolder
STDMETHODIMP SetItemAlloc(IMalloc *pmalloc);
private:
~CWirelessDeviceFolder();
HRESULT _CreateIDList(LPCTSTR pszName, LPCTSTR pszAddress, LPCTSTR pszTransport, WIRELESSITEM **ppmditem);
HRESULT _IDListForDevice(IObexDevice *pdev, LPITEMIDLIST *ppidl);
HRESULT _GetTypeOf(LPCWIRELESSITEM pmdi, LPTSTR pszBuffer, INT cchBuffer);
ULONG _GetAttributesOf(LPCWIRELESSITEM pmdi, ULONG rgfIn);
HRESULT _CreateExtractIcon(LPCWIRELESSITEM pmdi, REFIID riid, void **ppv);
HRESULT _Device(LPCWIRELESSITEM pmdi, REFIID riid, void **ppv);
static HRESULT CALLBACK _ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
// folder view callback handlers
HRESULT _OnBackgroundEnum(DWORD pv) { return S_OK; };
HRESULT _OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents);
LPCWIRELESSITEM _IsValid(LPCITEMIDLIST pidl);
DWORD _IsFolder(LPCWIRELESSITEM pmditem);
HRESULT _GetObex(REFIID riid, void **ppv);
HRESULT _GetName(LPCWIRELESSITEM pmdi, LPTSTR pszName, LPTSTR pszAddress, LPTSTR pszTransport);
HRESULT _CreateStgFolder(LPCITEMIDLIST pidl, IStorage *pstg, REFIID riid, void **ppv);
void *_Alloc(SIZE_T cb);
friend CWirelessDeviceEnum;
friend CWirelessDeviceDropTarget;
LONG _cRef;
IMalloc *_pmalloc;
LPITEMIDLIST _pidl;
IObex *_pObex;
};
class CWirelessDeviceEnum : public IEnumIDList
{
public:
CWirelessDeviceEnum(CWirelessDeviceFolder* prf, DWORD grfFlags);
~CWirelessDeviceEnum();
// 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();
STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; };
private:
HRESULT _InitEnum();
void _UnMarshall();
LONG _cRef;
CWirelessDeviceFolder* _pwdf;
DWORD _grfFlags;
IDeviceEnum *_pDeviceEnum;
IObex *_pobex;
IStream *_pstmDevice;
};
class CWirelessDeviceDropTarget : public IDropTarget
{
public:
CWirelessDeviceDropTarget(CWirelessDeviceFolder *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);
private:
~CWirelessDeviceDropTarget();
DWORD _GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState);
HRESULT _Transfer(IDataObject *pdtobj, UINT uiCmd);
LONG _cRef;
CWirelessDeviceFolder *_pwdf;
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
};
CWirelessDeviceFolder::CWirelessDeviceFolder() : _cRef(1)
{
ASSERT(NULL == _pidl);
ASSERT(NULL == _pmalloc);
DllAddRef();
}
CWirelessDeviceFolder::~CWirelessDeviceFolder()
{
ILFree(_pidl);
ATOMICRELEASE(_pmalloc);
if (_pObex)
{
_pObex->Shutdown();
_pObex->Release();
}
DllRelease();
}
HRESULT CWirelessDeviceFolder::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENTMULTI(CWirelessDeviceFolder, IShellFolder, IShellFolder2),
QITABENT (CWirelessDeviceFolder, IShellFolder2),
QITABENTMULTI(CWirelessDeviceFolder, IPersist, IPersistFolder2),
QITABENTMULTI(CWirelessDeviceFolder, IPersistFolder, IPersistFolder2),
QITABENT (CWirelessDeviceFolder, IPersistFolder2),
QITABENT (CWirelessDeviceFolder, IShellFolderViewCB),
QITABENT (CWirelessDeviceFolder, IDelegateFolder),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CWirelessDeviceFolder::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CWirelessDeviceFolder::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDAPI CWirelessDevices_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
{
CWirelessDeviceFolder *pwdf = new CWirelessDeviceFolder();
if (!pwdf)
return E_OUTOFMEMORY;
HRESULT hr = pwdf->QueryInterface(riid, ppv);
pwdf->Release();
return hr;
}
const GUID CLSID_Obex = {0x30a7bc00, 0x59b6, 0x40bb, 0xaa, 0x2b, 0x89, 0xeb, 0x49, 0xef, 0x27, 0x4e};
const IID IID_IObex = {0x0C5A5B12, 0x2979, 0x42D1, 0x9E, 0x15, 0xA6, 0x3E, 0x34, 0x38, 0x3B, 0x58};
HRESULT CWirelessDeviceFolder::_GetObex(REFIID riid, void **ppv)
{
HRESULT hr;
if (_pObex)
{
hr = _pObex->QueryInterface(riid, ppv);
}
else
{
hr = CoCreateInstance(CLSID_Obex, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IObex, &_pObex));
if (SUCCEEDED(hr))
{
hr = _pObex->Initialize();
if (SUCCEEDED(hr))
{
hr = _pObex->QueryInterface(riid, ppv);
}
else
{
_pObex->Release();
_pObex = NULL;
}
}
}
return hr;
}
LPCWIRELESSITEM CWirelessDeviceFolder::_IsValid(LPCITEMIDLIST pidl)
{
if (pidl && ((LPCWIRELESSITEM)pidl)->dwMagic == WIRELESSITEM_MAGIC)
return (LPCWIRELESSITEM)pidl;
return NULL;
}
DWORD CWirelessDeviceFolder::_IsFolder(LPCWIRELESSITEM pmditem)
{
return FILE_ATTRIBUTE_DIRECTORY;
}
// 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 *CWirelessDeviceFolder::_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 CWirelessDeviceFolder::_CreateIDList(LPCTSTR pszName, LPCTSTR pszAddress, LPCTSTR pszTransport, WIRELESSITEM **ppmditem)
{
HRESULT hr;
UINT cbName = lstrlen(pszName) + 1;
UINT cbAddress = lstrlen(pszAddress) + 1;
UINT cbTransport = lstrlen(pszTransport);
UINT cbInner = sizeof(WIRELESSITEM) - (sizeof(DELEGATEITEMID) - 1) +
(sizeof(WCHAR) * (cbName + cbAddress + cbTransport));
*ppmditem = (WIRELESSITEM *)_Alloc(cbInner);
if (*ppmditem)
{
(*ppmditem)->dwMagic = WIRELESSITEM_MAGIC;
(*ppmditem)->dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
StrCpyW((*ppmditem)->szName, pszName);
StrCpyW((*ppmditem)->szName + cbName, pszAddress);
StrCpyW((*ppmditem)->szName + cbName + cbAddress, pszTransport);
hr = S_OK;
}
else
hr = E_OUTOFMEMORY;
return hr;
}
// Creates an item identifier list for the objects in the namespace
HRESULT CWirelessDeviceFolder::_IDListForDevice(IObexDevice *pdev, LPITEMIDLIST *ppidl)
{
IPropertyBag *ppb;
HRESULT hr = pdev->EnumProperties(IID_PPV_ARG(IPropertyBag, &ppb));
if (SUCCEEDED(hr))
{
TCHAR szName[MAX_PATH], szAddress[64], szTransport[64];
SHPropertyBag_ReadStr(ppb, L"Name", szName, ARRAYSIZE(szName));
SHPropertyBag_ReadStr(ppb, L"Address", szAddress, ARRAYSIZE(szAddress));
SHPropertyBag_ReadStr(ppb, L"Transport", szTransport, ARRAYSIZE(szTransport));
WIRELESSITEM *pmditem;
hr = _CreateIDList(szName, szAddress, szTransport, &pmditem);
if (SUCCEEDED(hr))
{
*ppidl = (LPITEMIDLIST)pmditem;
}
ppb->Release();
}
return hr;
}
HRESULT CWirelessDeviceFolder::_GetTypeOf(LPCWIRELESSITEM pmdi, LPTSTR pszBuffer, INT cchBuffer)
{
*pszBuffer = 0; // null out the return buffer
LPCWSTR pwszName;
WSTR_ALIGNED_STACK_COPY(&pwszName, pmdi->szName);
LPTSTR pszExt = PathFindExtension(pwszName);
if (pszExt)
{
StrCpyN(pszBuffer, pszExt, cchBuffer);
}
return S_OK;
}
// IPersist
STDMETHODIMP CWirelessDeviceFolder::GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_WirelessDevices;
return S_OK;
}
// IPersistFolder
STDMETHODIMP CWirelessDeviceFolder::Initialize(LPCITEMIDLIST pidl)
{
ILFree(_pidl);
return SHILClone(pidl, &_pidl);
}
// IPersistFolder2
HRESULT CWirelessDeviceFolder::GetCurFolder(LPITEMIDLIST *ppidl)
{
if (_pidl)
return SHILClone(_pidl, ppidl);
*ppidl = NULL;
return S_FALSE;
}
// IShellFolder(2)
HRESULT CWirelessDeviceFolder::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);
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 CWirelessDeviceFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
{
HRESULT hr;
CWirelessDeviceEnum *penum = new CWirelessDeviceEnum(this, grfFlags);
if (penum)
{
hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
penum->Release();
}
else
{
hr = E_OUTOFMEMORY;
*ppenum = NULL;
}
return hr;
}
HRESULT CWirelessDeviceFolder::_GetName(LPCWIRELESSITEM pmdi, LPTSTR pszName, LPTSTR pszAddress, LPTSTR pszTransport)
{
LPCWSTR psz = pmdi->szName;
UINT cch = lstrlen(psz) + 1;
if (pszName)
StrCpy(pszName, psz);
psz += cch;
cch = lstrlen(psz) + 1;
if (pszAddress)
StrCpy(pszAddress, psz);
psz += cch;
cch = lstrlen(psz) + 1;
if (pszTransport)
StrCpy(pszTransport, psz);
return S_OK;
}
HRESULT CWirelessDeviceFolder::_Device(LPCWIRELESSITEM pmdi, REFIID riid, void **ppv)
{
TCHAR szName[MAX_PATH], szAddress[64], szTransport[64];
HRESULT hr = _GetName(pmdi, szName, szAddress, szTransport);
if (SUCCEEDED(hr))
{
IPropertyBag *ppb;
hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb));
if (SUCCEEDED(hr))
{
// store the class ID for the CD mastering folder
SHPropertyBag_WriteStr(ppb, L"Name", szName);
SHPropertyBag_WriteStr(ppb, L"Address", szAddress);
SHPropertyBag_WriteStr(ppb, L"Transport", szTransport);
IObex *pobex;
hr = _GetObex(IID_PPV_ARG(IObex, &pobex));
if (SUCCEEDED(hr))
{
IObexDevice *pdev;
hr = pobex->BindToDevice(ppb, &pdev);
if (SUCCEEDED(hr))
{
if (riid == IID_IStorage)
hr = pdev->BindToStorage(OBEX_DEVICE_CAP_PUSH, (IStorage **)ppv);
else
hr = pdev->QueryInterface(riid, ppv);
pdev->Release();
}
pobex->Release();
}
ppb->Release();
}
}
return hr;
}
HRESULT CWirelessDeviceFolder::_CreateStgFolder(LPCITEMIDLIST pidl, IStorage *pstg, REFIID riid, void **ppv)
{
*ppv = NULL;
IPersistStorage *ppstg;
HRESULT hr = CoCreateInstance(CLSID_StgFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPersistStorage, &ppstg));
if (SUCCEEDED(hr))
{
hr = ppstg->Load(pstg);
if (SUCCEEDED(hr))
{
IPersistFolder *ppf;
hr = ppstg->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
if (SUCCEEDED(hr))
{
hr = ppf->Initialize(pidl);
if (SUCCEEDED(hr))
hr = ppf->QueryInterface(riid, ppv);
ppf->Release();
}
}
ppstg->Release();
}
return hr;
}
HRESULT CWirelessDeviceFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
*ppv = NULL;
HRESULT hr = E_NOINTERFACE;
LPCWIRELESSITEM pmdi = _IsValid(pidl);
if (pmdi && _IsFolder(pmdi))
{
if (IID_IShellFolder == riid ||
IID_IShellFolder2 == riid)
{
IStorage *pstg;
hr = _Device(pmdi, IID_PPV_ARG(IStorage, &pstg));
if (SUCCEEDED(hr))
{
LPCITEMIDLIST pidlNext = _ILNext(pidl);
LPITEMIDLIST pidlSubFolder = ILCombineParentAndFirst(_pidl, pidl, pidlNext);
if (pidlSubFolder)
{
IShellFolder *psf;
hr = _CreateStgFolder(pidlSubFolder, pstg, IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr))
{
// if there's nothing left in the pidl, get the interface on this one.
if (ILIsEmpty(pidlNext))
hr = psf->QueryInterface(riid, ppv);
else
{
// otherwise, hand the rest of it off to the new shellfolder.
hr = psf->BindToObject(pidlNext, pbc, riid, ppv);
}
psf->Release();
}
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 CWirelessDeviceFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
LPCWIRELESSITEM pmdi1 = _IsValid(pidl1);
LPCWIRELESSITEM 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];
_GetTypeOf(pmdi1, szType1, ARRAYSIZE(szType1));
_GetTypeOf(pmdi2, szType2, ARRAYSIZE(szType2));
nCmp = StrCmpI(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)
{
nCmp = ualstrcmpi(pmdi1->szName, pmdi2->szName);
}
}
return ResultFromShort(nCmp);
}
HRESULT CWirelessDeviceFolder::_OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents)
{
*ppidl = _pidl;
*plEvents = SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \
SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \
SHCNE_MKDIR | SHCNE_RMDIR;
return S_OK;
}
STDMETHODIMP CWirelessDeviceFolder::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);
}
return hr;
}
HRESULT CWirelessDeviceFolder::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 = this;
sSFV.pshf = this;
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))
{
CWirelessDeviceDropTarget *psdt = new CWirelessDeviceDropTarget(this, hwnd);
if (psdt)
{
hr = psdt->QueryInterface(riid, ppv);
psdt->Release();
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
ULONG CWirelessDeviceFolder::_GetAttributesOf(LPCWIRELESSITEM pmdi, ULONG rgfIn)
{
return rgfIn & (SFGAO_FOLDER | SFGAO_CANLINK | SFGAO_CANCOPY | SFGAO_HASSUBFOLDER | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET);
}
HRESULT CWirelessDeviceFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
{
UINT rgfOut = *prgfInOut;
// return attributes of the namespace root?
if (!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++)
rgfOut &= _GetAttributesOf(_IsValid(apidl[i]), *prgfInOut);
}
*prgfInOut = rgfOut;
return S_OK;
}
HRESULT CALLBACK CWirelessDeviceFolder::_ItemsMenuCB(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 = StgDeleteUsingDataObject(hwnd, pdfmics->fMask, 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 CWirelessDeviceFolder::_CreateExtractIcon(LPCWIRELESSITEM pmdi, REFIID riid, void **ppv)
{
HRESULT hr = E_OUTOFMEMORY;
if (_IsFolder(pmdi))
{
UINT iIcon = II_FOLDER;
UINT iIconOpen = II_FOLDEROPEN;
TCHAR szModule[MAX_PATH];
GetModuleFileName(g_hinst, szModule, ARRAYSIZE(szModule));
hr = SHCreateDefExtIcon(szModule, iIcon, iIconOpen, GIL_PERCLASS, -1, riid, ppv);
}
return hr;
}
HRESULT CWirelessDeviceFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
REFIID riid, UINT *prgfInOut, void **ppv)
{
HRESULT hr = E_INVALIDARG;
LPCWIRELESSITEM pmdi = cidl ? _IsValid(apidl[0]) : NULL;
if (pmdi &&
(IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
{
WCHAR szName[MAX_PATH];
_GetName(pmdi, szName, NULL, NULL);
hr = SHCreateFileExtractIconW(szName, _IsFolder(pmdi), riid, ppv);
}
else if (IsEqualIID(riid, IID_IDataObject) && cidl)
{
hr = CIDLData_CreateInstance(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
}
#if 0
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, (LPCITEMIDLIST*)&pmdi, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
if (SUCCEEDED(hr))
{
HKEY ahk[3];
// this is broken for docfiles (shell\ext\stgfldr's keys work though)
// maybe because GetClassFile punts when it's not fs?
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)
{
// need to create a valid Assoc obj here
}
#endif
else if (IsEqualIID(riid, IID_IDropTarget) && 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
if (_IsFolder(pmdi))
{
IShellFolder *psf;
hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr))
{
hr = psf->CreateViewObject(hwnd, IID_IDropTarget, ppv);
psf->Release();
}
}
else
{
hr = CreateViewObject(hwnd, IID_IDropTarget, ppv);
}
}
return hr;
}
HRESULT CWirelessDeviceFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet)
{
HRESULT hr;
LPCWIRELESSITEM pmdi = _IsValid(pidl);
if (pmdi)
{
WCHAR szName[MAX_PATH];
_GetName(pmdi, szName, NULL, NULL);
if (dwFlags & SHGDN_FORPARSING)
{
if (dwFlags & SHGDN_INFOLDER)
{
hr = StringToStrRetW(szName, pStrRet); // relative name
}
else
{
TCHAR szTemp[MAX_PATH];
SHGetNameAndFlags(_pidl, dwFlags, szTemp, ARRAYSIZE(szTemp), NULL);
PathAppend(szTemp, szName);
hr = StringToStrRetW(szTemp, pStrRet);
}
}
else
{
hr = StringToStrRetW(szName, pStrRet);
}
}
else
hr = E_INVALIDARG;
return hr;
}
HRESULT CWirelessDeviceFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut)
{
return E_NOTIMPL;
}
static const struct
{
UINT iTitle;
UINT cchCol;
UINT iFmt;
}
g_aMediaDeviceColumns[] =
{
{IDS_NAME_COL, 20, LVCFMT_LEFT},
{IDS_SIZE_COL, 10, LVCFMT_RIGHT},
{IDS_TYPE_COL, 20, LVCFMT_LEFT},
{IDS_MODIFIED_COL, 20, LVCFMT_LEFT},
};
HRESULT CWirelessDeviceFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail)
{
HRESULT hr = S_OK;
TCHAR szTemp[MAX_PATH];
// is this a valid column?
if (iColumn >= ARRAYSIZE(g_aMediaDeviceColumns))
return E_NOTIMPL;
pDetail->str.uType = STRRET_CSTR;
pDetail->str.cStr[0] = 0;
if (NULL == pidl)
{
pDetail->fmt = g_aMediaDeviceColumns[iColumn].iFmt;
pDetail->cxChar = g_aMediaDeviceColumns[iColumn].cchCol;
LoadString(g_hinst, g_aMediaDeviceColumns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
hr = StringToStrRetW(szTemp, &(pDetail->str));
}
else
{
LPCWIRELESSITEM 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.
WCHAR szName[MAX_PATH];
_GetName(pmdi, szName, NULL, NULL);
switch (iColumn)
{
case DEV_COL_NAME:
hr = StringToStrRetW(szName, &(pDetail->str));
break;
case DEV_COL_SIZE:
if (!_IsFolder(pmdi))
{
ULARGE_INTEGER ullSize = pmdi->cbTotal;
StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp));
hr = StringToStrRetW(szTemp, &(pDetail->str));
}
break;
case DEV_COL_TYPE:
{
SHFILEINFO sfi = { 0 };
if (SHGetFileInfo(szName, _IsFolder(pmdi), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME))
hr = StringToStrRetW(sfi.szTypeName, &(pDetail->str));
break;
}
case DEV_COL_MODIFIED:
SHFormatDateTime(&pmdi->ftModified, NULL, szTemp, ARRAYSIZE(szTemp));
hr = StringToStrRetW(szTemp, &(pDetail->str));
break;
}
}
}
return hr;
}
// IDelegateFolder
HRESULT CWirelessDeviceFolder::SetItemAlloc(IMalloc *pmalloc)
{
IUnknown_Set((IUnknown**)&_pmalloc, pmalloc);
return S_OK;
}
CWirelessDeviceEnum::CWirelessDeviceEnum(CWirelessDeviceFolder *pmdf, DWORD grfFlags) : _cRef(1), _grfFlags(grfFlags)
{
_pwdf = pmdf;
_pwdf->AddRef();
IObex *pobex;
if (SUCCEEDED(_pwdf->_GetObex(IID_PPV_ARG(IObex, &pobex))))
{
CoMarshalInterThreadInterfaceInStream(IID_IObex, pobex, &_pstmDevice);
pobex->Release();
}
DllAddRef();
}
CWirelessDeviceEnum::~CWirelessDeviceEnum()
{
ATOMICRELEASE(_pobex);
ATOMICRELEASE(_pDeviceEnum);
ATOMICRELEASE(_pstmDevice);
_pwdf->Release();
DllRelease();
}
STDMETHODIMP_(ULONG) CWirelessDeviceEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CWirelessDeviceEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CWirelessDeviceEnum::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CWirelessDeviceEnum, IEnumIDList), // IID_IEnumIDList
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
void CWirelessDeviceEnum::_UnMarshall()
{
if (_pstmDevice)
CoGetInterfaceAndReleaseStream(_pstmDevice, IID_PPV_ARG(IObex, &_pobex));
_pstmDevice = NULL;
}
HRESULT CWirelessDeviceEnum::_InitEnum()
{
HRESULT hr = S_OK;
_UnMarshall();
if (NULL == _pDeviceEnum)
{
hr = _pobex ? _pobex->EnumDevices(&_pDeviceEnum, CLSID_NULL) : E_FAIL;
}
return hr;
}
HRESULT CWirelessDeviceEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
{
HRESULT hr = _InitEnum();
if (SUCCEEDED(hr))
{
for (UINT cItems = 0; (cItems != celt) && (S_OK == hr); )
{
LPITEMIDLIST pidl;
ULONG ulFetched;
IObexDevice *pdev;
hr = _pDeviceEnum->Next(1, &pdev, &ulFetched);
if (S_OK == hr && ulFetched)
{
hr = _pwdf->_IDListForDevice(pdev, &pidl);
pdev->Release();
}
else
hr = S_FALSE;
if (S_OK == hr)
{
if (!(_grfFlags & SHCONTF_FOLDERS))
{
ILFree(pidl);
pidl = NULL;
continue;
}
rgelt[cItems++] = pidl; // return the idlist
}
}
if (pceltFetched)
*pceltFetched = cItems;
}
return hr;
}
STDMETHODIMP CWirelessDeviceEnum::Reset()
{
ATOMICRELEASE(_pDeviceEnum);
return S_OK;
}
CWirelessDeviceDropTarget::CWirelessDeviceDropTarget(CWirelessDeviceFolder *pmdf, HWND hwnd) :
_cRef(1), _pwdf(pmdf), _hwnd(hwnd), _grfKeyStateLast(-1)
{
_pwdf->AddRef();
DllAddRef();
}
CWirelessDeviceDropTarget::~CWirelessDeviceDropTarget()
{
DragLeave();
ATOMICRELEASE(_pwdf);
DllRelease();
}
STDMETHODIMP_(ULONG) CWirelessDeviceDropTarget::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CWirelessDeviceDropTarget::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CWirelessDeviceDropTarget::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CWirelessDeviceDropTarget, IDropTarget),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
HRESULT CWirelessDeviceDropTarget::_Transfer(IDataObject *pdtobj, UINT uiCmd)
{
return E_FAIL;
}
DWORD CWirelessDeviceDropTarget::_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 CWirelessDeviceDropTarget::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 CWirelessDeviceDropTarget::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 CWirelessDeviceDropTarget::DragLeave()
{
ATOMICRELEASE(_pdtobj);
return S_OK;
}
STDMETHODIMP CWirelessDeviceDropTarget::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 (!(_grfKeyStateLast & MK_LBUTTON))
{
HMENU hMenu = SHLoadPopupMenu(g_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
{
switch (_GetDropEffect(pdwEffect, grfKeyState))
{
case DROPEFFECT_COPY: idCmd = DDIDM_COPY; break;
case DROPEFFECT_MOVE: idCmd = DDIDM_MOVE; break;
case DROPEFFECT_LINK: idCmd = DDIDM_LINK; break;
}
}
// now perform the operation, based on the command ID we have.
HRESULT hr = E_FAIL;
switch (idCmd)
{
case DDIDM_COPY:
case DDIDM_MOVE:
hr = _Transfer(pdtobj, idCmd);
if (SUCCEEDED(hr))
*pdwEffect = (idCmd == DDIDM_COPY) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
else
*pdwEffect = 0;
break;
case DDIDM_LINK:
{
WCHAR wzPath[MAX_PATH];
SHGetNameAndFlags(_pwdf->_pidl, SHGDN_FORPARSING, wzPath, ARRAYSIZE(wzPath), NULL);
hr = SHCreateLinks(_hwnd, wzPath, pdtobj, 0, NULL);
break;
}
}
// 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);
}
DragLeave();
return hr;
}