Isolating ASP .Net Applications: Example

how-to | home

General Architecture

As ASP .Net initializes its environment, it loads configured HTTP modules. We add our own HTTP module to this list and when ASP .Net calls our Init method we create the process default activation context and do not register any handlers - we do not want to be called again. Normally the Init method is only called in an application, but if multiple applications are configured to use the same application pool and they try to set the process default activation context, all subsequent calls will fail. We detect the situation when the CreateActCtx() API fails because the context is already set and we ignore this error. It may be a good idea to crash the application instead of masking the error if you want to enforce a clean and well-defined environment with only one application per application pool.

Creating the HTTP Module

Examples distributed with Manifest Maker include an HTTP module to manipulate the application's activation context. The project is normally installed in C:\Program Files\Maze Computer\Manifest Maker\Examples\aspnet20sample. This is an ASP .Net 2.0 class library and an example web application.

The HTTP module is implemented in the HttpModule_ProcessManifest class. The Init method is:


// ----------------------------------------------------------------------
// Create the 'process-default' activation context
// ----------------------------------------------------------------------
public void Init(HttpApplication application)
{
 // NOTE: We do not hook any events, no need to. Once we set the process
 //       -default activation context our job is done.

 string path = AppDomain.CurrentDomain.BaseDirectory;
 string file = Path.Combine(path, "webapp.manifest");
 UInt32 dwError = 0;

 bool bOK = ActivationContextHelper.CreateProcessContext(file, AppDomain.CurrentDomain.BaseDirectory, out dwError);

 // This is an error and we must make sure it does not go unnoticed
 if (!bOK && ActivationContextHelper.ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET != dwError)
 {
    string err = string.Format("HttpModule_ProcessManifest.Init: Cannot create process-default win32 sxs context, error={0} manifest={1} url={2}",
       dwError, file, application.Context.Request.Url);
    System.Diagnostics.Trace.WriteLine(err, "HttpModule_ProcessManifest");
    ApplicationException ex = new ApplicationException(err);
    throw ex;
 }

 if (application.Context.IsDebuggingEnabled)
 {
    string state = (bOK) ? "Activation context created." : "Activation context already existed.";
    string msg = string.Format("{0}; Init; manifest={1}; {2}", System.DateTime.Now.ToString(), file, state);
    System.Diagnostics.Trace.WriteLine(msg, "HttpModule_ProcessManifest");
 }
}

The ActivationContextHelper class performs the activation context creation. The manifest file name is hard-coded as webapp.manifest, but there is no reason why it could not be different or somehow configured. Your web.config file should look somewhat like this:


<?xml version="1.0"?>
<configuration>
<system.web>
<httpModules>

<add name="HttpModule_ProcessManifest" type="MazeComputer.AspManifestHelpers.HttpModule_ProcessManifest, AspManifestHelpers"/> 

</httpModules>
</system.web>
</configuration>

Of course there may be other settings, we only included here entries relevant to this particular case.

Running the HTTP Module Example

This example is not dependent on IIS6 because it does not use any functionality specific to IIS6. However the development web server installed with Visual Studio 2008 has an embedded manifest and cannot be used in this manner unless you modify the executable and remove the embedded manifest resource.

To run the example open the project (aspnet20sample.sln) using Microsoft Visual Studio 2005. First set the DropInManifest as the startup project then run the project. Your will get output similar to this:

Press this button to run the test:

Label

Now click the button. The text "Label" changes resulting in:

Press this button to run the test:

C:\Program Files\Maze Computer\Manifest Maker\Examples\aspnet20sample\DropInManifest\aspnet.sample.assembly\sampleDLL.dll

The DLL was loaded from the private assembly and the COM object was successfully accessed.

Attention: The development web server used by Visual Studio 2005 has an external manifest file (WebDev.WebServer.exe.manifest) which contains only a reference to Common Controls 6. You need to rename that file to test this solution using this web server. The development web server used by Visual Studio 2008 has an embedded manifest resource and cannot be used unless you remove the resource from the .EXE.

To verify that this is really working as expected, remove the above HTTP module from web.config and run the project again (make sure to first stop the Visual Studio 2005 web server). This time you get the familiar error message:

Server Error in '/DropInManifest' Application.
Retrieving the COM class factory for component with CLSID {4D880EAB-BF35-423A-A859-B1D9F2AC4CC1} failed due to the following error: 80040154.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Runtime.InteropServices.COMException: Retrieving the COM class factory for component with CLSID {4D880EAB-BF35-423A-A859-B1D9F2AC4CC1} failed due to the following error: 80040154.

Source Error:

...

Stack Trace:

[COMException (0x80040154): Retrieving the COM class factory for component with CLSID {4D880EAB-BF35-423A-A859-B1D9F2AC4CC1} failed due to the following error: 80040154.]
_Default.Button1_Click(Object sender, EventArgs e) +13
System.Web.UI.WebControls.Button.OnClick(EventArgs e) +75
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +98
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4919

Version Information: Microsoft .NET Framework Version:2.0.50727.1433; ASP.NET Version:2.0.50727.1433

If webapp.manifest does not exist in the web application directory, the HTTP module throws an exception:

Server Error in '/DropInManifest' Application.
HttpModule_ProcessManifest.Init: Cannot create process-default win32 sxs context, error=2 manifest=C:\Temp\Asp.Net.20\DropInManifest\webapp.manifest url=http://localhost:1499/DropInManifest/default.aspx

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ApplicationException: HttpModule_ProcessManifest.Init: Cannot create process-default win32 sxs context, error=2 manifest=C:\Temp\Asp.Net.20\DropInManifest\webapp.manifest url=http://localhost:1499/DropInManifest/default.aspx

Source Error:

...

Stack Trace:

[ApplicationException: HttpModule_ProcessManifest.Init: Cannot create process-default win32 sxs context, error=2 manifest=C:\Temp\Asp.Net.20\DropInManifest\webapp.manifest url=http://localhost:1499/DropInManifest/default.aspx]
MazeComputer.AspManifestHelpers.HttpModule_ProcessManifest.Init(HttpApplication application) +387
System.Web.HttpApplication.InitModulesCommon() +66
System.Web.HttpApplication.InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) +1006
System.Web.HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context) +259
System.Web.HttpApplicationFactory.GetApplicationInstance(HttpContext context) +114
System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +350

Version Information: Microsoft .NET Framework Version:2.0.50727.1433; ASP.NET Version:2.0.50727.1433


Read more: