#include "shellprv.h" #pragma hdrstop #include "idldata.h" #include "datautil.h" #include "ids.h" #include #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; }