OleCmdHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / AppModel / OleCmdHelper.cs / 1 / OleCmdHelper.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//          This is a helper class used for interop to process the 
//          IOleCommandTarget calls in browser hosting scenario
// 
// History:
//  06/09/03: kusumav     Moved from Application.cs to separate file.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Diagnostics;
using System.Runtime.InteropServices; 

using System.Windows.Threading;
using System.Windows;
using System.Security; 
using System.Security.Permissions;
using System.Windows.Input; 
using System.Windows.Interop; 
using System.Windows.Navigation;
using System.Windows.Controls; 

using MS.Internal.Documents;                               // DocumentApplicationDocumentViewer
using MS.Internal.PresentationFramework;                   // SecurityHelper
using MS.Internal.KnownBoxes; 
using MS.Win32;
 
namespace MS.Internal.AppModel 
{
    #region OleCmdHelper class 
    // 
    // OleCmd helper class for processing IOleCommandTarget calls in browser hosting scenario
    // 
    internal sealed class OleCmdHelper : MarshalByRefObject, IOleCmdMappingService 
    {
        internal const int 
            OLECMDERR_E_NOTSUPPORTED = unchecked((int)0x80040100), 
            OLECMDERR_E_DISABLED     = unchecked((int)0x80040101),
            OLECMDERR_E_UNKNOWNGROUP = unchecked((int)0x80040104); 
        internal const uint CommandUnsupported = 0;
        internal const uint CommandEnabled = (uint)(UnsafeNativeMethods.OLECMDF.OLECMDF_ENABLED | UnsafeNativeMethods.OLECMDF.OLECMDF_SUPPORTED);
        internal const uint CommandDisabled = (uint)UnsafeNativeMethods.OLECMDF.OLECMDF_SUPPORTED;
 
        // IMPORTANT: Keep this in [....] with wcp\host\inc\hostservices.idl
        internal static readonly Guid CGID_ApplicationCommands = new Guid(0xebbc8a63, 0x8559, 0x4892, 0x97, 0xa8, 0x31, 0xe9, 0xb0, 0xe9, 0x85, 0x91); 
        internal static readonly Guid CGID_EditingCommands = new Guid(0xc77ce45, 0xd1c, 0x4f2a, 0xb2, 0x93, 0xed, 0xd5, 0xe2, 0x7e, 0xba, 0x47); 

        internal OleCmdHelper() 
        {
        }

        ///  
        ///     Critical:This code path enables you to add arbitrary commands
        ///     to a mapping table which is a critical resource 
        ///  
        [SecurityCritical]
        void IOleCmdMappingService.AddMapping(uint olecmd, RoutedCommand command) 
        {
            _oleCmdMappingTable.Value[olecmd] = command;
        }
 
        event EventHandler IOleCmdMappingService.UpdatingMappingTable
        { 
            add { _updateMappingTable += value; } 
            remove { _updateMappingTable -= value; }
        } 

        /// 
        ///     Critical: This code calls into _DoqueryStatus
        ///  
        /// 
        /// The native code passes queries here only for the recognized command groups: 
        /// standard (NULL), ApplicaitonCommands, EditingCommands. 
        /// 
        [SecurityCritical] 
        internal void QueryStatus(Guid guidCmdGroup, uint cmdId, ref uint flags)
        {
            /***IMPORTANT:
              Make sure to return allowed and appropriate values according to the specification of 
              IOleCommandTarget::QueryStatus(). In particular:
                - OLECMDF_SUPPORTED without OLECMDF_ENABLED should not be blindly returned for 
                    unrecognized commands. 
                - Some code in IE treats OLECMDERR_E_xxx differently from generic failures.
                - E_NOTIMPL is not an acceptable return value. 
            */

            if (Application.Current == null || Application.IsShuttingDown == true)
            { 
                Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
            } 
 
            // Get values from mapping here else mark them as disabled ==>
            // i.e "supported but not enabled" and is the equivalent of disabled since 
            // there is no explicit "disabled" OLECMD flag

            IDictionary oleCmdMappingTable = GetOleCmdMappingTable(guidCmdGroup);
            if (oleCmdMappingTable == null) 
            {
                Marshal.ThrowExceptionForHR(OleCmdHelper.OLECMDERR_E_UNKNOWNGROUP); 
            } 
            CommandWithArgument command = oleCmdMappingTable[cmdId] as CommandWithArgument;
            if (command == null) 
            {
                flags = CommandUnsupported;
                return;
            } 
            // Go through the Dispatcher in order to use its SynchronizationContext and also
            // so that any application exception caused during event routing is reported via 
            // Dispatcher.UnhandledException. 
            // The above code is not in the callback, because it throws, and we don't want the
            // application to get these exceptions. (The COM Interop layer turns them into HRESULTs.) 
            bool enabled = (bool)Application.Current.Dispatcher.Invoke(
                DispatcherPriority.Send, new DispatcherOperationCallback(QueryEnabled), command);
            flags = enabled ? CommandEnabled : CommandDisabled;
        } 

        ///  
        ///     Critical: Calls the critical CommandWithArgument.QueryEnabled(). 
        /// 
        [SecurityCritical] 
        private object QueryEnabled(object command)
        {
            if (Application.Current.MainWindow == null)
                return false; 
            IInputElement target = FocusManager.GetFocusedElement(Application.Current.MainWindow);
            if (target == null) 
            { 
                // This will always succeed because Window is IInputElement
                target = (IInputElement)Application.Current.MainWindow; 
            }
            return BooleanBoxes.Box(((CommandWithArgument)command).QueryEnabled(target, null));
        }
 
        /// 
        ///     Critical: This code calls into ExecCommandCallback helper 
        ///  
        /// 
        /// The native code passes here only commands of the recognized command groups: 
        /// standard (NULL), ApplicaitonCommands, EditingCommands.
        /// 
        [SecurityCritical]
        internal void ExecCommand(Guid guidCmdGroup, uint commandId, object arg) 
        {
            if (Application.Current == null || Application.IsShuttingDown == true) 
            { 
                Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
            } 

            int hresult = (int)Application.Current.Dispatcher.Invoke(
                DispatcherPriority.Send,
                new DispatcherOperationCallback(ExecCommandCallback), 
                new object[] { guidCmdGroup, commandId, arg });
            // Note: ExecCommandCallback() returns an HRESULT instead of throwing for the reason 
            // explained in QueryStatus(). 
            if(hresult < 0)
            { 
                Marshal.ThrowExceptionForHR(hresult);
            }
        }
 
        /// 
        ///    Critical:This API calls into Execute 
        ///  
        [SecurityCritical]
        private object ExecCommandCallback(object arguments) 
        {
            object[] args = (object[])arguments;
            Invariant.Assert(args.Length == 3);
            Guid guidCmdGroup = (Guid)args[0]; 
            uint commandId = (uint)args[1];
            object arg = args[2]; 
 
            IDictionary oleCmdMappingTable = GetOleCmdMappingTable(guidCmdGroup);
            if (oleCmdMappingTable == null) 
                return OLECMDERR_E_UNKNOWNGROUP;
            CommandWithArgument command = oleCmdMappingTable[commandId] as CommandWithArgument;
            if (command == null)
                return OLECMDERR_E_NOTSUPPORTED; 

            if (Application.Current.MainWindow == null) 
                return OLECMDERR_E_DISABLED; 
            IInputElement target = FocusManager.GetFocusedElement(Application.Current.MainWindow);
            if (target == null) 
            {
                // This will always succeed because Window is IInputElement
                target = (IInputElement)Application.Current.MainWindow;
            } 
            return command.Execute(target, arg) ? NativeMethods.S_OK : OLECMDERR_E_DISABLED;
        } 
 
        /// 
        ///    Critical:This API accesses the commandmapping table and returns it 
        ///    TreatAsSafe: It returns a copy which is safe
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private IDictionary GetOleCmdMappingTable(Guid guidCmdGroup) 
        {
            IDictionary mappingTable = null; 
 
            if (guidCmdGroup.Equals(CGID_ApplicationCommands))
            { 
                EnsureApplicationCommandsTable();
                mappingTable = _applicationCommandsMappingTable.Value;
            }
            else if(guidCmdGroup.Equals(Guid.Empty)) 
            {
                EnsureOleCmdMappingTable(); 
                mappingTable = _oleCmdMappingTable.Value; 
            }
            else if (guidCmdGroup.Equals(CGID_EditingCommands)) 
            {
                EnsureEditingCommandsTable();
                mappingTable = _editingCommandsMappingTable.Value;
            } 

            return mappingTable; 
        } 
        /// 
        ///     Critical: This code initializes the OleCmdMappingTable which is a critical for 
        ///     set data structure
        ///     TreatAsSafe: All the values that it adds are predefined handlers in this class
        ///     no external values
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private void EnsureOleCmdMappingTable() 
        { 
            if (_oleCmdMappingTable.Value == null)
            { 
                _oleCmdMappingTable.Value = new SortedList(10);

                //Add applevel commands here
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_SAVE, new CommandWithArgument(ApplicationCommands.Save)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_SAVEAS, new CommandWithArgument(ApplicationCommands.SaveAs));
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PRINT, new CommandWithArgument(ApplicationCommands.Print)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_CUT, new CommandWithArgument(ApplicationCommands.Cut)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_COPY, new CommandWithArgument(ApplicationCommands.Copy));
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PASTE, new CommandWithArgument(ApplicationCommands.Paste)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PROPERTIES, new CommandWithArgument(ApplicationCommands.Properties));

                //Set the Enabled property of Stop and Refresh commands correctly
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_REFRESH, new CommandWithArgument(NavigationCommands.Refresh)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_STOP, new CommandWithArgument(NavigationCommands.BrowseStop));
 
                //Ensure the mapping table is up-to-date taking into consideration 
                //any navigations that may be changed the input manager (which is currently
                //per root, so top level navs will require us to reconstruct the table) 
                UpdateMappingTable(_oleCmdMappingTable.Value);
            }
        }
        ///  
        ///     Critical: This code initializes the OleCmdMappingTable which is a critical for
        ///     set data structure 
        ///     TreatAsSafe: All the values that it adds are predefined handlers in this class 
        ///     no external values
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void EnsureApplicationCommandsTable()
        {
            if (_applicationCommandsMappingTable.Value == null) 
            {
                /* we want to possible add 26 entries, so the capacity should be 
                 * 26/0.72 = 19 for default of 1.0 load factor*/ 
                _applicationCommandsMappingTable.Value = new Hashtable(19);
 
                //Add applevel commands here
                // Note: The keys are added as uint type so that the default container comparer works
                // when we try to look up a command by a uint cmdid.
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Cut, new CommandWithArgument(ApplicationCommands.Cut)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Copy, new CommandWithArgument(ApplicationCommands.Copy));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Paste, new CommandWithArgument(ApplicationCommands.Paste)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_SelectAll, new CommandWithArgument(ApplicationCommands.SelectAll)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Find, new CommandWithArgument(ApplicationCommands.Find));
 
                // Add standard navigation commands
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Refresh, new CommandWithArgument(NavigationCommands.Refresh));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Stop, new CommandWithArgument(NavigationCommands.BrowseStop));
 
                // add document viewer commands
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_SignDocument, new CommandWithArgument(DocumentApplicationDocumentViewer.Sign)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_RequestSignature, new CommandWithArgument(DocumentApplicationDocumentViewer.RequestSigners)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_ViewSignature, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowSignatureSummary));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_Set, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMPublishingUI)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_View, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMPermissions));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_Restrict, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMCredentialManager));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_In, new CommandWithArgument(NavigationCommands.IncreaseZoom));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_Out, new CommandWithArgument(NavigationCommands.DecreaseZoom)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_400, new CommandWithArgument(NavigationCommands.Zoom, 400));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_250, new CommandWithArgument(NavigationCommands.Zoom, 250)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_150, new CommandWithArgument(NavigationCommands.Zoom, 150)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_100, new CommandWithArgument(NavigationCommands.Zoom, 100));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_75, new CommandWithArgument(NavigationCommands.Zoom, 75)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_50, new CommandWithArgument(NavigationCommands.Zoom, 50));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_25, new CommandWithArgument(NavigationCommands.Zoom, 25));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_PageWidth, new CommandWithArgument(DocumentViewer.FitToWidthCommand));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_WholePage, new CommandWithArgument(DocumentViewer.FitToHeightCommand)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_TwoPages, new CommandWithArgument(DocumentViewer.FitToMaxPagesAcrossCommand, 2));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_Thumbnails, new CommandWithArgument(DocumentViewer.ViewThumbnailsCommand)); 
 
                //Ensure the mapping table is up-to-date taking into consideration
                //any navigations that may be changed the input manager (which is currently 
                //per root, so top level navs will require us to reconstruct the table)
                UpdateMappingTable(_applicationCommandsMappingTable.Value);
            }
        } 

        ///  
        /// Critical: Initializes _editingCommandsMappingTable, which is a critical for set. 
        /// TreatAsSafe: Only predefined commands are used. EditingCommands are enabled in partial trust.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void EnsureEditingCommandsTable()
        {
            if (_editingCommandsMappingTable.Value == null) 
            {
                _editingCommandsMappingTable.Value = new SortedList(2); 
                // Note: The keys are added as uint type so that the default container comparer works 
                // when we try to look up a command by a uint cmdid.
                _editingCommandsMappingTable.Value.Add((uint)EditingCommandIds.Backspace, 
                    new CommandWithArgument(System.Windows.Documents.EditingCommands.Backspace));
                _editingCommandsMappingTable.Value.Add((uint)EditingCommandIds.Delete,
                    new CommandWithArgument(System.Windows.Documents.EditingCommands.Delete));
            } 
        }
 
        /// 
        /// Critical - creates a command binding. Performs an elevation to register the Binding.
        /// 
        [SecurityCritical]
        private void UpdateMappingTable(IDictionary mappingTable)
        {
            if (_updateMappingTable != null) 
            {
                _updateMappingTable(this, EventArgs.Empty); 
            } 

            Window window = Application.Current.MainWindow; 
            if (window != null)
            {
                window.InputBindings.Add(new KeyBinding(NavigationCommands.Refresh, Key.F5, ModifierKeys.Control));
                foreach (CommandWithArgument command in mappingTable.Values) 
                {
                    CommandBinding commandBinding ; 
 
                    //
                    // if this is a secure command - a demand will be performed at 
                    // registration of the command binding.
                    //  So we perform an assert.
                    //  Considered safe - as we're only allowing registration of the command ( e.g. paste)
                    // 
                    ISecureCommand secCommand = command.Command as ISecureCommand;
                    if ( secCommand != null ) 
                    { 
                        secCommand.UserInitiatedPermission.Assert();
                    } 

                    try
                    {
                        commandBinding = new CommandBinding(command.Command); 
                    }
                    finally 
                    { 
                        if (secCommand != null)
                            CodeAccessPermission.RevertAssert(); 
                    }

                    if (command.Command == ApplicationCommands.Properties)
                    { 
                        commandBinding.CanExecute += new CanExecuteRoutedEventHandler(OnPropertiesQueryEnabled);
                        commandBinding.Executed += new ExecutedRoutedEventHandler(OnPropertiesCommand); 
                    } 
                    window.CommandBindings.Add(commandBinding);
                } 
            }
        }

        ///  
        /// This is the method that gets called when ApplicationCommands.Properties ExecutedRoutedEventHandler get called.
        /// 
        /// assumptions: 
        /// It should only get called if we are hosted in browser.
        ///  
        private void OnPropertiesQueryEnabled(object sender, CanExecuteRoutedEventArgs args)
        {
            //only hookup the Properties Command if we are hosted in browser and this is a container
            bool isContainer = Application.Current.MimeType == MimeType.Document; 

            args.CanExecute = (BrowserInteropHelper.IsBrowserHosted && isContainer); 
        } 

        ///  
        /// This is the method that gets called when ApplicationCommands.PropertiesCommand
        /// CommandEventInvokeHandlers get called.
        ///
        /// assumptions: 
        /// It should only get called if we are hosted in browser, and if we are
        /// hosting a container. We do nothing for EXEs 
        ///  
        private void OnPropertiesCommand(object sender, ExecutedRoutedEventArgs execArgs)
        { 
            // todo:
            // Task:  40616: MetaData/Properties - View Document Properties
            // The plan for containers is to wire up a winforms dialog that can
            // show properties for a package.  I recommend having Application 
            // create a method that can be overridden by DocumentApplication
            // which can respond to this command. The call would look like: 
            // 
            // Application.OnPropertiesCommand(sender, execArgs);
            // 
            // The Application implementation would do nothing, satisfying xapp.
        }

        private SecurityCriticalDataForSet _oleCmdMappingTable; 
        private SecurityCriticalDataForSet _applicationCommandsMappingTable;
        private SecurityCriticalDataForSet _editingCommandsMappingTable; 
 
        private  EventHandler   _updateMappingTable;
    } 
    #endregion OleCmdHelper class

    #region IOleCmdMappingService interface
    internal interface IOleCmdMappingService 
    {
        void AddMapping(uint olecmd, RoutedCommand command); 
        event EventHandler UpdatingMappingTable; 
    }
    #endregion IOleCmdMappingService interface 

    #region CommandAndArgument class

    ///  
    /// This wrapper class helps store default arguments for commands.
    /// The primary scenario for this class is the Zoom command where we 
    /// have multiple menu items and want to fire a single event with an 
    /// argument.  We cannot attach an argument value to the native menu
    /// item so when we do the translation we add it. 
    /// 
    internal class CommandWithArgument
    {
        ///  
        ///     Critical: This can be used to spoof paste command
        ///  
        [SecurityCritical] 
        public CommandWithArgument(RoutedCommand command) : this(command, null)
        { } 

        /// 
        ///     Critical: This can be used to spoof paste command
        ///  
        [SecurityCritical]
        public CommandWithArgument(RoutedCommand command, object argument) 
        { 
            _command = new SecurityCriticalDataForSet(command);
            _argument = argument; 
        }

        /// 
        ///    Critical:This API calls into ExecuteCore and CriticalCanExecute 
        /// 
        [SecurityCritical] 
        public bool Execute(IInputElement target, object argument) 
        {
            if (argument == null) 
            {
                argument = _argument;
            }
            if (_command.Value == ApplicationCommands.Paste || _command.Value == ApplicationCommands.Copy) 
            {
                bool unused; 
                if (_command.Value.CriticalCanExecute(argument, target, true, out unused)) 
                {
                    _command.Value.ExecuteCore(argument, target, true); 
                    return true;
                }
                return false;
            } 
            if (_command.Value.CanExecute(argument, target))
            { 
                _command.Value.Execute(argument, target); 
                return true;
            } 
            return false;
        }

 
        /// 
        ///     Critical: This code calls into Routedcommand.QueryStatus 
        ///     with a trusted bit, that can be used to cause an elevation. 
        /// 
        [SecurityCritical] 
        public bool QueryEnabled(IInputElement target, object argument)
        {
            if (argument == null)
            { 
                argument = _argument;
            } 
            if (_command.Value == ApplicationCommands.Paste) 
            {
                bool unused; 
                return _command.Value.CriticalCanExecute(argument, target, true, out unused);
            }
            return _command.Value.CanExecute(argument, target);
 
        }
 
        public RoutedCommand Command 
        {
            get 
            {
                return _command.Value;
            }
        } 

        private object _argument; 
 
        /// 
        ///     Critical: This data is critical for set since it is used to make a security decision 
        /// 
        private SecurityCriticalDataForSet _command;
    }
 
    #endregion
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//          This is a helper class used for interop to process the 
//          IOleCommandTarget calls in browser hosting scenario
// 
// History:
//  06/09/03: kusumav     Moved from Application.cs to separate file.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Diagnostics;
using System.Runtime.InteropServices; 

using System.Windows.Threading;
using System.Windows;
using System.Security; 
using System.Security.Permissions;
using System.Windows.Input; 
using System.Windows.Interop; 
using System.Windows.Navigation;
using System.Windows.Controls; 

using MS.Internal.Documents;                               // DocumentApplicationDocumentViewer
using MS.Internal.PresentationFramework;                   // SecurityHelper
using MS.Internal.KnownBoxes; 
using MS.Win32;
 
namespace MS.Internal.AppModel 
{
    #region OleCmdHelper class 
    // 
    // OleCmd helper class for processing IOleCommandTarget calls in browser hosting scenario
    // 
    internal sealed class OleCmdHelper : MarshalByRefObject, IOleCmdMappingService 
    {
        internal const int 
            OLECMDERR_E_NOTSUPPORTED = unchecked((int)0x80040100), 
            OLECMDERR_E_DISABLED     = unchecked((int)0x80040101),
            OLECMDERR_E_UNKNOWNGROUP = unchecked((int)0x80040104); 
        internal const uint CommandUnsupported = 0;
        internal const uint CommandEnabled = (uint)(UnsafeNativeMethods.OLECMDF.OLECMDF_ENABLED | UnsafeNativeMethods.OLECMDF.OLECMDF_SUPPORTED);
        internal const uint CommandDisabled = (uint)UnsafeNativeMethods.OLECMDF.OLECMDF_SUPPORTED;
 
        // IMPORTANT: Keep this in [....] with wcp\host\inc\hostservices.idl
        internal static readonly Guid CGID_ApplicationCommands = new Guid(0xebbc8a63, 0x8559, 0x4892, 0x97, 0xa8, 0x31, 0xe9, 0xb0, 0xe9, 0x85, 0x91); 
        internal static readonly Guid CGID_EditingCommands = new Guid(0xc77ce45, 0xd1c, 0x4f2a, 0xb2, 0x93, 0xed, 0xd5, 0xe2, 0x7e, 0xba, 0x47); 

        internal OleCmdHelper() 
        {
        }

        ///  
        ///     Critical:This code path enables you to add arbitrary commands
        ///     to a mapping table which is a critical resource 
        ///  
        [SecurityCritical]
        void IOleCmdMappingService.AddMapping(uint olecmd, RoutedCommand command) 
        {
            _oleCmdMappingTable.Value[olecmd] = command;
        }
 
        event EventHandler IOleCmdMappingService.UpdatingMappingTable
        { 
            add { _updateMappingTable += value; } 
            remove { _updateMappingTable -= value; }
        } 

        /// 
        ///     Critical: This code calls into _DoqueryStatus
        ///  
        /// 
        /// The native code passes queries here only for the recognized command groups: 
        /// standard (NULL), ApplicaitonCommands, EditingCommands. 
        /// 
        [SecurityCritical] 
        internal void QueryStatus(Guid guidCmdGroup, uint cmdId, ref uint flags)
        {
            /***IMPORTANT:
              Make sure to return allowed and appropriate values according to the specification of 
              IOleCommandTarget::QueryStatus(). In particular:
                - OLECMDF_SUPPORTED without OLECMDF_ENABLED should not be blindly returned for 
                    unrecognized commands. 
                - Some code in IE treats OLECMDERR_E_xxx differently from generic failures.
                - E_NOTIMPL is not an acceptable return value. 
            */

            if (Application.Current == null || Application.IsShuttingDown == true)
            { 
                Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
            } 
 
            // Get values from mapping here else mark them as disabled ==>
            // i.e "supported but not enabled" and is the equivalent of disabled since 
            // there is no explicit "disabled" OLECMD flag

            IDictionary oleCmdMappingTable = GetOleCmdMappingTable(guidCmdGroup);
            if (oleCmdMappingTable == null) 
            {
                Marshal.ThrowExceptionForHR(OleCmdHelper.OLECMDERR_E_UNKNOWNGROUP); 
            } 
            CommandWithArgument command = oleCmdMappingTable[cmdId] as CommandWithArgument;
            if (command == null) 
            {
                flags = CommandUnsupported;
                return;
            } 
            // Go through the Dispatcher in order to use its SynchronizationContext and also
            // so that any application exception caused during event routing is reported via 
            // Dispatcher.UnhandledException. 
            // The above code is not in the callback, because it throws, and we don't want the
            // application to get these exceptions. (The COM Interop layer turns them into HRESULTs.) 
            bool enabled = (bool)Application.Current.Dispatcher.Invoke(
                DispatcherPriority.Send, new DispatcherOperationCallback(QueryEnabled), command);
            flags = enabled ? CommandEnabled : CommandDisabled;
        } 

        ///  
        ///     Critical: Calls the critical CommandWithArgument.QueryEnabled(). 
        /// 
        [SecurityCritical] 
        private object QueryEnabled(object command)
        {
            if (Application.Current.MainWindow == null)
                return false; 
            IInputElement target = FocusManager.GetFocusedElement(Application.Current.MainWindow);
            if (target == null) 
            { 
                // This will always succeed because Window is IInputElement
                target = (IInputElement)Application.Current.MainWindow; 
            }
            return BooleanBoxes.Box(((CommandWithArgument)command).QueryEnabled(target, null));
        }
 
        /// 
        ///     Critical: This code calls into ExecCommandCallback helper 
        ///  
        /// 
        /// The native code passes here only commands of the recognized command groups: 
        /// standard (NULL), ApplicaitonCommands, EditingCommands.
        /// 
        [SecurityCritical]
        internal void ExecCommand(Guid guidCmdGroup, uint commandId, object arg) 
        {
            if (Application.Current == null || Application.IsShuttingDown == true) 
            { 
                Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
            } 

            int hresult = (int)Application.Current.Dispatcher.Invoke(
                DispatcherPriority.Send,
                new DispatcherOperationCallback(ExecCommandCallback), 
                new object[] { guidCmdGroup, commandId, arg });
            // Note: ExecCommandCallback() returns an HRESULT instead of throwing for the reason 
            // explained in QueryStatus(). 
            if(hresult < 0)
            { 
                Marshal.ThrowExceptionForHR(hresult);
            }
        }
 
        /// 
        ///    Critical:This API calls into Execute 
        ///  
        [SecurityCritical]
        private object ExecCommandCallback(object arguments) 
        {
            object[] args = (object[])arguments;
            Invariant.Assert(args.Length == 3);
            Guid guidCmdGroup = (Guid)args[0]; 
            uint commandId = (uint)args[1];
            object arg = args[2]; 
 
            IDictionary oleCmdMappingTable = GetOleCmdMappingTable(guidCmdGroup);
            if (oleCmdMappingTable == null) 
                return OLECMDERR_E_UNKNOWNGROUP;
            CommandWithArgument command = oleCmdMappingTable[commandId] as CommandWithArgument;
            if (command == null)
                return OLECMDERR_E_NOTSUPPORTED; 

            if (Application.Current.MainWindow == null) 
                return OLECMDERR_E_DISABLED; 
            IInputElement target = FocusManager.GetFocusedElement(Application.Current.MainWindow);
            if (target == null) 
            {
                // This will always succeed because Window is IInputElement
                target = (IInputElement)Application.Current.MainWindow;
            } 
            return command.Execute(target, arg) ? NativeMethods.S_OK : OLECMDERR_E_DISABLED;
        } 
 
        /// 
        ///    Critical:This API accesses the commandmapping table and returns it 
        ///    TreatAsSafe: It returns a copy which is safe
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private IDictionary GetOleCmdMappingTable(Guid guidCmdGroup) 
        {
            IDictionary mappingTable = null; 
 
            if (guidCmdGroup.Equals(CGID_ApplicationCommands))
            { 
                EnsureApplicationCommandsTable();
                mappingTable = _applicationCommandsMappingTable.Value;
            }
            else if(guidCmdGroup.Equals(Guid.Empty)) 
            {
                EnsureOleCmdMappingTable(); 
                mappingTable = _oleCmdMappingTable.Value; 
            }
            else if (guidCmdGroup.Equals(CGID_EditingCommands)) 
            {
                EnsureEditingCommandsTable();
                mappingTable = _editingCommandsMappingTable.Value;
            } 

            return mappingTable; 
        } 
        /// 
        ///     Critical: This code initializes the OleCmdMappingTable which is a critical for 
        ///     set data structure
        ///     TreatAsSafe: All the values that it adds are predefined handlers in this class
        ///     no external values
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private void EnsureOleCmdMappingTable() 
        { 
            if (_oleCmdMappingTable.Value == null)
            { 
                _oleCmdMappingTable.Value = new SortedList(10);

                //Add applevel commands here
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_SAVE, new CommandWithArgument(ApplicationCommands.Save)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_SAVEAS, new CommandWithArgument(ApplicationCommands.SaveAs));
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PRINT, new CommandWithArgument(ApplicationCommands.Print)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_CUT, new CommandWithArgument(ApplicationCommands.Cut)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_COPY, new CommandWithArgument(ApplicationCommands.Copy));
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PASTE, new CommandWithArgument(ApplicationCommands.Paste)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_PROPERTIES, new CommandWithArgument(ApplicationCommands.Properties));

                //Set the Enabled property of Stop and Refresh commands correctly
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_REFRESH, new CommandWithArgument(NavigationCommands.Refresh)); 
                _oleCmdMappingTable.Value.Add((uint)UnsafeNativeMethods.OLECMDID.OLECMDID_STOP, new CommandWithArgument(NavigationCommands.BrowseStop));
 
                //Ensure the mapping table is up-to-date taking into consideration 
                //any navigations that may be changed the input manager (which is currently
                //per root, so top level navs will require us to reconstruct the table) 
                UpdateMappingTable(_oleCmdMappingTable.Value);
            }
        }
        ///  
        ///     Critical: This code initializes the OleCmdMappingTable which is a critical for
        ///     set data structure 
        ///     TreatAsSafe: All the values that it adds are predefined handlers in this class 
        ///     no external values
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void EnsureApplicationCommandsTable()
        {
            if (_applicationCommandsMappingTable.Value == null) 
            {
                /* we want to possible add 26 entries, so the capacity should be 
                 * 26/0.72 = 19 for default of 1.0 load factor*/ 
                _applicationCommandsMappingTable.Value = new Hashtable(19);
 
                //Add applevel commands here
                // Note: The keys are added as uint type so that the default container comparer works
                // when we try to look up a command by a uint cmdid.
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Cut, new CommandWithArgument(ApplicationCommands.Cut)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Copy, new CommandWithArgument(ApplicationCommands.Copy));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Paste, new CommandWithArgument(ApplicationCommands.Paste)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_SelectAll, new CommandWithArgument(ApplicationCommands.SelectAll)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Find, new CommandWithArgument(ApplicationCommands.Find));
 
                // Add standard navigation commands
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Refresh, new CommandWithArgument(NavigationCommands.Refresh));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Stop, new CommandWithArgument(NavigationCommands.BrowseStop));
 
                // add document viewer commands
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_SignDocument, new CommandWithArgument(DocumentApplicationDocumentViewer.Sign)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_RequestSignature, new CommandWithArgument(DocumentApplicationDocumentViewer.RequestSigners)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Digitalsignatures_ViewSignature, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowSignatureSummary));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_Set, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMPublishingUI)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_View, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMPermissions));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.Edit_Permission_Restrict, new CommandWithArgument(DocumentApplicationDocumentViewer.ShowRMCredentialManager));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_In, new CommandWithArgument(NavigationCommands.IncreaseZoom));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_Out, new CommandWithArgument(NavigationCommands.DecreaseZoom)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_400, new CommandWithArgument(NavigationCommands.Zoom, 400));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_250, new CommandWithArgument(NavigationCommands.Zoom, 250)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_150, new CommandWithArgument(NavigationCommands.Zoom, 150)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_100, new CommandWithArgument(NavigationCommands.Zoom, 100));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_75, new CommandWithArgument(NavigationCommands.Zoom, 75)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_50, new CommandWithArgument(NavigationCommands.Zoom, 50));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_25, new CommandWithArgument(NavigationCommands.Zoom, 25));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_PageWidth, new CommandWithArgument(DocumentViewer.FitToWidthCommand));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_WholePage, new CommandWithArgument(DocumentViewer.FitToHeightCommand)); 
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_TwoPages, new CommandWithArgument(DocumentViewer.FitToMaxPagesAcrossCommand, 2));
                _applicationCommandsMappingTable.Value.Add((uint)AppCommands.View_Zoom_Thumbnails, new CommandWithArgument(DocumentViewer.ViewThumbnailsCommand)); 
 
                //Ensure the mapping table is up-to-date taking into consideration
                //any navigations that may be changed the input manager (which is currently 
                //per root, so top level navs will require us to reconstruct the table)
                UpdateMappingTable(_applicationCommandsMappingTable.Value);
            }
        } 

        ///  
        /// Critical: Initializes _editingCommandsMappingTable, which is a critical for set. 
        /// TreatAsSafe: Only predefined commands are used. EditingCommands are enabled in partial trust.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void EnsureEditingCommandsTable()
        {
            if (_editingCommandsMappingTable.Value == null) 
            {
                _editingCommandsMappingTable.Value = new SortedList(2); 
                // Note: The keys are added as uint type so that the default container comparer works 
                // when we try to look up a command by a uint cmdid.
                _editingCommandsMappingTable.Value.Add((uint)EditingCommandIds.Backspace, 
                    new CommandWithArgument(System.Windows.Documents.EditingCommands.Backspace));
                _editingCommandsMappingTable.Value.Add((uint)EditingCommandIds.Delete,
                    new CommandWithArgument(System.Windows.Documents.EditingCommands.Delete));
            } 
        }
 
        /// 
        /// Critical - creates a command binding. Performs an elevation to register the Binding.
        /// 
        [SecurityCritical]
        private void UpdateMappingTable(IDictionary mappingTable)
        {
            if (_updateMappingTable != null) 
            {
                _updateMappingTable(this, EventArgs.Empty); 
            } 

            Window window = Application.Current.MainWindow; 
            if (window != null)
            {
                window.InputBindings.Add(new KeyBinding(NavigationCommands.Refresh, Key.F5, ModifierKeys.Control));
                foreach (CommandWithArgument command in mappingTable.Values) 
                {
                    CommandBinding commandBinding ; 
 
                    //
                    // if this is a secure command - a demand will be performed at 
                    // registration of the command binding.
                    //  So we perform an assert.
                    //  Considered safe - as we're only allowing registration of the command ( e.g. paste)
                    // 
                    ISecureCommand secCommand = command.Command as ISecureCommand;
                    if ( secCommand != null ) 
                    { 
                        secCommand.UserInitiatedPermission.Assert();
                    } 

                    try
                    {
                        commandBinding = new CommandBinding(command.Command); 
                    }
                    finally 
                    { 
                        if (secCommand != null)
                            CodeAccessPermission.RevertAssert(); 
                    }

                    if (command.Command == ApplicationCommands.Properties)
                    { 
                        commandBinding.CanExecute += new CanExecuteRoutedEventHandler(OnPropertiesQueryEnabled);
                        commandBinding.Executed += new ExecutedRoutedEventHandler(OnPropertiesCommand); 
                    } 
                    window.CommandBindings.Add(commandBinding);
                } 
            }
        }

        ///  
        /// This is the method that gets called when ApplicationCommands.Properties ExecutedRoutedEventHandler get called.
        /// 
        /// assumptions: 
        /// It should only get called if we are hosted in browser.
        ///  
        private void OnPropertiesQueryEnabled(object sender, CanExecuteRoutedEventArgs args)
        {
            //only hookup the Properties Command if we are hosted in browser and this is a container
            bool isContainer = Application.Current.MimeType == MimeType.Document; 

            args.CanExecute = (BrowserInteropHelper.IsBrowserHosted && isContainer); 
        } 

        ///  
        /// This is the method that gets called when ApplicationCommands.PropertiesCommand
        /// CommandEventInvokeHandlers get called.
        ///
        /// assumptions: 
        /// It should only get called if we are hosted in browser, and if we are
        /// hosting a container. We do nothing for EXEs 
        ///  
        private void OnPropertiesCommand(object sender, ExecutedRoutedEventArgs execArgs)
        { 
            // todo:
            // Task:  40616: MetaData/Properties - View Document Properties
            // The plan for containers is to wire up a winforms dialog that can
            // show properties for a package.  I recommend having Application 
            // create a method that can be overridden by DocumentApplication
            // which can respond to this command. The call would look like: 
            // 
            // Application.OnPropertiesCommand(sender, execArgs);
            // 
            // The Application implementation would do nothing, satisfying xapp.
        }

        private SecurityCriticalDataForSet _oleCmdMappingTable; 
        private SecurityCriticalDataForSet _applicationCommandsMappingTable;
        private SecurityCriticalDataForSet _editingCommandsMappingTable; 
 
        private  EventHandler   _updateMappingTable;
    } 
    #endregion OleCmdHelper class

    #region IOleCmdMappingService interface
    internal interface IOleCmdMappingService 
    {
        void AddMapping(uint olecmd, RoutedCommand command); 
        event EventHandler UpdatingMappingTable; 
    }
    #endregion IOleCmdMappingService interface 

    #region CommandAndArgument class

    ///  
    /// This wrapper class helps store default arguments for commands.
    /// The primary scenario for this class is the Zoom command where we 
    /// have multiple menu items and want to fire a single event with an 
    /// argument.  We cannot attach an argument value to the native menu
    /// item so when we do the translation we add it. 
    /// 
    internal class CommandWithArgument
    {
        ///  
        ///     Critical: This can be used to spoof paste command
        ///  
        [SecurityCritical] 
        public CommandWithArgument(RoutedCommand command) : this(command, null)
        { } 

        /// 
        ///     Critical: This can be used to spoof paste command
        ///  
        [SecurityCritical]
        public CommandWithArgument(RoutedCommand command, object argument) 
        { 
            _command = new SecurityCriticalDataForSet(command);
            _argument = argument; 
        }

        /// 
        ///    Critical:This API calls into ExecuteCore and CriticalCanExecute 
        /// 
        [SecurityCritical] 
        public bool Execute(IInputElement target, object argument) 
        {
            if (argument == null) 
            {
                argument = _argument;
            }
            if (_command.Value == ApplicationCommands.Paste || _command.Value == ApplicationCommands.Copy) 
            {
                bool unused; 
                if (_command.Value.CriticalCanExecute(argument, target, true, out unused)) 
                {
                    _command.Value.ExecuteCore(argument, target, true); 
                    return true;
                }
                return false;
            } 
            if (_command.Value.CanExecute(argument, target))
            { 
                _command.Value.Execute(argument, target); 
                return true;
            } 
            return false;
        }

 
        /// 
        ///     Critical: This code calls into Routedcommand.QueryStatus 
        ///     with a trusted bit, that can be used to cause an elevation. 
        /// 
        [SecurityCritical] 
        public bool QueryEnabled(IInputElement target, object argument)
        {
            if (argument == null)
            { 
                argument = _argument;
            } 
            if (_command.Value == ApplicationCommands.Paste) 
            {
                bool unused; 
                return _command.Value.CriticalCanExecute(argument, target, true, out unused);
            }
            return _command.Value.CanExecute(argument, target);
 
        }
 
        public RoutedCommand Command 
        {
            get 
            {
                return _command.Value;
            }
        } 

        private object _argument; 
 
        /// 
        ///     Critical: This data is critical for set since it is used to make a security decision 
        /// 
        private SecurityCriticalDataForSet _command;
    }
 
    #endregion
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK