Freigeben über


C++ object for activating and Deactivating Contexts

As explained earlier, the activation context system is implemented as a per-thread (or per-fiber) stack. Activating a context pushes the context onto the stack, deactivating it pops it from the stack. To ensure that the same sequence of pushes and pops is performed, each ActivateActCtx call gets a cookie - an opaque value that identifies the activation request. That cookie is then passed to the DeactivateAcCtx call, and the system verifies that it matches the context at the top of the stack. If the cookie you ask to deactivate is not the top of the stack, the Win32 SEH exception STATUS_SXS_EARLY_DEACTIVATION is raised; if the cookie is not in the current thread's stack at all, STATUS_SXS_INVALID_DEACTIVATION is raised. These errors are extremely hard to track down.

Being a good citizen of activation contexts can be done with a simple rule - perform both the activation and deactivation in the same function. Here's a sample C++ object that does this for you. Be sure to compile asynchronous exception unwinding - the Visual C++ compiler option is /EHa. If you're using some other compiler, look for an option that calls destructors as both C++ and SEH exceptions flow past your frame and forces the compiler to assume any individual instruction may cause an SEH to be raised.

 class CActCtxActivator {
  HANDLE m_hContext;
  ULONG_PTR m_Cookie;
  CActCtxActivator(const CActCtxActivator &);
  void operator=(const CActCtxActivator &);
public:
  // Addref the context to avoid someone dereffing it under you
  CActCtxActivator(HANDLE hContext) {
    m_Cookie = 0;
    m_hContext = hContext;
    if (m_hContext != INVALID_HANDLE_VALUE)
      AddRefActCtx(m_hContext);
  }
  // Deactivate if it was active, deref if we had a real one
  ~CActCtxActivator() {
    Deactivate();
    if (m_hContext != INVALID_HANDLE_VALUE) {
      HANDLE hContext = m_hContext;
      m_hContext = INVALID_HANDLE_VALUE;
      ReleaseActCtx(hContext);
    }
  }
  // Activate the context. Feel free to use "throw" if you're a C++ EH kind of
  // person.  Double activation is a programmer's error - this class is not
  // reentrant
  bool Activate() {
    if (m_Cookie != 0)
      RaiseException(ERROR_INTERNAL_ERROR, 0, 0, 0);
    return ActivateActCtx(Ctx.m_hContext, &m_Cookie) ? true : false;
  }
  // Deactivate if activated - somewhat asymmetric behavior with Activate, as
  // it doesn't fail when the context isn't active.
  void Deactivate() {
    if (m_Cookie != 0) {
      ULONG_PTR Cookie = m_Cookie;
      m_Cookie = 0;
      DeactivateActCtx(0, Cookie);
    }
  }
};

So now, you have the ability to control your activation and deactivation automagically with an instance and a global context handle. Assuming that context handle is called g_hActCtx, you can use this in the following way:

 void Foo() {
  CActCtxActivator Activator(g_hActCtx);
  if (Activator.Activate()) {
    if (Bar()) {
      if (!Zot())
    return;
    }
  }
}

I'll soon use the above object to deal with calling out to plugins to avoid polluting their contexts (the other direction of being a good citizen.) As with any code posted here, it's provided as-is without warranty for any purpose.

[Edit 1/17/2006: I knew this felt familiar; my memory is hazy, but I vaguely recall writing this up before many moons ago...]