PRB: Nested Virtual Roots Can Lose Session State

ID: Q173307


The information in this article applies to:


SYMPTOMS

When browsing to a child virtual root that is nested within a parent virtual root, and then navigating 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's like 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 with each virtual directory containing a Global.asa:

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

Once the user browses to the root directory, which in a default installation is the wwwroot directory, 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 <home> directory is browsed, IIS continues to send the same SessionCookie back to the browser, but 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 designing Web projects. Instead use one virtual root for each Web project containing the Global.asa for that project.


STATUS

Microsoft is researching this problem and will post new information here in the Microsoft Knowledge Base as it becomes available. This behavior is by design.


MORE INFORMATION

Steps to Reproduce Behavior

  1. Configuration: In Microsoft Internet Service Manager, "Enable Default Document" in the Directories tab needs to include "Default.asp."


  2. 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.


  3. Copy the following Global.asa file into the WWWRoot directory:
    
       <SCRIPT LANGUAGE=VBScript RUNAT=Server>
    
       Sub Application_OnStart
         Application("MyTestApplication") = "WWWRoot: Welcome to _
           Application Level Scope!"
         Application("WWWRoot") = "Chickens Eat..."
       Response.Write("Fire Application_OnStart for WWWRoot" & "<br>")
       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" & "<br>")
       End Sub
    
       </SCRIPT> 


  4. 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:" & "<br>")
       Response.Write Application("MyTestApplication") & "<br>"
       Response.Write Session("MyTestSession") & "<br>"
       Response.Write "Session.SessionID = " &
         Session("SessionID") & "<br><p>"
    
       Response.Write("Application Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Application("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Application("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Application("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Application("WWWTest4") & "<br><p>"
    
       Response.Write("Session Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Session("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Session("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Session("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Session("WWWTest4") & "<br><p>"
       %>
    
       <BR>
       <P>
    
       <A
       HREF=<LINK TYPE="GENERIC"      VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest2/>_</LINK>
       Click here to go to WWWTest2</A><BR>
       <A
       HREF=<LINK TYPE="GENERIC" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest3/>_</LINK>
       Click here to go to WWWTest3>/A><BR>
       <A
       HREF=<LINK TYPE="GENERIC" VALUE="http://&lt;%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/>_">http://<%=Request.ServerVariables("SERVER_NAME")%>/WWWTest4/>_</LINK>
       Click here to go to WWWTest4>/A><BR>
    
       </BODY>
       </HTML> 


  5. 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" & "<br>")
       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" & "<br>")
       End Sub
    
       </SCRIPT> 


  6. 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:" & "<br>")
       Response.Write Application("MyTestApplication") & "<br>"
       Response.Write Session("MyTestSession") & "<br>"
       Response.Write "Session.SessionID = " & Session("SessionID")
         & "<br><p>"
    
       Response.Write("Application Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Application("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Application("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Application("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Application("WWWTest4") & "<br><p>"
    
       Response.Write("Session Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Session("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Session("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Session("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Session("WWWTest4") & "<br><p>"
       %>
    
       <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> 


  7. 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" & "<br>")
       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" & "<br>")
       End Sub
    
       </SCRIPT> 


  8. 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:" & "<br>")
       Response.Write Application("MyTestApplication") & "<br>"
       Response.Write Session("MyTestSession") & "<br>"
       Response.Write "Session.SessionID = " & Session("SessionID") & "
       <br><p>"
    
       Response.Write("Application Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Application("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Application("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Application("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Application("WWWTest4") & "<br><p>"
    
       Response.Write("Session Scoped Variables:" & "<br>")
       Response.Write "WWWRoot: " & Session("WWWRoot") & "<br>"
       Response.Write "WWWTest2: " & Session("WWWTest2") & "<br>"
       Response.Write "WWWTest3: " & Session("WWWTest3") & "<br>"
       Response.Write "WWWTest4: " & Session("WWWTest4") & "<br><p>"
       %>
    
       <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> 


  9. 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" & "<br>")
       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" & "<br>")
       End Sub
    
       </SCRIPT> 


  10. 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:" & "<br>")
         Response.Write Application("MyTestApplication") & "<br>"
         Response.Write Session("MyTestSession") & "<br>"
         Response.Write "Session.SessionID = " & Session("SessionID") &
            "<br><p>"
    
         Response.Write("Application Scoped Variables:" & "<br>")
         Response.Write "WWWRoot: " & Application("WWWRoot") & "<br>"
         Response.Write "WWWTest2: " & Application("WWWTest2") & "<br>"
         Response.Write "WWWTest3: " & Application("WWWTest3") & "<br>"
         Response.Write "WWWTest4: " & Application("WWWTest4") & "<br><p>"
    
         Response.Write("Session Scoped Variables:" & "<br>")
         Response.Write "WWWRoot: " & Session("WWWRoot") & "<br>"
         Response.Write "WWWTest2: " & Session("WWWTest2") & "<br>"
         Response.Write "WWWTest3: " & Session("WWWTest3") & "<br>"
         Response.Write "WWWTest4: " & Session("WWWTest4") & "<br><p>"
         %>
    
         <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> 


  11. 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.


  12. Open Internet Explorer 3.02 and click Options on the View menu. Then click Advanced and select "Warn before accepting cookies." Browse to http://<servername>/ 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.


  13. Close and open Internet Explorer 3.02 again and browse to http://<servername>/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!"


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.


REFERENCES

For the latest Knowledge Base artices and other support information on Visual InterDev and Active Server Pages, see the following page on the Microsoft Technical Support site:

http://support.microsoft.com/support/vinterdev/

Additional query words:


Keywords          : kbASP kbVisID kbWebServer kbGrpASP 
Version           : winnt:
Platform          : winnt 
Issue type        : kbprb 

Last Reviewed: May 27, 1999