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 IIS because it does not use any functionality specific to IIS. It works both in IIS and in the Visual Studio development server. However the development web server installed with Visual Studio (WebDev.WebServer20.exe, WebDev.WebServer40.exe) has an embedded manifest and cannot be used in this manner unless you modify the executable and remove the embedded manifest resource and rename or delete the external manifest (WebDev.WebServer20.exe.manifest, WebDev.WebServer40.exe.manifest).

To run the example open the project (aspnet20sample.sln) using Microsoft Visual Studio. 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.

Note: Visual Studio development server has manifests that need to be removed, see above. Use IIS if you do not wish to modify development web server installation.

To verify that this is really working as expected, remove the above HTTP module from web.config and run the project again. 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: