Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PenIMC Activation Context Handling #2890

Merged
merged 5 commits into from
Jun 11, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -46,6 +46,3 @@ extern "C" ULONG_PTR WINAPI RegisterDllForSxSCOM()
// Return the context cookie : caller is responsible for deactivating the context.
return activationContextCookie;
}



Original file line number Diff line number Diff line change
@@ -75,6 +75,12 @@ internal static class UnsafeNativeMethods
[ThreadStatic]
private static IPimcManager3 _pimcManagerThreadStatic;

/// <summary>
/// The cookie for the PenIMC activation context.
/// </summary>
[ThreadStatic]
private static IntPtr _pimcActCtxCookie = IntPtr.Zero;

#endregion

#region PenIMC
@@ -86,26 +92,50 @@ internal static class UnsafeNativeMethods

/// <summary>
/// Make sure we load penimc.dll from WPF's installed location to avoid two instances of it.
/// </summary>
static UnsafeNativeMethods()
///
/// 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()
{
// Register PenIMC for SxS COM for the lifetime of the process. No need to store the
// cookie; PenIMC's ActivationContext will never be removed from the ActivationContext
// stack.
//
// RegisterDllForSxSCOM returns a non-zero ActivationContextCookie if SxS registration
// succeeds, or IntPtr.Zero if SxS registration fails.
if (IntPtr.Zero == RegisterDllForSxSCOM())
if (_pimcActCtxCookie == IntPtr.Zero)
{
throw new InvalidOperationException(SR.Get(SRID.PenImcSxSRegistrationFailed, ExternDll.Penimc));
}
// 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.Get(SRID.PenImcSxSRegistrationFailed, ExternDll.Penimc));
}

// Ensure PenIMC loaded from the correct location.
var uncheckedDlls = WpfDllVerifier.VerifyWpfDllSet(ExternDll.Penimc);
// Ensure PenIMC loaded from the correct location.
var uncheckedDlls = WpfDllVerifier.VerifyWpfDllSet(ExternDll.Penimc);

if (uncheckedDlls.Contains(ExternDll.Penimc))
if (uncheckedDlls.Contains(ExternDll.Penimc))
{
throw new DllNotFoundException(SR.Get(SRID.PenImcDllVerificationFailed, ExternDll.Penimc));
}
}
}

/// <summary>
/// Deactivates the activation context for PenIMC objects.
/// </summary>
internal static void DeactivatePenImcClasses()
{
if (_pimcActCtxCookie != IntPtr.Zero)
{
throw new DllNotFoundException(SR.Get(SRID.PenImcDllVerificationFailed, ExternDll.Penimc));
if(DeactivateActCtx(0, _pimcActCtxCookie))
{
_pimcActCtxCookie = IntPtr.Zero;
}
}
}

@@ -586,7 +616,8 @@ internal static extern bool GetLastSystemEventData(
/// <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)]
[return: MarshalAs(UnmanagedType.Interface)]
[DllImport(ExternDll.Ole32, ExactSpelling = true, PreserveSig = false)]
private static extern object CoCreateInstance(
[In]
ref Guid clsid,
@@ -595,6 +626,20 @@ private static extern object CoCreateInstance(
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, PreserveSig = false)]
private static extern bool DeactivateActCtx(int flags, IntPtr activationCtxCookie);
}

#if OLD_ISF
Original file line number Diff line number Diff line change
@@ -1164,7 +1164,12 @@ internal void ThreadProc()
Debug.WriteLine(String.Format("PenThreadWorker::ThreadProc(): Update __penContextWeakRefList loop"));
#endif

WorkerOperation [] workerOps = null;
// We need to ensure that the PenIMC COM objects can be used from this thread.
// Try this every outer loop since we're, generally, about to do management
// operations.
MS.Win32.Penimc.UnsafeNativeMethods.EnsurePenImcClassesActivated();

WorkerOperation[] workerOps = null;

lock(_workerOperationLock)
{
@@ -1289,6 +1294,9 @@ internal void ThreadProc()
_pimcContexts[i].ShutdownComm();
}

// Ensure that any activation contexts used on this thread are cleaned.
MS.Win32.Penimc.UnsafeNativeMethods.DeactivatePenImcClasses();

// Make sure the _pimcResetHandle is still valid after Dispose is called and before
// our thread exits.
GC.KeepAlive(this);