|
// 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.
//#define OLD_ISF
using System.Runtime.InteropServices;
using System.Windows.Interop;
using MS.Internal;
using MS.Internal.PresentationCore;
namespace MS.Win32.Penimc
{
internal static class UnsafeNativeMethods
{
// The flags in this region are all in support of COM hardening to add resilience
// to (OSGVSO:10779198).
// They are special arguments to COM calls that allow us to re-purpose them for
// functions relating to this hardening.
#region PenIMC Operations Flags
/// <summary>
/// Instruct IPimcManager3.GetTablet to release the external lock on itself.
/// </summary>
private const UInt32 ReleaseManagerExt = 0xFFFFDEAD;
/// <summary>
/// Instruct IPimcTablet3.GetCursorButtonCount to release the external lock on itself.
/// </summary>
private const int ReleaseTabletExt = -1;
/// <summary>
/// Instruct IPimcTablet3.GetCursorButtonCount to return the GIT key for the WISP Tablet.
/// </summary>
private const int GetWispTabletKey = -2;
/// <summary>
/// Instruct IPimcTablet3.GetCursorButtonCount to return the GIT key for the WISP Tablet Manager.
/// </summary>
private const int GetWispManagerKey = -3;
/// <summary>
/// Instruct IPimcTablet3.GetCursorButtonCount to acquire the external lock on itself.
/// </summary>
private const int LockTabletExt = -4;
/// <summary>
/// Instruct IPimcContext3.GetPacketPropertyInfo to return the GIT key for the WISP Tablet Context.
/// </summary>
private const int GetWispContextKey = -1;
#endregion
#region Stylus Input Thread Manager
/// <summary>
/// The GIT key to use when managing the WISP Tablet Manager objects
/// </summary>
[ThreadStatic]
private static UInt32? _wispManagerKey;
/// <summary>
/// Whether or not the WISP Tablet Manager server object has been locked in the MTA.
/// </summary>
[ThreadStatic]
private static bool _wispManagerLocked = false;
[ThreadStatic]
private static IPimcManager3 _pimcManagerThreadStatic;
/// <summary>
/// The cookie for the PenIMC activation context.
/// </summary>
[ThreadStatic]
private static IntPtr _pimcActCtxCookie = IntPtr.Zero;
#endregion
#region PenIMC
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern IntPtr RegisterDllForSxSCOM();
#endregion
/// <summary>
/// Make sure we load penimc.dll from WPF's installed location to avoid two instances of it.
///
/// Add an activation context to the thread's stack to ensure the registration-free COM objects
/// are available.
/// </summary>
/// <remarks>
/// PenIMC COM objects are only directly used (functions called on their interfaces) from inside the
/// PenThread. As such, the PenThreads need to create the activation context. The various Dispatcher
/// threads need not do so as they merely pass the RCWs around in manged objects and will only use
/// them via operations queued on their associated PenThread.
/// </remarks>
internal static void EnsurePenImcClassesActivated()
{
if (_pimcActCtxCookie == IntPtr.Zero)
{
// Register PenIMC for SxS COM for the lifetime of the thread.
//
// RegisterDllForSxSCOM returns a non-zero ActivationContextCookie if SxS registration
// succeeds, or IntPtr.Zero if SxS registration fails.
if ((_pimcActCtxCookie = RegisterDllForSxSCOM()) == IntPtr.Zero)
{
throw new InvalidOperationException(SR.Format(SR.PenImcSxSRegistrationFailed, ExternDll.Penimc));
}
}
}
/// <summary>
/// Deactivates the activation context for PenIMC objects.
/// </summary>
internal static void DeactivatePenImcClasses()
{
if (_pimcActCtxCookie != IntPtr.Zero)
{
if(DeactivateActCtx(0, _pimcActCtxCookie))
{
_pimcActCtxCookie = IntPtr.Zero;
}
}
}
/// <summary>
/// Returns IPimcManager3 interface. Creates this object the first time per thread.
/// </summary>
internal static IPimcManager3 PimcManager
{
get
{
if (_pimcManagerThreadStatic == null)
{
_pimcManagerThreadStatic = CreatePimcManager();
}
return _pimcManagerThreadStatic;
}
}
/// <summary>
/// Creates a new instance of PimcManager.
/// </summary>
private static IPimcManager3 CreatePimcManager()
{
// Instantiating PimcManager using "new PimcManager()" results
// in calling CoCreateInstanceForApp from an immersive process
// (like designer). Such a call would fail because PimcManager is not
// in the allowlist for that call. Hence we call CoCreateInstance directly.
// Note: Normally WPF is not supported for immersive processes
// but designer is an exception.
Guid clsid = Guid.Parse(PimcConstants.PimcManager3CLSID);
Guid iid = Guid.Parse(PimcConstants.IPimcManager3IID);
object pimcManagerObj = CoCreateInstance(ref clsid,
null,
0x1, /*CLSCTX_INPROC_SERVER*/
ref iid);
return ((IPimcManager3)pimcManagerObj);
}
#region COM Locking/Unlocking Functions
#region General
/// <summary>
/// Calls WISP GIT lock functions on Win8+.
/// On Win7 these will always fail since WISP objects are always proxies (WISP is out of proc).
/// </summary>
/// <param name="gitKey">The GIT key for the object to lock.</param>
internal static void CheckedLockWispObjectFromGit(UInt32 gitKey)
{
if (OSVersionHelper.IsOsWindows8OrGreater)
{
if (!LockWispObjectFromGit(gitKey))
{
throw new InvalidOperationException();
}
}
}
/// <summary>
/// Calls WISP GIT unlock functions on Win8+.
/// On Win7 these will always fail since WISP objects are always proxies (WISP is out of proc).
/// </summary>
/// <param name="gitKey">The GIT key for the object to unlock.</param>
internal static void CheckedUnlockWispObjectFromGit(UInt32 gitKey)
{
if (OSVersionHelper.IsOsWindows8OrGreater)
{
if (!UnlockWispObjectFromGit(gitKey))
{
throw new InvalidOperationException();
}
}
}
#endregion
#region Manager
/// <summary>
///
/// Calls into GetTablet with a special flag that indicates we should release
/// the lock obtained previously by a CoLockObjectExternal call.
/// </summary>
/// <param name="manager">The manager to release the lock for.</param>'
private static void ReleaseManagerExternalLockImpl(IPimcManager3 manager)
{
IPimcTablet3 unused = null;
manager.GetTablet(ReleaseManagerExt, out unused);
}
/// <summary>
/// Calls into GetTablet with a special flag that indicates we should release
/// the lock obtained previously by a CoLockObjectExternal call.
/// </summary>
/// <param name="manager">The manager to release the lock for.</param>'
internal static void ReleaseManagerExternalLock()
{
if (_pimcManagerThreadStatic != null)
{
ReleaseManagerExternalLockImpl(_pimcManagerThreadStatic);
}
}
/// <summary>
/// Queries and sets the GIT key for the WISP Tablet Manager
/// </summary>
/// <param name="tablet">The tablet to call through</param>
internal static void SetWispManagerKey(IPimcTablet3 tablet)
{
UInt32 latestKey = QueryWispKeyFromTablet(GetWispManagerKey, tablet);
// Assert here to ensure that every call through to this specific manager has the same
// key. This should be guaranteed since these calls are always done on the thread the tablet
// is created on and all tablets created on a particular thread should be through the same
// manager.
Invariant.Assert(!_wispManagerKey.HasValue || _wispManagerKey.Value == latestKey);
_wispManagerKey = latestKey;
}
/// <summary>
/// Calls down into PenIMC in order to lock the WISP Tablet Manager.
/// </summary>
internal static void LockWispManager()
{
if (!_wispManagerLocked && _wispManagerKey.HasValue)
{
CheckedLockWispObjectFromGit(_wispManagerKey.Value);
_wispManagerLocked = true;
}
}
/// <summary>
/// Calls down into PenIMC in order to unlock the WISP Tablet Manager.
/// </summary>
internal static void UnlockWispManager()
{
if (_wispManagerLocked && _wispManagerKey.HasValue)
{
CheckedUnlockWispObjectFromGit(_wispManagerKey.Value);
_wispManagerLocked = false;
}
}
#endregion
#region Tablet
/// <summary>
/// Calls into GetCursorButtonCount with a special flag that indicates we should acquire
/// the external lock by a CoLockObjectExternal call.
/// </summary>
/// <param name="manager">The tablet to acquire the lock for.</param>'
internal static void AcquireTabletExternalLock(IPimcTablet3 tablet)
{
int unused = 0;
// Call through with special param to release the external lock on the tablet.
tablet.GetCursorButtonCount(LockTabletExt, out unused);
}
/// <summary>
/// Calls into GetCursorButtonCount with a special flag that indicates we should release
/// the lock obtained previously by a CoLockObjectExternal call.
/// </summary>
/// <param name="manager">The tablet to release the lock for.</param>'
internal static void ReleaseTabletExternalLock(IPimcTablet3 tablet)
{
int unused = 0;
// Call through with special param to release the external lock on the tablet.
tablet.GetCursorButtonCount(ReleaseTabletExt, out unused);
}
/// <summary>
/// Queries the GIT key from the PenIMC Tablet
/// </summary>
/// <param name="keyType">The kind of key to instruct the tablet to return</param>
/// <param name="tablet">The tablet to call through</param>
/// <returns>The GIT key for the requested operation</returns>
private static UInt32 QueryWispKeyFromTablet(int keyType, IPimcTablet3 tablet)
{
int key = 0;
tablet.GetCursorButtonCount(keyType, out key);
if(key == 0)
{
throw new InvalidOperationException();
}
return (UInt32)key;
}
/// <summary>
/// Queries the GIT key for the WISP Tablet
/// </summary>
/// <param name="tablet">The tablet to call through</param>
/// <returns>The GIT key for the WISP Tablet</returns>
internal static UInt32 QueryWispTabletKey(IPimcTablet3 tablet)
{
return QueryWispKeyFromTablet(GetWispTabletKey, tablet);
}
#endregion
#region Context
/// <summary>
/// Queries the GIT key for the WISP Tablet Context
/// </summary>
/// <param name="context">The context to query through</param>
/// <returns>The GIT key for the WISP Tablet Context</returns>
internal static UInt32 QueryWispContextKey(IPimcContext3 context)
{
int key = 0;
Guid unused = Guid.Empty;
int unused2 = 0;
int unused3 = 0;
float unused4 = 0;
context.GetPacketPropertyInfo(GetWispContextKey, out unused, out key, out unused2, out unused3, out unused4);
if (key == 0)
{
throw new InvalidOperationException();
}
return (UInt32)key;
}
#endregion
#endregion
#if OLD_ISF
/// <summary>
/// Managed wrapper for IsfCompressPropertyData
/// </summary>
/// <param name="pbInput">Input byte array</param>
/// <param name="cbInput">number of bytes in byte array</param>
/// <param name="pnAlgoByte">
/// In: Preferred algorithm Id
/// Out: Best algorithm with parameters
/// </param>
/// <param name="pcbOutput">
/// In: output buffer size (of pbOutput)
/// Out: Actual number of bytes needed for compressed data
/// </param>
/// <param name="pbOutput">Buffer to hold the output</param>
/// <returns>Status</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern int IsfCompressPropertyData(
[In] byte [] pbInput,
uint cbInput,
ref byte pnAlgoByte,
ref uint pcbOutput,
[In, Out] byte [] pbOutput
);
/// <summary>
/// Managed wrapper for IsfDecompressPropertyData
/// </summary>
/// <param name="pbCompressed">Input buffer to be decompressed</param>
/// <param name="cbCompressed">Number of bytes in the input buffer to be decompressed</param>
/// <param name="pcbOutput">
/// In: Output buffer capacity
/// Out: Actual number of bytes required to hold uncompressed bytes
/// </param>
/// <param name="pbOutput">Output buffer</param>
/// <param name="pnAlgoByte">Algorithm id and parameters</param>
/// <returns>Status</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern int IsfDecompressPropertyData(
[In] byte [] pbCompressed,
uint cbCompressed,
ref uint pcbOutput,
[In, Out] byte [] pbOutput,
ref byte pnAlgoByte
);
/// <summary>
/// Managed wrapper for IsfCompressPacketData
/// </summary>
/// <param name="hCompress">Handle to the compression engine (null is ok)</param>
/// <param name="pbInput">Input buffer</param>
/// <param name="cInCount">Number of bytes in the input buffer</param>
/// <param name="pnAlgoByte">
/// In: Preferred compression algorithm byte
/// Out: Actual compression algorithm byte
/// </param>
/// <param name="pcbOutput">
/// In: Output buffer capacity
/// Out: Actual number of bytes required to hold compressed bytes
/// </param>
/// <param name="pbOutput">Output buffer</param>
/// <returns>Status</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern int IsfCompressPacketData(
CompressorSafeHandle hCompress,
[In] int [] pbInput,
[In] uint cInCount,
ref byte pnAlgoByte,
ref uint pcbOutput,
[In, Out] byte [] pbOutput
);
/// <summary>
/// Managed wrapper for IsfDecompressPacketData
/// </summary>
/// <param name="hCompress">Handle to the compression engine (null is ok)</param>
/// <param name="pbCompressed">Input buffer of compressed bytes</param>
/// <param name="pcbCompressed">
/// In: Size of the input buffer
/// Out: Actual number of compressed bytes decompressed.
/// </param>
/// <param name="cInCount">Count of int's in the compressed buffer</param>
/// <param name="pbOutput">Output buffer to receive the decompressed int's</param>
/// <param name="pnAlgoData">Algorithm bytes</param>
/// <returns>Status</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern int IsfDecompressPacketData(
CompressorSafeHandle hCompress,
[In] byte [] pbCompressed,
ref uint pcbCompressed,
uint cInCount,
[In, Out] int [] pbOutput,
ref byte pnAlgoData
);
/// <summary>
/// Managed wrapper for IsfLoadCompressor
/// </summary>
/// <param name="pbInput">Input buffer where compressor is saved</param>
/// <param name="pcbInput">
/// In: Size of the input buffer
/// Out: Number of bytes in the input buffer decompressed to construct compressor
/// </param>
/// <returns>Handle to the compression engine loaded</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern CompressorSafeHandle IsfLoadCompressor(
[In] byte [] pbInput,
ref uint pcbInput
);
/// <summary>
/// Managed wrapper for IsfReleaseCompressor
/// </summary>
/// <param name="hCompress">Handle to the Compression Engine to be released</param>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
internal static extern void IsfReleaseCompressor(
IntPtr hCompress
);
#endif
/// <summary>
/// Managed wrapper for GetPenEvent
/// </summary>
/// <param name="commHandle">Win32 event handle to wait on for new stylus input.</param>
/// <param name="handleReset">Win32 event the signals a reset.</param>
/// <param name="evt">Stylus event that was triggered.</param>
/// <param name="stylusPointerId">Stylus Device ID that triggered input.</param>
/// <param name="cPackets">Count of packets returned.</param>
/// <param name="cbPacket">Byte count of packet data returned.</param>
/// <param name="pPackets">Array of ints containing the packet data.</param>
/// <returns>true if succeeded, false if failed or shutting down.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetPenEvent(
IntPtr commHandle,
IntPtr handleReset,
out int evt,
out int stylusPointerId,
out int cPackets,
out int cbPacket,
out IntPtr pPackets);
/// <summary>
/// Managed wrapper for GetPenEventMultiple
/// </summary>
/// <param name="cCommHandles">Count of elements in commHandles.</param>
/// <param name="commHandles">Array of Win32 event handles to wait on for new stylus input.</param>
/// <param name="handleReset">Win32 event the signals a reset.</param>
/// <param name="iHandle">Index to the handle that triggered return.</param>
/// <param name="evt">Stylus event that was triggered.</param>
/// <param name="stylusPointerId">Stylus Device ID that triggered input.</param>
/// <param name="cPackets">Count of packets returned.</param>
/// <param name="cbPacket">Byte count of packet data returned.</param>
/// <param name="pPackets">Array of ints containing the packet data.</param>
/// <returns>true if succeeded, false if failed or shutting down.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetPenEventMultiple(
int cCommHandles,
IntPtr[] commHandles,
IntPtr handleReset,
out int iHandle,
out int evt,
out int stylusPointerId,
out int cPackets,
out int cbPacket,
out IntPtr pPackets);
/// <summary>
/// Managed wrapper for GetLastSystemEventData
/// </summary>
/// <param name="commHandle">Specifies PimcContext object handle to get event data on.</param>
/// <param name="evt">ID of system event that was triggered.</param>
/// <param name="modifier">keyboar modifier (unused).</param>
/// <param name="key">Keyboard key (unused).</param>
/// <param name="x">X position in device units of gesture.</param>
/// <param name="y">Y position in device units of gesture.</param>
/// <param name="cursorMode">Mode of the cursor.</param>
/// <param name="buttonState">State of stylus buttons (flick returns custom data in this).</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetLastSystemEventData(
IntPtr commHandle,
out int evt,
out int modifier,
out int key,
out int x,
out int y,
out int cursorMode,
out int buttonState);
/// <summary>
/// Managed wrapper for CreateResetEvent
/// </summary>
/// <param name="handle">Win32 event handle created.</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateResetEvent(out IntPtr handle);
/// <summary>
/// Managed wrapper for DestroyResetEvent
/// </summary>
/// <param name="handle">Win32 event handle to destroy.</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DestroyResetEvent(IntPtr handle);
/// <summary>
/// Managed wrapper for RaiseResetEvent
/// </summary>
/// <param name="handle">Win32 event handle to set.</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RaiseResetEvent(IntPtr handle);
/// <summary>
/// Managed wrapper for LockObjectExtFromGit
/// </summary>
/// <param name="gitKey">The key used to refer to this object in the GIT.</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool LockWispObjectFromGit(UInt32 gitKey);
/// <summary>
/// Managed wrapper for UnlockObjectExtFromGit
/// </summary>
/// <param name="gitKey">The key used to refer to this object in the GIT.</param>
/// <returns>true if succeeded, false if failed.</returns>
[DllImport(ExternDll.Penimc, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnlockWispObjectFromGit(UInt32 gitKey);
/// <summary>
/// Managed wrapper for CoCreateInstance
/// </summary>
/// <param name="clsid">CLSID of the COM class to be instantiated</param>
/// <param name="punkOuter">Aggregate object</param>
/// <param name="context">Context in which the newly created object will run</param>
/// <param name="iid">Identifier of the Interface</param>
/// <returns>Returns the COM object created by CoCreateInstance</returns>
[return: MarshalAs(UnmanagedType.Interface)]
[DllImport(ExternDll.Ole32, ExactSpelling = true, PreserveSig = false)]
private static extern object CoCreateInstance(
[In]
ref Guid clsid,
[MarshalAs(UnmanagedType.Interface)]
object punkOuter,
int context,
[In]
ref Guid iid);
/// <summary>
/// Deactivates the specified Activation Context.
/// </summary>
/// <remarks>
/// See: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-deactivateactctx
/// </remarks>
/// <param name="flags">Flags that indicate how the deactivation is to occur.</param>
/// <param name="activationCtxCookie">The ULONG_PTR that was passed into the call to ActivateActCtx.
/// This value is used as a cookie to identify a specific activated activation context.</param>
/// <returns>True on success, false otherwise.</returns>
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport(ExternDll.Kernel32, ExactSpelling = true)]
private static extern bool DeactivateActCtx(int flags, IntPtr activationCtxCookie);
}
#if OLD_ISF
internal class CompressorSafeHandle: SafeHandle
{
private CompressorSafeHandle()
: this(true)
{
}
private CompressorSafeHandle(bool ownHandle)
: base(IntPtr.Zero, ownHandle)
{
}
// Do not provide a finalizer - SafeHandle's critical finalizer will
// call ReleaseHandle for you.
public override bool IsInvalid
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
get
{
return IsClosed || handle == IntPtr.Zero;
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
override protected bool ReleaseHandle()
{
//
// return code from this is void.
// internally it just calls delete on
// the compressor pointer
//
UnsafeNativeMethods.IsfReleaseCompressor(handle);
handle = IntPtr.Zero;
return true;
}
public static CompressorSafeHandle Null
{
get
{
return new CompressorSafeHandle(false);
}
}
}
#endif
}
|