Microsoft KB Archive/173307

= PRB: Nested Virtual Roots Can Lose Session State =

Article ID: 173307

Article Last Modified on 5/2/2006

-

APPLIES TO


 * Microsoft Active Server Pages 4.0
 * Microsoft Internet Information Server 3.0
 * Microsoft Internet Information Server 4.0
 * Microsoft Internet Information Services 5.0

-



This article was previously published under Q173307



SYMPTOMS
When you browse to a child virtual root that is nested within a parent virtual root and then navigate to the parent virtual root session, variables appear to be lost.

The following table summarizes the behavior:                             Child 1 app state held  Child 2 app state held

Root app called first                   No                No   Root app called before Child 1 only      No                Yes Root app called before Child 2 only     Yes               No   Root app called last                     Yes               Yes



CAUSE
NOTE: There is a registry entry, CheckForNestedVroots(Default = 1), that ASP uses to check for nested virtual roots so that if a new nested virtual root is created while the system is running, the running application sees the new nested Global.asa file as soon as it is saved. From this registry parameter you can infer that ASP is designed to check for Global.asa files in subdirectories.

However, this behavior works in only one case: If all nested applications start before the root application, ASP checks for Global.asa files in all linked applications (both above and at the same level of the calling application). However, if the root application is called first or called before any nested application, only the root application's session state (sessionid and session variables) is maintained; it is not as if ASP does not check for nested virtual roots.

Internet Service Manager allows users to set up directories and their subdirectories as virtual roots. This creates a situation where there are virtual roots with nested virtual roots, which themselves can contain Global.asa files with Application_OnStart and Session_OnStart subroutines.

Here is an example directory structure in which each virtual directory contains a Global.asa file:

  C:\InetPub\wwwroot  Global.asa C:\InetPub\wwwroot\Test2 (Nested) Global.asa C:\InetPub\wwwroot\Test2\Test3 (Nested) Global.asa C:\InetPub\wwwroot\Test4 Global.asa

After the user browses to the root directory, which is the Wwwroot directory in a default installation, the SessionID is created and does not change even when navigating to other nested virtual roots, but the Application-scoped variables continue to change each time the users browses a page in a different virtual root. This causes the Application variables to appear to be lost.

However, if the root directory is the last directory navigated to and has never been visited before, then each nested directory maintains its own SessionID and Application and Session scoped variables.

This behavior is caused by the fact that once the root directory is browsed, IIS continues to send the same SessionCookie back to the browser; however, if nested virtual roots are browsed prior to browsing the home root, SessionCookies are sent for each nested virtual root, thereby generating multiple SessionIDs for each nested virtual root. The first time the home root is browsed the SessionCookie is fixed thereafter.



RESOLUTION
Do not use nested virtual roots when you design Web projects; instead, use one virtual root for each Web project that contains the Global.asa for that project.



STATUS
This behavior is by design.



Steps to Reproduce Behavior
 Configuration: In Microsoft Internet Service Manager, "Enable Default Document" in the Directories tab needs to include "Default.asp." Using Windows Explorer create a directory named WWWTest2 under the InetPub\wwwroot directory. Create a subdirectory under WWWTest2 called WWWTest3. Then create a directory at the same level as WWWTest2 called WWWTest4.  Copy the following Global.asa file into the WWWRoot directory: 

Sub Application_OnStart Application("MyTestApplication") = "WWWRoot: Welcome to _      Application Level Scope!" Application("WWWRoot") = "Chickens Eat..." Response.Write("Fire Application_OnStart for WWWRoot" & " ") End Sub

Sub Session_OnStart Session("MyTestSession") = "WWWRoot: Welcome to session      level scope!" Session("SessionID") = Session.SessionID Session("WWWRoot") = "Chicken Food!" Response.Write("Fire Session_OnStart for WWWRoot" & " ") End Sub

</SCRIPT> </li>  Copy the following Default.asp file into the WWWRoot directory: <%@ LANGUAGE="VBSCRIPT" %> <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0"> <META HTTP-EQUIV="Content-Type" content="text/html;  charset=iso-8859-1"> <TITLE>Test Nested Virtual Roots</TITLE> </HEAD> <BODY>

<%  Response.Write("Common Variable Names:" & " ") Response.Write Application("MyTestApplication") & " " Response.Write Session("MyTestSession") & " " Response.Write "Session.SessionID = " & Session("SessionID") & " "

Response.Write("Application Scoped Variables:" & " ") Response.Write "WWWRoot: " & Application("WWWRoot") & " " Response.Write "WWWTest2: " & Application("WWWTest2") & " " Response.Write "WWWTest3: " & Application("WWWTest3") & " " Response.Write "WWWTest4: " & Application("WWWTest4") & " "

Response.Write("Session Scoped Variables:" & " ") Response.Write "WWWRoot: " & Session("WWWRoot") & " " Response.Write "WWWTest2: " & Session("WWWTest2") & " " Response.Write "WWWTest3: " & Session("WWWTest3") & " " Response.Write "WWWTest4: " & Session("WWWTest4") & " " %>

<BR> <P>

<A HREF=<WWLINK TYPE="GENERIC"     VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/>_</WWLINK> Click here to go to WWWTest2</A><BR> <A HREF=<WWLINK TYPE="GENERIC" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/>_</WWLINK> Click here to go to WWWTest3>/A><BR> <A HREF=<WWLINK TYPE="GENERIC" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/>_</WWLINK> Click here to go to WWWTest4>/A><BR>

</BODY> </HTML> </li>  Copy the following Global.asa file into the WWWTest2 directory: <SCRIPT LANGUAGE=VBScript RUNAT=Server>

Sub Application_OnStart Application("MyTestApplication") = "WWWTest2: Welcome to _    Application Level Scope!" Application("WWWTest2") = "Dogs Eat..." Response.Write("Fire Application_OnStart for WWWTest2" & " ") End Sub

Sub Session_OnStart Session("MyTestSession") = "WWWTest2: Welcome to session_       level scope!" Session("SessionID") = Session.SessionID Session("WWWTest2") = "Dog Food!" Response.Write("Fire Session_OnStart for WWWTest2" & " ") End Sub

</SCRIPT> </li>  Copy the following Default.asp file into the WWWTest2 directory: <%@ LANGUAGE="VBSCRIPT" %> <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0"> <META HTTP-EQUIV="Content-Type" content="text/html;     charset=iso-8859-1"> <TITLE>Test Nested Virtual Roots</TITLE> </HEAD> <BODY>

<%  Response.Write("Common Variable Names:" & " ") Response.Write Application("MyTestApplication") & " " Response.Write Session("MyTestSession") & " " Response.Write "Session.SessionID = " & Session("SessionID") & " "

Response.Write("Application Scoped Variables:" & " ") Response.Write "WWWRoot: " & Application("WWWRoot") & " " Response.Write "WWWTest2: " & Application("WWWTest2") & " " Response.Write "WWWTest3: " & Application("WWWTest3") & " " Response.Write "WWWTest4: " & Application("WWWTest4") & " "

Response.Write("Session Scoped Variables:" & " ") Response.Write "WWWRoot: " & Session("WWWRoot") & " " Response.Write "WWWTest2: " & Session("WWWTest2") & " " Response.Write "WWWTest3: " & Session("WWWTest3") & " " Response.Write "WWWTest4: " & Session("WWWTest4") & " " %>

<BR> <P>

<A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWRoot/>_ Click here to go to WWWRoot</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/>_ Click here to go to WWWTest3</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/>_ Click here to go to WWWTest4</A><BR>

</BODY> </HTML> </li>  Copy the following Global.asa file into the WWWTest3 directory: <SCRIPT LANGUAGE=VBScript RUNAT=Server>

Sub Application_OnStart Application("MyTestApplication") = "WWWTest3: Welcome to _      Application Level Scope!" Application("WWWTest3") = "Cows Eat..." Response.Write("Fire Application_OnStart for WWWTest3" & " ") End Sub

Sub Session_OnStart Session("MyTestSession") = "WWWTest3: Welcome to session_      level scope!" Session("SessionID") = Session.SessionID Session("WWWTest3") = "Cow Food!" Response.Write("Fire Session_OnStart for WWWTest3" & " ") End Sub </SCRIPT> </li>  Copy the following Default.asp file into the WWWTest3 directory: <%@ LANGUAGE="VBSCRIPT" %> <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0"> <META HTTP-EQUIV="Content-Type" content="text/html; _     charset=iso-8859-1"> <TITLE>Test Nested Virtual Roots</TITLE> </HEAD> <BODY>

<%  Response.Write("Common Variable Names:" & " ") Response.Write Application("MyTestApplication") & " " Response.Write Session("MyTestSession") & " " Response.Write "Session.SessionID = " & Session("SessionID") & "   "

Response.Write("Application Scoped Variables:" & " ") Response.Write "WWWRoot: " & Application("WWWRoot") & " " Response.Write "WWWTest2: " & Application("WWWTest2") & " " Response.Write "WWWTest3: " & Application("WWWTest3") & " " Response.Write "WWWTest4: " & Application("WWWTest4") & " "

Response.Write("Session Scoped Variables:" & " ") Response.Write "WWWRoot: " & Session("WWWRoot") & " " Response.Write "WWWTest2: " & Session("WWWTest2") & " " Response.Write "WWWTest3: " & Session("WWWTest3") & " " Response.Write "WWWTest4: " & Session("WWWTest4") & " " %>

<BR> <P>

<A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWRoot/>_ Click here to go to WWWRoot</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/> Click_ here to go to WWWTest2</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/> Click here to go to WWWTest4</A><BR> </BODY> </HTML> </li>  Copy the following Global.asa file into the WWWTest4 directory: <SCRIPT LANGUAGE=VBScript RUNAT=Server>

Sub Application_OnStart Application("MyTestApplication") = "WWWTest4: Welcome to _      Application Level Scope!" Application("WWWTest4") = "Tony Drinks..." Response.Write("Fire Application_OnStart for WWWTest4" & " ") End Sub

Sub Session_OnStart Session("MyTestSession") = "WWWTest4: Welcome to session_       level scope!" Session("SessionID") = Session.SessionID Session("WWWTest4") = "Good Wine!" Response.Write("Fire Session_OnStart for WWWTest4" & " ") End Sub </SCRIPT> </li>  Copy the following Default.asp file into the WWWTest4 directory: <%@ LANGUAGE="VBSCRIPT" %> <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0"> <META HTTP-EQUIV="Content-Type" content="text/html;_       charset=iso-8859-1"> <TITLE>Test Nested Virtual Roots</TITLE> </HEAD> <BODY>

<%    Response.Write("Common Variable Names:" & " ") Response.Write Application("MyTestApplication") & " " Response.Write Session("MyTestSession") & " " Response.Write "Session.SessionID = " & Session("SessionID") & " "

Response.Write("Application Scoped Variables:" & " ") Response.Write "WWWRoot: " & Application("WWWRoot") & " " Response.Write "WWWTest2: " & Application("WWWTest2") & " " Response.Write "WWWTest3: " & Application("WWWTest3") & " " Response.Write "WWWTest4: " & Application("WWWTest4") & " "

Response.Write("Session Scoped Variables:" & " ") Response.Write "WWWRoot: " & Session("WWWRoot") & " " Response.Write "WWWTest2: " & Session("WWWTest2") & " " Response.Write "WWWTest3: " & Session("WWWTest3") & " " Response.Write "WWWTest4: " & Session("WWWTest4") & " " %>

<BR> <P>

<A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWRoot/> Click here to go to WWWRoot</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/> Click here to go to WWWTest2</A><BR> <A HREF=http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/> Click here to go to WWWTest3</A><BR>

</BODY> </HTML> </li> Open Internet Service Manager, right-click the WWWService and select the Directories tab. Click Add and add WWWTest2, WWWTest3, and WWWTest4 as virtual directories. Start and stop IIS server.</li> Open Internet Explorer 3.02 and click Options on the View menu. Then click Advanced and select "Warn before accepting cookies." Browse to http://%3Cservername%3E/ WWWTest3. The Application and Session variables should say "Cows Eat Cow Food!" (NOTE: If the error "800a01a8" Object required: Response appears, Refresh the page.)

Now click WWWRoot2 and they should read "Dogs Eat Dog Food!" Next click WWWRoot4 and they should read "Tony Drinks Good Wine!"

Finally, click WWWRoot and they should say "Chickens Eat Chicken Food!" Since you navigated to the WWWRoot directory last, all session and application variables will be in sync.</li> Close and open Internet Explorer 3.02 again and browse to http://%3Cservername%3E/WWWTest3. Again the Application and Session variables should read "Cows Eat Cow Food!" Now click the WWWRoot directory and they read "Chickens Eat Chicken Food!" This is not too surprising, but when you click WWWTest4 they read "Tony Drinks Chicken Food!"</li></ol>

Notice that once the WWWRoot directory is browsed that cookies are no longer set, thereby freezing the session variables and resulting the appearance that session state has been lost, but application variables continue to change.

<div class="references_section">