Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

The Mini Shell Extension Framework – Part III

, 18 Sep 2005
Discussion of a small C++ framework to create Windows shell extensions (IShellFolderImpl).
msf091.zip
include
columnprovider.rgs
contextmenu.rgs
contextmenu_sf.rgs
extension.rgs
extractimage_sf.rgs
infotip.rgs
infotip_sf.rgs
propertysheetext.rgs
propertysheetext_sf.rgs
shell32missing.def
shell32missing.lib
shellfolder.rgs
shellfolderwin98.rgs
vvvsample
Copy of sample1.vvv
menuicon.bmp
sample.def
sample.manifest
sample1.vvv
vvv.ico
vvvtest
sample.yyy
sample1.vvv
sample2.vvv
//
// (C) Copyright by Victor Derks <vba64@xs4all.nl>
//
// See README.TXT for the details of the software licence.
//
#include "stdafx.h"
#include "../include/shellfolderimpl.h"
#include "../include/browserframeoptionsimpl.h"
#include "../include/itemnamelimitsimpl.h"
#include "../include/strutil.h"
#include "../include/queryinfo.h"
#include "../include/cfhdrop.h"
#include "../include/menu.h"
#include "shellfolderclsid.h"
#include "shellfolderviewcb.h"
#include "shellfolderdataobject.h"
#include "enumidlist.h"
#include "vvvitem.h"
#include "vvvfile.h"
#include "columns.h"
#include "vvvpropertysheet.h"
#include "resource.h"


// Defines for the item context menu.
const UINT ID_DFM_CMD_OPEN = 0;


class ATL_NO_VTABLE CShellFolder :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CShellFolder, &__uuidof(CShellFolder)>,
	public IShellFolderImpl<CShellFolder, CVVVItem>,
	public IBrowserFrameOptionsImpl,
	public IItemNameLimitsImpl<CShellFolder, CVVVItem>
{
public:
	static HRESULT WINAPI UpdateRegistry(BOOL bRegister) throw()
	{
		UINT nResId = IsShell5OrHigher() ? IDR_SHELLFOLDER : IDR_SHELLFOLDER_WIN98;
		return IShellFolderImpl<CShellFolder, CVVVItem>::UpdateRegistry(
			nResId, bRegister,
			L"Sample ShellExtension ShellFolder", wszVVVExtension, IDS_SHELLFOLDER_TYPE);
	}


	DECLARE_PROTECT_FINAL_CONSTRUCT()

	BEGIN_COM_MAP(CShellFolder)
		COM_INTERFACE_ENTRY2(IPersist, IPersistFolder2)
		COM_INTERFACE_ENTRY(IPersistFolder)
		COM_INTERFACE_ENTRY(IPersistFolder2)
		COM_INTERFACE_ENTRY(IPersistIDList)
		COM_INTERFACE_ENTRY(IShellFolder)  // included for backwards (win9x) compatiblity.
		COM_INTERFACE_ENTRY(IShellFolder2)
		COM_INTERFACE_ENTRY(IShellDetails) // included for backwards (win9x) compatiblity.
		COM_INTERFACE_ENTRY(IBrowserFrameOptions)
		COM_INTERFACE_ENTRY(IShellIcon)
		COM_INTERFACE_ENTRY(IItemNameLimits)
		COM_INTERFACE_ENTRY(IDropTarget)   // enable drag and drop support.
	END_COM_MAP()


	CShellFolder()
	{
		// Register the columns the folder supports in 'detailed' mode.
		RegisterColumn(IDS_SHELLEXT_NAME, LVCFMT_LEFT);
		RegisterColumn(IDS_SHELLEXT_SIZE, LVCFMT_RIGHT);
	}


	// Purpose: called by MSF when the shellfolder needs to show a subfolder.
	void InitializeSubFolder(const CVVVItems& items)
	{
		m_strSubFolder.Empty();

		for (CVVVItems::const_iterator it = items.begin(); it != items.end(); ++it)
		{
			if (!m_strSubFolder.IsEmpty())
			{
				m_strSubFolder += _T("\\");
			}

			m_strSubFolder += ToString(it->GetID());
		}
	}


	// Purpose: Create the shellfolderviewcb that will be used to catch callback events
	//          generated by the system folder view.
	CComPtr<IShellFolderViewCB> CreateShellFolderViewCB()
	{
		return CShellFolderViewCB::CreateInstance(GetRootFolder());
	}


	// Purpose: called by MSF/shell when a number of items are selected and a IDataObject
	//          that contains the items is required.
	CComPtr<IDataObject> CreateDataObject(const ITEMIDLIST* pidlFolder, UINT cidl, const ITEMIDLIST** ppidl)
	{
		return CShellFolderDataObject::CreateInstance(pidlFolder, cidl, ppidl, this);
	}


	// Purpose: called by MSF/shell when it want the current list of 
	//          all items  The shell will walk all IDs and then release the enum.
	CComPtr<IEnumIDList> CreateEnumIDList(HWND /*hwnd*/, DWORD grfFlags)
	{
		auto_ptr<CVVVItems> qitems = CVVVFile(GetPathFolderFile(), m_strSubFolder).GetItems(grfFlags);
		return CEnumIDList::CreateInstance(GetUnknown(), qitems);
	}


	// Purpose: called by MSF when there is no global settings for all items.
	SFGAOF GetAttributeOf(unsigned int cidl, const CVVVItem& item, SFGAOF /*sfgofMask*/) const
	{
		return item.GetAttributeOf(cidl == 1, IsReadOnly(GetPathFolderFile()));
	}


	// Purpose: called by the default context menu. Gives an option to merge
	//          extra commands into the menu.
	HRESULT OnDfmMergeContextMenu(IDataObject* pdataobject, UINT /*uFlags*/, QCMINFO& qcminfo)
	{
		CCfShellIdList cfshellidlist(pdataobject);

		if (cfshellidlist.GetItemCount() == 1)
		{
			// Add 'open' if only 1 item is selected.
			CMenu menu(true);
			menu.AddDefaultItem(ID_DFM_CMD_OPEN, _T("&Open"));
			MergeMenus(qcminfo, menu);

			// Note: XP will automatic make first menu item the default.
			//       Win98, ME and 2k don't do this, so must add as default item.
		}

		return S_OK;
	}


	// Purpose: Called to get the help string for added menu items.
	CString OnDfmGetHelpText(unsigned short nCmdId)
	{
		return LoadString(IDS_SHELLFOLDER_DFM_HELP_BASE + nCmdId);
	}


	HRESULT OnDfmInvokeAddedCommand(HWND hwnd, IDataObject* pdataobject, int nId)
	{
		switch (nId)
		{
			case ID_DFM_CMD_OPEN:
				OnOpen(hwnd, pdataobject);
				break;

			default:
				ATLASSERT(false); // unknown command id detected.
				break;
		}

		return S_OK;
	}


	// Purpose: handle 'open' by showing the name of the selected item.
	void OnOpen(HWND hwnd, IDataObject* pdataobject)
	{
		CVVVItems items;

		RetrieveItems(pdataobject, items);
		ATLASSERT(items.size() == 1);

		if (items[0].IsFolder())
		{
			CPidl pidlFolder(items[0].CreateShellItemIdList());

			GetShellBrowser().BrowseObject(pidlFolder, SBSP_DEFBROWSER | SBSP_RELATIVE);
		}
		else
		{
			CString strMessage = _T("Open on: ") + items[0].GetName();
			IsolationAwareMessageBox(hwnd, strMessage, _T("Open"), MB_OK | MB_ICONQUESTION);
		}
	}


	// Purpose: Called by the shell/MSF when an item must be renamed.
	void OnSetNameOf(HWND /*hwnd*/, CVVVItem& item, const TCHAR* szNewName, SHGDNF shgndf)
	{
		RaiseExceptionIf(shgndf != SHGDN_NORMAL && shgndf != SHGDN_INFOLDER); // not supported 'name'.

		item.SetDisplayName(szNewName, shgndf);

		CVVVFile(GetPathFolderFile(), m_strSubFolder).SetItem(item);
	}


	// Purpose: handles the 'properties request.
	//          The property sheet/page allows the user to change 
	//          the name and size of an item.
	long OnProperties(HWND hwnd, CVVVItems& items)
	{
		ATLASSERT(items.size() == 1);
		CVVVItem& item = items[0];

		long wEventId;
		if (CVVVPropertySheet(item, this).DoModal(hwnd, wEventId) > 0 && wEventId != 0)
		{
			CVVVFile vvvfile(GetPathFolderFile(), m_strSubFolder);
			vvvfile.SetItem(item);
		}

		return wEventId;
	}


	// Purpose: Called by MSF/shell when items must be deleted.
	long OnDelete(HWND hwnd, CVVVItems& items)
	{
		if (hwnd != NULL && !UserConfirmsFileDelete(hwnd, items))
			return 0; // user wants to abort the file deletion process.

		CVVVFile(GetPathFolderFile(), m_strSubFolder).DeleteItems(items);

		return SHCNE_DELETE;
	}


	// Purpose: called by the standard MSF drag handler during drag operations.
	bool IsSupportedClipboardFormat(IDataObject* pdataobject)
	{
		return CCfHDrop::IsFormat(pdataobject);
	}


	// Purpose: called when items are pasted or droped on the shellfolder.
	DWORD AddItemsFromDataObject(DWORD dwEffect, IDataObject* pdataobject)
	{
		CCfHDrop cfhdrop(pdataobject);

		unsigned int nFiles = cfhdrop.GetFileCount();
		for (unsigned int i = 0; i < nFiles; ++i)
		{
			AddItem(cfhdrop.GetFile(i));
		}

		// The VVV sample cannot use optimized move. Just return dwEffect as passed.
		return dwEffect;
	}


	void OnError(HRESULT hr, HWND hwnd, EErrorContext /*errorcontext*/)
	{
		CString strMsg = LoadString(IDS_SHELLFOLDER_CANNOT_PERFORM) + FormatLastError(static_cast<DWORD>(hr));

		IsolationAwareMessageBox(hwnd, strMsg,
			LoadString(IDS_SHELLEXT_ERROR_CAPTION), MB_OK | MB_ICONERROR);
	}

private:

	// Purpose: Ask the user if he is really sure about the file delete action.
	//          Deleted files cannot be restored from the recycle bin.
	bool UserConfirmsFileDelete(HWND hwnd, const CVVVItems& items)
	{
		CString strMessage;
		UINT    nCaptionResId;

		if (items.size() == 1)
		{
			strMessage.FormatMessage(IDS_SHELLFOLDER_DELETE, items[0].GetDisplayName().GetString());
			nCaptionResId = IDS_SHELLFOLDER_FILE_DELETE_CAPTION;
		}
		else
		{
			strMessage.FormatMessage(IDS_SHELLFOLDER_MULTIPLE_DELETE,
				ToString(static_cast<unsigned int>(items.size())).GetString());
			nCaptionResId = IDS_SHELLFOLDER_FILES_DELETE_CAPTION;
		}

		return IsolationAwareMessageBox(hwnd, strMessage,
			LoadString(nCaptionResId), MB_YESNO | MB_ICONQUESTION) == IDYES;
	}


	void AddItem(const CString& strFile)
	{
		const auto_ptr<CVVVItem> qitem(CVVVFile(GetPathFolderFile(), m_strSubFolder).AddItem(strFile));

		ReportAddItem(*qitem);
	}


	bool IsReadOnly(const CString& strFileName) const
	{
		DWORD dwAttributes = GetFileAttributes(strFileName);
		return (dwAttributes & FILE_ATTRIBUTE_READONLY) != 0;
	}


	// Member variables
	CString m_strSubFolder;
};


OBJECT_ENTRY_AUTO(__uuidof(CShellFolder), CShellFolder)

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Victor Derks
Software Developer (Senior) Schneider Electric
Netherlands Netherlands
Victor lives in Nijmegen, the oldest city in The Netherlands.
He studied Applied Physics in Delft and now works for GE Healthcare.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 18 Sep 2005
Article Copyright 2005 by Victor Derks
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid