File: System\Windows\OleServicesContext.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// Description: Ole Services for DragDrop and Clipboard.
 
using MS.Win32;
using MS.Internal;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Input;
 
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
 
namespace System.Windows
{
    //------------------------------------------------------
    //
    //  OleServicesContext class
    //
    //------------------------------------------------------
 
    /// <summary>
    /// This class manages Ole services for DragDrop and Clipboard.
    /// The instance of OleServicesContext class is created per Thread...Dispatcher.
    /// </summary>
    /// <remarks>
    /// </remarks>
    // threading issues
    //  - This class needs to be modified to marshal calls over to the Dispatcher/STA thread.
    //  - Once we have an event we can listen to when a Dispatcher
    //    shuts down, we should use that.  We currently listen to Dispatcher shutdown, which has no thread
    //    affinity -- it could happen on any thread, which breaks us.
    internal class OleServicesContext
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Instantiates a OleServicesContext.
        /// </summary>
        private OleServicesContext()
        {
            // We need to get the Dispatcher Thread in order to get OLE DragDrop and Clipboard services that
            // require STA.
            SetDispatcherThread();
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Get the ole services context associated with the current Thread.
        /// </summary>
        internal static OleServicesContext CurrentOleServicesContext
        {
            get
            {
                OleServicesContext oleServicesContext;
 
                // Get the ole services context from the Thread data slot.
                oleServicesContext = (OleServicesContext)Thread.GetData(OleServicesContext._threadDataSlot);
 
                if (oleServicesContext == null)
                {
                    // Create OleSErvicesContext instance.
                    oleServicesContext = new OleServicesContext();
 
                    // Save the ole services context into the UIContext data slot.
                    Thread.SetData(OleServicesContext._threadDataSlot, oleServicesContext);
                }
 
                return oleServicesContext;
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// OleSetClipboard - Call OLE Interopo OleSetClipboard()
        /// </summary>
        internal int OleSetClipboard(IComDataObject dataObject)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.OleSetClipboard(dataObject);
        }
 
        /// <summary>
        /// OleGetClipboard - Call OLE Interop OleGetClipboard()
        /// </summary>
        internal int OleGetClipboard(ref IComDataObject dataObject)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.OleGetClipboard(ref dataObject);
        }
 
        /// <summary>
        /// OleFlushClipboard - Call OLE Interop OleFlushClipboard()
        /// </summary>
        internal int OleFlushClipboard()
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.OleFlushClipboard();
        }
 
        /// <summary>
        /// OleIsCurrentClipboard - OleIsCurrentClipboard only works for the data object 
        /// used in the OleSetClipboard. This means that it can’t be called by the consumer 
        /// of the data object to determine if the object that was on the clipboard at the 
        /// previous OleGetClipboard call is still on the Clipboard.
        /// </summary>
        internal int OleIsCurrentClipboard(IComDataObject dataObject)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.OleIsCurrentClipboard(dataObject);
        }
 
        /// <summary>
        /// OleDoDragDrop - Call OLE Interop DoDragDrop()
        /// Initiate OLE DragDrop
        /// </summary>
        internal void OleDoDragDrop(IComDataObject dataObject, UnsafeNativeMethods.IOleDropSource dropSource, int allowedEffects, int[] finalEffect)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            InputManager inputManager = (InputManager)Dispatcher.CurrentDispatcher.InputManager;
            if (inputManager != null)
            {
                inputManager.InDragDrop = true;
            }
            try
            {
                UnsafeNativeMethods.DoDragDrop(dataObject, dropSource, allowedEffects, finalEffect);
            }
            finally
            {
                if (inputManager != null)
                {
                    inputManager.InDragDrop = false;
                }
            }
        }
 
        /// <summary>
        /// OleRegisterDragDrop - Call OLE Interop RegisterDragDrop()
        /// </summary>
        internal int OleRegisterDragDrop(HandleRef windowHandle, UnsafeNativeMethods.IOleDropTarget dropTarget)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.RegisterDragDrop(windowHandle, dropTarget);
        }
 
        /// <summary>
        /// OleRevokeDragDrop - Call OLE Interop RevokeDragDrop()
        /// </summary>
        internal int OleRevokeDragDrop(HandleRef windowHandle)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            return UnsafeNativeMethods.RevokeDragDrop(windowHandle);
        }
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        /// SetDispatcherThread - Initialize OleServicesContext that will call Ole initialize for ole services(DragDrop and Clipboard)
        /// and add the disposed event handler of Dispatcher to clean up resources and uninitalize Ole.
        /// </summary>
        private void SetDispatcherThread()
        {
            int hr;
 
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            // Initialize Ole services.
            // Balanced with OleUninitialize call in OnDispatcherShutdown.
            hr = OleInitialize();
 
            if (!NativeMethods.Succeeded(hr))
            {
                throw new SystemException(SR.Format(SR.OleServicesContext_oleInitializeFailure, hr));
            }
 
            // Add Dispatcher.Shutdown event handler. 
            // We will call ole Uninitialize and clean up the resource when UIContext is terminated.
            Dispatcher.CurrentDispatcher.ShutdownFinished += new EventHandler(OnDispatcherShutdown);
        }
 
        /// <summary>
        /// This is a callback when Dispatcher is shut down.
        /// </summary>
        /// <remarks>
        /// This method must be called before shutting down the application
        /// on the dispatcher thread.  It must be called by the same
        /// thread running the dispatcher and the thread must have its
        /// ApartmentState property set to ApartmentState.STA.
        /// </remarks>
        private void OnDispatcherShutdown(object sender, EventArgs args)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
            }
 
            // Uninitialize Ole services.
            // Balanced with OleInitialize call in SetDispatcherThread.
            OleUninitialize();
        }
 
        // Wrapper for UnsafeNativeMethods.OleInitialize, useful for debugging.
        private int OleInitialize()
        {
#if DEBUG
            _debugOleInitializeRefCount++;
#endif // DEBUG
            return UnsafeNativeMethods.OleInitialize();
        }
 
        // Wrapper for UnsafeNativeMethods.OleUninitialize, useful for debugging.
        private int OleUninitialize()
        {
            int hr;
 
            hr = UnsafeNativeMethods.OleUninitialize();
#if DEBUG
            _debugOleInitializeRefCount--;
            Invariant.Assert(_debugOleInitializeRefCount >= 0, "Unbalanced call to OleUnitialize!");
#endif // DEBUG
 
            return hr;
        }
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        // This is a slot to store OleServicesContext class per thread.
        private static readonly LocalDataStoreSlot _threadDataSlot = Thread.AllocateDataSlot();
 
#if DEBUG
        // Ref count of calls to OleInitialize/OleUnitialize.
        private int _debugOleInitializeRefCount;
#endif // DEBUG
 
        #endregion Private Fields
    }
}