| File: MS\Internal\WindowsRuntime\Windows\UI\ViewManagement\InputPane.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. // // using System.IO; using System.Runtime.InteropServices; using System.Windows.Interop; namespace MS.Internal.WindowsRuntime { namespace Windows.UI.ViewManagement { /// <summary> /// DevDiv:1193138 /// This class wraps the corresponding WinRT APIs for InputPane. This is used to show the touch keyboard. /// Note that WinRT events belonging to this class are not included for simplicity. /// /// This class uses RCWs for WinRT objects in order to properly cast from a WinRT IActivationFactory and /// therefore implements IDisposable in order to allow for fast RCW cleanup. /// </summary> internal class InputPane : IDisposable { #region Fields /// <summary> /// Bool to check if the WinRT input pane is supported /// </summary> private static readonly bool _isSupported; /// <summary> /// Activation factory to instantiate InputPane RCWs /// </summary> private static object _winRtActivationFactory; /// <summary> /// The appropriate RCW for calling TryShow/Hide /// </summary> private InputPaneRcw.IInputPane2 _inputPane; #endregion #region Constructors /// <summary> /// Acquires the InputPane type from the winmd /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] static InputPane() { // We don't want to throw here - so wrap in try..catch try { // If we cannot get a new activation factory, then we cannot support // this platform. As such, null out the type to guard instantiations. if (GetWinRtActivationFactory(forceInitialization: true) == null) { _isSupported = false; } _isSupported = true; } catch { _isSupported = false; } } /// <summary> /// Checks that the InputPane type was loaded and gets a new InputPane for the parent window. /// </summary> /// <exception cref="PlatformNotSupportedException"></exception> private InputPane(IntPtr? hwnd) { if (!_isSupported) { throw new PlatformNotSupportedException(); } try { if (hwnd.HasValue) { InputPaneRcw.IInputPaneInterop inputPaneInterop; try { // Get the IActivationFactory and cast to IInputPaneInterop. The WinRT pattern for // static factory types implements an activation factory that allows casting to one // or more COM interfaces containing "static" methods to use as initialization. In // the case of InputPane this is an interop class that contains an init function // designed to take an HWND. This interface is cloaked and not part of the WinRT // projections and therefore cannot be seen by reflecting on the type. inputPaneInterop = GetWinRtActivationFactory() as InputPaneRcw.IInputPaneInterop; } catch (COMException) { // Do a fine grained catch here to detect the activation factory going stale. // If this happens, we retry the cast querying a new factory. If this retry fails // something else is going wrong, allow the error to be handled by the outer block. inputPaneInterop = GetWinRtActivationFactory(forceInitialization: true) as InputPaneRcw.IInputPaneInterop; } _inputPane = inputPaneInterop?.GetForWindow(hwnd.Value, typeof(InputPaneRcw.IInputPane2).GUID); } } catch (COMException) { // Something went wrong in acquiring/using one of the COM RCWs above. // This is not a fatal error and just means that this particular attempt to initialize InputPane // has failed or the platform does not support this access (this can be caught at the Type init // but it is possible that is not the case). } if (_inputPane == null) { throw new PlatformNotSupportedException(); } } #endregion #region Member Functions /// <summary> /// Wraps creation in a manner analagous to the WinRT interface to this class. /// </summary> /// <returns>A new InputPane</returns> /// <exception cref="PlatformNotSupportedException"></exception> internal static InputPane GetForWindow(HwndSource source) { return new InputPane(source?.Handle ?? null); } /// <summary> /// Attempts to show the touch keyboard /// </summary> /// <returns>True if successful, false otherwise</returns> internal bool TryShow() { bool result = false; try { result = _inputPane?.TryShow() ?? false; } catch (COMException) { // It's possible that the IInputPane2 has gone stale for some reason // in that case we should catch the exception here and simply return // false indicating that the KB did not show. } return result; } /// <summary> /// Attempts to hide the touch keyboard /// </summary> /// <returns>True if successful, false otherwise</returns> internal bool TryHide() { bool result = false; try { result = _inputPane?.TryHide() ?? false; } catch (COMException) { // It's possible that the IInputPane2 has gone stale for some reason // in that case we should catch the exception here and simply return // false indicating that the KB did not show. } return result; } /// <summary> /// Creates, caches, and returns a WinRT activation factory for use with the InputPane runtime type. /// </summary> /// <param name="forceInitialization">If true, will create a new IActivationFactory. If false will /// only create a new IActivationFactory if there is no valid cached instance available.</param> /// <returns>An IActivationFactory of InputPane or null if it fails to instantiate.</returns> private static object GetWinRtActivationFactory(bool forceInitialization = false) { if (forceInitialization || _winRtActivationFactory == null) { try { _winRtActivationFactory = InputPaneRcw.GetInputPaneActivationFactory(); } catch (Exception e) when (e is TypeLoadException || e is FileNotFoundException || e is EntryPointNotFoundException || e is DllNotFoundException || e.HResult == NativeMethods.E_NOINTERFACE || e.HResult == NativeMethods.REGDB_E_CLASSNOTREG) { // Catch the set of exceptions that are considered activation exceptions, // as well as exception with HResults that can be returned from DllGetActivationFactory when it fails. // <see cref="https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.windowsruntime.windowsruntimemarshal.getactivationfactory(v=vs.110).aspx"/> // On some Windows SKUs, notably ServerCore, a failing static dependency in InputPane seems to cause a // FileNotFoundException during acquisition of the activation factory. We explicitly catch this exception // here to alleviate this issue. This is not an ideal solution to the platform bug, but keeps WPF applications // from being exposed to the issue. // We also catch an EntryPointNotFoundException and DllNotFoundExceptions for when WinRT isn't supported on the platform. _winRtActivationFactory = null; } } return _winRtActivationFactory; } #endregion #region IDisposable private bool _disposed = false; ~InputPane() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases the _inputPane RCW /// </summary> /// <param name="disposing">True if called from a Dispose() call, false when called from the finalizer</param> private void Dispose(bool disposing) { if (!_disposed) { if (_inputPane != null) { try { // Release the input pane here Marshal.ReleaseComObject(_inputPane); } catch { // Don't want to raise any exceptions in a finalizer, eat them here } _inputPane = null; } _disposed = true; } } #endregion } } }