Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2029 lines
62 KiB

#include "shellprv.h"
#pragma hdrstop
#ifdef CAIRO_DS
#include "dsdata.h"
#endif
// #define FULL_DEBUG
TCHAR const c_szMayChangeDefault[] = TEXT("CLSID\\%s\\shellex\\MayChangeDefaultMenu");
//=============================================================================
// CDefFolderMenu class
//=============================================================================
extern IContextMenu2Vtbl c_CDefFolderMenuVtbl; // forward
#pragma warning(disable: 4200) // zero-sized array in struct
#define GetFldFirst(this) (this->idDefMax + this->idCmdFirst)
typedef struct _CDefFolderMenu // deffm
{
IContextMenu2 cm; // IContextMenu base class
UINT cRef; // Reference count
HWND hwndOwner; // Owner window
LPSHELLFOLDER psf; // Shell folder
LPDROPTARGET pdtgt; // Drop target of selected item
LPFNDFMCALLBACK lpfn; // Callback
UINT idCmdFirst; // base id
UINT idDefMax; // Default object commands ID MAX
UINT idFldMax; // Folder command ID MAX
UINT idVerbMax; // Add-in command (verbs) ID MAX
UINT idStaticMax; // Static menu items.
UINT idFld2Max; // 2nd range of Folder command ID MAX
HDSA hdsaStatics; // For static menu items.
HDXA hdxa; // Dynamic menu array
LPDATAOBJECT pdtobj; // Data object
BOOL bUnderKeys; // Data is directly under key, not
// shellex\ContextMenuHandlers
UINT nKeys; // Number of class keys
HKEY hkeyClsKeys[]; // Class keys
} CDefFolderMenu, *LPDEFFOLDERMENU;
STDAPI CDefFolderMenu_CreateHKeyMenu(HWND hwndOwner, HKEY hkey, LPCONTEXTMENU *ppcm)
{
HRESULT hres = E_OUTOFMEMORY;
LPDEFFOLDERMENU pdeffm = (void*)LocalAlloc(LPTR, SIZEOF(CDefFolderMenu) + SIZEOF(HKEY));
if (pdeffm)
{
pdeffm->cm.lpVtbl = &c_CDefFolderMenuVtbl;
pdeffm->cRef = 0;
pdeffm->hwndOwner = hwndOwner;
pdeffm->hdxa = HDXA_Create();
if (pdeffm->hdxa)
{
if (hkey) {
RegOpenKeyEx(hkey, NULL, 0L, MAXIMUM_ALLOWED, &pdeffm->hkeyClsKeys[0]);
pdeffm->nKeys = 1;
pdeffm->bUnderKeys = TRUE;
}
*ppcm = (IContextMenu *)&pdeffm->cm;
pdeffm->cRef = 1;
}
hres = NOERROR;
}
return hres;
}
ULONG CDefFolderMenu_Destroy(CDefFolderMenu *this);
STDAPI CDefFolderMenu_Create2(LPCITEMIDLIST pidlFolder,
HWND hwndOwner,
UINT cidl, LPCITEMIDLIST *apidl,
LPSHELLFOLDER psf,
LPFNDFMCALLBACK lpfn,
UINT nKeys,
const HKEY *ahkeyClsKeys,
LPCONTEXTMENU *ppcm)
{
HRESULT hres = E_OUTOFMEMORY;
LPDEFFOLDERMENU pdeffm = (void*)LocalAlloc(LPTR, SIZEOF(CDefFolderMenu)+nKeys*SIZEOF(HKEY));
if (pdeffm)
{
pdeffm->cm.lpVtbl = &c_CDefFolderMenuVtbl;
pdeffm->cRef = 0;
pdeffm->hwndOwner = hwndOwner;
pdeffm->lpfn = lpfn;
pdeffm->psf = psf;
if (psf) {
psf->lpVtbl->AddRef(psf);
}
// Is anything selected?
if (cidl)
{
// Yes.
pdeffm->hdxa = HDXA_Create();
if (pdeffm->hdxa)
{
hres = psf->lpVtbl->GetUIObjectOf(psf,
hwndOwner, cidl, apidl,
&IID_IDataObject, NULL, &pdeffm->pdtobj);
if (SUCCEEDED(hres))
{
UINT nCurKey, nTotKeys = 0;
for (nCurKey = 0; nCurKey < nKeys; ++nCurKey)
{
HKEY hkeyClsKey = ahkeyClsKeys[nCurKey];
UINT nPrevKey;
if (!hkeyClsKey)
{
continue;
}
// Make a copy of the key for menu's use
if (RegOpenKeyEx(hkeyClsKey, NULL, 0L, MAXIMUM_ALLOWED,
&pdeffm->hkeyClsKeys[nTotKeys]) == ERROR_SUCCESS)
{
++nTotKeys;
}
}
pdeffm->nKeys = nTotKeys;
*ppcm = (IContextMenu *)&pdeffm->cm;
pdeffm->cRef = 1;
}
}
}
else
{
// Nope.
Assert(pdeffm->hdxa == NULL);
Assert(pdeffm->pdtobj == NULL);
pdeffm->cRef = 1;
*ppcm = (IContextMenu *)&pdeffm->cm;
hres = NOERROR;
}
if (SUCCEEDED(hres))
{
hres = lpfn(psf, hwndOwner, NULL, DFM_ADDREF, 0, 0);
if (hres == E_NOTIMPL)
{
// I guess there was no initialization to do
hres = NOERROR;
}
}
if (!SUCCEEDED(hres))
{
CDefFolderMenu_Destroy(pdeffm);
}
}
return hres;
}
STDAPI CDefFolderMenu_Create(LPCITEMIDLIST pidlFolder,
HWND hwndOwner,
UINT cidl, LPCITEMIDLIST *apidl,
LPSHELLFOLDER psf,
LPFNDFMCALLBACK lpfn,
HKEY hkeyProgID, HKEY hkeyBaseProgID,
LPCONTEXTMENU *ppcm)
{
HKEY ahkeyClsKeys[2];
ahkeyClsKeys[0] = hkeyProgID;
ahkeyClsKeys[1] = hkeyBaseProgID;
// Note that Create2 will remove NULL and duplicate HKEY's
return CDefFolderMenu_Create2(pidlFolder, hwndOwner, cidl, apidl, psf, lpfn, 2, ahkeyClsKeys, ppcm);
}
//=============================================================================
// CDefFolderMenu : Members
//=============================================================================
HRESULT CDefFolderMenu_QueryInterface(IContextMenu2 *pcm, REFIID riid, LPVOID *ppvObj)
{
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
if (IsEqualIID(riid, &IID_IContextMenu) ||
IsEqualIID(riid, &IID_IContextMenu2) ||
IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = this;
this->cRef++;
return NOERROR;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
ULONG CDefFolderMenu_AddRef(IContextMenu2 *pcm)
{
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
this->cRef++;
return this->cRef;
}
//----------------------------------------------------------------------------
void StaticItems_Destroy(LPDEFFOLDERMENU lpdeffm)
{
if (lpdeffm->hdsaStatics)
DSA_Destroy(lpdeffm->hdsaStatics);
}
ULONG CDefFolderMenu_Destroy(CDefFolderMenu *this)
{
UINT nKeys;
if (this->hdxa) {
HDXA_Destroy(this->hdxa);
}
if (this->psf) {
this->psf->lpVtbl->Release(this->psf);
}
if (this->pdtgt) {
this->pdtgt->lpVtbl->Release(this->pdtgt);
}
if (this->pdtobj) {
this->pdtobj->lpVtbl->Release(this->pdtobj);
}
for (nKeys=this->nKeys-1; (int)nKeys>=0; --nKeys)
{
RegCloseKey(this->hkeyClsKeys[nKeys]);
}
StaticItems_Destroy(this);
LocalFree((HLOCAL)this);
return 0;
}
ULONG CDefFolderMenu_Release(IContextMenu2 *pcm)
{
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
this->cRef--;
if (this->cRef > 0) {
return this->cRef;
}
if (this->lpfn)
this->lpfn(this->psf, this->hwndOwner, NULL, DFM_RELEASE, this->idDefMax, 0);
return CDefFolderMenu_Destroy(this);
}
//
// REVIEW: We need this function because current version of USER.EXE does
// not support pop-up only menu.
//
HMENU _LoadPopupMenu2(HINSTANCE hinst, UINT id)
{
HMENU hmenuParent = LoadMenu(hinst, MAKEINTRESOURCE(id));
if (hmenuParent) {
HMENU hpopup = GetSubMenu(hmenuParent, 0);
RemoveMenu(hmenuParent, 0, MF_BYPOSITION);
DestroyMenu(hmenuParent);
return hpopup;
}
return NULL;
}
HMENU _LoadPopupMenu(UINT id)
{
return _LoadPopupMenu2(HINST_THISDLL, id);
}
int _SHMergePopupMenus(HMENU hmMain, HMENU hmMerge, int idCmdFirst, int idCmdLast)
{
int i;
int idTemp, idMax = idCmdFirst;
for (i=GetMenuItemCount(hmMerge)-1; i>=0; --i)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_ID|MIIM_SUBMENU;
mii.cch = 0; // just in case
if (!GetMenuItemInfo(hmMerge, i, TRUE, &mii))
{
continue;
}
idTemp = Shell_MergeMenus(_GetMenuFromID(hmMain, mii.wID),
mii.hSubMenu, (UINT)0, idCmdFirst, idCmdLast,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
if (idMax < idTemp)
{
idMax = idTemp;
}
}
return(idMax);
}
void CDefFolderMenu_MergeMenu(HINSTANCE hinst, UINT idMainMerge, UINT idPopupMerge, LPQCMINFO pqcm)
{
HMENU hmMerge;
UINT idMax = pqcm->idCmdFirst, idTemp;
if (idMainMerge
&& (hmMerge = _LoadPopupMenu2(hinst, idMainMerge))!=NULL)
{
idMax = Shell_MergeMenus(
pqcm->hmenu, hmMerge, pqcm->indexMenu,
pqcm->idCmdFirst, pqcm->idCmdLast,
MM_ADDSEPARATOR|MM_SUBMENUSHAVEIDS);
DestroyMenu(hmMerge);
}
if (idPopupMerge
&& (hmMerge=LoadMenu(hinst, MAKEINTRESOURCE(idPopupMerge)))!=NULL)
{
idTemp = _SHMergePopupMenus(pqcm->hmenu, hmMerge,
pqcm->idCmdFirst, pqcm->idCmdLast);
if (idMax < idTemp)
{
idMax = idTemp;
}
DestroyMenu(hmMerge);
}
pqcm->idCmdFirst = idMax;
}
ULONG DefFolderMenu_GetAttributes(CDefFolderMenu *this, ULONG dwAttrMask)
{
int nItems, i;
STGMEDIUM medium;
FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
HIDA hida;
LPITEMIDLIST *ppidl;
LPIDA pida;
if (FAILED(this->pdtobj->lpVtbl->GetData(this->pdtobj, &fmte, &medium)))
{
#ifdef CAIRO_DS
fmte.cfFormat = g_cfDS_HIDA;
if (FAILED(this->pdtobj->lpVtbl->GetData(this->pdtobj, &fmte, &medium)))
{
return(0);
}
#else
return(0);
#endif
}
hida = (HIDA)medium.hGlobal;
pida = GlobalLock(hida);
nItems = HIDA_GetCount(hida);
ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, nItems * SIZEOF(LPCITEMIDLIST));
if (ppidl)
{
BOOL fAllocated;
for (i = nItems - 1; i >= 0; --i)
{
ppidl[i] = (LPITEMIDLIST)IDA_GetRelativeIDListPtr(pida, i, &fAllocated);
}
this->psf->lpVtbl->GetAttributesOf(this->psf, nItems, ppidl,
&dwAttrMask);
if (fAllocated) {
for (i = nItems - 1; i >= 0; --i)
{
ILFree(ppidl[i]);
}
}
LocalFree((HLOCAL)ppidl);
}
else
{
dwAttrMask = 0;
}
SHReleaseStgMedium(&medium);
return(dwAttrMask);
}
void _DisableRemoveMenuItem(HMENU hmInit, UINT uID, BOOL bAvail, BOOL bRemoveUnavail)
{
if (bAvail)
{
EnableMenuItem(hmInit, uID, MF_ENABLED|MF_BYCOMMAND);
}
else if (bRemoveUnavail)
{
DeleteMenu(hmInit, uID, MF_BYCOMMAND);
}
else
{
EnableMenuItem(hmInit, uID, MF_GRAYED|MF_BYCOMMAND);
}
}
//
// Enable/disable menuitems in the "File" pulldown.
//
void Def_InitFileCommands(ULONG dwAttr, HMENU hmInit, UINT idCmdFirst, BOOL bContext)
{
idCmdFirst -= SFVIDM_FIRST;
_DisableRemoveMenuItem(hmInit, SFVIDM_FILE_RENAME+idCmdFirst,
dwAttr & SFGAO_CANRENAME, bContext);
_DisableRemoveMenuItem(hmInit, SFVIDM_FILE_DELETE+idCmdFirst,
dwAttr & SFGAO_CANDELETE, bContext);
_DisableRemoveMenuItem(hmInit, SFVIDM_FILE_LINK+idCmdFirst,
dwAttr & SFGAO_CANLINK, bContext);
#if 0
// Never remove the "Properties" command
_DisableRemoveMenuItem(hmInit, SFVIDM_FILE_PROPERTIES+idCmdFirst,
dwAttr & SFGAO_HASPROPSHEET, FALSE);
#else
// Check to see if the folder supports properties on objects, if it is
// the context menu then we are allowed to remove the item, otherwise just
// gray it.
_DisableRemoveMenuItem( hmInit, SFVIDM_FILE_PROPERTIES+idCmdFirst, dwAttr & SFGAO_HASPROPSHEET, bContext );
#endif
}
HRESULT DataObj_SetDWORD(IDataObject *pdtobj, UINT cf, DWORD dw)
{
HRESULT hres = E_OUTOFMEMORY;
DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD));
if (pdw)
{
*pdw = dw;
hres = DataObj_SetGlobal(pdtobj, cf, pdw);
if (FAILED(hres))
GlobalFree((HGLOBAL)pdw);
}
return hres;
}
HRESULT DataObj_GetDWORD(IDataObject *pdtobj, UINT cf, DWORD *pdwOut)
{
STGMEDIUM medium;
FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
HRESULT hres;
medium.pUnkForRelease = NULL;
medium.hGlobal = NULL;
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
if (SUCCEEDED(hres))
{
DWORD *pdw = (DWORD *)GlobalLock(medium.hGlobal);
if (pdw)
{
*pdwOut = *pdw;
GlobalUnlock(medium.hGlobal);
}
else
{
hres = E_UNEXPECTED;
}
SHReleaseStgMedium(&medium);
}
return hres;
}
HRESULT DataObj_SetPasteSucceeded(IDataObject *pdtobj, DWORD dwEffect)
{
return DataObj_SetDWORD(pdtobj, g_cfPasteSucceeded, dwEffect);
}
DWORD DataObj_GetPerformedEffect(IDataObject *pdtobj)
{
DWORD dw;
if (FAILED(DataObj_GetDWORD(pdtobj, g_cfPerformedDropEffect, &dw)))
dw = 0;
return dw;
}
HRESULT DataObj_SetPerformedEffect(IDataObject *pdtobj, DWORD dwEffect)
{
return DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, dwEffect);
}
HRESULT DataObj_SetPreferredEffect(IDataObject *pdtobj, DWORD dwEffect)
{
return DataObj_SetDWORD(pdtobj, g_cfPreferredDropEffect, dwEffect);
}
DWORD DataObj_GetPreferredEffect(IDataObject *pdtobj, DWORD dwDefault)
{
DWORD dw;
if (FAILED(DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, &dw)))
dw = dwDefault;
return dw;
}
__inline DWORD _GetClipboardEffect(IDataObject *pdtobj)
{
return DataObj_GetPreferredEffect(pdtobj,
DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK);
}
BOOL Def_IsPasteAvailable(LPDROPTARGET pdtgt, LPDWORD pdwEffect)
{
DECLAREWAITCURSOR;
LPDATAOBJECT pdtobjClipbrd;
BOOL fRet = FALSE;
SetWaitCursor();
*pdwEffect = 0; // assume none
// Count the number of clipboard formats available, if there are none then there
// is no point making the clipboard available.
if ( CountClipboardFormats() > 0 )
{
if (pdtgt && SHGetClipboard(&pdtobjClipbrd)==NOERROR)
{
POINTL pt = {0,0};
DWORD dwEffect, dwEffectLink;
DWORD dwEffectOffered = _GetClipboardEffect(pdtobjClipbrd);
//
// Check if we can paste.
//
dwEffect = (dwEffectOffered & (DROPEFFECT_MOVE|DROPEFFECT_COPY));
if (dwEffect)
{
if (SUCCEEDED(pdtgt->lpVtbl->DragEnter(pdtgt,
pdtobjClipbrd, MK_RBUTTON, pt, &dwEffect)))
{
pdtgt->lpVtbl->DragLeave(pdtgt);
}
else
{
dwEffect = 0;
}
}
//
// Check if we can past-link.
//
dwEffectLink = (dwEffectOffered & DROPEFFECT_LINK);
if (dwEffectLink)
{
if (SUCCEEDED(pdtgt->lpVtbl->DragEnter(pdtgt,
pdtobjClipbrd, MK_RBUTTON, pt, &dwEffectLink)))
{
pdtgt->lpVtbl->DragLeave(pdtgt);
dwEffect |= dwEffectLink;
}
}
fRet = (dwEffect & (DROPEFFECT_MOVE | DROPEFFECT_COPY));
*pdwEffect = dwEffect;
pdtobjClipbrd->lpVtbl->Release(pdtobjClipbrd);
}
}
ResetWaitCursor();
return fRet;
}
void Def_InitEditCommands(ULONG dwAttr, HMENU hmInit, UINT idCmdFirst, LPDROPTARGET pdtgt, UINT fContext)
{
DWORD dwEffect = 0;
BOOL bEnableUndo;
TCHAR szMenuText[80];
idCmdFirst -= SFVIDM_FIRST;
// enable undo if there's an undo history
bEnableUndo = IsUndoAvailable();
if (bEnableUndo)
{
GetUndoText(PeekUndoAtom(), szMenuText, UNDO_MENUTEXT);
}
else
{
LoadString(HINST_THISDLL, IDS_UNDOMENU, szMenuText, ARRAYSIZE(szMenuText));
}
if (szMenuText[0]) {
ModifyMenu(hmInit, SFVIDM_EDIT_UNDO + idCmdFirst,
MF_BYCOMMAND | MF_STRING,
SFVIDM_EDIT_UNDO + idCmdFirst,
szMenuText);
}
_DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_UNDO + idCmdFirst,
bEnableUndo, fContext);
_DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_CUT+idCmdFirst,
dwAttr & SFGAO_CANMOVE, fContext);
_DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_COPY+idCmdFirst,
dwAttr & SFGAO_CANCOPY, fContext);
//
// Enable the "Paste" menuitem if the background drop target can
// handle what's in the clipboard
//
// Never remove the "Paste" command
_DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_PASTE+idCmdFirst,
Def_IsPasteAvailable(pdtgt, &dwEffect), fContext & DIEC_SELECTIONCONTEXT);
_DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_PASTELINK+idCmdFirst,
dwEffect & DROPEFFECT_LINK, fContext & DIEC_SELECTIONCONTEXT);
// _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_PASTESPECIAL+idCmdFirst,
// dwEffect & (DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK), fContext & DIEC_SELECTIONCONTEXT);
}
const TCHAR c_szStatic[] = TEXT("Static");
//----------------------------------------------------------------------------
typedef struct
{
CLSID clsid;
UINT idCmd;
} STATICITEMINFO;
typedef STATICITEMINFO *PSTATICITEMINFO;
#define DSAII_APPEND 0x7fff
//----------------------------------------------------------------------------
int StaticItems_ExtractIcon(HKEY hkeyMenuItem)
{
HKEY hkeyDefIcon;
DWORD cb;
TCHAR szDefIcon[MAX_PATH];
int iDefIcon;
int iImage = -1;
DWORD dwType;
if (RegOpenKey(hkeyMenuItem, c_szDefaultIcon, &hkeyDefIcon) == ERROR_SUCCESS)
{
cb = SIZEOF(szDefIcon);
dwType = REG_SZ;
if (RegQueryValueEx(hkeyDefIcon, NULL, NULL, &dwType, (LPBYTE)szDefIcon, &cb) == ERROR_SUCCESS)
{
//DebugMsg(DM_TRACE, "si_a: DefIcon %s.", szDefIcon);
iDefIcon = PathParseIconLocation(szDefIcon);
iImage = Shell_GetCachedImageIndex(szDefIcon, iDefIcon, 0);
}
RegCloseKey(hkeyDefIcon);
}
return iImage;
}
//----------------------------------------------------------------------------
UINT StaticItems_Add(LPDEFFOLDERMENU pdeffm, HMENU hmenu, UINT idCmd,
UINT idCmdLast, HKEY hkey)
{
HDSA hdsaStatics;
HKEY hkeyStatic, hkeyClass, hkeyMenuItem;
TCHAR szClass[MAX_PATH];
TCHAR szCLSID[MAX_PATH];
TCHAR szMenuText[MAX_PATH];
TCHAR szMenuItem[32];
int i, iMenuItem;
DWORD cb;
STATICITEMINFO sii;
BOOL fOutOfIds = FALSE;
MENUITEMINFO mii;
int iIcon;
DWORD dwType;
#ifdef DEFCM_DEBUG
DebugMsg(DM_TRACE, TEXT("si_a: Adding static items."));
#endif
if (idCmd > idCmdLast)
{
DebugMsg(DM_ERROR, TEXT("si_a: Out of command ids!"));
return idCmd;
}
Assert(!pdeffm->hdsaStatics);
// Create a hdsaStatics.
hdsaStatics = DSA_Create(SIZEOF(STATICITEMINFO), 1);
if (hdsaStatics)
{
// Try to open the "Static" subkey.
if (RegOpenKey(hkey, c_szStatic, &hkeyStatic) == ERROR_SUCCESS)
{
// For each subkey of static.
for (i = 0; RegEnumKey(hkeyStatic, i, szClass, ARRAYSIZE(szClass)) == ERROR_SUCCESS; i++)
{
// DebugMsg(DM_TRACE, "si_a: Found %s.", szClass);
// Record the GUID.
if (RegOpenKey(hkeyStatic, szClass, &hkeyClass) == ERROR_SUCCESS)
{
cb = SIZEOF(szCLSID);
dwType = REG_SZ;
if (RegQueryValueEx(hkeyClass, NULL, NULL, &dwType, (LPBYTE)szCLSID, &cb) == ERROR_SUCCESS)
{
// While there are further subkeys 0..X
iMenuItem = 0;
//wsprintf(szMenuItem, "%d", iMenuItem);
szMenuItem[0] = TEXT('0');
szMenuItem[1] = TEXT('\0');
while (!fOutOfIds && RegOpenKey(hkeyClass, szMenuItem, &hkeyMenuItem) == ERROR_SUCCESS)
{
// Get all the command text.
cb = SIZEOF(szMenuText);
dwType = REG_SZ;
if (RegQueryValueEx(hkeyMenuItem, NULL, NULL, &dwType, (LPBYTE)szMenuText, &cb) == ERROR_SUCCESS)
{
// DebugMsg(DM_TRACE, "si_a: Cmd %s.", szMenuText);
// Get the icon
iIcon = StaticItems_ExtractIcon(hkeyMenuItem);
// Store the info.
SHCLSIDFromString(szCLSID, &sii.clsid);
sii.idCmd = idCmd;
DSA_InsertItem(hdsaStatics, DSAII_APPEND, &sii);
// Create the menu.
AppendMenu(hmenu, MF_STRING, idCmd, szMenuText);
// Add the icon if there is one.
if (iIcon != -1)
{
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_DATA;
mii.dwItemData = iIcon;
SetMenuItemInfo(hmenu, idCmd, FALSE, &mii);
}
// Next command.
idCmd++;
if (idCmd > idCmdLast)
{
DebugMsg(DM_ERROR, TEXT("si_a: Out of command ids!"));
fOutOfIds = TRUE;
}
else
{
iMenuItem++;
wsprintf(szMenuItem, TEXT("%d"), iMenuItem);
}
}
RegCloseKey(hkeyMenuItem);
}
}
RegCloseKey(hkeyClass);
}
}
RegCloseKey(hkeyStatic);
}
else
{
// DebugMsg(DM_TRACE, "si_i: No static menu items.");
}
pdeffm->hdsaStatics = hdsaStatics;
}
else
{
DebugMsg(DM_TRACE, TEXT("si_i: Can't alloc static menu item.."));
}
return idCmd;
}
void _SHPrettyMenu(HMENU hm)
{
BOOL bSeparated = TRUE;
int i;
for (i=GetMenuItemCount(hm)-1; i>0; --i)
{
if (_SHIsMenuSeparator(hm, i))
{
if (bSeparated)
{
DeleteMenu(hm, i, MF_BYPOSITION);
}
bSeparated = TRUE;
}
else
{
bSeparated = FALSE;
}
}
// The above loop does not handle the case of many separators at
// the beginning of the menu
while (_SHIsMenuSeparator(hm, 0))
{
DeleteMenu(hm, 0, MF_BYPOSITION);
}
}
HRESULT Def_InitDropTarget(CDefFolderMenu* this, DWORD *pdwAttr)
{
STGMEDIUM medium;
LPIDA pida;
HRESULT hres = ResultFromScode(E_FAIL);
// already have one?
if (this->pdtgt)
return NOERROR;
if (this->pdtobj) {
DWORD dwAttr;
dwAttr = DefFolderMenu_GetAttributes(this,
SFGAO_CANRENAME|SFGAO_CANDELETE|SFGAO_CANLINK|SFGAO_HASPROPSHEET|
SFGAO_CANCOPY|SFGAO_CANMOVE|SFGAO_DROPTARGET);
pida = DataObj_GetHIDA(this->pdtobj, &medium);
if ((dwAttr & SFGAO_DROPTARGET) && medium.hGlobal)
{
BOOL fAllocated;
LPCITEMIDLIST pidl = IDA_GetRelativeIDListPtr(pida, 0, &fAllocated);
// ok if it fails... initeditcommands will grey out paste option
hres = this->psf->lpVtbl->GetUIObjectOf
(this->psf, this->hwndOwner, 1, &pidl, &IID_IDropTarget, 0, &this->pdtgt);
if (fAllocated) {
ILFree ((LPITEMIDLIST)pidl);
}
}
if (medium.hGlobal)
HIDA_ReleaseStgMedium(pida, &medium);
Assert(pdwAttr);
*pdwAttr = dwAttr;
} else {
hres = this->psf->lpVtbl->CreateViewObject(this->psf, this->hwndOwner, &IID_IDropTarget, &this->pdtgt);
}
return hres;
}
//----------------------------------------------------------------------------
TCHAR const c_szMenuHandler[] = STRREG_SHEX_MENUHANDLER;
HRESULT CDefFolderMenu_QueryContextMenu(IContextMenu2 *pcm, HMENU hmenu,
UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
DECLAREWAITCURSOR;
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
QCMINFO qcm = { hmenu, indexMenu, idCmdFirst, idCmdLast };
HDCA hdca;
HRESULT hres;
BOOL fUseDefExt;
SetWaitCursor();
this->idCmdFirst = idCmdFirst;
// first add in the folder commands like cut/copy/paste
if (this->pdtobj && !(uFlags & (CMF_VERBSONLY | CMF_DVFILE)))
{
ULONG dwAttr;
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DCM_ITEM, 0, &qcm);
//
// If there is previously got drop target, release it.
//
if (this->pdtgt) {
this->pdtgt->lpVtbl->Release(this->pdtgt);
this->pdtgt=NULL;
}
Def_InitDropTarget(this, &dwAttr);
if (!(uFlags & CMF_CANRENAME))
{
dwAttr &= ~(SFGAO_CANRENAME);
}
Def_InitFileCommands(dwAttr, hmenu, idCmdFirst, TRUE);
Def_InitEditCommands(dwAttr, hmenu, idCmdFirst, this->pdtgt, DIEC_SELECTIONCONTEXT);
}
this->idDefMax = qcm.idCmdFirst - this->idCmdFirst;
//
// DFM_MERGECONTEXTMENU returns (S_FALSE) if we should not
// add any verbs.
//
if (this->lpfn) {
hres = this->lpfn(this->psf, this->hwndOwner, this->pdtobj, DFM_MERGECONTEXTMENU, uFlags, (LPARAM)&qcm);
fUseDefExt = (hres == NOERROR);
if ((hres == ResultFromShort(-1)) && !(uFlags & CMF_NODEFAULT) &&
(GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0) == -1)) {
SetMenuDefaultItem(hmenu, (this->idCmdFirst - SFVIDM_FIRST) + SFVIDM_FILE_PROPERTIES, MF_BYCOMMAND);
}
} else {
fUseDefExt = FALSE;
}
this->idFldMax = qcm.idCmdFirst - this->idCmdFirst;
// add registry verbs
if ((this->pdtobj && !(uFlags & CMF_NOVERBS)) ||
(!this->pdtobj && !this->psf && this->nKeys)) // this second case is for find extensions
{
//
// Put the separator between container menuitems and object menuitems
// only if we don't have the separator at the insertion point.
//
MENUITEMINFO mii;
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_TYPE;
mii.cch = 0; // WARNING: We must put 0 here!!!!
mii.dwTypeData = NULL;
mii.fType = MFT_SEPARATOR; // to avoid ramdom result.
if (GetMenuItemInfo(hmenu, indexMenu, TRUE, &mii) && !(mii.fType&MFT_SEPARATOR))
{
InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, (UINT)-1, NULL);
}
hdca = DCA_Create();
if (hdca)
{
UINT nKeys;
//
// Add default extensions, only if the folder callback returned
// NOERROR. The Printer and Control folder returns S_FALSE
// indicating that they don't need any default extension.
//
if (fUseDefExt)
{
//
// Always add this default extention at the top.
//
DCA_AddItem(hdca, &CLSID_ShellFileDefExt);
//
// Always add the viewer extention right next to it.
//
DCA_AddItem(hdca, &CLSID_ShellViewerExt);
}
//
// Append menu for all classes. this does not apply for links
// (nKeys == 0)
//
for (nKeys=0; nKeys<this->nKeys; ++nKeys)
{
DCA_AddItemsFromKey(hdca, this->hkeyClsKeys[nKeys],
this->bUnderKeys ? NULL : c_szMenuHandler);
}
qcm.idCmdFirst = HDXA_AppendMenuItems(this->hdxa, this->pdtobj,
this->nKeys, this->hkeyClsKeys,
NULL, hmenu, indexMenu, qcm.idCmdFirst, idCmdLast,
uFlags, hdca);
//
// if no default menu got set, choose the first one.
//
if (!(uFlags & CMF_NODEFAULT) &&
GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1)
SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
DCA_Destroy(hdca);
}
this->idVerbMax = qcm.idCmdFirst - this->idCmdFirst;
// what are statics?
if (uFlags & CMF_INCLUDESTATIC)
{
qcm.idCmdFirst = StaticItems_Add(this, hmenu, qcm.idCmdFirst, idCmdLast, this->hkeyClsKeys[0]);
}
this->idStaticMax = qcm.idCmdFirst - this->idCmdFirst;
//
// Remove the separator if we did not add any.
//
if ((this->idStaticMax == this->idFldMax))
{
Assert(GetMenuState(hmenu, 0, MF_BYPOSITION) & MF_SEPARATOR);
DeleteMenu(hmenu, 0, MF_BYPOSITION);
}
}
// And now we give the callback the option to put (more) commands on top
// of everything else
if (this->lpfn)
{
this->lpfn(this->psf, this->hwndOwner, this->pdtobj, DFM_MERGECONTEXTMENU_TOP, uFlags, (LPARAM)&qcm);
}
this->idFld2Max = qcm.idCmdFirst - this->idCmdFirst;
_SHPrettyMenu(hmenu);
ResetWaitCursor();
return ResultFromShort(this->idFld2Max);
}
HRESULT CDefFolderMenu_ProcessEditPaste(LPDEFFOLDERMENU this, HWND hwndOwner, BOOL fLink)
{
LPDROPTARGET pdtgt;
HRESULT hres;
DWORD dwAttr;
DECLAREWAITCURSOR;
SetWaitCursor();
hres = Def_InitDropTarget(this, &dwAttr);
pdtgt = this->pdtgt;
/// set this to fail so that we'll also beep down below
//if (!pdtgt)
// hres = E_FAIL;
if (SUCCEEDED(hres))
{
LPDATAOBJECT pdtobjClipbrd;
hres = SHGetClipboard(&pdtobjClipbrd);
if (hres == NOERROR)
{
POINTL ptl = {0, 0}; // should not be used
DWORD dwEffectIn = _GetClipboardEffect(pdtobjClipbrd);
DWORD dwEffect;
DWORD grfKeyState;
if (fLink) {
// MK_FAKEDROP to avoid drag/drop pop up menu
grfKeyState = MK_LBUTTON| MK_CONTROL | MK_SHIFT | MK_FAKEDROP;
dwEffectIn &= DROPEFFECT_LINK;
} else {
grfKeyState = MK_LBUTTON;
dwEffectIn &= ~DROPEFFECT_LINK;
}
//
// OLE document says we never call Drop() without calling
// DragEnter(). Let's do it even though it is meaningless.
//
dwEffect = dwEffectIn;
hres = pdtgt->lpVtbl->DragEnter(pdtgt, pdtobjClipbrd, grfKeyState, ptl, &dwEffect);
if (dwEffect) {
dwEffect = dwEffectIn;
hres = pdtgt->lpVtbl->Drop(pdtgt, pdtobjClipbrd, grfKeyState, ptl, &dwEffect);
} else {
pdtgt->lpVtbl->DragLeave(pdtgt);
}
//
// if we just did a paste and we moved the files we cant paste
// them again (because they moved!) so empty the clipboard
//
// dwEffect is zero when the target optimized the move...
//
if (SUCCEEDED(hres) && ((dwEffect == DROPEFFECT_MOVE) ||
(DataObj_GetPerformedEffect(pdtobjClipbrd) == DROPEFFECT_MOVE)))
{
//
// dwEffect is MOVE when the source should delete the data
//
if (dwEffect == DROPEFFECT_MOVE)
DataObj_SetPasteSucceeded(pdtobjClipbrd, dwEffect);
SHSetClipboard(NULL);
}
pdtobjClipbrd->lpVtbl->Release(pdtobjClipbrd);
}
}
ResetWaitCursor();
if (FAILED(hres))
MessageBeep(0);
return hres;
}
//----------------------------------------------------------------------------
#define CMD_ID_FIRST 1
#define CMD_ID_LAST 0x7fff
//----------------------------------------------------------------------------
void StaticItems_InvokeCommand(LPDEFFOLDERMENU pdeffm, UINT iCmd)
{
PSTATICITEMINFO psii;
LPCONTEXTMENU pcm;
HMENU hmenu;
CMINVOKECOMMANDINFO ici;
#ifdef DEFCM_DEBUG
DebugMsg(DM_TRACE, TEXT("si_ic: Invoking static command."));
#endif
if (pdeffm->hdsaStatics)
{
psii = DSA_GetItemPtr(pdeffm->hdsaStatics, iCmd);
if (psii)
{
if (SUCCEEDED(SHCoCreateInstance(NULL, &psii->clsid, NULL,
&IID_IContextMenu, &pcm)))
{
hmenu = CreatePopupMenu();
if (hmenu)
{
pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST, CMF_NORMAL);
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFO);
ici.fMask = 0;
ici.hwnd = NULL;
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(CMD_ID_FIRST+iCmd);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_NORMAL;
pcm->lpVtbl->InvokeCommand(pcm, &ici);
DestroyMenu(hmenu);
}
else
{
DebugMsg(DM_TRACE, TEXT("si_ic: Can't create popup menu."));
}
pcm->lpVtbl->Release(pcm);
}
else
{
DebugMsg(DM_TRACE, TEXT("si_ic: No ContextMenu."));
}
}
else
{
DebugMsg(DM_ERROR, TEXT("si_ic: Invalid static command."));
}
}
else
{
DebugMsg(DM_TRACE, TEXT("si_ic: No static commands."));
}
}
TCHAR const c_szNewFolder[] = CMDSTR_NEWFOLDER;
TCHAR const c_szViewList[] = CMDSTR_VIEWLIST;
TCHAR const c_szViewDetails[] = CMDSTR_VIEWDETAILS;
#ifdef UNICODE
CHAR const c_szNewFolderAnsi[] = CMDSTR_NEWFOLDERA;
#endif
const struct {
LPCTSTR pszCmd;
UINT idDFMCmd;
UINT idDefCmd;
} c_sDFMCmdInfo[] = {
{ c_szDelete, DFM_CMD_DELETE, DCMIDM_DELETE },
{ c_szCut, DFM_CMD_MOVE, DCMIDM_CUT },
{ c_szCopy, DFM_CMD_COPY, DCMIDM_COPY },
{ c_szPaste, DFM_CMD_PASTE, DCMIDM_PASTE },
{ c_szLink, DFM_CMD_LINK, DCMIDM_LINK },
{ c_szProperties, DFM_CMD_PROPERTIES, DCMIDM_PROPERTIES },
{ c_szNewFolder, DFM_CMD_NEWFOLDER, 0 },
{ c_szViewList, DFM_CMD_VIEWLIST, 0 },
{ c_szViewDetails, DFM_CMD_VIEWDETAILS, 0 },
{ c_szPaste, DFM_CMD_PASTE, 0 },
{ c_szPasteLink, DFM_CMD_PASTELINK, 0 },
// { c_szPasteSpecial, DFM_CMD_PASTESPECIAL, 0 },
{ c_szRename, 0, DCMIDM_RENAME },
};
HRESULT CDefFolderMenu_InvokeCommand(IContextMenu2 *pcm, LPCMINVOKECOMMANDINFO pici)
{
HRESULT hres = NOERROR;
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
UINT idCmd = (UINT)-1;
UINT idCmdLocal; // this is used within each if block for the local idCmd value
#ifdef UNICODE
BOOL fUnicode = FALSE;
LPCMINVOKECOMMANDINFOEX picix = (LPCMINVOKECOMMANDINFOEX)pici; // This value is only usable if fUnicode=TRUE
if (pici->cbSize >= SIZEOF(CMINVOKECOMMANDINFOEX)
&& (pici->fMask & CMIC_MASK_UNICODE) == CMIC_MASK_UNICODE)
{
fUnicode = TRUE;
}
#endif
if (pici->cbSize < SIZEOF(CMINVOKECOMMANDINFO))
return E_INVALIDARG;
if (HIWORD(pici->lpVerb))
{
int i;
LPCTSTR lpVerb;
#ifdef UNICODE
WCHAR szVerb[MAX_PATH];
if (!fUnicode || picix->lpVerbW == NULL)
{
MultiByteToWideChar(CP_ACP, 0,
picix->lpVerb, -1,
szVerb, ARRAYSIZE(szVerb));
lpVerb = szVerb;
}
else
{
lpVerb = picix->lpVerbW;
}
#else
lpVerb = pici->lpVerb;
#endif
idCmdLocal = idCmd;
for (i = 0; i < ARRAYSIZE(c_sDFMCmdInfo) ; i++)
{
if (lstrcmpi(lpVerb, c_sDFMCmdInfo[i].pszCmd)==0) {
idCmdLocal = c_sDFMCmdInfo[i].idDFMCmd;
// We need to use goto because idFldMax might not be initialized
// yet (QueryContextMenu might have not been called).
goto ProcessCommand;
}
}
// see if this is a command provided by name by the callback
if (*lpVerb && SUCCEEDED(this->lpfn(this->psf, this->hwndOwner,
this->pdtobj, DFM_MAPCOMMANDNAME, (WPARAM)&idCmdLocal,
(LPARAM)lpVerb)))
{
goto ProcessCommand;
}
// we need to give the verbs a chance in case they asked for it by string
goto ProcessVerb;
}
else
{
idCmd = LOWORD((UINT)pici->lpVerb);
}
if (idCmd < this->idDefMax)
{
int i;
idCmdLocal = idCmd;
for (i=0; i<ARRAYSIZE(c_sDFMCmdInfo); ++i)
{
if (idCmdLocal == c_sDFMCmdInfo[i].idDefCmd)
{
idCmdLocal = c_sDFMCmdInfo[i].idDFMCmd;
goto ProcessCommand;
}
}
hres = E_INVALIDARG;
}
else if (idCmd < this->idFldMax)
{
LPARAM lParam;
#ifdef UNICODE
WCHAR szLParamBuffer[MAX_PATH];
#endif
idCmdLocal = idCmd - this->idDefMax;
ProcessCommand:
#ifdef UNICODE
if (!fUnicode || picix->lpParametersW == NULL)
{
if (pici->lpParameters == NULL)
{
lParam = (LPARAM)NULL;
}
else
{
MultiByteToWideChar(CP_ACP, 0,
pici->lpParameters, -1,
szLParamBuffer, ARRAYSIZE(szLParamBuffer));
lParam = (LPARAM)szLParamBuffer;
}
}
else
{
lParam = (LPARAM)picix->lpParametersW;
}
#else
lParam = (LPARAM)pici->lpParameters;
#endif
switch (idCmdLocal) {
case DFM_CMD_LINK:
#ifdef UNICODE
if (!fUnicode || picix->lpDirectoryW == NULL)
{
if (pici->lpDirectory == NULL)
{
lParam = (LPARAM)NULL;
}
else
{
MultiByteToWideChar(CP_ACP, 0,
pici->lpDirectory, -1,
szLParamBuffer,
ARRAYSIZE(szLParamBuffer));
lParam = (LPARAM)szLParamBuffer;
}
}
else
{
lParam = (LPARAM)picix->lpDirectoryW;
}
#else
lParam = (LPARAM)pici->lpDirectory;
#endif
break;
case DFM_CMD_PROPERTIES:
if (pici->fMask & CMIC_MASK_MODAL) {
idCmdLocal = DFM_CMD_MODALPROP;
}
break;
}
// This is a folder menu
hres = this->lpfn(this->psf, this->hwndOwner, this->pdtobj, DFM_INVOKECOMMAND, idCmdLocal, lParam);
// Check if we need to execute the default code.
if (hres == S_FALSE)
{
hres = NOERROR; // assume no error
if (this->pdtobj)
{
switch(idCmdLocal) {
case DFM_CMD_MOVE:
case DFM_CMD_COPY:
DataObj_SetPreferredEffect(this->pdtobj,
(idCmdLocal == DFM_CMD_MOVE)?
DROPEFFECT_MOVE : (DROPEFFECT_COPY|DROPEFFECT_LINK));
ShellFolderView_SetPoints(this->hwndOwner, this->pdtobj);
SHSetClipboard(this->pdtobj);
// this needs to be done after the set clipboard so
// that the hwndView chain will be set right
ShellFolderView_SetClipboard(this->hwndOwner, idCmdLocal);
break;
case DFM_CMD_LINK:
SHCreateLinks(pici->hwnd, NULL, this->pdtobj, lParam ? SHCL_USETEMPLATE | SHCL_USEDESKTOP : SHCL_USETEMPLATE, NULL);
break;
case DFM_CMD_PASTE:
hres = CDefFolderMenu_ProcessEditPaste(this, pici->hwnd, FALSE);
break;
case DFM_CMD_PASTELINK:
hres = CDefFolderMenu_ProcessEditPaste(this, pici->hwnd, TRUE);
break;
default:
DebugMsg(DM_TRACE, TEXT("sh TR - command not processed in %s at %d (%x)"),
__FILE__, __LINE__, idCmdLocal);
break;
}
}
else
{
// This is a background menu. Process common command ids.
switch(idCmdLocal)
{
case DFM_CMD_PASTE:
hres = CDefFolderMenu_ProcessEditPaste(this, pici->hwnd, FALSE);
break;
case DFM_CMD_PASTELINK:
hres = CDefFolderMenu_ProcessEditPaste(this, pici->hwnd, TRUE);
break;
default:
// Only our commands should come here
break;
}
}
}
}
else if (idCmd < this->idVerbMax)
{
idCmdLocal = idCmd - this->idFldMax;
ProcessVerb:
{
CMINVOKECOMMANDINFOEX ici;
UINT idCmdSave;
if (pici->cbSize > SIZEOF(CMINVOKECOMMANDINFOEX))
{
hmemcpy(&ici,pici,SIZEOF(CMINVOKECOMMANDINFOEX));
// BUGBUG - We should probably alloc another use it to retain size
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
}
else
hmemcpy(&ici,pici,pici->cbSize);
if (!HIWORD(pici->lpVerb))
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmdLocal);
// One of extension menu is selected.
idCmdSave = (UINT)ici.lpVerb;
if (HDXA_LetHandlerProcessCommand(this->hdxa, &ici) == idCmdSave)
{
// hdxa failed to handle it
hres = E_INVALIDARG;
}
}
}
else if (idCmd < this->idStaticMax)
{
StaticItems_InvokeCommand(this, idCmd-this->idVerbMax);
}
else if (idCmd < this->idFld2Max)
{
idCmdLocal = idCmd - this->idStaticMax;
goto ProcessCommand;
}
else
{
hres = E_INVALIDARG;
}
return hres;
}
HRESULT CDefFolderMenu_GetCommandString(IContextMenu2 *pcm,
UINT idCmd,
UINT uType,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax)
{
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
HRESULT hres = E_INVALIDARG;
UINT idCmdLocal;
int i;
if (HIWORD(idCmd))
{
//
// BUGBUG - BobDay - This seems to be an undocumented feature. Passing a
// string for the idCmd. Should this string be ansi? tchar?
//
// This must be a string
LPTSTR pCmd = (LPTSTR)idCmd;
if (HDXA_GetCommandString(this->hdxa, idCmd, uType, pwReserved, pszName,
cchMax) == NOERROR)
{
return(NOERROR);
}
// Convert the string into an ID
for (i=0; i<ARRAYSIZE(c_sDFMCmdInfo); ++i)
{
if (!lstrcmpi(pCmd, c_sDFMCmdInfo[i].pszCmd))
{
idCmdLocal = c_sDFMCmdInfo[i].idDFMCmd;
goto ProcessCommand;
}
}
return E_INVALIDARG;
}
if (idCmd < this->idDefMax)
{
idCmdLocal = idCmd;
switch (uType)
{
case GCS_HELPTEXTA:
// HACK: DCM commands are in the same order as SFV commands
return(LoadStringA(HINST_THISDLL,
idCmdLocal + (UINT)(SFVIDM_FIRST + SFVIDS_MH_FIRST),
(LPSTR)pszName, cchMax) ? NOERROR : E_OUTOFMEMORY);
break;
case GCS_HELPTEXTW:
// HACK: DCM commands are in the same order as SFV commands
return(LoadStringW(HINST_THISDLL,
idCmdLocal + (UINT)(SFVIDM_FIRST + SFVIDS_MH_FIRST),
(LPWSTR)pszName, cchMax) ? NOERROR : E_OUTOFMEMORY);
break;
case GCS_VERBA:
case GCS_VERBW:
for (i=0; i<ARRAYSIZE(c_sDFMCmdInfo); ++i)
{
if (idCmdLocal == c_sDFMCmdInfo[i].idDefCmd)
{
#ifdef UNICODE
if (uType==GCS_VERBW)
{
lstrcpyn((LPWSTR)pszName, c_sDFMCmdInfo[i].pszCmd, cchMax);
}
else
{
WideCharToMultiByte(CP_ACP, 0,
c_sDFMCmdInfo[i].pszCmd, -1,
(LPSTR)pszName, cchMax,
NULL, NULL );
}
#else
if (uType==GCS_VERBW)
{
MultiByteToWideChar(CP_ACP, 0,
c_sDFMCmdInfo[i].pszCmd, -1,
(LPWSTR)pszName, cchMax );
}
else
{
lstrcpyn((LPSTR)pszName, c_sDFMCmdInfo[i].pszCmd, cchMax);
}
#endif
return(NOERROR);
}
}
return E_INVALIDARG;
case GCS_VALIDATEA:
case GCS_VALIDATEW:
// BUGBUG: We should do something here, but I am too lazy
default:
return E_NOTIMPL;
}
} else if (idCmd < this->idFldMax)
{
idCmdLocal = (idCmd - this->idDefMax);
ProcessCommand:
if (!this->lpfn)
{
// REVIEW: If there is no callback, how can idFldMax be > 0?
return E_NOTIMPL;
}
// This is a folder menu
switch (uType)
{
case GCS_HELPTEXTA:
return(this->lpfn(this->psf, this->hwndOwner, this->pdtobj, DFM_GETHELPTEXT,
(WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName));
case GCS_HELPTEXTW:
return(this->lpfn(this->psf, this->hwndOwner, this->pdtobj, DFM_GETHELPTEXTW,
(WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName));
case GCS_VALIDATEA:
case GCS_VALIDATEW:
return(this->lpfn(this->psf, this->hwndOwner, this->pdtobj,
DFM_VALIDATECMD, idCmdLocal, 0));
default:
return E_NOTIMPL;
}
}
else if (idCmd < this->idVerbMax)
{
idCmdLocal = idCmd - this->idFldMax;
// One of extension menu is selected.
hres = HDXA_GetCommandString(this->hdxa, idCmdLocal, uType, pwReserved, pszName, cchMax);
}
else if (idCmd < this->idStaticMax)
{
// What are STATICs?
}
else if (idCmd < this->idFld2Max)
{
idCmdLocal = idCmd - this->idStaticMax;
goto ProcessCommand;
}
return hres;
}
HRESULT CDefFolderMenu_HandleMenuMsg(IContextMenu2 *pcm, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDefFolderMenu *this = IToClass(CDefFolderMenu, cm, pcm);
switch (uMsg) {
case WM_MEASUREITEM:
uMsg = DFM_WM_MEASUREITEM;
wParam = GetFldFirst(this);
break;
case WM_DRAWITEM:
uMsg = DFM_WM_DRAWITEM;
wParam = GetFldFirst(this);
break;
case WM_INITMENUPOPUP:
uMsg = DFM_WM_INITMENUPOPUP;
lParam = GetFldFirst(this);
break;
default:
return E_FAIL;
}
return this->lpfn(this->psf, this->hwndOwner, this->pdtobj, uMsg, wParam, lParam);
}
IContextMenu2Vtbl c_CDefFolderMenuVtbl = {
CDefFolderMenu_QueryInterface,
CDefFolderMenu_AddRef, // no need to thunk it
CDefFolderMenu_Release,
CDefFolderMenu_QueryContextMenu,
CDefFolderMenu_InvokeCommand,
CDefFolderMenu_GetCommandString,
CDefFolderMenu_HandleMenuMsg,
};
//=============================================================================
// HXMA stuff
//=============================================================================
//===========================================================================
// CMI functions
//===========================================================================
//
// Function: HDXA_AppendMenuItems, public (not exported)
//
// This function enumerate all the context menu handlers and let them
// append menuitems. Each context menu handler will create an object
// which support IContextMenu interface. We call QueryContextMenu()
// member function of all those IContextMenu object to let them append
// menuitems. For each IContextMenu object, we create ContextMenuInfo
// struct and append it to hdxa (which is a dynamic array of ContextMenuInfo).
//
// The caller will release all those IContextMenu objects, by calling
// its Release() member function.
//
// Arguments:
// hdxa -- Handler of the dynamic ContextMenuInfo struct array
// pdata -- Specifies the selected items (files)
// hkeyShellEx -- Specifies the reg.dat class we should enumurate handlers
// hkeyProgID -- Specifies the program identifier of the selected file/directory
// pszHandlerKey -- Specifies the reg.dat key to the handler list
// pidlFolder -- Specifies the folder (drop target)
// hmenu -- Specifies the menu to be modified
// uInsert -- Specifies the position to be insert menuitems
// idCmdFirst -- Specifies the first menuitem ID to be used
// idCmdLast -- Specifies the last menuitem ID to be used
//
// Returns:
// The first menuitem ID which is not used.
//
// History:
// 02-25-93 SatoNa Created
//
UINT HDXA_AppendMenuItems(HDXA hdxa, LPDATAOBJECT pdtobj,
UINT nKeys, HKEY *ahkeyClsKeys,
LPCITEMIDLIST pidlFolder,
HMENU hmenu, UINT uInsert,
UINT idCmdFirst, UINT idCmdLast,
UINT fFlags,
HDCA hdca)
{
int idca;
const UINT idCmdBase = idCmdFirst;
// Apparently, somebody has already called into here with this object. We
// need to keep the ID ranges separate, so we'll put the new ones at the
// end.
// BUGBUG: If QueryContextMenu is called too many times, we will run out of
// ID range and not add anything. We could try storing the information
// used to create each pcm (HKEY, GUID, and fFlags) and reuse some of them,
// but then we would have to worry about what if the number of commands
// grows and other details; this is just not worth the effort since
// probably nobody will ever have a problem. The rule of thumb is to
// create an IContextMenu, do the QueryContextMenu and InvokeCommand, and
// then Release it.
idca = DSA_GetItemCount(hdxa);
if (idca > 0)
{
ContextMenuInfo *pcmi = DSA_GetItemPtr(hdxa, idca-1);
idCmdFirst += pcmi->idCmdMax;
}
//
// Note that we need to reverse the order because each extension
// will intert menuitems "above" uInsert.
//
for (idca = DCA_GetItemCount(hdca) - 1; idca >= 0; idca--)
{
LPSHELLEXTINIT psei = NULL;
LPCONTEXTMENU pcm = NULL;
int nCurKey;
//
// Let's avoid creating an instance (loading the DLL) when:
// 1. fFlags has CMF_DEFAULTONLY and
// 2. CLSID\clsid\MayChangeDefault does not exist
//
if (fFlags & CMF_DEFAULTONLY)
{
const CLSID* pclsid = DCA_GetItem(hdca, idca);
if (pclsid && !IsEqualGUID(pclsid, &CLSID_ShellFileDefExt))
{
TCHAR szCLSID[GUIDSTR_MAX];
TCHAR szRegKey[GUIDSTR_MAX + ARRAYSIZE(c_szMayChangeDefault)];
StringFromGUID2A(pclsid, szCLSID, ARRAYSIZE(szCLSID));
wsprintf(szRegKey, c_szMayChangeDefault, szCLSID);
if (RegQueryValue(HKEY_CLASSES_ROOT, szRegKey, NULL, NULL) != ERROR_SUCCESS)
{
DebugMsg(DM_TRACE, TEXT("sh TR - HDXA_AppendMenuItems skipping %s"), szCLSID);
continue;
}
}
}
for (nCurKey = 0; nCurKey < (int)nKeys; nCurKey++)
{
HRESULT hres;
UINT citems;
if (!psei && FAILED(DCA_CreateInstance(hdca, idca, &IID_IShellExtInit, &psei)))
break;
// Try all the class keys in order
if (FAILED(psei->lpVtbl->Initialize(psei, pidlFolder, pdtobj,
ahkeyClsKeys[nCurKey])))
{
continue;
}
// Only get the pcm after initializing
if (!pcm && FAILED(psei->lpVtbl->QueryInterface(psei,
&IID_IContextMenu, &pcm)))
{
continue; // break?
}
hres = pcm->lpVtbl->QueryContextMenu
(pcm, hmenu, uInsert, idCmdFirst, idCmdLast,
fFlags);
citems = SCODE_CODE(GetScode(hres));
if (SUCCEEDED(hres) && citems)
{
ContextMenuInfo cmi;
cmi.pcm = pcm;
cmi.idCmdFirst = idCmdFirst - idCmdBase;
cmi.idCmdMax = cmi.idCmdFirst + citems;
if (DSA_InsertItem(hdxa, 0x7fff, &cmi) == -1)
{
// There is no "clean" way to remove menu items, so
// we should check the add to the DSA before adding the
// menu items
DebugMsg(DM_ERROR, TEXT("filemenu.c ERROR: DSA_GetItemPtr failed (memory overflow)"));
}
else
{
pcm->lpVtbl->AddRef(pcm);
}
idCmdFirst += citems;
FullDebugMsg(DM_TRACE, TEXT("sh TR - HDXA_Append: %d, %d"), idCmdFirst, citems);
//
// keep going if it is our internal handler
//
if (!IsEqualGUID((CLSID*)DCA_GetItem(hdca, idca), &CLSID_ShellFileDefExt))
break; // not out handler stop
pcm->lpVtbl->Release(pcm);
pcm = NULL;
psei->lpVtbl->Release(psei);
psei = NULL;
continue; // next hkey
}
}
if (pcm)
pcm->lpVtbl->Release(pcm);
if (psei)
psei->lpVtbl->Release(psei);
}
return idCmdFirst;
}
//
// Function: HDXA_LetHandlerProcessCommand, public (not exported)
//
// This function is called after the user select one of add-in menu items.
// This function calls IncokeCommand method of corresponding context menu
// object.
//
// hdxa -- Handler of the dynamic ContextMenuInfo struct array
// idCmd -- Specifies the menu item ID
// hwndParent -- Specifies the parent window.
// pszWorkingDir -- Specifies the working directory.
//
// Returns:
// IDCMD_PROCESSED, if InvokeCommand method is called; idCmd, otherwise
//
// History:
// 03-03-93 SatoNa Created
//
UINT HDXA_LetHandlerProcessCommand(HDXA hdxa, LPCMINVOKECOMMANDINFOEX pici)
{
int icmi;
UINT idCmd = (UINT)pici->lpVerb;
//
// One of add-in menuitems is selected. Let the context
// menu handler process it.
//
for (icmi = 0; icmi < DSA_GetItemCount(hdxa); icmi++)
{
ContextMenuInfo *pcmi = DSA_GetItemPtr(hdxa, icmi);
//
// Check if it is for this context menu handler.
//
// Notes: We can't use InRange macro because idCmdFirst might
// be equal to idCmdLast.
// if (InRange(idCmd, pcmi->idCmdFirst, pcmi->idCmdMax-1))
if (HIWORD(pici->lpVerb))
{
if (SUCCEEDED(pcmi->pcm->lpVtbl->InvokeCommand(pcmi->pcm,
(LPCMINVOKECOMMANDINFO)pici)))
{
idCmd = IDCMD_PROCESSED;
break;
}
}
else if ((idCmd >= pcmi->idCmdFirst) && (idCmd < pcmi->idCmdMax))
{
//
// Yes, it is. Let it handle this menuitem.
//
CMINVOKECOMMANDINFOEX ici;
if (pici->cbSize > SIZEOF(CMINVOKECOMMANDINFOEX))
{
hmemcpy(&ici,pici,SIZEOF(CMINVOKECOMMANDINFOEX));
// BUGBUG - We should probably alloc another use it to retain size
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
}
else
hmemcpy(&ici,pici,pici->cbSize);
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - pcmi->idCmdFirst);
#ifdef DEBUG
#ifdef SZKEYDEBUG
// The keydebug data is not set anywhere causes beeps...
DebugMsg(DM_TRACE, TEXT("sh TR - HDXA_LetHandleProcessCommand idCmd=%d %s:(%d,%d)"),
idCmd, pcmi->szKeyDebug, pcmi->idCmdFirst, pcmi->idCmdMax);
#else
DebugMsg(DM_TRACE, TEXT("sh TR - HDXA_LetHandleProcessCommand idCmd=%d:(%d,%d)"),
idCmd, pcmi->idCmdFirst, pcmi->idCmdMax);
#endif
#endif
if (SUCCEEDED(pcmi->pcm->lpVtbl->InvokeCommand(pcmi->pcm,
(LPCMINVOKECOMMANDINFO)&ici)))
{
idCmd = IDCMD_PROCESSED;
}
break;
}
}
if (idCmd != IDCMD_PROCESSED)
{
DebugMsg(DM_ERROR, TEXT("filemenu.c - ERROR: Nobody processed (%d)"), idCmd);
}
return idCmd;
}
HRESULT HDXA_GetCommandString(HDXA hdxa, UINT idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
{
HRESULT hres = E_INVALIDARG;
int icmi;
LPTSTR pCmd = (LPTSTR)idCmd;
VDATEINPUTBUF(pszName, CHAR, cchMax);
if (!hdxa)
{
return(hres);
}
//
// One of add-in menuitems is selected. Let the context
// menu handler process it.
//
for (icmi = 0; icmi < DSA_GetItemCount(hdxa); icmi++)
{
ContextMenuInfo *pcmi = DSA_GetItemPtr(hdxa, icmi);
if (HIWORD(idCmd))
{
// This must be a string command; see if this handler wants it
if (pcmi->pcm->lpVtbl->GetCommandString(pcmi->pcm, idCmd, uType,
pwReserved, pszName, cchMax) == NOERROR)
{
return(NOERROR);
}
}
//
// Check if it is for this context menu handler.
//
// Notes: We can't use InRange macro because idCmdFirst might
// be equal to idCmdLast.
// if (InRange(idCmd, pcmi->idCmdFirst, pcmi->idCmdMax-1))
else if (idCmd >= pcmi->idCmdFirst && idCmd < pcmi->idCmdMax)
{
//
// Yes, it is. Let it handle this menuitem.
//
LPCONTEXTMENU pcm = pcmi->pcm;
hres = pcm->lpVtbl->GetCommandString(pcm,
idCmd-pcmi->idCmdFirst, uType, pwReserved, pszName, cchMax);
break;
}
}
return hres;
}
//
// Function: HDXA_DeleteAll, public (not exported)
//
// This function releases all the IContextMenu objects in the dynamic
// array of ContextMenuInfo,
//
// History:
// 03-25-93 SatoNa Created
//
void HDXA_DeleteAll(HDXA hdxa)
{
if (hdxa)
{
int icmi;
//
// Release all the IContextMenu objects, then destroy the DSA.
//
for (icmi = 0; icmi < DSA_GetItemCount(hdxa); icmi++)
{
ContextMenuInfo *pcmi = DSA_GetItemPtr(hdxa, icmi);
LPCONTEXTMENU pcm = pcmi->pcm;
if (pcm)
{
pcm->lpVtbl->Release(pcm);
}
}
DSA_DeleteAllItems(hdxa);
}
}
//
// Function: HDXA_Destroy, public (not exported)
//
// This function releases all the IContextMenu objects in the dynamic
// array of ContextMenuInfo, then destroys the dynamic array.
//
// History:
// 03-03-93 SatoNa Created
//
void HDXA_Destroy(HDXA hdxa)
{
if (hdxa)
{
HDXA_DeleteAll(hdxa);
DSA_Destroy(hdxa);
}
}