.NET Context Menu Handler
April 7th, 2005 by EricI’ve been working on a context menu handler shell extension in C# lately. There are a few samples that people have produced on the web (for example, The Code Project, pek.com, and TheServerSide.net). I’ve found a better way of implementing IShellExtInit.Initialize than in the examples I’ve seen.
The point of IShellExtInit.Initialize is for the shell to let your handler know which files are selected so that you can display your context menu based on, believe it or not, context. Here is a typical implementation:
int IShellExtInit.Initialize (IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID) { try { // save the information about the selection m_dataObject = null; if (lpdobj != (IntPtr)0) { m_dataObject = (ShellLib.IDataObject)Marshal.GetObjectForIUnknown(lpdobj); FORMATETC fmt = new FORMATETC(); fmt.cfFormat = CLIPFORMAT.CF_HDROP; fmt.ptd = 0; fmt.dwAspect = DVASPECT.DVASPECT_CONTENT; fmt.lindex = -1; fmt.tymed = TYMED.TYMED_HGLOBAL; STGMEDIUM medium = new STGMEDIUM(); m_dataObject.GetData(ref fmt, ref medium); m_hDrop = medium.hGlobal; } } catch(Exception) { } return 0; }
Then later on, to get the selected file:
// Get the file name to work with StringBuilder sb = new StringBuilder(1024); Helpers.DragQueryFile(m_hDrop, 0, sb, sb.Capacity + 1);
There’s a fair amount of nasty interop going on here, which is sometimes inevitable for things like shell extensions. In this case, however, you can actually use the .NET Framework’s DataObject class instead for much simpler code:
int IShellExtInit.Initialize (IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID) { DataObject dataObject = new DataObject(lpdobj); string[] selectedFiles = (string[])dataObject.GetData(DataFormats.FileDrop); }
Though I’ve since learned that writing shell extensions in managed code is a Bad Thing. Raymond Chen mentions this in reference to a discussion thread on MSDN.
The trouble is that only one version of the CLR can be loaded into a process at a time. If you use .NET 2.0, but another extension uses 1.1, and that other extension gets loaded first, then you lose.