| File: System\Windows\OleServicesContext.cs | Web Access |
| Project: src\wpf\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. // 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; /// <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 { // This is a slot to store OleServicesContext class per thread. private static readonly LocalDataStoreSlot s_threadDataSlot = Thread.AllocateDataSlot(); #if DEBUG // Ref count of calls to OleInitialize/OleUnitialize. private int _debugOleInitializeRefCount; #endif // DEBUG /// <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(); } /// <summary> /// Get the ole services context associated with the current Thread. /// </summary> internal static OleServicesContext CurrentOleServicesContext { get { // Get the ole services context from the Thread data slot. OleServicesContext oleServicesContext = (OleServicesContext)Thread.GetData(s_threadDataSlot); if (oleServicesContext is null) { // Create OleSErvicesContext instance. oleServicesContext = new OleServicesContext(); // Save the ole services context into the UIContext data slot. Thread.SetData(s_threadDataSlot, oleServicesContext); } return oleServicesContext; } } /// <summary> /// Ensure the current thread is STA and OLE is initialized. /// </summary> internal static void EnsureThreadState() { // Poke the CurrentOleServicesContext to ensure it is created, this will throw if the current thread is not STA. _ = CurrentOleServicesContext; } /// <summary> /// 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; inputManager?.InDragDrop = true; try { UnsafeNativeMethods.DoDragDrop(dataObject, dropSource, allowedEffects, finalEffect); } finally { inputManager?.InDragDrop = false; } } /// <summary> /// 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> /// 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); } /// <summary> /// 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 the <see cref="Dispatcher"/> is shut down. /// </summary> /// <remarks> /// <para> /// 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 /// <see cref="ApartmentState.STA"/>. /// </para> /// </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 = UnsafeNativeMethods.OleUninitialize(); #if DEBUG _debugOleInitializeRefCount--; Invariant.Assert(_debugOleInitializeRefCount >= 0, "Unbalanced call to OleUnitialize!"); #endif // DEBUG return hr; } }