%-- Copyright (c) 2008 Gustavo G. Duarte (http://duartes.org/gustavo) This file is licensed under the terms of the MIT/X11 open source license. See copying.txt in the root directory of this project (above /src) or http://en.wikipedia.org/wiki/MIT_License for the terms of copyright. --%> <%@ Control Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.UserControl" %> <%@ Import namespace="System.Collections.Generic"%> <%@ Import namespace="System.Security.Principal"%> <%@ Import namespace="System.Threading"%> <%@ Import namespace="System.Globalization"%> <%@ Import namespace="System.Diagnostics"%> <%@ Import namespace="System.Reflection"%>
When troubleshooting ASP.NET problems you often ask two questions: which process is the code running in?; and which Windows user is it running as? The default setups are thus:
| Windows + IIS | Worker Process | ASP.NET threads |
|---|---|---|
| Vista + IIS 7.0 | w3wp.exe running as NETWORK SERVICE, one per application pool | IUSR, well-known account with SID S-1-5-17, see this post |
| Windows 2003 + IIS 6.0 | w3wp.exe running as NETWORK SERVICE, one per application pool | IUSR_MACHINE_NAME, per-machine anonymous IIS user |
| Windows 2000/XP + IIS 5.0 | aspnet_wp.exe, one for all ASP.NET applications | ASPNET, per-machine ASP.NET worker account |
These defaults however are subject to many factors, so often you need to find the answers yourself. The cheat sheet below shows you the relevant classes and properties to use. ProcessMonitor is a great tool for troubleshooting Windows runtime problems, especially permission and access denied issues. If you wish to read up on security and .NET, see Keith Brown's superb online book, The .NET Developer's Guide to Windows Security.
<%= StartTable("HttpRequest.LogonUserIdentity And Process") %> <% Add(Environment.GetCommandLineArgs()[0], "Process.GetCurrentProcess().MainModule.FileName", "GetCurrentProcess() has security demands and may not be available in certain situations. One work-around " + "is to use Environment.GetCommandLineArgs()[0] to at least get the file name"); Add(Process.GetCurrentProcess().Id, "Process.GetCurrentProcess().Id", "The Windows process ID for the process running your code. This is the process you attach to for debugging, " + "watch for perf monitoring, and kill to restart your app 'cold'."); Add(Process.GetCurrentProcess().MainModule.FileVersionInfo, "Process.GetCurrentProcess().MainModule.FileVersionInfo"); Add(Process.GetCurrentProcess().StartTime, "Process.GetCurrentProcess().StartTime"); Add(this.Request.LogonUserIdentity.AuthenticationType, "this.Request.LogonUserIdentity.AuthenticationType"); Add(this.Request.LogonUserIdentity.Groups, "this.Request.LogonUserIdentity.Groups", "These are the Windows groups which contain the user running your ASP.NET code."); Add(this.Request.LogonUserIdentity.Name, "this.Request.LogonUserIdentity.Name", "This is the Windows user running your ASP.NET code. This user should be granted the permissions needed " + "by your application."); Add(this.Request.LogonUserIdentity.ImpersonationLevel, "this.Request.LogonUserIdentity.ImpersonationLevel", "This is 'Impersonation' if the thread running your code is impersonating a Windows user, otherwise it's 'None'. " + "In IIS 6 and 7, this is normally 'Impersonation' because the ASP.NET threads run under a different user than the " + "IIS worker process."); Add(this.Request.LogonUserIdentity.IsAnonymous, "this.Request.LogonUserIdentity.IsAnonymous"); Add(this.Request.LogonUserIdentity.IsSystem, "this.Request.LogonUserIdentity.IsSystem", "True if your code is running as Local System. This should never be the case."); Add(this.Request.LogonUserIdentity.User.Value, "this.Request.LogonUserIdentity.User.Value", "The SID for the account."); %> <%= EndTable() %>Path names in ASP.NET may be confusing, but the concepts are simple:
There are two useful ASP.NET tools to manipulate paths. The HttpServerUtility.MapPath method converts a virtual path to a physical path. The VirtualPathUtility class has several methods for manipulating virtual paths.
<%= StartTable("HttpRequest and Page Paths") %> <% Add(this.Request.ApplicationPath, "this.Request.ApplicationPath", "Gets the IIS root virtual path for " + "your ASP.net application. ASP.NET transforms ~/ into this value when making an absolute virtual path."); Add(this.Request.AppRelativeCurrentExecutionFilePath, "this.Request.AppRelativeCurrentExecutionFilePath", "Returns the app-relative virtual path for the " + "HttpHandler currently executing, normally your page. " + EquivalentTo("this.Page.AppRelativeVirtualPath")); Add(this.Request.CurrentExecutionFilePath, "this.Request.CurrentExecutionFilePath", "Returns the absolute virtual path for the " + "HttpHandler currently executing, normally your page."); Add(this.Request.PhysicalApplicationPath, "this.Request.PhysicalApplicationPath", "The root of your application in the server's file system."); Add(this.Request.PhysicalPath, "this.Request.PhysicalPath", "The file system location for the " + " " + "HttpHandler currently executing, normally your page."); Add(this.Page.TemplateSourceDirectory, "this.Page.TemplateSourceDirectory"); %> <%= EndTable() %>Click here for an example where the ApplicationPath is not /.
Your ASP.NET code runs inside a .NET AppDomain, which in turn runs inside a Windows process. The IIS version and configurations determine where your AppDomain will run. IIS 6 and 7 by default run AppDomains in a Worker Process called w3wp.exe. There is one worker process for each IIS application pool. Inside the Worker Process, there is one AppDomain for each application in the pool. Here are some useful tidbits on the current AppDomain and process:
<%= StartTable() %> <% Add(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, "AppDomain.CurrentDomain.SetupInformation.ConfigurationFile", "Configuration file for the current AppDomain. For ASP.net this is the web.config file."); Add(HttpRuntime.AppDomainAppId, "HttpRuntime.AppDomainAppId", "The /LM/W3SVC part is fixed for a given IIS instance. The following number is the Site ID within IIS. " + "The last piece is the " + "IIS root virtual path for the application (or 'ROOT' for /)."); Add(HttpRuntime.AppDomainAppPath, "HttpRuntime.AppDomainAppPath", "The filesystem folder containing your application. " + EquivalentTo("AppDomain.CurrentDomain.BaseDirectory")); Add(HttpRuntime.AppDomainAppVirtualPath, "HttpRuntime.AppDomainAppVirtualPath"); Add(HttpRuntime.AppDomainId, "HttpRuntime.AppDomainId", EquivalentTo("AppDomain.CurrentDomain.FriendlyName")); Add(HttpRuntime.AspClientScriptPhysicalPath, "HttpRuntime.AspClientScriptPhysicalPath"); Add(HttpRuntime.AspClientScriptVirtualPath, "HttpRuntime.AspClientScriptVirtualPath"); Add(HttpRuntime.AspInstallDirectory, "HttpRuntime.AspInstallDirectory"); Add(HttpRuntime.BinDirectory, "HttpRuntime.BinDirectory", EquivalentTo("AppDomain.CurrentDomain.RelativeSearchPath")); Add(HttpRuntime.ClrInstallDirectory, "HttpRuntime.ClrInstallDirectory"); Add(HttpRuntime.CodegenDir, "HttpRuntime.CodegenDir", "This is where ASP.NET stores generated source files for your pages, controls, etc. Watch this folder to see " + "what a processed page becomes. Pages are compiled to DLLs which are also stored here. " + EquivalentTo("AppDomain.CurrentDomain.DynamicDirectory")); Add(HttpRuntime.MachineConfigurationDirectory, "HttpRuntime.MachineConfigurationDirectory"); %> <%= EndTable() %>.NET code is packaged into .NET assemblies. It is often important to know which assemblies are loaded into your ASP.NET AppDomain because many problems boil down to missing or incorrect assemblies. The method <%= MsdnLinkFromSnippet("AppDomain.CurrentDomain.GetAssemblies")%> returns a list of assemblies loaded in your AppDomain (it requires full trust). Here's the output for the current domain:
Count of loaded assemblies: <%= AppDomain.CurrentDomain.GetAssemblies().Length %>
| Assemblies |
|---|
|
<%= HttpUtility.HtmlEncode(a.ManifestModule.FullyQualifiedName)%> <%= HttpUtility.HtmlEncode(a.FullName) %> |