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.
HTH
Kev