#if PREVIEW_HANDLER using Microsoft.Win32; using ShellExtensions.Interop; using ShellExtensions.Interop.Common; using ShellExtensions.Resources; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace ShellExtensions { /// /// This is the base class for all preview handlers and provides their basic functionality. /// To create a custom preview handler a class must derive from this, use the , /// and implement 1 or more of the following interfaces: /// , /// , /// . /// public abstract class PreviewHandler : ICustomQueryInterface, IPreviewHandler, IPreviewHandlerVisuals, IOleWindow, IObjectWithSite, IInitializeWithStream, IInitializeWithFile { private bool _isPreviewShowing; private IntPtr _parentHwnd; private IPreviewHandlerFrame _frame; /// /// Gets whether the preview is currently showing /// public bool IsPreviewShowing => _isPreviewShowing; /// /// Called immediately before the preview is to be shown. /// protected virtual void Initialize() { } /// /// Called when the preview is no longer shown. /// protected virtual void Uninitialize() { } #region Required functions - Abstract functions /// /// This should return the window handle to be displayed in the Preview. /// protected abstract IntPtr Handle { get; } /// /// Called to update the bounds and position of the preview control /// /// protected abstract void UpdateBounds(NativeRect bounds); /// /// Called when an exception occurs during the initialization of the control /// /// protected abstract void HandleInitializeException(Exception caughtException); /// /// Called when the preview control obtains focus. /// protected abstract void SetFocus(); /// /// Called when a request is received to set or change the background color according to the user's preferences. /// /// An int representing the ARGB color protected abstract void SetBackground(int argb); /// /// Called when a request is received to set or change the foreground color according to the user's preferences. /// /// An int representing the ARGB color protected abstract void SetForeground(int argb); /// /// Called to set the font of the preview control according to the user's preferences. /// /// protected abstract void SetFont(LogFont font); /// /// Called to set the parent of the preview control. /// /// protected abstract void SetParentHandle(IntPtr handle); #endregion #region IPreviewHandler Members void IPreviewHandler.SetWindow(IntPtr hwnd, ref NativeRect rect) { _parentHwnd = hwnd; UpdateBounds(rect); SetParentHandle(_parentHwnd); } void IPreviewHandler.SetRect(ref NativeRect rect) { UpdateBounds(rect); } void IPreviewHandler.DoPreview() { _isPreviewShowing = true; try { Initialize(); } catch (Exception exc) { HandleInitializeException(exc); } } void IPreviewHandler.Unload() { Uninitialize(); _isPreviewShowing = false; } void IPreviewHandler.SetFocus() { SetFocus(); } void IPreviewHandler.QueryFocus(out IntPtr phwnd) { phwnd = HandlerNativeMethods.GetFocus(); } HResult IPreviewHandler.TranslateAccelerator(ref Message pmsg) { return _frame != null ? _frame.TranslateAccelerator(ref pmsg) : HResult.False; } #endregion #region IPreviewHandlerVisuals Members void IPreviewHandlerVisuals.SetBackgroundColor(NativeColorRef color) { SetBackground((int)color.Dword); } void IPreviewHandlerVisuals.SetTextColor(NativeColorRef color) { SetForeground((int)color.Dword); } void IPreviewHandlerVisuals.SetFont(ref LogFont plf) { SetFont(plf); } #endregion #region IOleWindow Members void IOleWindow.GetWindow(out IntPtr phwnd) { phwnd = Handle; } void IOleWindow.ContextSensitiveHelp(bool fEnterMode) { // Preview handlers don't support context sensitive help. (As far as I know.) throw new NotImplementedException(); } #endregion #region IObjectWithSite Members void IObjectWithSite.SetSite(object pUnkSite) { _frame = pUnkSite as IPreviewHandlerFrame; } void IObjectWithSite.GetSite(ref Guid riid, out object ppvSite) { ppvSite = _frame; } #endregion #region IInitializeWithStream Members void IInitializeWithStream.Initialize(System.Runtime.InteropServices.ComTypes.IStream stream, AccessModes fileMode) { IPreviewFromStream preview = this as IPreviewFromStream; if (preview == null) { throw new InvalidOperationException( string.Format(System.Globalization.CultureInfo.InvariantCulture, LocalizedMessages.PreviewHandlerUnsupportedInterfaceCalled, nameof(IPreviewFromStream))); } using (var storageStream = new StorageStream(stream, fileMode != AccessModes.ReadWrite)) { preview.Load(storageStream); } } #endregion #region IInitializeWithFile Members void IInitializeWithFile.Initialize(string filePath, AccessModes fileMode) { IPreviewFromFile preview = this as IPreviewFromFile; if (preview == null) { throw new InvalidOperationException( string.Format(System.Globalization.CultureInfo.InvariantCulture, LocalizedMessages.PreviewHandlerUnsupportedInterfaceCalled, nameof(IPreviewFromFile))); } preview.Load(new FileInfo(filePath)); } #endregion #region ComRegistration /// /// Called when the assembly is registered via RegAsm. /// /// Type to register. [ComRegisterFunction] private static void Register(Type registerType) { if (registerType != null && registerType.IsSubclassOf(typeof(PreviewHandler))) { object[] attrs = registerType.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); if (attrs != null && attrs.Length == 1) { PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; ThrowIfNotValid(registerType); RegisterPreviewHandler(registerType.GUID, attr); } else { throw new NotSupportedException( string.Format(System.Globalization.CultureInfo.InvariantCulture, LocalizedMessages.PreviewHandlerInvalidAttributes, registerType.Name)); } } } /// /// Called when the assembly is Unregistered via RegAsm. /// /// Type to unregister [ComUnregisterFunction] private static void Unregister(Type registerType) { if (registerType != null && registerType.IsSubclassOf(typeof(PreviewHandler))) { object[] attrs = registerType.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); if (attrs != null && attrs.Length == 1) { PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; UnregisterPreviewHandler(registerType.GUID, attr); } } } private static void RegisterPreviewHandler(Guid previewerGuid, PreviewHandlerAttribute attribute) { string guid = previewerGuid.ToString("B"); // Create a new prevhost AppID so that this always runs in its own isolated process using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) using (RegistryKey appIdKey = appIdsKey.CreateSubKey(attribute.AppId)) { appIdKey.SetValue("DllSurrogate", @"%SystemRoot%\system32\prevhost.exe", RegistryValueKind.ExpandString); } // Add preview handler to preview handler list using (RegistryKey handlersKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) { handlersKey.SetValue(guid, attribute.Name, RegistryValueKind.String); } // Modify preview handler registration using (RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID")) using (RegistryKey idKey = clsidKey.OpenSubKey(guid, true)) { idKey.SetValue("DisplayName", attribute.Name, RegistryValueKind.String); idKey.SetValue("AppID", attribute.AppId, RegistryValueKind.String); idKey.SetValue("DisableLowILProcessIsolation", attribute.DisableLowILProcessIsolation ? 1 : 0, RegistryValueKind.DWord); using (RegistryKey inproc = idKey.OpenSubKey("InprocServer32", true)) { inproc.SetValue("ThreadingModel", "Apartment", RegistryValueKind.String); } } foreach (string extension in attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { Trace.WriteLine("Registering extension '" + extension + "' with previewer '" + guid + "'"); // Set preview handler for specific extension using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(extension)) using (RegistryKey shellexKey = extensionKey.CreateSubKey("shellex")) using (RegistryKey previewKey = shellexKey.CreateSubKey(HandlerNativeMethods.IPreviewHandlerGuid.ToString("B"))) { previewKey.SetValue(null, guid, RegistryValueKind.String); } } } private static void UnregisterPreviewHandler(Guid previewerGuid, PreviewHandlerAttribute attribute) { string guid = previewerGuid.ToString("B"); foreach (string extension in attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { Trace.WriteLine("Unregistering extension '" + extension + "' with previewer '" + guid + "'"); using (RegistryKey shellexKey = Registry.ClassesRoot.OpenSubKey(extension + "\\shellex", true)) { shellexKey.DeleteSubKey(HandlerNativeMethods.IPreviewHandlerGuid.ToString(), false); } } using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) { appIdsKey.DeleteSubKey(attribute.AppId, false); } using (RegistryKey classesKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) { classesKey.DeleteValue(guid, false); } } private static void ThrowIfNotValid(Type type) { var interfaces = type.GetInterfaces(); if (!interfaces.Any(x => x == typeof(IPreviewFromStream) || x == typeof(IPreviewFromFile))) { throw new NotImplementedException( string.Format(System.Globalization.CultureInfo.InvariantCulture, LocalizedMessages.PreviewHandlerInterfaceNotImplemented, type.Name)); } } #endregion #region ICustomQueryInterface Members CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) { ppv = IntPtr.Zero; // Forces COM to not use the managed (free threaded) marshaler if (iid == HandlerNativeMethods.IMarshalGuid) { return CustomQueryInterfaceResult.Failed; } if ((iid == HandlerNativeMethods.IInitializeWithStreamGuid && !(this is IPreviewFromStream)) || (iid == HandlerNativeMethods.IInitializeWithFileGuid && !(this is IPreviewFromFile))) { return CustomQueryInterfaceResult.Failed; } return CustomQueryInterfaceResult.NotHandled; } #endregion } } #endif