Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

660 lines
22 KiB

#include "stdafx.h"
#pragma hdrstop
#include "datautil.h"
#include "_security.h"
#include <urlmon.h>
#define COPYMOVETO_REGKEY TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer")
#define COPYMOVETO_SUBKEY TEXT("CopyMoveTo")
#define COPYMOVETO_VALUE TEXT("LastFolder")
class CCopyMoveToMenu : public IContextMenu3
, public IShellExtInit
, public CObjectWithSite
, public IFolderFilter
{
public:
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
STDMETHOD_(ULONG,AddRef)(void);
STDMETHOD_(ULONG,Release)(void);
// IContextMenu
STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
// IContextMenu2
STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
// IContextMenu3
STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
// IShellExtInit
STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
// IFolderFilter
STDMETHODIMP ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem);
STDMETHODIMP GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags);
private:
BOOL m_bMoveTo;
LONG m_cRef;
HMENU m_hmenu;
UINT m_idCmdFirst;
BOOL m_bFirstTime;
LPITEMIDLIST m_pidlSource;
IDataObject * m_pdtobj;
CCopyMoveToMenu(BOOL bMoveTo = FALSE);
~CCopyMoveToMenu();
HRESULT _DoDragDrop(LPCMINVOKECOMMANDINFO pici, LPCITEMIDLIST pidlFolder);
void _GenerateDialogTitle(LPTSTR szTitle, int nBuffer);
friend HRESULT CCopyToMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut);
friend HRESULT CMoveToMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut);
};
CCopyMoveToMenu::CCopyMoveToMenu(BOOL bMoveTo) : m_cRef(1), m_bMoveTo(bMoveTo)
{
DllAddRef();
// Assert that the member variables are zero initialized during construction
ASSERT(!m_pidlSource);
}
CCopyMoveToMenu::~CCopyMoveToMenu()
{
Pidl_Set(&m_pidlSource, NULL);
ATOMICRELEASE(m_pdtobj);
DllRelease();
}
HRESULT CCopyToMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
{
CCopyMoveToMenu *pcopyto = new CCopyMoveToMenu();
if (pcopyto)
{
HRESULT hres = pcopyto->QueryInterface(riid, ppvOut);
pcopyto->Release();
return hres;
}
*ppvOut = NULL;
return E_OUTOFMEMORY;
}
HRESULT CMoveToMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
{
CCopyMoveToMenu *pmoveto = new CCopyMoveToMenu(TRUE);
if (pmoveto)
{
HRESULT hres = pmoveto->QueryInterface(riid, ppvOut);
pmoveto->Release();
return hres;
}
*ppvOut = NULL;
return E_OUTOFMEMORY;
}
HRESULT CCopyMoveToMenu::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CCopyMoveToMenu, IContextMenu3),
QITABENTMULTI(CCopyMoveToMenu, IContextMenu, IContextMenu3),
QITABENTMULTI(CCopyMoveToMenu, IContextMenu2, IContextMenu3),
QITABENT(CCopyMoveToMenu, IShellExtInit),
QITABENT(CCopyMoveToMenu, IObjectWithSite),
QITABENT(CCopyMoveToMenu, IFolderFilter),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CCopyMoveToMenu::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG CCopyMoveToMenu::Release()
{
ASSERT( 0 != m_cRef );
ULONG cRef = InterlockedDecrement(&m_cRef);
if ( 0 == cRef )
{
delete this;
}
return cRef;
}
HRESULT CCopyMoveToMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
// if they want the default menu only (CMF_DEFAULTONLY) OR
// this is being called for a shortcut (CMF_VERBSONLY)
// we don't want to be on the context menu
if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
return NOERROR;
UINT idCmd = idCmdFirst;
TCHAR szMenuItem[80];
m_idCmdFirst = idCmdFirst;
LoadString(g_hinst, m_bMoveTo? IDS_CMTF_MOVETO: IDS_CMTF_COPYTO, szMenuItem, ARRAYSIZE(szMenuItem));
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmd++, szMenuItem);
return ResultFromShort(idCmd-idCmdFirst);
}
struct BROWSEINFOINITSTRUCT
{
LPITEMIDLIST *ppidl;
BOOL bMoveTo;
IDataObject *pdtobj;
CCopyMoveToMenu *pCMTM;
LPITEMIDLIST *ppidlSource;
};
int BrowseCallback(HWND hwnd, UINT msg, LPARAM lParam, LPARAM lpData)
{
int idResource = 0;
switch (msg)
{
case BFFM_IUNKNOWN:
// Try to get an IFolderFilterSite from the lParam, so we can set ourselves as the filter.
if (lParam)
{
IFolderFilterSite *pFilterSite;
HRESULT hr = ((IUnknown*)lParam)->QueryInterface(IID_PPV_ARG(IFolderFilterSite, &pFilterSite));
if (SUCCEEDED(hr))
{
IUnknown *pUnk = NULL;
if (SUCCEEDED(((BROWSEINFOINITSTRUCT *)lpData)->pCMTM->QueryInterface(IID_PPV_ARG(IUnknown, &pUnk))))
{
pFilterSite->SetFilter(pUnk);
pUnk->Release();
}
pFilterSite->Release();
}
}
break;
case BFFM_INITIALIZED:
{
BROWSEINFOINITSTRUCT* pbiis = (BROWSEINFOINITSTRUCT*)lpData;
// Set the caption. ('Select a destination')
TCHAR szTitle[100];
if (LoadString(g_hinst, pbiis->bMoveTo ? IDS_CMTF_CAPTION_MOVE : IDS_CMTF_CAPTION_COPY, szTitle, ARRAYSIZE(szTitle)))
{
SetWindowText(hwnd, szTitle);
}
// Set the text of the Ok Button.
SendMessage(hwnd, BFFM_SETOKTEXT, 0, (LPARAM)MAKEINTRESOURCE((pbiis->bMoveTo) ? IDS_MOVE : IDS_COPY));
// Set My Computer expanded.
// NOTE: If IShellNameSpace is made public, we can get this from IObjectWithSite on the IUnknown
// passed to us by BFFM_IUNKNOWN. Then we can call Expand() on IShellNameSpace instead.
LPITEMIDLIST pidlMyComputer;
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
if (SUCCEEDED(hr))
{
SendMessage(hwnd, BFFM_SETEXPANDED, FALSE, (LPARAM)pidlMyComputer);
ILFree(pidlMyComputer);
}
// Set the default selected pidl
SendMessage(hwnd, BFFM_SETSELECTION, FALSE, (LPARAM)*(((BROWSEINFOINITSTRUCT *)lpData)->ppidl));
break;
}
case BFFM_VALIDATEFAILEDW:
idResource = IDS_PathNotFoundW;
// FALL THRU...
case BFFM_VALIDATEFAILEDA:
if (0 == idResource) // Make sure we didn't come from BFFM_VALIDATEFAILEDW
idResource = IDS_PathNotFoundA;
ShellMessageBox(g_hinst, hwnd,
MAKEINTRESOURCE(idResource),
MAKEINTRESOURCE(IDS_CMTF_COPYORMOVE_DLG_TITLE),
MB_OK|MB_ICONERROR, (LPVOID)lParam);
return 1; // 1:leave dialog up for another try...
/*NOTREACHED*/
case BFFM_SELCHANGED:
if (lParam)
{
// Here, during a move operation, we want to disable the move (ok) button when the destination
// folder is the same as the source.
// During a move or copy operation, we want to disable the move/copy (ok) button when the
// destination is not a drop target.
// In all other cases, we enable the ok/move/copy button.
BROWSEINFOINITSTRUCT *pbiis = (BROWSEINFOINITSTRUCT *)lpData;
if (pbiis)
{
BOOL bEnableOK = FALSE;
IShellFolder *psf;
if ((!pbiis->bMoveTo || !ILIsEqual(*pbiis->ppidlSource, (LPITEMIDLIST)lParam)) &&
(SUCCEEDED(SHBindToObjectEx(NULL, (LPITEMIDLIST)lParam, NULL, IID_PPV_ARG(IShellFolder, &psf)))))
{
IDropTarget *pdt;
if (SUCCEEDED(psf->CreateViewObject(hwnd, IID_PPV_ARG(IDropTarget, &pdt))))
{
POINTL pt = {0, 0};
DWORD dwEffect;
DWORD grfKeyState;
if (pbiis->bMoveTo)
{
dwEffect = DROPEFFECT_MOVE;
grfKeyState = MK_SHIFT | MK_LBUTTON;
}
else
{
dwEffect = DROPEFFECT_COPY;
grfKeyState = MK_CONTROL | MK_LBUTTON;
}
if (SUCCEEDED(pdt->DragEnter(pbiis->pdtobj, grfKeyState, pt, &dwEffect)))
{
if (dwEffect)
{
bEnableOK = TRUE;
}
pdt->DragLeave();
}
pdt->Release();
}
psf->Release();
}
SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM)bEnableOK);
}
}
break;
}
return 0;
}
HRESULT CCopyMoveToMenu::_DoDragDrop(LPCMINVOKECOMMANDINFO pici, LPCITEMIDLIST pidlFolder)
{
// This should always succeed because the caller (SHBrowseForFolder) should
// have weeded out the non-folders.
IShellFolder *psf;
HRESULT hr = SHBindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr))
{
IDropTarget *pdrop;
hr = psf->CreateViewObject(pici->hwnd, IID_PPV_ARG(IDropTarget, &pdrop));
if (SUCCEEDED(hr)) // Will fail for some targets. (Like Nethood->Entire Network)
{
DWORD grfKeyState;
DWORD dwEffect;
if (m_bMoveTo)
{
grfKeyState = MK_SHIFT | MK_LBUTTON;
dwEffect = DROPEFFECT_MOVE;
}
else
{
grfKeyState = MK_CONTROL | MK_LBUTTON;
dwEffect = DROPEFFECT_COPY;
}
hr = SimulateDropWithPasteSucceeded(pdrop, m_pdtobj, grfKeyState, NULL, dwEffect, NULL, FALSE);
pdrop->Release();
}
psf->Release();
}
if (FAILED_AND_NOT_CANCELED(hr))
{
// Go modal during the UI.
IUnknown_EnableModless(_punkSite, FALSE);
ShellMessageBox(g_hinst, pici->hwnd, MAKEINTRESOURCE(IDS_CMTF_ERRORMSG),
MAKEINTRESOURCE(IDS_CABINET), MB_OK|MB_ICONEXCLAMATION);
IUnknown_EnableModless(_punkSite, TRUE);
}
return hr;
}
void CCopyMoveToMenu::_GenerateDialogTitle(LPTSTR szTitle, int nBuffer)
{
szTitle[0] = 0;
if (m_pdtobj)
{
int nItemCount = DataObj_GetHIDACount(m_pdtobj);
TCHAR szDescription[200];
if (nItemCount > 1)
{
DWORD_PTR rg[1];
rg[0] = (DWORD_PTR)nItemCount;
// More than one item is selected. Don't bother listing all items.
DWORD dwMessageId = m_bMoveTo ? IDS_CMTF_MOVE_MULTIPLE_DLG_TITLE2 : IDS_CMTF_COPY_MULTIPLE_DLG_TITLE2;
if (LoadString(g_hinst, dwMessageId, szDescription, ARRAYSIZE(szDescription)) > 0)
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szDescription, 0, 0, szTitle, nBuffer, (va_list*)rg);
}
else if (nItemCount == 1)
{
// We have only one item selected. Use its name.
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(m_pdtobj, &medium);
if (pida)
{
LPITEMIDLIST pidlFull = IDA_FullIDList(pida, 0);
if (pidlFull)
{
TCHAR szItemName[MAX_PATH];
HRESULT hres = SHGetNameAndFlags(pidlFull, SHGDN_INFOLDER, szItemName, ARRAYSIZE(szItemName), NULL);
if (SUCCEEDED(hres))
{
DWORD_PTR rg[1];
rg[0] = (DWORD_PTR)szItemName;
DWORD dwMessageId = m_bMoveTo ? IDS_CMTF_MOVE_DLG_TITLE2 : IDS_CMTF_COPY_DLG_TITLE2;
if (LoadString(g_hinst, dwMessageId, szDescription, ARRAYSIZE(szDescription)))
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szDescription, 0, 0, szTitle, nBuffer, (va_list*)rg);
}
ILFree(pidlFull);
}
HIDA_ReleaseStgMedium(pida, &medium);
}
}
else
{
// no HIDA, just default to something.
DWORD dwMessageId = m_bMoveTo ? IDS_CMTF_MOVE_DLG_TITLE : IDS_CMTF_COPY_DLG_TITLE;
LoadString(g_hinst, dwMessageId, szTitle, nBuffer);
}
}
}
/**
* Determines if the pidl still exists. If it does not, if frees it
* and replaces it with a My Documents pidl
*/
void _BFFSwitchToMyDocsIfPidlNotExist(LPITEMIDLIST *ppidl)
{
IShellFolder *psf;
LPCITEMIDLIST pidlChild;
if (SUCCEEDED(SHBindToIDListParent(*ppidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
{
DWORD dwAttr = SFGAO_VALIDATE;
if (FAILED(psf->GetAttributesOf(1, &pidlChild, &dwAttr)))
{
// This means the pidl no longer exists.
// Use my documents instead.
LPITEMIDLIST pidlMyDocs;
if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidlMyDocs)))
{
// Good. Now we can get rid of the old pidl and use this one.
ILFree(*ppidl);
*ppidl = pidlMyDocs;
}
}
psf->Release();
}
}
HRESULT CCopyMoveToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
HRESULT hres;
if (m_pdtobj)
{
HKEY hkey = NULL;
IStream *pstrm = NULL;
LPITEMIDLIST pidlSelectedFolder = NULL;
LPITEMIDLIST pidlFolder = NULL;
TCHAR szTitle[MAX_PATH + 200];
BROWSEINFOINITSTRUCT biis =
{ // passing the address of pidl because it is not init-ed yet
// but it will be before call to SHBrowseForFolder so save one assignment
&pidlSelectedFolder,
m_bMoveTo,
m_pdtobj,
this,
&m_pidlSource
};
BROWSEINFO bi =
{
pici->hwnd,
NULL,
NULL,
szTitle,
BIF_VALIDATE | BIF_NEWDIALOGSTYLE | BIF_UAHINT | BIF_NOTRANSLATETARGETS,
BrowseCallback,
(LPARAM)&biis
};
_GenerateDialogTitle(szTitle, ARRAYSIZE(szTitle));
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, COPYMOVETO_REGKEY, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkey))
{
pstrm = OpenRegStream(hkey, COPYMOVETO_SUBKEY, COPYMOVETO_VALUE, STGM_READWRITE);
if (pstrm) // OpenRegStream will fail if the reg key is empty.
ILLoadFromStream(pstrm, &pidlSelectedFolder);
// This will switch the pidl to My Docs if the pidl does not exist.
// This prevents us from having My Computer as the default (that's what happens if our
// initial set selected call fails).
// Note: ideally, we would check in BFFM_INITIALIZED, if our BFFM_SETSELECTION failed
// then do a BFFM_SETSELECTION on My Documents instead. However, BFFM_SETSELECTION always
// returns zero (it's doc'd to do this to, so we can't change). So we do the validation
// here instead. There is still a small chance that this folder will be deleted in between our
// check here, and when we call BFFM_SETSELECTION, but oh well.
_BFFSwitchToMyDocsIfPidlNotExist(&pidlSelectedFolder);
}
// Go modal during the UI.
IUnknown_EnableModless(_punkSite, FALSE);
pidlFolder = SHBrowseForFolder(&bi);
IUnknown_EnableModless(_punkSite, TRUE);
if (pidlFolder)
{
hres = _DoDragDrop(pici, pidlFolder);
}
else
hres = E_FAIL;
if (pstrm)
{
if (S_OK == hres)
{
TCHAR szFolder[MAX_PATH];
if (SUCCEEDED(SHGetNameAndFlags(pidlFolder, SHGDN_FORPARSING, szFolder, ARRAYSIZE(szFolder), NULL))
&& !PathIsRemote(szFolder))
{
ULARGE_INTEGER uli;
// rewind the stream to the beginning so that when we
// add a new pidl it does not get appended to the first one
pstrm->Seek(g_li0, STREAM_SEEK_SET, &uli);
ILSaveToStream(pstrm, pidlFolder);
#if DEBUG
// pfortier 3/23/01:
// We've been seeing a problem where the result of this is the My Computer folder.
// Since we can never copy there, that doesn't make any sense.
// ASSERT that this isn't true!
LPITEMIDLIST pidlMyComputer;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer)))
{
ASSERTMSG(!ILIsEqual(pidlMyComputer, pidlFolder), "SHBrowseForFolder returned My Computer as a copyto destination!");
ILFree(pidlMyComputer);
}
#endif
}
}
pstrm->Release();
}
if (hkey)
{
RegCloseKey(hkey);
}
ILFree(pidlFolder); // ILFree() works for NULL pidls.
ILFree(pidlSelectedFolder); // ILFree() works for NULL pidls.
}
else
hres = E_INVALIDARG;
return hres;
}
HRESULT CCopyMoveToMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
{
return E_NOTIMPL;
}
HRESULT CCopyMoveToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
}
HRESULT CCopyMoveToMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
HRESULT hr = S_OK;
switch(uMsg)
{
//case WM_INITMENUPOPUP:
// break;
case WM_DRAWITEM:
{
DRAWITEMSTRUCT * pdi = (DRAWITEMSTRUCT *)lParam;
if (pdi->CtlType == ODT_MENU && pdi->itemID == m_idCmdFirst)
{
FileMenu_DrawItem(NULL, pdi);
}
break;
}
case WM_MEASUREITEM:
{
MEASUREITEMSTRUCT *pmi = (MEASUREITEMSTRUCT *)lParam;
if (pmi->CtlType == ODT_MENU && pmi->itemID == m_idCmdFirst)
{
FileMenu_MeasureItem(NULL, pmi);
}
break;
}
default:
hr = E_NOTIMPL;
break;
}
if (plres)
*plres = 0;
return hr;
}
HRESULT CCopyMoveToMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
{
HRESULT hres = S_OK;
if (!pdtobj)
return E_INVALIDARG;
IUnknown_Set((IUnknown **) &m_pdtobj, (IUnknown *) pdtobj);
ASSERT(m_pdtobj);
// (jeffreys) pidlFolder is now NULL when pdtobj is non-NULL
// See comments above the call to HDXA_AppendMenuItems2 in
// defcm.cpp!CDefFolderMenu::QueryContextMenu. Raid #232106
if (!pidlFolder)
{
hres = PidlFromDataObject(m_pdtobj, &m_pidlSource);
if (SUCCEEDED(hres))
{
// Make it the parent pidl of this pidl
if (!ILRemoveLastID(m_pidlSource))
{
hres = E_INVALIDARG;
}
}
}
else if (!Pidl_Set(&m_pidlSource, pidlFolder))
{
hres = E_OUTOFMEMORY;
}
return hres;
}
HRESULT CCopyMoveToMenu::ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
{
LPITEMIDLIST pidlNotShown;
HRESULT hr = S_OK;
LPITEMIDLIST pidlFolderActual; // Why is pidlFolder is NULL???
if (SUCCEEDED(SHGetIDListFromUnk(psf, &pidlFolderActual)))
{
LPITEMIDLIST pidlFull = ILCombine(pidlFolderActual, pidlItem);
if (pidlFull)
{
// Filter out control panel and recycle bin.
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidlNotShown)))
{
if (ILIsEqual(pidlFull, pidlNotShown))
hr = S_FALSE;
ILFree(pidlNotShown);
}
if ((hr == S_OK) && (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_BITBUCKET, &pidlNotShown))))
{
if (ILIsEqual(pidlFull, pidlNotShown))
hr = S_FALSE;
ILFree(pidlNotShown);
}
ILFree(pidlFull);
}
ILFree(pidlFolderActual);
}
return hr;
}
HRESULT CCopyMoveToMenu::GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags)
{
// Only want drop targets - this doesn't appear to work.
*pgrfFlags |= SFGAO_DROPTARGET;
return S_OK;
}