Click here to Skip to main content
15,886,258 members
Articles / Programming Languages / C#

Implement an Autoplay Handler

Rate me:
Please Sign up or sign in to vote.
5.00/5 (22 votes)
18 Sep 2006CPOL8 min read 117.7K   1.7K   78  
Implementing an Autoplay handler in C#.
#define USE_REG_SCRIPT

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

using Microsoft.Win32;

using Almdal.RegistryScript;
using Almdal.SharedPipes;
/*******************************************************************************************
 *	Author: Tim Almdal
 *	Date: Sept, 2006
 *			tnalmdal (at) shaw-dot-net
 * 
 *	AutoPlayOnArrivalHandler class. Shell extension to be notified
 *  when need media is attached to the machine.
 * 
 *  Copyright (c) 2006 T. ALmdal
 *
 *  Permission is hereby granted, free of charge, to any person obtaining 
 *  a copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation 
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 *  and/or sell copies of the Software, and to permit persons to whom the 
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included 
 *  in all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
/*******************************************************************************************/
namespace Almdal.AutoPlayListener {
	[ComVisible(true),
	Guid("EC2A75BC-680C-4af0-B306-EEDF980C0AE3")]
	public class AutoPlayOnArrivalHandler : IDropTarget {
		protected const string guid = "{EC2A75BC-680C-4af0-B306-EEDF980C0AE3}";

		protected static uint AUTOPLAY_SHELLIDLISTS = WinApi.RegisterClipboardFormat(WinApi.CFSTR_AUTOPLAY_SHELLIDLISTS);

        /// <summary>
        /// This message is sent to the main window, when new files are detected on removable media
        /// </summary>
        public static int START_LOAD = (int)WinApi.RegisterWindowMessage("3B36DF78-BBA4-4d02-B50D-AF0FFB8DEF34");

        private SharedPipe _pipe = null;
        private StreamWriter _writer = null;

        #region constructor
		/// <summary>
		/// Constructor which craetes the anyonmous pipe
		/// </summary>
		public AutoPlayOnArrivalHandler() {
			//EventLog.WriteEntry("Almdal.AutoPlayOnArrivalHandler", "Constructor invokation");
            _pipe = new SharedPipe();
            _writer = _pipe.Writer;
        }

        #endregion

        #region IDropTarget Members

        public int DragEnter(IntPtr pDataObj, ulong grfKeyState, POINTL pt, ref ulong pdwEffect) {
            pdwEffect = (ulong)DROPEFFECT.COPY;
            return 0;
        }

        public int DragOver(ulong grfKeyState, POINTL pt, ref ulong pdwEffect) {
            pdwEffect = (ulong)DROPEFFECT.COPY;
            return 0;
        }

        public int DragLeave() {return 0;}

        public int Drop(IntPtr pDataObj, ulong grfKeyState, POINTL pt, ref ulong pdwEffect) {
			Process myProcess = FindOurExe();						//	Find the Loader Window

            WinApi.SendMessage(myProcess.MainWindowHandle,			//	Send the window the handle to
                               START_LOAD,							//	Read end of the pipe.
                               _pipe.ReadHandle(myProcess.Handle),
                               0);

            // Get info about the files that are coming in the droptarget interface
            IDataObject pdataobject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj);
            FORMATETC fmt = new FORMATETC();

			fmt.cfFormat = AUTOPLAY_SHELLIDLISTS;
            
            fmt.ptd = IntPtr.Zero;
            fmt.dwAspect = DVASPECT.DVASPECT_CONTENT;
            fmt.lindex = -1;
            fmt.tymed = TYMED.TYMED_HGLOBAL;

            int hr = pdataobject.QueryGetData(ref fmt);				//	Check that the format is the Autoplay shell IDlists

            if (hr == 0) {
                hr = HandleAutoplayShellIDList(pdataobject);		//	Process the ShellIDList format
            }
            else {
				throw new NotImplementedException();				//	we haven't implemented the clipboard format
            }

            Marshal.FinalReleaseComObject(pdataobject);

            return hr;
        }

        #endregion

		#region Find our Executable
		/// <summary>
		/// Retrieve the Load Window executable from the registry.  And then start it in a 
		/// seperate process.  Wait for the window to start.
		/// </summary>
		/// <returns>The process that contains the load window</returns>
		private Process StartOurExe() {
			Process myProcess = null;
			try {
                RegistryKey hklm = Registry.LocalMachine;
                RegistryKey rk = hklm.CreateSubKey("Software\\Almdal\\AutoPlayDemo");
                string executable = (string)rk.GetValue("{0F08197F-AF66-4198-9673-C5B5A33AACED}");
                if (rk.GetValue("{0F08197F-AF66-4198-9673-C5B5A33AACED}") == null) {
                    throw new Exception("Client Not defined, Run the Demo program first to initialize the location of the executable");
                }
                myProcess = Process.Start(executable);
				while (myProcess.MainWindowHandle == IntPtr.Zero) {
					myProcess.Refresh();
				}
			} catch (Exception ex) {
				EventLog.WriteEntry("Almdal.AutoPlayOnArrivalHandler", ex.Message, EventLogEntryType.Error);
                throw ex;
			}
			return myProcess;
		}

		/// <summary>
		/// Locates the Load window executable if it is already running or
		/// starts it if it isn't
		/// </summary>
		/// <returns></returns>
		private Process FindOurExe() {
			Process[] all = Process.GetProcesses();
			Process myProcess = null;
			foreach (Process p in all) {
				try {
					if ("Idle".Equals(p.ProcessName) || "System".Equals(p.ProcessName)) continue;
					if (p.MainModule.ModuleName.StartsWith("WindowsApplication2")) {
						myProcess = p;
						break;
					}
				} catch (Win32Exception ex) {
					EventLog.WriteEntry("Almdal.AutoPlayOnArrivalHandler", p.ProcessName + ": " + ex.Message, EventLogEntryType.Warning);
					throw ex;
				}
			}
			return (myProcess != null) ? myProcess : StartOurExe();	//	If it is not found, then start it
		}
        #endregion

        #region Handle Autoplay Drop
        /// <summary>
        /// Handles the autoplay drop target
        /// </summary>
        /// <param name="pdataobject">Shell Drop Data Object</param>
        /// <returns>An HRESULT (Error code)</returns>
        private int HandleAutoplayShellIDList(IDataObject pdataobject) {
            int hr = 0;
            try {
                FORMATETC fmt = new FORMATETC();
                fmt.cfFormat = AUTOPLAY_SHELLIDLISTS;
                fmt.ptd = IntPtr.Zero;
                fmt.dwAspect = DVASPECT.DVASPECT_CONTENT;
                fmt.lindex = -1;
                fmt.tymed = TYMED.TYMED_HGLOBAL;
                STGMEDIUM medium = new STGMEDIUM();

                hr = pdataobject.GetData(ref fmt, ref medium);
                if (hr == 0) {
					CIDA cida = (CIDA)WinApi.GlobalLock(medium.hGlobal);

                    if (!IntPtr.Zero.Equals(cida)) {                //  actually got the lock on the global memory
						hr = ProcessCIDA(cida);

						WinApi.GlobalUnlock(cida.Detach());
                    }
					WinApi.ReleaseStgMedium(ref medium);
                }

            } catch (Exception ex) {
                EventLog.WriteEntry("Almdal.AutoPlayOnArrivalHandler", "HandleAutoplayShellIDList: "+ex.Message, EventLogEntryType.Error);
            }
            return hr;
        }

		/// <summary>
		/// Loop over the PIDLs in tne CIDA and send the file names to loader window
		/// </summary>
		/// <param name="cida">A CIDA Structure</param>
		/// <returns>An HRESULT (Error code)</returns>
		private int ProcessCIDA(CIDA cida) {
			int hr = 0;
			int count = cida.Count;
			for (int iItem = 1; iItem < count; ++iItem) {//    item zero is the folder (so there count + 1 items
				IntPtr folder = (IntPtr)cida.Folder;
				IntPtr item = (IntPtr)cida[iItem];
				PIdl full = (PIdl)WinApi.ILCombine(folder, item);

				if (!full.isNull()) {
					IntPtr ptr;
					IntPtr pidlItem;
					hr = WinApi.SHBindToParent((IntPtr)full, WinApi.IID_IShellFolder, out ptr, out pidlItem);

					if (hr == 0) {
						IShellFolder psf = (IShellFolder)Marshal.GetObjectForIUnknown(ptr);

						STRRET strDisplayName;

						// Or this in if you only want the filename: SHGDN_INFOLDER
						hr = (int)((IShellFolder)psf).GetDisplayNameOf(pidlItem, SHGNO.SHGDN_FORPARSING, out strDisplayName);

						if (hr == 0) {
							StringBuilder szDisplayName = new StringBuilder(WinApi.MAX_PATH);

							hr = WinApi.StrRetToBSTR(ref strDisplayName, pidlItem, out szDisplayName);

							if (hr == 0) {				//	If we actuallygot the file namethen ship it to the loader window
								_writer.WriteLine(szDisplayName.ToString());
								_writer.Flush();
							}
						}
						Marshal.FinalReleaseComObject(psf);
					}

					WinApi.ILFree(full.Detach());
				}
			}
			return hr;
		}
        #endregion

        #region Registration
        [System.Runtime.InteropServices.ComRegisterFunctionAttribute()]
        static void RegisterServer(String str1) {
            try {
#if USE_REG_SCRIPT
                DoRegistration(true);
#else
				RegistryKey hkcr = Registry.ClassesRoot;
				RegistryKey rk = hkcr.CreateSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler.1");
				rk.SetValue("", "Demo autoplay handler");
				RegistryKey rk2 = rk.CreateSubKey("CLSID");
				rk2.SetValue("", guid);
				rk2.Close();
				rk.Close();

				rk = hkcr.CreateSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler");
				rk.SetValue("", "Demo autoplay handler");
				rk2 = rk.CreateSubKey("CLSID");
				rk2.SetValue("", guid);
				rk2.Close();

				rk2 = rk.CreateSubKey("CurVer");
				rk2.SetValue("", "Almdal.AutoPlayListener.AutoPlayOnArrivalHandler.1");
				rk2.Close();

				rk2 = rk.CreateSubKey("shell\\import\\DropTarget");
				rk2.SetValue("CLSID", guid);
				rk2.Close();
				rk.Close();

				hkcr.Close();

				RegistryKey hklm = Registry.LocalMachine;
				rk = hklm.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers");
				rk2 = rk.OpenSubKey("EventHandlers\\ShowPicturesOnArrival", true);
				rk2.SetValue("DemoAutoPlayOnArrival", "");
				rk2.Close();

				rk2 = rk.OpenSubKey("Handlers", true);
				RegistryKey rk3 = rk2.CreateSubKey("DemoAutoPlayOnArrival");
				rk3.SetValue("Action", "Load Files");

				//  http://www.kennyandkarin.com/Kenny/CodeCorner/Tools/IconBrowser/
				//  resource ID=32512, dllLoc
				string dllLoc = typeof(AutoPlayOnArrivalHandler).Assembly.Location;
				StringBuilder icon = new StringBuilder(dllLoc).Append(", 32512");
				rk3.SetValue("DefaultIcon", icon.ToString());
				rk3.SetValue("InvokeProgID", "Almdal.AutoPlayListener.AutoPlayOnArrivalHandler");
				rk3.SetValue("InvokeVerb", "import");
				rk3.SetValue("Provider", "Autoplay Demo Loader");
				rk3.Close();
				rk2.Close();
				rk.Close();
				hklm.Close();
#endif
            } catch (Exception e) {
                System.Console.WriteLine(e.ToString());
            }
        }

        [System.Runtime.InteropServices.ComUnregisterFunctionAttribute()]
        static void UnregisterServer(String str1) {
            try {
#if USE_REG_SCRIPT
                DoRegistration(false);
#else
                RegistryKey hkcr = Registry.ClassesRoot;
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler.1\\CLSID", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler.1", false);

				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler\\CLSID", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler\\CurVer", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler\\shell\\import\\DropTarget", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler\\shell\\import", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler\\shell", false);
				hkcr.DeleteSubKey("Almdal.AutoPlayListener.AutoPlayOnArrivalHandler", false);
				hkcr.Close();

                RegistryKey hklm = Registry.LocalMachine;
                RegistryKey rk = hklm.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers");
                RegistryKey rk2 = rk.OpenSubKey("EventHandlers\\ShowPicturesOnArrival", true);
				rk2.DeleteValue("DemoAutoPlayOnArrival");
                rk2.Close();

                rk2 = rk.OpenSubKey("Handlers", true);
				rk2.DeleteSubKey("DemoAutoPlayOnArrival", false);
                rk2.Close();
                rk.Close();
                hklm.Close();
#endif
            } catch (Exception e) {
                System.Console.WriteLine(e.ToString());
            }
        }

#if USE_REG_SCRIPT
        /// <summary>
        /// Private method to register or unregister the shell extension.
        /// 
        /// the HKCR\CLSID\guid key is inserted into the registry by the .Net registration
        /// framework.  See the Dino Esposito's article "Write Shell Extensions with C#
        /// http://www.theserverside.net/tt/articles/showarticle.tss?id=ShellExtensions
        /// for a detail discussion of C# shell extension registry.
        /// 
        /// The rest of the registration is to hook the shell extension into Autoplay handlers.
        /// </summary>
        /// <param name="register">Flag to indicate whether to register (true) the shell extension or unregister (false)</param>
        private static void DoRegistration(bool register) {
            string regScript = Almdal.AutoPlayListener.Properties.Resources.RegistryScript;
            CRegistryScript script = new CRegistryScript();

            //  the HKCR\CLSID\guid key is inserted into the registry by
            //  the .Net framework.  See the Dino Esposito's article
            //  http://www.theserverside.net/tt/articles/showarticle.tss?id=ShellExtensions
            //  for more details on creating a shell extension in C#.
            script.AddVariable("PROGID", typeof(AutoPlayOnArrivalHandler).FullName);
            script.AddVariable("VERSION", FileVersion);
            script.AddVariable("DESCRIPTION", "Demo autoplay handler");
            script.AddVariable("CLSID", typeof(AutoPlayOnArrivalHandler).GUID.ToString("B"));
            script.AddVariable("HANDLERNAME", "DemoAutoPlayOnArrival");
            script.AddVariable("Action", "Load Files");
            script.AddVariable("Provider", "Autoplay Demo File Loader");
            script.AddVariable("InvokeVerb", "import");
            script.AddVariable("ICON", "32512");
            script.AddVariable("MODULE", typeof(AutoPlayOnArrivalHandler).Assembly.Location);

            if (register) {
                script.Register(regScript);
            }
            else {
                script.Unregister(regScript);
            }
        }

        /// <summary>
        /// Static property to retrieve the Assembly File Version from the assembly
        /// </summary>
        private static string FileVersion {
            get {
                object[] attrs = typeof(AutoPlayOnArrivalHandler).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
                return (attrs.Length > 0) ? ((AssemblyFileVersionAttribute)attrs[0]).Version : "1";
            }
        }
#endif
        #endregion
    }
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions