#include "shellprv.h" #include "caggunk.h" #include "datautil.h" #include "ids.h" #include "defview.h" #include "_security.h" #include "shitemid.h" #include "idlcomm.h" #include "cowsite.h" extern "C" { #include "bookmk.h" #include "fstreex.h" } #define TF_DRAGDROP 0x04000000 STDAPI_(BOOL) ExtractImageURLFromCFHTML(LPSTR pszHTML, SIZE_T cbSizeHTML, LPSTR pszImg, DWORD dwSize); STDAPI_(BOOL) IsFromSneakernetBriefcase(LPCITEMIDLIST pidlSource, LPCTSTR pszTarget); STDAPI_(BOOL) IsBriefcaseRoot(IDataObject* pDataObj); STDAPI_(BOOL) DroppingAnyFolders(HDROP hDrop); typedef struct { HWND hwnd; DWORD dwFlags; POINTL pt; CHAR szUrl[INTERNET_MAX_URL_LENGTH]; } ADDTODESKTOP; DWORD CALLBACK AddToActiveDesktopThreadProc(void* pv) { ADDTODESKTOP* pToAD = (ADDTODESKTOP*)pv; CHAR szFilePath[MAX_PATH]; DWORD cchFilePath = SIZECHARS(szFilePath); BOOL fAddComp = TRUE; if (SUCCEEDED(PathCreateFromUrlA(pToAD->szUrl, szFilePath, &cchFilePath, 0))) { TCHAR szPath[MAX_PATH]; SHAnsiToTChar(szFilePath, szPath, ARRAYSIZE(szPath)); // If the Url is in the Temp directory if (PathIsTemporary(szPath)) { if (IDYES == ShellMessageBox(g_hinst, pToAD->hwnd, MAKEINTRESOURCE(IDS_REASONS_URLINTEMPDIR), MAKEINTRESOURCE(IDS_AD_NAME), MB_YESNO | MB_ICONQUESTION)) { TCHAR szFilter[64], szTitle[64]; TCHAR szFilename[MAX_PATH]; LPTSTR psz; OPENFILENAME ofn = {0}; LoadString(g_hinst, IDS_ALLFILESFILTER, szFilter, ARRAYSIZE(szFilter)); LoadString(g_hinst, IDS_SAVEAS, szTitle, ARRAYSIZE(szTitle)); psz = szFilter; //Strip out the # and make them Nulls for SaveAs Dialog while (*psz) { if (*psz == (WCHAR)('#')) *psz = (WCHAR)('\0'); psz++; } lstrcpy(szFilename, PathFindFileName(szPath)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = pToAD->hwnd; ofn.hInstance = g_hinst; ofn.lpstrFilter = szFilter; ofn.lpstrFile = szFilename; ofn.nMaxFile = ARRAYSIZE(szFilename); ofn.lpstrTitle = szTitle; ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; if (GetSaveFileName(&ofn)) { SHFILEOPSTRUCT sfo = {0}; szPath[lstrlen(szPath) + 1] = 0; ofn.lpstrFile[lstrlen(ofn.lpstrFile) + 1] = 0; sfo.hwnd = pToAD->hwnd; sfo.wFunc = FO_COPY; sfo.pFrom = szPath; sfo.pTo = ofn.lpstrFile; cchFilePath = SIZECHARS(szPath); if (SHFileOperation(&sfo) == 0 && SUCCEEDED(UrlCreateFromPath(szPath, szPath, &cchFilePath, 0))) { SHTCharToAnsi(szPath, pToAD->szUrl, ARRAYSIZE(pToAD->szUrl)); } else fAddComp = FALSE; } else fAddComp = FALSE; } else fAddComp = FALSE; } } if (fAddComp) CreateDesktopComponents(pToAD->szUrl, NULL, pToAD->hwnd, pToAD->dwFlags, pToAD->pt.x, pToAD->pt.y); LocalFree((HLOCAL)pToAD); return 0; } // {F20DA720-C02F-11CE-927B-0800095AE340} const GUID CLSID_CPackage = {0xF20DA720L, 0xC02F, 0x11CE, 0x92, 0x7B, 0x08, 0x00, 0x09, 0x5A, 0xE3, 0x40}; // old packager guid... // {0003000C-0000-0000-C000-000000000046} const GUID CLSID_OldPackage = {0x0003000CL, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}; typedef struct { DWORD dwDefEffect; IDataObject* pdtobj; POINTL pt; DWORD* pdwEffect; HKEY hkeyProgID; HKEY hkeyBase; HMENU hmenu; UINT idCmd; DWORD grfKeyState; } FSDRAGDROPMENUPARAM; typedef struct { HMENU hMenu; UINT uCopyPos; UINT uMovePos; UINT uLinkPos; } FSMENUINFO; class CFSDropTarget : public CAggregatedUnknown, CObjectWithSite, public IDropTarget { public: // *** IUnknown *** STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) { return CAggregatedUnknown::QueryInterface(riid, ppvObj); }; STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef(); }; STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release(); }; // *** IDropTarget *** STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); STDMETHODIMP DragLeave(); STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); protected: CFSDropTarget(IUnknown* punkOuter); ~CFSDropTarget(); HRESULT v_InternalQueryInterface(REFIID riid, void** ppvObj); HRESULT _Init(CFSFolder* pFSFolder, HWND hwnd); BOOL _IsDesktopFolder() { return _pFolder->_pidl && ILIsEmpty(_pFolder->_pidl); }; HRESULT _GetDDInfoFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); HRESULT _GetDDInfoOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo); DWORD _PickDefFSOperation(DWORD dwCurEffectAvail); HRESULT _GetPath(LPTSTR pszPath); HRESULT _ZoneCheckDataObject(DWORD dwEffect); DWORD _LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed); DWORD _GetStdDefEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect); DWORD _DetermineEffects(DWORD grfKeyState, DWORD* pdwEffectInOut, HMENU hmenu); static VOID _AddVerbs(DWORD* pdwEffects, DWORD dwEffectAvail, DWORD dwDefEffect, UINT idCopy, UINT idMove, UINT idLink, DWORD dwForceEffect, FSMENUINFO* pfsMenuInfo); HRESULT _DragDropMenu(FSDRAGDROPMENUPARAM* pddm); HRESULT _CreatePackage(); HRESULT _CreateURLDeskComp(int x, int y); HRESULT _CreateDeskCompImage(POINTL pt); void _GetStateFromSite(); CFSFolder* _pFolder; HWND _hwndOwner; // EVIL: used as a site and UI host DWORD _grfKeyStateLast; // for previous DragOver/Enter IDataObject* _pdtobj; DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover DWORD _dwData; // DTID_* DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT BOOL _fSameHwnd; // the drag source and target are the same folder BOOL _fDragDrop; // BOOL _fBkDropTarget; POINT _ptDrop; private: friend HRESULT CFSDropTarget_CreateInstance(CFSFolder* pFSFolder, HWND hwnd, IDropTarget** ppdt); }; STDAPI CFSDropTarget_CreateInstance(CFSFolder* pFSFolder, HWND hwnd, IDropTarget** ppdt) { ASSERT(pFSFolder && ppdt); HRESULT hr; *ppdt = NULL; CFSDropTarget* pFSDT = new CFSDropTarget(NULL); if (pFSDT) { hr = pFSDT->_Init(pFSFolder, hwnd); if (SUCCEEDED(hr)) pFSDT->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt)); pFSDT->Release(); } else { hr = E_OUTOFMEMORY; } return hr; } CFSDropTarget::CFSDropTarget(IUnknown* punkOuter) : CAggregatedUnknown(punkOuter) { ASSERT(NULL == _hwndOwner); ASSERT(0 == _grfKeyStateLast); ASSERT(NULL == _pdtobj); ASSERT(0 == _dwEffectLastReturned); ASSERT(0 == _dwData); ASSERT(0 == _dwEffectPreferred); } CFSDropTarget::~CFSDropTarget() { if (_pFolder) CFSFolder_Release((IShellFolder2*)&(_pFolder->sf)); // if we hit this a lot maybe we should just release it AssertMsg(_pdtobj == NULL, TEXT("didn't get matching DragLeave. this=%#08lx"), this); } HRESULT CFSDropTarget::_Init(CFSFolder* pFSFolder, HWND hwnd) { _pFolder = pFSFolder; CFSFolder_AddRef((IShellFolder2*)&(_pFolder->sf)); _hwndOwner = hwnd; return S_OK; } HRESULT CFSDropTarget::v_InternalQueryInterface(REFIID riid, void** ppvObj) { static const QITAB qit[] = { QITABENT(CFSDropTarget, IDropTarget), QITABENT(CFSDropTarget, IObjectWithSite), QITABENTMULTI2(CFSDropTarget, IID_IDropTargetWithDADSupport, IDropTarget), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP CFSDropTarget::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { ASSERT(NULL == _pdtobj); // req DragDrop protocol, someone forgot to call DragLeave // init our registerd data formats IDLData_InitializeClipboardFormats(); _grfKeyStateLast = grfKeyState; _dwData = 0; IUnknown_Set((IUnknown**)&_pdtobj, pDataObj); if (pDataObj) { IEnumFORMATETC* penum; HRESULT hres = pDataObj->EnumFormatEtc(DATADIR_GET, &penum); if (SUCCEEDED(hres)) { FORMATETC fmte; ULONG celt; while (penum->Next(1, &fmte, &celt) == S_OK) { if (fmte.cfFormat == CF_HDROP && (fmte.tymed & TYMED_HGLOBAL)) _dwData |= DTID_HDROP; if (fmte.cfFormat == g_cfHIDA && (fmte.tymed & TYMED_HGLOBAL)) _dwData |= DTID_HIDA; if (fmte.cfFormat == g_cfEmbeddedObject && (fmte.tymed & TYMED_ISTORAGE)) _dwData |= DTID_EMBEDDEDOBJECT; if (fmte.cfFormat == g_cfFileContents && (fmte.tymed & (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE))) _dwData |= DTID_CONTENTS; if (fmte.cfFormat == g_cfFileGroupDescriptorA && (fmte.tymed & TYMED_HGLOBAL)) _dwData |= DTID_FDESCA; if (fmte.cfFormat == g_cfFileGroupDescriptorW && (fmte.tymed & TYMED_HGLOBAL)) _dwData |= DTID_FDESCW; if ((fmte.cfFormat == g_cfPreferredDropEffect) && (fmte.tymed & TYMED_HGLOBAL) && (DROPEFFECT_NONE != (_dwEffectPreferred = DataObj_GetDWORD(pDataObj, g_cfPreferredDropEffect, DROPEFFECT_NONE)))) { _dwData |= DTID_PREFERREDEFFECT; } #ifdef DEBUG TCHAR szFormat[MAX_PATH]; if (GetClipboardFormatName(fmte.cfFormat, szFormat, ARRAYSIZE(szFormat))) { TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %s, tymed %d", szFormat, fmte.tymed); } else { TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %d, tymed %d", fmte.cfFormat, fmte.tymed); } #endif // DEBUG } penum->Release(); } // HACK: // Win95 always did the GetData below which can be quite expensive if // the data is a directory structure on an ftp server etc. // dont check for FD_LINKUI if the data object has a preferred effect if ((_dwData & (DTID_PREFERREDEFFECT | DTID_CONTENTS)) == DTID_CONTENTS) { if (_dwData & DTID_FDESCA) { FORMATETC fmteRead = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (pDataObj->GetData(&fmteRead, &medium) == S_OK) { FILEGROUPDESCRIPTORA* pfgd = (FILEGROUPDESCRIPTORA*)GlobalLock(medium.hGlobal); if (pfgd) { if (pfgd->cItems >= 1) { if (pfgd->fgd[0].dwFlags & FD_LINKUI) _dwData |= DTID_FD_LINKUI; } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } else if (_dwData & DTID_FDESCW) { FORMATETC fmteRead = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (pDataObj->GetData(&fmteRead, &medium) == S_OK) { FILEGROUPDESCRIPTORW* pfgd = (FILEGROUPDESCRIPTORW*)GlobalLock(medium.hGlobal); if (pfgd) { if (pfgd->cItems >= 1) { if (pfgd->fgd[0].dwFlags & FD_LINKUI) _dwData |= DTID_FD_LINKUI; } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } } if (OleQueryCreateFromData(pDataObj) == S_OK) _dwData |= DTID_OLEOBJ; if (OleQueryLinkFromData(pDataObj) == S_OK) _dwData |= DTID_OLELINK; DebugMsg(DM_TRACE, TEXT("sh TR - CIDL::DragEnter this->dwData = %x"), _dwData); } // stash this away if (pdwEffect) _dwEffectLastReturned = *pdwEffect; DWORD dwDefault = _DetermineEffects(grfKeyState, pdwEffect, NULL); // The cursor always indicates the default action. ASSERT(pdwEffect); *pdwEffect = dwDefault; _dwEffectLastReturned = *pdwEffect; TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragEnter() _grfKeyStateLast=%#08lx, dwDefault=%#08lx", _grfKeyStateLast, dwDefault); return S_OK; } STDMETHODIMP CFSDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { ASSERT(pdwEffect); if (_grfKeyStateLast != grfKeyState) { _grfKeyStateLast = grfKeyState; DWORD dwDefault = _DetermineEffects(grfKeyState, pdwEffect, NULL); // The cursor always indicates the default action. *pdwEffect = dwDefault; _dwEffectLastReturned = *pdwEffect; } else { *pdwEffect = _dwEffectLastReturned; } TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragOver() this=%#08lx, _pdtobj=%#08lx, _grfKeyStateLast=%#08lx, *pdwEffect=%#08lx", this, _pdtobj, _grfKeyStateLast, *pdwEffect); return S_OK; } STDMETHODIMP CFSDropTarget::DragLeave() { TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragLeave() this=%#08lx, _grfKeyStateLast=%#08lx, _pdtobj=%#08lx", this, _grfKeyStateLast, _pdtobj); ATOMICRELEASE(_pdtobj); return S_OK; } // init data from our site that we will need in processing the drop void CFSDropTarget::_GetStateFromSite() { IShellFolderView* psfv; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_PPV_ARG(IShellFolderView, &psfv)))) { _fSameHwnd = S_OK == psfv->IsDropOnSource((IDropTarget*)this); _fDragDrop = S_OK == psfv->GetDropPoint(&_ptDrop); _fBkDropTarget = S_OK == psfv->IsBkDropTarget(NULL); psfv->Release(); } } STDMETHODIMP CFSDropTarget::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { HRESULT hr; BOOL fLinkOnly; // OLE may give us a different data object (fully marshalled) // from the one we've got on DragEnter (does not seem to happen on Win2k?) IUnknown_Set((IUnknown**)&_pdtobj, pDataObj); _GetStateFromSite(); // note, that on the drop the mouse buttons are not down so the grfKeyState // is not what we saw on the DragOver/DragEnter, thus we need to cache // the grfKeyState to detect left vs right drag // ASSERT(this->grfKeyStateLast == grfKeyState); HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_TEMPLATEDD); DWORD dwDefEffect = _DetermineEffects(grfKeyState, pdwEffect, hmenu); if (dwDefEffect == DROPEFFECT_NONE) { *pdwEffect = DROPEFFECT_NONE; DAD_SetDragImage(NULL, NULL); hr = S_OK; goto DragLeaveAndReturn; } // Get the hkeyProgID and hkeyBaseProgID HKEY hkeyBaseProgID, hkeyProgID; SHGetClassKey(_pFolder->_pidl, &hkeyProgID, &hkeyBaseProgID); TCHAR szPath[MAX_PATH]; _GetPath(szPath); // Set fLinkOnly if the only option is link and the source hasn't // explicitly told us that it wants only a link created. fLinkOnly = ((*pdwEffect == DROPEFFECT_LINK) && (!(_dwData & DTID_PREFERREDEFFECT) || (_dwEffectPreferred != DROPEFFECT_LINK))); // this doesn't actually do the menu if (grfKeyState MK_LBUTTON) FSDRAGDROPMENUPARAM ddm; ddm.dwDefEffect = dwDefEffect; ddm.pdtobj = pDataObj; ddm.pt = pt; ddm.pdwEffect = pdwEffect; ddm.hkeyProgID = hkeyProgID; ddm.hkeyBase = hkeyBaseProgID; ddm.hmenu = hmenu; ddm.grfKeyState = grfKeyState; hr = _DragDropMenu(&ddm); SHCloseClassKey(hkeyProgID); SHCloseClassKey(hkeyBaseProgID); DestroyMenu(hmenu); if (hr == S_FALSE) { // let callers know where this is about to go // SHScrap cares because it needs to close the file so we can copy/move it DataObj_SetDropTarget(pDataObj, &CLSID_ShellFSFolder); switch (ddm.idCmd) { case DDIDM_CONTENTS_DESKCOMP: hr = CreateDesktopComponents(NULL, pDataObj, _hwndOwner, 0, ddm.pt.x, ddm.pt.y); break; case DDIDM_CONTENTS_DESKURL: hr = _CreateURLDeskComp(ddm.pt.x, ddm.pt.y); break; case DDIDM_CONTENTS_DESKIMG: hr = _CreateDeskCompImage(ddm.pt); break; case DDIDM_CONTENTS_COPY: case DDIDM_CONTENTS_MOVE: case DDIDM_CONTENTS_LINK: hr = FS_AsyncCreateFileFromClip(_hwndOwner, szPath, pDataObj, pt, pdwEffect, _fBkDropTarget); break; case DDIDM_SCRAP_COPY: case DDIDM_SCRAP_MOVE: case DDIDM_DOCLINK: hr = FS_CreateBookMark(_hwndOwner, szPath, pDataObj, pt, pdwEffect); break; case DDIDM_OBJECT_COPY: case DDIDM_OBJECT_MOVE: { hr = _CreatePackage(); if (E_UNEXPECTED == hr) { // _CreatePackage() can only expand certain types of packages // back into files. For example, it doesn't handle CMDLINK files. // If _CreatePackage() didn't recognize the stream format, we fall // back to FS_CreateBookMark(), which should create a scrap: hr = FS_CreateBookMark(_hwndOwner, szPath, pDataObj, pt, pdwEffect); } break; } case DDIDM_COPY: case DDIDM_SYNCCOPY: case DDIDM_SYNCCOPYTYPE: case DDIDM_MOVE: case DDIDM_LINK: default: hr = _ZoneCheckDataObject(*pdwEffect); if (S_OK == hr) { FSTHREADPARAM* pfsthp = (FSTHREADPARAM*)LocalAlloc(LPTR, SIZEOF(FSTHREADPARAM)); if (pfsthp) { CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObj, &pfsthp->pstmDataObj); ASSERT(pfsthp->pDataObj == NULL); pfsthp->dwEffect = *pdwEffect; pfsthp->fLinkOnly = fLinkOnly; pfsthp->fSameHwnd = _fSameHwnd; pfsthp->fDragDrop = _fDragDrop; pfsthp->ptDrop = _ptDrop; pfsthp->fBkDropTarget = _fBkDropTarget; pfsthp->bSyncCopy = (DDIDM_SYNCCOPY == ddm.idCmd) || (DDIDM_SYNCCOPYTYPE == ddm.idCmd); pfsthp->idCmd = ddm.idCmd; pfsthp->pidl = ILClone(_pFolder->_pidl); pfsthp->grfKeyState = _grfKeyStateLast; pfsthp->hwndOwner = _hwndOwner; _GetPath(pfsthp->szPath); if (DataObj_CanGoAsync(pDataObj) || DataObj_GoAsyncForCompat(pDataObj)) { // create another thread to avoid blocking the source thread. if (SHCreateThread(FileDropTargetThreadProc, pfsthp, CTF_COINIT, NULL)) { hr = S_OK; } else { FreeFSThreadParam(pfsthp); // cleanup hr = E_OUTOFMEMORY; } } else { FileDropTargetThreadProc(pfsthp); // synchronously } } else hr = E_OUTOFMEMORY; } // in these CF_HDROP cases "Move" is always an optimized move, we delete the // source. make sure we don't return DROPEFFECT_MOVE so the source does not // try to do this too... // even if we have not done anything yet since we may have // kicked of a thread to do this DataObj_SetDWORD(pDataObj, g_cfLogicalPerformedDropEffect, *pdwEffect); if (DROPEFFECT_MOVE == *pdwEffect) *pdwEffect = DROPEFFECT_NONE; break; } } DragLeaveAndReturn: DragLeave(); TraceMsg(TF_DRAGDROP, "CFSDropTarget::Drop(pDataObj=%#08lx) this=%#08lx, _grfKeyStateLast=%#08lx, *pdwEffect=%#08lx, hr=%#08lx", _pdtobj, this, _grfKeyStateLast, *pdwEffect, hr); if (FAILED(hr)) *pdwEffect = DROPEFFECT_NONE; ASSERT(*pdwEffect == DROPEFFECT_COPY || *pdwEffect == DROPEFFECT_LINK || *pdwEffect == DROPEFFECT_MOVE || *pdwEffect == DROPEFFECT_NONE); return hr; } VOID CFSDropTarget::_AddVerbs(DWORD* pdwEffects, DWORD dwEffectAvail, DWORD dwDefEffect, UINT idCopy, UINT idMove, UINT idLink, DWORD dwForceEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); MENUITEMINFO mii; TCHAR szCmd[MAX_PATH]; if (NULL != pfsMenuInfo) { mii.cbSize = sizeof(mii); mii.dwTypeData = szCmd; mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; mii.fType = MFT_STRING; } if ((DROPEFFECT_COPY == (DROPEFFECT_COPY & dwEffectAvail)) && ((0 == (*pdwEffects & DROPEFFECT_COPY)) || (dwForceEffect & DROPEFFECT_COPY))) { ASSERT(0 != idCopy); if (NULL != pfsMenuInfo) { LoadString(HINST_THISDLL, idCopy + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd)); mii.fState = MFS_ENABLED | ((DROPEFFECT_COPY == dwDefEffect) ? MFS_DEFAULT : 0); mii.wID = idCopy; InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uCopyPos, TRUE, &mii); pfsMenuInfo->uCopyPos++; pfsMenuInfo->uMovePos++; pfsMenuInfo->uLinkPos++; } } if ((DROPEFFECT_MOVE == (DROPEFFECT_MOVE & dwEffectAvail)) && ((0 == (*pdwEffects & DROPEFFECT_MOVE)) || (dwForceEffect & DROPEFFECT_MOVE))) { ASSERT(0 != idMove); if (NULL != pfsMenuInfo) { LoadString(HINST_THISDLL, idMove + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd)); mii.fState = MFS_ENABLED | ((DROPEFFECT_MOVE == dwDefEffect) ? MFS_DEFAULT : 0); mii.wID = idMove; InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uMovePos, TRUE, &mii); pfsMenuInfo->uMovePos++; pfsMenuInfo->uLinkPos++; } } if ((DROPEFFECT_LINK == (DROPEFFECT_LINK & dwEffectAvail)) && ((0 == (*pdwEffects & DROPEFFECT_LINK)) || (dwForceEffect & DROPEFFECT_LINK))) { ASSERT(0 != idLink); if (NULL != pfsMenuInfo) { LoadString(HINST_THISDLL, idLink + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd)); mii.fState = MFS_ENABLED | ((DROPEFFECT_LINK == dwDefEffect) ? MFS_DEFAULT : 0); mii.wID = idLink; InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uLinkPos, TRUE, &mii); pfsMenuInfo->uLinkPos++; } } *pdwEffects |= dwEffectAvail; } DWORD CFSDropTarget::_GetStdDefEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect) { DWORD dwDefEffect = 0; // Alter the default effect depending on modifier keys. switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT)) { case MK_CONTROL: dwDefEffect = DROPEFFECT_COPY; break; case MK_SHIFT: dwDefEffect = DROPEFFECT_MOVE; break; case MK_SHIFT | MK_CONTROL: dwDefEffect = DROPEFFECT_LINK; break; case MK_ALT: dwDefEffect = 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) & dwAllEffectAvail; if (dwPreferred) { if (dwPreferred & DROPEFFECT_MOVE) { dwDefEffect = DROPEFFECT_MOVE; } else if (dwPreferred & DROPEFFECT_COPY) { dwDefEffect = DROPEFFECT_COPY; } else if (dwPreferred & DROPEFFECT_LINK) { dwDefEffect = DROPEFFECT_LINK; } } else { dwDefEffect = dwOrigDefEffect; } break; } return dwDefEffect & dwCurEffectAvail; } HRESULT CFSDropTarget::_GetDDInfoDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(_dwData & DTID_HDROP); ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()) { hr = IsDeskCompHDrop(_pdtobj); if (S_OK == hr) { DWORD dwDefEffect = 0; DWORD dwEffectAdd = dwEffectsAvail & DROPEFFECT_LINK; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK, // force add the DDIDM_CONTENTS_DESKCOMP verb pfsMenuInfo); } } return hr; } HRESULT CFSDropTarget::_GetDDInfoBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(_dwData & DTID_HDROP); ASSERT(pdwEffects); HRESULT hr = S_FALSE; // Is this the sneakernet case? ASSERT(_pdtobj); STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(_pdtobj, &medium); if (pida) { LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1); if (pidlParent) { TCHAR szTargetFolder[MAX_PATH]; _GetPath(szTargetFolder); if (IsFromSneakernetBriefcase(pidlParent, szTargetFolder)) { // Yes; show the non-default briefcase cm FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM mediumT; if (SUCCEEDED(_pdtobj->GetData(&fmte, &mediumT))) { BOOL fSyncCopyType = DroppingAnyFolders((HDROP)mediumT.hGlobal); DWORD dwDefEffect = 0; DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SYNCCOPY, 0, 0, 0, pfsMenuInfo); // Call _AddVerbs() again to force "Sync Copy of Type" as a 2nd DROPEFFECT_COPY verb: if (fSyncCopyType && (DROPEFFECT_COPY & dwEffectsAvail)) { _AddVerbs(pdwEffects, DROPEFFECT_COPY, 0, DDIDM_SYNCCOPYTYPE, 0, 0, DROPEFFECT_COPY, pfsMenuInfo); } ReleaseStgMedium(&mediumT); } } } HIDA_ReleaseStgMedium(pida, &medium); } return hr; } HRESULT CFSDropTarget::_GetDDInfoHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(_dwData & DTID_HDROP); ASSERT(pdwEffects); DWORD dwDefEffect = 0; DWORD dwEffectAdd = dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE); if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, _PickDefFSOperation(dwEffectAdd)); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_COPY, DDIDM_MOVE, 0, 0, pfsMenuInfo); return S_OK; } HRESULT CFSDropTarget::_GetDDInfoFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); if ((_dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) || (_dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW)) { DWORD dwEffectAdd, dwSuggestedEffect; // HACK: if there is a preferred drop effect and no HIDA // then just take the preferred effect as the available effects // this is because we didn't actually check the FD_LINKUI bit // back when we assembled dwData! (performance) if ((_dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) == DTID_PREFERREDEFFECT) { dwEffectAdd = _dwEffectPreferred; dwSuggestedEffect = _dwEffectPreferred; } else if (_dwData & DTID_FD_LINKUI) { dwEffectAdd = DROPEFFECT_LINK; dwSuggestedEffect = DROPEFFECT_LINK; } else { dwEffectAdd = DROPEFFECT_COPY | DROPEFFECT_MOVE; dwSuggestedEffect = DROPEFFECT_COPY; } dwEffectAdd &= dwEffectsAvail; DWORD dwDefEffect = 0; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, dwSuggestedEffect); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_CONTENTS_COPY, DDIDM_CONTENTS_MOVE, DDIDM_CONTENTS_LINK, 0, pfsMenuInfo); return S_OK; } else { return S_FALSE; } } HRESULT CFSDropTarget::_GetDDInfoHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(_dwData & DTID_HIDA); ASSERT(pdwEffects); DWORD dwDefEffect = 0; DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail; // NOTE: we only add a HIDA default effect if HDROP isn't going to add a default // effect. This preserves shell behavior with file system data objects without // requiring us to change the enumerator order in CIDLData. When we do change // the enumerator order, we can remove this special case: if ((NULL != pdwDefaultEffect) && ((0 == (_dwData & DTID_HDROP)) || (0 == _GetStdDefEffect(grfKeyFlags, dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE), dwEffectsAvail, _PickDefFSOperation(dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE)))))) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_LINK, 0, pfsMenuInfo); return S_OK; } HRESULT CFSDropTarget::_GetDDInfoOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(_dwData & DTID_EMBEDDEDOBJECT); ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (NULL != pdwDefaultEffect) { *pdwDefaultEffect = 0; } FORMATETC fmte = {g_cfObjectDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; ASSERT(NULL != _pdtobj); if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { // we've got an object descriptor OBJECTDESCRIPTOR* pOD = (OBJECTDESCRIPTOR*)GlobalLock(medium.hGlobal); if (pOD) { if (IsEqualCLSID(CLSID_OldPackage, pOD->clsid) || IsEqualCLSID(CLSID_CPackage, pOD->clsid)) { // This is a package - proceed DWORD dwDefEffect = 0; DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_OBJECT_COPY, DDIDM_OBJECT_MOVE, 0, 0, pfsMenuInfo); hr = S_OK; } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } return hr; } HRESULT CFSDropTarget::_GetDDInfoDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (NULL != pdwDefaultEffect) { *pdwDefaultEffect = 0; } if (!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()) { FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { //DANGER WILL ROBINSON: //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with // it as is it were ANSI. Find a way to escape the sequences... CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { CHAR szUrl[MAX_URL_STRING]; if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl))) { // The HTML contains an image tag - carry on... DWORD dwDefEffect = 0; DWORD dwEffectAdd = DROPEFFECT_LINK; // NOTE: ignoring dwEffectsAvail! if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail | DROPEFFECT_LINK, DROPEFFECT_LINK); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_CONTENTS_DESKIMG, 0, pfsMenuInfo); hr = S_OK; } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } return hr; } HRESULT CFSDropTarget::_GetDDInfoDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (NULL != pdwDefaultEffect) { *pdwDefaultEffect = 0; } if (!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()) { FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { // DANGER WILL ROBINSON: // HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with // it as is it were ANSI. Find a way to escape the sequences... CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { int nScheme = GetUrlSchemeA(pszData); if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP)) { // This is an internet scheme - carry on... DWORD dwDefEffect = 0; DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_CONTENTS_DESKURL, DROPEFFECT_LINK, // force add this verb pfsMenuInfo); hr = S_OK; } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } return hr; } HRESULT CFSDropTarget::_GetDDInfoOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (_dwData & DTID_OLEOBJ) { DWORD dwDefEffect = 0; DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SCRAP_COPY, DDIDM_SCRAP_MOVE, 0, 0, pfsMenuInfo); hr = S_OK; } return hr; } HRESULT CFSDropTarget::_GetDDInfoOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail, DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo) { ASSERT(pdwEffects); HRESULT hr = S_FALSE; if (_dwData & DTID_OLELINK) { DWORD dwDefEffect = 0; DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail; if (NULL != pdwDefaultEffect) { dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK); *pdwDefaultEffect = dwDefEffect; } _AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_DOCLINK, 0, pfsMenuInfo); hr = S_OK; } return hr; } HRESULT CFSDropTarget::_CreateURLDeskComp(int x, int y) { // This code should only be entered if DDIDM_CONTENTS_DESKURL was added to the menu, // and it has these checks: ASSERT(!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()); STGMEDIUM medium; FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hr = _pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hr)) { //DANGER WILL ROBINSON: //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with // it as is it were ANSI. Find a way to escape the sequences... CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { int nScheme = GetUrlSchemeA(pszData); if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP)) { // This is an internet scheme - URL hr = CreateDesktopComponents(pszData, NULL, _hwndOwner, DESKCOMP_URL, x, y); } GlobalUnlock(medium.hGlobal); } else { hr = E_FAIL; } ReleaseStgMedium(&medium); } return hr; } HRESULT CFSDropTarget::_CreateDeskCompImage(POINTL pt) { ASSERT(!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()); FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; HRESULT hres = _pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hres)) { //DANGER WILL ROBINSON: //HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with // it as is it were ANSI. Find a way to escape the sequences... CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { CHAR szUrl[MAX_URL_STRING]; if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl))) { // The HTML contains an image tag - carry on... ADDTODESKTOP* pToAD = (ADDTODESKTOP*)LocalAlloc(LPTR, sizeof(*pToAD)); if (pToAD) { pToAD->hwnd = _hwndOwner; lstrcpyA(pToAD->szUrl, szUrl); pToAD->dwFlags = DESKCOMP_IMAGE; pToAD->pt = pt; if (SHCreateThread(AddToActiveDesktopThreadProc, pToAD, CTF_COINIT, NULL)) { hres = NOERROR; } else { LocalFree(pToAD); hres = E_OUTOFMEMORY; } } else { hres = E_OUTOFMEMORY; } } else { hres = E_FAIL; } GlobalUnlock(medium.hGlobal); } else { hres = E_FAIL; } ReleaseStgMedium(&medium); } return hres; } // read byte by byte until we hit the null terminating char // return: the number of bytes read STDAPI StringReadFromStream(IStream* pstm, LPSTR pszBuf, UINT cchBuf) { UINT cch = 0; do { pstm->Read(pszBuf, sizeof(CHAR), NULL); cch++; } while (*pszBuf++ && cch <= cchBuf); return cch; } STDAPI CopyStreamToFile(IStream* pstmSrc, LPCTSTR pszFile) { IStream* pstmFile; HRESULT hr = SHCreateStreamOnFile(pszFile, OF_CREATE | OF_WRITE | OF_SHARE_DENY_WRITE, &pstmFile); if (SUCCEEDED(hr)) { hr = CopyStreamUI(pstmSrc, pstmFile, NULL); pstmFile->Release(); } return hr; } HRESULT CFSDropTarget::_CreatePackage() { ILockBytes* pLockBytes; HRESULT hr = CreateILockBytesOnHGlobal(NULL, TRUE, &pLockBytes); if (SUCCEEDED(hr)) { STGMEDIUM medium; medium.tymed = TYMED_ISTORAGE; hr = StgCreateDocfileOnILockBytes(pLockBytes, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &medium.pstg); if (SUCCEEDED(hr)) { FORMATETC fmte = {g_cfEmbeddedObject, NULL, DVASPECT_CONTENT, -1, TYMED_ISTORAGE}; ASSERT(NULL != _pdtobj); hr = _pdtobj->GetDataHere(&fmte, &medium); if (SUCCEEDED(hr)) { IStream* pstm; #ifdef DEBUG STATSTG stat; if (SUCCEEDED(medium.pstg->Stat(&stat, STATFLAG_NONAME))) { ASSERT(IsEqualCLSID(CLSID_OldPackage, stat.clsid) || IsEqualCLSID(CLSID_CPackage, stat.clsid)); } #endif // DEBUG #define PACKAGER_ICON 2 #define PACKAGER_CONTENTS L"\001Ole10Native" #define PACKAGER_EMBED_TYPE 3 hr = medium.pstg->OpenStream(PACKAGER_CONTENTS, 0, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pstm); if (SUCCEEDED(hr)) { DWORD dw; WORD w; CHAR szName[MAX_PATH]; CHAR szTemp[MAX_PATH]; if (SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // pkg size SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // pkg appearance (PACKAGER_ICON == w) && SUCCEEDED(StringReadFromStream(pstm, szName, ARRAYSIZE(szName))) && SUCCEEDED(StringReadFromStream(pstm, szTemp, ARRAYSIZE(szTemp))) && // icon path SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // icon index SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // panetype (PACKAGER_EMBED_TYPE == w) && SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // filename size SUCCEEDED(pstm->Read(szTemp, dw, NULL)) && // filename SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL))) // get file size { // The rest of the stream is the file contents TCHAR szPath[MAX_PATH], szBase[MAX_PATH], szDest[MAX_PATH]; _GetPath(szPath); SHAnsiToTChar(szName, szBase, ARRAYSIZE(szBase)); PathAppend(szPath, szBase); PathYetAnotherMakeUniqueName(szDest, szPath, NULL, szBase); TraceMsg(TF_GENERAL, "CFSIDLDropTarget pkg: %s", szDest); hr = CopyStreamToFile(pstm, szDest); if (SUCCEEDED(hr)) { SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szDest, NULL); if (_fBkDropTarget && _hwndOwner) { FS_PositionFileFromDrop(_hwndOwner, szDest, NULL); } } } else { hr = E_UNEXPECTED; } pstm->Release(); } } medium.pstg->Release(); } pLockBytes->Release(); } return hr; } HRESULT CFSDropTarget::_GetPath(LPTSTR pszPath) { return CFSFolder_GetPath(_pFolder, pszPath); } STDAPI_(BOOL) AllRegisteredPrograms(HDROP hDrop); // Returns: // If the data object does NOT contain HDROP -> "none" // else if the source is root or registered progam -> "link" // else if this is within a volume -> "move" // else if this is a briefcase -> "move" // else -> "copy" DWORD CFSDropTarget::_PickDefFSOperation(DWORD dwCurEffectAvail) { ASSERT(_pdtobj); DWORD dwDefEffect = 0; // assume no HDROP FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { TCHAR szPath[MAX_PATH], szFolder[MAX_PATH]; _GetPath(szFolder); DragQueryFile((HDROP)medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)); // focused item // Determine the default operation depending on the item. if (PathIsRoot(szPath) || AllRegisteredPrograms((HDROP)medium.hGlobal)) { dwDefEffect = DROPEFFECT_LINK; } else if (PathIsSameRoot(szPath, szFolder)) { dwDefEffect = DROPEFFECT_MOVE; } else if (IsBriefcaseRoot(_pdtobj)) { // briefcase default to move even accross volumes dwDefEffect = DROPEFFECT_MOVE; } else { dwDefEffect = DROPEFFECT_COPY; } ReleaseStgMedium(&medium); } else { // GetData failed. Let's see if QueryGetData failed or not. if (SUCCEEDED(_pdtobj->QueryGetData(&fmte))) { // this means this data object has HDROP but can't // provide it until it is dropped. Let's assume we are copying. dwDefEffect = DROPEFFECT_COPY; } } // Switch default verb if the dwCurEffectAvail hint suggests that we picked an // unavailable effect (this code applies to MOVE and COPY only): dwCurEffectAvail &= (DROPEFFECT_MOVE | DROPEFFECT_COPY); if ((DROPEFFECT_MOVE == dwDefEffect) && (DROPEFFECT_COPY == dwCurEffectAvail)) { // If we were going to return MOVE, and only COPY is available, return COPY: dwDefEffect = DROPEFFECT_COPY; } else if ((DROPEFFECT_COPY == dwDefEffect) && (DROPEFFECT_MOVE == dwCurEffectAvail)) { // If we were going to return COPY, and only MOVE is available, return MOVE: dwDefEffect = DROPEFFECT_MOVE; } return dwDefEffect; } HRESULT CFSDropTarget::_ZoneCheckDataObject(DWORD dwEffect) { ASSERT(NULL != _pdtobj); CHAR szUrl[INTERNET_MAX_URL_LENGTH]; szUrl[0] = 0; // Grab a URL and use it for the zone check if possible: if (!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_NOADDDESKCOMP) && _IsDesktopFolder()) { FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, sizeof(szUrl)); GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } else { fmte.cfFormat = g_cfShellURL; if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) { CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal); if (pszData) { lstrcpynA(szUrl, (LPCSTR)pszData, ARRAYSIZE(szUrl)); GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } } if (szUrl[0]) { return ZoneCheckUrlA(szUrl, URLACTION_SHELL_MOVE_OR_COPY, PUAF_FORCEUI_FOREGROUND | PUAF_WARN_IF_DENIED | PUAF_CHECK_TIFS, NULL); } else { return ZoneCheckHDrop(_pdtobj, dwEffect, URLACTION_SHELL_MOVE_OR_COPY, PUAF_FORCEUI_FOREGROUND | PUAF_WARN_IF_DENIED | PUAF_CHECK_TIFS, NULL); } } // make sure that the default effect is among the allowed effects DWORD CFSDropTarget::_LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed) { if (dwDefEffect & dwEffectsAllowed) return dwDefEffect; if (dwEffectsAllowed & DROPEFFECT_COPY) return DROPEFFECT_COPY; if (dwEffectsAllowed & DROPEFFECT_MOVE) return DROPEFFECT_MOVE; if (dwEffectsAllowed & DROPEFFECT_LINK) return DROPEFFECT_LINK; return DROPEFFECT_NONE; } typedef struct { HRESULT(CFSDropTarget::* pfnGetDragDropInfo)( IN FORMATETC* pfmte, IN DWORD grfKeyFlags, IN DWORD dwEffectsAvail, IN OUT DWORD* pdwEffectsUsed, OUT DWORD* pdwDefaultEffect, IN OUT FSMENUINFO* pfsMenuInfo); FORMATETC fmte; } FS_DATA_HANDLER; #define CFFMTE(cf, tymed) {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, tymed} // This function returns the default effect. // This function also modifies *pdwEffectInOut to indicate "available" operations. DWORD CFSDropTarget::_DetermineEffects(DWORD grfKeyState, DWORD* pdwEffectInOut, HMENU hmenu) { DWORD dwDefEffect = DROPEFFECT_NONE; DWORD dwEffectsUsed = DROPEFFECT_NONE; // NOTE: the order is important (particularly for multiple entries with the same FORMATETC) FS_DATA_HANDLER rg_data_handlers[] = { _GetDDInfoFileContents, CFFMTE(g_cfFileGroupDescriptorW, TYMED_HGLOBAL), _GetDDInfoFileContents, CFFMTE(g_cfFileGroupDescriptorA, TYMED_HGLOBAL), _GetDDInfoFileContents, CFFMTE(g_cfFileContents, TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE), _GetDDInfoBriefcase, CFFMTE(CF_HDROP, TYMED_HGLOBAL), _GetDDInfoHDROP, CFFMTE(CF_HDROP, TYMED_HGLOBAL), _GetDDInfoDeskCompHDROP, CFFMTE(CF_HDROP, TYMED_HGLOBAL), _GetDDInfoHIDA, CFFMTE(g_cfHIDA, TYMED_HGLOBAL), _GetDDInfoOlePackage, CFFMTE(g_cfEmbeddedObject, TYMED_ISTORAGE), _GetDDInfoDeskImage, CFFMTE(g_cfHTML, TYMED_HGLOBAL), _GetDDInfoDeskComp, CFFMTE(g_cfShellURL, TYMED_HGLOBAL), _GetDDInfoOleObj, CFFMTE(0, TYMED_HGLOBAL), _GetDDInfoOleLink, CFFMTE(0, TYMED_HGLOBAL), }; // Loop through formats, factoring in both the order of the enumerator and // the order of our rg_data_handlers to determine the default effect // (and possibly, to create the drop context menu) FSMENUINFO fsmi = {hmenu, 0, 0, 0}; IEnumFORMATETC* penum; AssertMsg((NULL != _pdtobj), TEXT("CFSDropTarget::_DetermineEffects() _pdtobj is NULL but we need it. this=%#08lx"), this); if (_pdtobj && SUCCEEDED(_pdtobj->EnumFormatEtc(DATADIR_GET, &penum))) { FORMATETC fmte; ULONG celt; while (penum->Next(1, &fmte, &celt) == S_OK) { for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++) { if (rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat && rg_data_handlers[i].fmte.dwAspect == fmte.dwAspect && (0 != (rg_data_handlers[i].fmte.tymed & fmte.tymed))) { (this->*(rg_data_handlers[i].pfnGetDragDropInfo))( &fmte, grfKeyState, *pdwEffectInOut, &dwEffectsUsed, (DROPEFFECT_NONE == dwDefEffect) ? &dwDefEffect : NULL, (NULL != hmenu) ? &fsmi : NULL); } } } penum->Release(); } // Loop through the rg_data_handlers that don't have an associated clipboard format last for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++) { if (0 == rg_data_handlers[i].fmte.cfFormat) { (this->*(rg_data_handlers[i].pfnGetDragDropInfo))( NULL, grfKeyState, *pdwEffectInOut, &dwEffectsUsed, (DROPEFFECT_NONE == dwDefEffect) ? &dwDefEffect : NULL, (NULL != hmenu) ? &fsmi : NULL); } } *pdwEffectInOut &= dwEffectsUsed; dwDefEffect = _LimitDefaultEffect(dwDefEffect, *pdwEffectInOut); DebugMsg(TF_FSTREE, TEXT("CFSDT::GetDefaultEffect dwDef=%x, dwEffUsed=%x, *pdw=%x"), dwDefEffect, dwEffectsUsed, *pdwEffectInOut); return dwDefEffect; } struct FS_EFFECT { UINT uID; DWORD dwEffect; }; // This is used to map command id's back to dropeffect's: const FS_EFFECT c_IDFSEffects[] = { DDIDM_COPY, DROPEFFECT_COPY, DDIDM_MOVE, DROPEFFECT_MOVE, DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK, DDIDM_LINK, DROPEFFECT_LINK, DDIDM_SCRAP_COPY, DROPEFFECT_COPY, DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE, DDIDM_DOCLINK, DROPEFFECT_LINK, DDIDM_CONTENTS_COPY, DROPEFFECT_COPY, DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE, DDIDM_CONTENTS_LINK, DROPEFFECT_LINK, DDIDM_CONTENTS_DESKIMG, DROPEFFECT_LINK, DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important) DDIDM_SYNCCOPY, DROPEFFECT_COPY, DDIDM_OBJECT_COPY, DROPEFFECT_COPY, DDIDM_OBJECT_MOVE, DROPEFFECT_MOVE, DDIDM_CONTENTS_DESKURL, DROPEFFECT_LINK, }; HRESULT CFSDropTarget::_DragDropMenu(FSDRAGDROPMENUPARAM* pddm) { HRESULT hres = E_OUTOFMEMORY; // assume error DWORD dwEffectOut = 0; // assume no-ope. if (pddm->hmenu) { int nItem; UINT idCmd; UINT idCmdFirst = DDIDM_EXTFIRST; HDXA hdxa = HDXA_Create(); HDCA hdca = DCA_Create(); if (hdxa && hdca) { // BUGBUG (toddb): Even though pddm->hkeyBase does not have the same value as // pddm->hkeyProgID they can both be the same registry key (HKCR\FOLDER, for example). // As a result we sometimes enumerate this key twice looking for the same data. As // this is sometimes a slow operation we should avoid this. The comparision // done below was never valid on NT and might not be valid on win9x. // Add extended menu for "Base" class. if (pddm->hkeyBase && pddm->hkeyBase != pddm->hkeyProgID) DCA_AddItemsFromKey(hdca, pddm->hkeyBase, STRREG_SHEX_DDHANDLER); // Enumerate the DD handlers and let them append menu items. if (pddm->hkeyProgID) DCA_AddItemsFromKey(hdca, pddm->hkeyProgID, STRREG_SHEX_DDHANDLER); idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, 1, &pddm->hkeyProgID, _pFolder->_pidl, pddm->hmenu, 0, DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca); } // If this dragging is caused by the left button, simply choose // the default one, otherwise, pop up the context menu. If there // is no key state info and the original effect is the same as the // current effect, choose the default one, otherwise pop up the // context menu. if ((_grfKeyStateLast & MK_LBUTTON) || (!_grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect))) { idCmd = GetMenuDefaultItem(pddm->hmenu, MF_BYCOMMAND, 0); // This one MUST be called here. Please read its comment block. DAD_DragLeave(); if (_hwndOwner) SetForegroundWindow(_hwndOwner); } else { // Note that SHTrackPopupMenu calls DAD_DragLeave(). idCmd = SHTrackPopupMenu(pddm->hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pddm->pt.x, pddm->pt.y, 0, _hwndOwner, NULL); } // We also need to call this here to release the dragged image. DAD_SetDragImage(NULL, NULL); // Check if the user selected one of add-in menu items. if (idCmd == 0) { hres = S_OK; // Canceled by the user, return S_OK } else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST)) { // Yes. Let the context menu handler process it. CMINVOKECOMMANDINFOEX ici = { SIZEOF(CMINVOKECOMMANDINFOEX), 0L, _hwndOwner, (LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST), NULL, NULL, SW_NORMAL, }; // record if the shift/control keys were down at the time of the drop if (_grfKeyStateLast & MK_SHIFT) { ici.fMask |= CMIC_MASK_SHIFT_DOWN; } if (_grfKeyStateLast & MK_CONTROL) { ici.fMask |= CMIC_MASK_CONTROL_DOWN; } // We may not want to ignore the error code. (Can happen when you use the context menu // to create new folders, but I don't know if that can happen here.). HDXA_LetHandlerProcessCommandEx(hdxa, &ici, NULL); hres = S_OK; } else { for (nItem = 0; nItem < ARRAYSIZE(c_IDFSEffects); ++nItem) { if (idCmd == c_IDFSEffects[nItem].uID) { dwEffectOut = c_IDFSEffects[nItem].dwEffect; break; } } // if hmenuReplace had menu commands other than DDIDM_COPY, // DDIDM_MOVE, DDIDM_LINK, and that item was selected, // this assert will catch it. (dwEffectOut is 0 in this case) ASSERT(nItem < ARRAYSIZE(c_IDFSEffects)); hres = S_FALSE; } if (hdca) DCA_Destroy(hdca); if (hdxa) HDXA_Destroy(hdxa); pddm->idCmd = idCmd; } *(pddm->pdwEffect) = dwEffectOut; return hres; }