Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I determine if an "IIsWebDirectory" or "IIsWebVirtualDir" is an ASP.NET Application?

I am currently writing an C# asp.net application to connect to an IIS server and query the virtual directory/web directory information.

I am able to determine that there are that two types I should be dealing with is an "IIsWebDirectory" and "IIsWebVirtualDir".

How to I determine if they have been configured to be an "application" in C#?

You can also view my adventures in C# and IIS here and here

UPDATE

@Kev - I took the information in your answer and developed the following simple solution to check to see if the AppFriendlyName is not set to "Default Application"

private void CheckIfApp(DirectoryEntry de)
{
    if(de.SchemaClassName.Equals("IIsWebDirectory") || de.SchemaClassName.Equals("IIsWebVirtualDir"))
    {
        if (de.Properties["AppFriendlyName"].Value != null)
        {
            string friendlyName = de.Properties["AppFriendlyName"].Value.ToString();

            if (!friendlyName.Equals("Default Application"))
            {
                //do something...
            }
        }
    }
}
like image 371
Michael Kniskern Avatar asked Mar 12 '09 21:03

Michael Kniskern


1 Answers

There are three ways you can approach this problem:

  • Plain old System.DirectoryServices
  • Parse the IIS metabase.xml file
  • System.DirectoryServices and some COM interop

Plain old System.DirectoryServices

Determining whether an IIsWebDirectory or an IIsWebVirtualDir IIS admin object is configured to be an application using System.DirectoryServices alone can sometimes be a non-obvious business because of metabase property inheritance.

For example, when you create an 'application' there are normally three properties set on the IIsWebDirectory or IIsWebVirtualDir metabase admin objects -

AppFriendlyName
AppIsolated
AppRoot

In the metabase you would see something like:

<!-- This is an application -->
<IIsWebVirtualDir   Location ="/LM/W3SVC/1/ROOT/MyApp"
    AccessFlags="AccessRead | AccessScript"
    AppFriendlyName="MyAppKev"
    AppIsolated="2"
    AppRoot="/LM/W3SVC/1/Root/MyApp"    
    >
</IIsWebVirtualDir>

Now you would think that it would be as simple as checking for the presence of these three properties, and in particular the AppIsolated property because that's the property used to indicate the Application Isolation type (in-process [0], out-of-process [1] or pooled [2]) - and every application requires this property. As a sidenote, on a server running IIS6 you would only see AppIsolated=0|1 if IIS was running in IIS5 compatibility mode. When you create your own applications you should always set AppIsolated=2 which means the site or application will run in one of your application pools (w3wp.exe).

Anyway... because of metabase property inheritance, checking for any of the three properties listed above doesn't guarantee the object you're examining is actually an application - whether it be by using the ADSI, WMI or DirectoryServices API's. Even if the object you are checking is just a virtual directory (not application) you'll still get values returned because they would be inherited from the parent application.

For example, if /MyVdir is a virtual directory (not application) located in the Default Website, you would still see a value for AppIsolated, this is because it is inherited from IIS://Localhost/w3svc/1/root). The same applies with the AppFriendlyName and AppRoot properties.

The approach I took here was to compare the DirectoryEntry.Path property with the AppRoot property on the target admin object. AppRoot is a property that indicates where a particular application is rooted. This can be handy if you are examining an IIS admin object deep down in the site's metabase hierarchy and you need to know where its application is rooted at.

So, say I had an application located at:

IIS://Localhost/w3svc/1/root/MyApp

...and say we have an instance of DirectoryEntry:

DirectoryEntry de = new DirectoryEntry("IIS://Localhost/w3svc/1/root/MyApp");

The de.Path property would be set to IIS://Localhost/w3svc/1/root/MyApp, the AppRoot admin object property, de.Properties["AppRoot"].Value, would be set to /LM/W3SVC/1/Root/MyApp. All you need to do is strip off the leading IIS://Localhost and /LM strings and do a case-insensitive string compare. If there's a match then the object at that path is an application.

Parse the IIS metabase.xml file

There is another way that I've used in the past as part of a dead server rebuild job where we had the metabase file, a database table of vdirs and apps we knew we'd created and not much else - the server had 1200 websites and a multitude of virtual directories and applications before it popped. Basically I loaded the whole metabase.xml file into an XMLDocument. I then used XPath to look for the presence of the AppIsolated attribute where the Location attibute matched the path I was interested in.

This is a extract of code that should give you the general jist of the idea:

private XmlNamespaceManager _nsm = 
        new XmlNamespaceManager(new NameTable());
private XmlDocument _doc = new XmlDocument();
_doc.Load(@"c:\windows\system32\inetsrv\metabase.xml");
_nsm.AddNamespace("iis", "urn:microsoft-catalog:XML_Metabase_V64_0");

// lmPath could be build from the DirectoryEntry.Path property
string lmPath = "/lm/w3svc/1/root/myapp";

// Try as an IIsWebDirectory object
string iisWebDirectoryXPath = 
    String.Format(@"//iis:IIsWebDirectory[translate(@Location, 
            'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
            'abcdefghijklmnopqrstuvwxyz') = '{0}']",
            lmPath);

mbNode = _doc.DocumentElement.SelectSingleNode(iisWebDirectoryXPath, _nsm);

if(mbNode != null)
{
    // We found an IIsWebDirectory - is it an application though?
    if (mbNode.Attributes["AppIsolated"] != null)
    {
        // IIsWebDirectory is an Application
    }
}
else
{
    // Is our object an IIsWebVirtualDir?
    string iisWebVirtualDirectoryXPath = 
        String.Format(@"//iis:IIsWebVirtualDir[translate(@Location, 
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
                'abcdefghijklmnopqrstuvwxyz') = '{0}']",
                lmPath);

    mbNode = _doc.DocumentElement
            .SelectSingleNode(iisWebVirtualDirectoryXPath, _nsm);

    if (mbNode != null)
    {
        // Yes it's an IIsWebVirtualDir
        if (mbNode.Attributes["Path"] != null)
        {
            if(mbNode.Attributes["AppIsolated"] != null)
            {
                // And it's an application
            }
        }
    }
}

Parsing the raw metabase.xml file works ok as long as there isn't a high turn over of applications/virtual directories. This is because in-memory metabase updates are not flushed to the metabase.xml file immediately. I wouldn't recommend doing this, I only approached the problem this way purely for the purposes of system recovery.

System.DirectoryServices and some COM interop

Finally there is a third way (and probably the simplest) which you may not be able to test properly if your development PC is not running IIS6/Windows 2003 (I don't have an XP machine available to verify if this works with IIS 5.1). What you do is add a reference to a COM library called 'Active DS IIS Extension Dll' (%systemroot%\system32\inetsrv\iisext.dll), it's listed on the COM tab in Visual Studio's Add Reference dialogue. When you add this reference, Visual Studio will also automatically resolve and reference a dependency, 'Active DS Type Library' (%systemroot%\system32\activeds.tlb). In your solution references folder you'll see them listed as 'ActiveDS' and 'IISExt'.

Using the 'Active DS IIS Extension' library we can test if an IIS admin object at a particular path is in fact an IIS Application by doing the following:

using (DirectoryEntry de = 
        new DirectoryEntry("IIS://Localhost/w3svc/1/root/MyApp"))
{
    // Cast our native underlying object to the correct interface
    IISExt.IISApp2 app = (IISExt.IISApp2)de.NativeObject;
    int status = app.AppGetStatus2();

    switch(status)
    {
        case 0:
            Console.WriteLine("It's an app but it's stopped.");
            break;

        case 1:
            Console.WriteLine("It's an app and it's running.");
            break;

        case 2:
            Console.WriteLine("No application found here.");
            break;
    }

}

There is a fourth way using WMI but I've only dipped my toes in the water for some fairly simple IIS/App Pool management tasks.

like image 157
Kev Avatar answered Nov 15 '22 00:11

Kev