Click to Rate and Give Feedback
Isolating Components

Well-authored components do not perturb the environment of the hosting application, nor do they leak activation contexts. Well-authored components perform their own context management rather than relying on the hosting application's environment.

The author of the hosted component is in the best position to know exactly which other assemblies the component requires. Relying on the host application to provide the correct environment for your hosted component is a probable source of errors. Instead, create a manifest for your hosted component that specifies all its dependencies, and then compile using ISOLATION_AWARE_ENABLED. This ensures that outside calls made by your component are isolated and use the correct versions. Because the activation context that ISOLATION_AWARE_ENABLED uses is per-DLL, it is safe to use in multiple DLLs, each with their own manifest calling out dependencies.

If it is not possible to compile with ISOLATION_AWARE_ENABLED, then use a solution like the one presented in Using Callbacks From Hosted Components.

You should activate your own activation context in all the entrypoints that the hosting application can call to ensure that your hosted component runs entirely with the correct activation context. You can use a C++ helper object to facilitate changing all the entrypoints. For example, you could use a C++ class such as the following:

class CActivationContext {
	HANDLE m_hActivationContext;
public:
	CActivationContext() : m_hActivationContext(INVALID_HANDLE_VALUE) {}
	VOID Set(HANDLE hActCtx) {
		if (hActCtx != INVALID_HANDLE_VALUE)
			AddRefActCtx(hActCtx);

		if (m_hActivationContext != INVALID_HANDLE_VALUE)
			ReleaseActCtx(m_hActivationContext);
		m_hActivationContext = hActCtx;
	}

	~CActivationContext() {
		if (m_hActivationContext != INVALID_HANDLE_VALUE)
			ReleaseActCtx(m_hActivationContext);
	}
	BOOL Activate(ULONG_PTR &ulpCookie) {
		return ActivateActCtx(m_hActivationContext, &ulpCookie);
	}
	BOOL Deactivate(ULONG_PTR ulpCookie) {
		return DeactivateActCtx(0, ulpCookie);
	}
};

class CActCtxActivator {
	CActivationContext &m_ActCtx;
	ULONG_PTR m_Cookie;
	bool m_fActivated;
public:
	CActCtxActivator(CActivationContext& src, bool fActivate = true) 
		: m_ActCtx(src), m_Cookie(0), m_fActivated(false) {
		if (fActivate) {
			if (src.Activate(m_Cookie))
				m_fActivated = true;
		}
	}
	~CActCtxActivator() {
		if (m_fActivated) {
			m_ActCtx.Deactivate(m_Cookie);
			m_fActivated = false;
		}
	}
};

This can then be used in your component, using a global variable to store the activation context that should be activated on each entrypoint. In this way, you can isolate your component from your hosting application.

CActivationContext s_GlobalContext;

void MyExportedEntrypoint(void) {
	CActCtxActivator ScopedContext(s_GlobalContext);
	// Do whatever work here
	// Destructor automatically deactivates the context
}

void MyInitializerFunction() {
	HANDLE hActCtx;
	ACTCTX actctx = {sizeof(actctx)};
	hActCtx = CreateActCtx(&actctx);
	s_GlobalContext.Set(hActCtx);
	ReleaseActCtx(hActCtx);
	// The static destructor for s_GlobalContext destroys the
	// activation context on component unload.
}


Send comments about this topic to Microsoft

Build date: 11/1/2007

Tags What's this?: Add a tag
Community Content   What is Community Content?
Add new content RSS  Annotations
Use of ISOLATION_AWARE_ENABLED versus a C++ RAII class      Gideon7   |   Edit   |  

The MSDN article is somewhat confusing, as it describes two different approaches to context activation whose combined use appears to be overkill.

If you #define ISOLATION_AWARE_ENABLED=1 before including Windows.h you get large amount of inline code that wraps the XP-theme-aware APIs such as CreateWindowEx() and LoadLibrary() in order to activate the COMCTL32.DLL V6 context, which is loaded from resource ID 3 - See WinBase.inl WinbaseIsolationAwarePrivatetRgzlnPgpg actCtx.lpResourceName = (LPCWSTR)(ULONG_PTR)3. The context is wrapped around each API call such that the context is activated upon each API call and deactivated upon each API return.

If you define a C++ RAII class - such as CActCtxActivator as shown above - and load its manifest from resource ID 2 (ISOLATIONAWARE_MANIFEST_RESOURCE_ID), you get a way to establish the dynamic context implicitly. Simply declare the class object on the stack at each entry point in your code. It gives you an automatic activation context that is established for the life of your code (and deactivates correctly in the face of C++ exceptions) and also works for any dependent DLLs called by your code unless overridden. This is the approach used by MFC USRDLLs, via AFX_MANAGE_STATE(AfxGetStaticModuleState()). See atlmfc\src\mfc\appcore.cpp and afxstate.cpp for the complete implementation. Use of resource ID 2 also ensures that the context is activated when DllMain loads.

Use of ISOLATION_AWARE_ENABLED combined with a C++ RAII class is overkill, as the context is already activated for the life of the code by the RAII class.

The advantage of using ISOLATION_AWARE_ENABLED in lieu of a C++ RAII class is that you don't need to worry about handling callouts to old third-party DLLs or old in-process COM objects that might not be XP-theme-aware. The disadvantages are several: it requires that you have control over all source code, it bloats your application, and it omits important APIs such as MessageBox, and it is a hack (in my personal opinion).

The best documentation available from Microsoft is at http://blogs.msdn.com/junfeng/archive/2007/06/26/rt-manifest-resource-and-isolation-aware-enabled.aspx (JunFeng Zhang's Microsoft blog).

Processing
© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker