#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
}
}