|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionHere in Part VI of the Guide, I'll introduce a lesser-used type of shell extension, the drop handler. This type can be used to add drag-and-drop functionality to Explorer, where the file being dropped on (the drop target) determines the extension that gets invoked. This article assumes that you understand the basics of shell extensions, and are familiar with the MFC classes used to interact with the shell. If you need a refresher on the MFC classes, you should read Part IV, since the same techniques will be used in this article. Remember that VC 7 (and probably VC 8) users will need to change some settings before compiling. See the README section in Part I for the details. Back in Part IV, I talked about the drag and drop handler, which is invoked during a right-button drag-and-drop operation. Explorer also lets us write an extension that is invoked during a left-button drag-and-drop operation, where the extension operates on the file that is being dropped on. For example, WinZip contains a drop handler that lets you add files to a zip by dropping them on the zip. When you drag files over a zip, Explorer indicates that the zip is a drop target by highlighting the zip file and changing the cursor so it has a + icon:
If there were no drop handler, then nothing would happen when you tried to drag files over a zip:
Drop handlers are really only useful in this way if you have your own file type, like WinZip does. A much more
interesting thing to do with drop handlers is add items to the Send To menu. The Send To menu shows
the contents of the
If you don't seen how drop handlers figure into this, take a look at a directory listing of the 12/23/05 3:39a 129 3½ Floppy (A).lnk 12/28/05 1:42p 0 Desktop (create shortcut).DeskLink 12/28/05 1:42p 0 Mail Recipient.MAPIMail 12/23/05 12:31p 0 My Documents.mydocs 5/25/06 11:05a 1,267 Notepad.lnk Notice those weird extensions like ".DeskLink". Those zero-byte files are there to cause the items to appear in the Send To menu, and the extensions are listed in the registry. They are not normal associations, though, since the files have no verbs like open or print. What they do have are drop handlers. When you select one of those items on the Send To menu, Explorer invokes the associated drop handler. Here's another look at the menu, with the drop targets indicated:
This article's sample project will be a clone of the old "Send To Any Folder" Powertoy - it will copy or move files to any directory on your computer. The Initialization InterfaceYou should be familiar with the set-up steps now, so I'll skip the instructions for going through the VC wizards.
If you're following along in the wizards, make a new ATL COM app called SendToClone, with a C++ implementation
class Since a drop handler is invoked on just one file (the file being dropped on), initialization is done through
the We'll need to add #include <comdef.h> #include <shlobj.h> class CSendToShlExt : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSendToShlExt, &CLSID_SendToShlExt>, public IPersistFile { BEGIN_COM_MAP(CSendToShlExt) COM_INTERFACE_ENTRY(IPersistFile) END_COM_MAP() public: // IPersistFile STDMETHOD(GetClassID)(LPCLSID) { return E_NOTIMPL; } STDMETHOD(IsDirty)() { return E_NOTIMPL; } STDMETHOD(Load)(LPCOLESTR, DWORD) { return S_OK; } STDMETHOD(Save)(LPCOLESTR, BOOL) { return E_NOTIMPL; } STDMETHOD(SaveCompleted)(LPCOLESTR) { return E_NOTIMPL; } STDMETHOD(GetCurFile)(LPOLESTR*) { return E_NOTIMPL; } Notice that Taking Part in the Drag and Drop OperationIn order to do its job, our extension has to communicate with the drop source, which is Explorer itself. Our
extension gets a list of files being dragged, and it tells Explorer whether it will accept the files if the user
drops them. This communication happens through an interface, naturally:
To add class CSendToShlExt : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSendToShlExt, &CLSID_SendToShlExt>, public IPersistFile, public IDropTarget { BEGIN_COM_MAP(CSendToShlExt) COM_INTERFACE_ENTRY(IPersistFile) COM_INTERFACE_ENTRY(IDropTarget) END_COM_MAP() public: // IDropTarget STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { return E_NOTIMPL; } STDMETHODIMP DragLeave(); STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); protected: CStringList m_lsDroppedFiles; } As in the earlier extensions, we'll use a list of strings to hold the names of the files being dragged. The
DragEnter()The prototype for HRESULT IDropTarget::DragEnter ( IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect );
As I mentioned earlier, Our
STDMETHODIMP CSendToShlExt::DragEnter (
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect )
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
COleDataObject dataobj;
TCHAR szItem[MAX_PATH];
UINT uNumFiles;
HGLOBAL hglobal;
HDROP hdrop;
dataobj.Attach ( pDataObj, FALSE );
// Read the list of items from the data object. They're stored in HDROP
// form, so just get the HDROP handle and then use the drag 'n' drop APIs
// on it.
hglobal = dataobj.GetGlobalData ( CF_HDROP );
if ( NULL != hglobal )
{
hdrop = (HDROP) GlobalLock ( hglobal );
uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 );
for ( UINT uFile = 0; uFile < uNumFiles; uFile++ )
{
if ( 0 != DragQueryFile ( hdrop, uFile, szItem, MAX_PATH ))
m_lsDroppedFiles.AddTail ( szItem );
}
GlobalUnlock ( hglobal );
}
Now it's time to return a value in
The only effect we'll return is if ( m_lsDroppedFiles.GetCount() > 0 ) { *pdwEffect = DROPEFFECT_COPY; return S_OK; } else { *pdwEffect = DROPEFFECT_NONE; return E_INVALIDARG; } } DragLeave()
STDMETHODIMP CSendToShlExt::DragLeave()
{
return S_OK;
}
Drop()If the user selects our Send To menu item, Explorer calls HRESULT IDropTarget::Drop (
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect );
The first three parameters are the same as STDMETHODIMP CSendToShlExt::Drop (
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect )
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
CSendToCloneDlg dlg ( &m_lsDroppedFiles );
dlg.DoModal();
*pdwEffect = DROPEFFECT_COPY;
return S_OK;
}
The dialog looks like this:
It's a pretty straightforward MFC dialog, and you can find the source in the SendToCloneDlg.cpp file.
I did the actual moving and copying using the As with the extension in Part II, we need to add a manifest to the DLL's
resources so our dialog gets themed on XP. However, just adding the manifest isn't enough in this case, because
the code that creates and manages the dialog is in MFC. MFC isn't compiled with the There is a good explanation of the situation in a post from Dave Anderson in this
newsgroup thread. To summarize, before we show the dialog, we need to use the activation context APIs
so Windows uses our manifest and uses version 6 of the common controls for our dialog. This code is encapsulated
in the STDMETHODIMP CSendToShlExt::Drop(...)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
CSendToCloneDlg dlg ( &m_lsDroppedFiles );
CAxtCtx ctx;
dlg.DoModal();
*pdwEffect = DROPEFFECT_COPY;
return S_OK;
}"/shell/cshellfileop.asp">
After adding this code, the dialog becomes properly themed:
But wait -- how do we tell Explorer about our drop handler? And how do we get an item in the Send To menu? I'll explain how to do this in the next section. Registering the ExtensionRegistering a drop handler is a bit different than other types of extensions, since it requires creating a new
association under HKCR
{
.SendToClone = s 'CLSID\{B7F3240E-0E29-11D4-8D3B-80CD3621FB09}'
NoRemove CLSID
{
ForceRemove {B7F3240E-0E29-11D4-8D3B-80CD3621FB09} = s 'Send To Any Folder Clone'
{
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
val NeverShowExt = s ''
DefaultIcon = s '%MODULE%,0'
shellex
{
DropHandler = s '{B7F3240E-0E29-11D4-8D3B-80CD3621FB09}'
}
}
}
}
The first line is the one that makes the association. It creates a new extension, The "Send To Any Folder Clone" string is the file type description that appears in Explorer if you
browse to the The only remaining detail is to create a file in the LPITEMIDLIST pidl; TCHAR szSendtoPath[MAX_PATH]; HANDLE hFile; LPMALLOC pMalloc; if ( SUCCEEDED( SHGetSpecialFolderLocation ( NULL, CSIDL_SENDTO, &pidl ) )) { if ( SHGetPathFromIDList ( pidl, szSendtoPath ) ) { PathAppend ( szSendtoPath, _T("Some other folder.SendToClone") ); hFile = CreateFile ( szSendtoPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); CloseHandle ( hFile ); } if ( SUCCEEDED( SHGetMalloc ( &pMalloc ) )) { pMalloc->Free ( pidl ); pMalloc->Release(); } } Here's what the new Send To menu item looks like, with our own item:
As always, on NT-based OSes, we need to add our extension to the list of "approved" extensions. The
code to do this is in the To Be Continued...Coming up in Part VII, I'll answer some reader requests and demonstrate two new variations of context menu extensions. Copyright and LicenseThis article is copyrighted material, ©2000-2006 by Michael Dunn. I realize this isn't going to stop people from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation, I would just like to be aware of the translation so I can post a link to it here. The demo code that accompanies this article is released to the public domain. I release it this way so that the code can benefit everyone. (I don't make the article itself public domain because having the article available only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are benefitting from my code) but is not required. Attribution in your own source code is also appreciated but not required. Revision HistoryApril 20, 2000: Article first published. | ||||||||||||||||||||