Copyright
IISManager is a product of luckzj. Anyone could use this product for free but without commercial purpose. If there’s any problem using IISManager, feel free to contact me at luckzj12@163.com. Or visit my website http://soft-bin.com.
The original address of this article is:
Introduction
We can use IIS management tools provided by Microsoft to manage our IIS server. However, sometimes we need to manipulate our IIS server inside our application. For example, to deploy our website related applications. I will give a brief on how to manage IIS server using C#. Along with that, I will also provide an IIS management DLL called IISManager and its source code writing in C# language.
The target version of IIS discussed in this article is IIS6.0 or higher. Version of IIS on Windows Xp is IIS 5.1 which cannot meet this requirement. However, you can also use IISManager to manage it under the condition that you don't do operations unsupported by IIS 5.1.
IISManager can only provide website server management classes for the moment. However, I would continue to work with it and make it a perfect tool to manipulate IIS servers.
I will start this article with Active Directory Service.
Active Directory Service
I will use Active Directory Service (ADS) to manipulate IIS server. No further discussion would be made about ADS for there’s already too many introductions on the web. I would only discuss how to use it here.
To get access to Active Directory Service, we would use classes under the namespace System.DirectoryServices
which is defined in .NET assembly System.DirectoryServices
. So we need to add this assembly to our project.
The most important class for IISManager in System.DirectoryServices
is DirectoryEntry
. We can take DirectoryEntry
as a node which contains a set of properties, methods and child nodes. We can use DirectoryEntry.Properties[key]
to access properties and use DirectoryEntry.Invoke
to access methods of the node. We can get the prototype name of the node with DirectoryEntry.SchemaClassName
.
The constructor of DirectoryEntry
accepts one parameter which represents an ADS node. I will use an IIS ADS node to construct a DirectoryEntry
instance and use it to access IIS servers.
Open IIS website with DirectoryEntry
We can use the following way to get a DirectoryEntry
instance to access IIS Active DirectoryServices
(ADS):
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
“IIS://localhost/W3SVC” is the name of IIS ADS.
We just created an IIS ADS server object. There may be many websites on this node, we must find the website we are looking for:
private DirectroyEntry websiteEntry = null;
internal const string IIsWebServer = "IIsWebServer";
protected IISWebsite(DirectoryEntry Server)
{
websiteEntry = Server;
}
public static IISWebsite OpenWebsite(string name)
{
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
IEnumerator ie = Services.Children.GetEnumerator();
DirectoryEntry Server = null;
while (ie.MoveNext())
{
Server = (DirectoryEntry)ie.Current;
if (Server.SchemaClassName == IIsWebServer)
{
if (Server.Properties["ServerComment"][0].ToString() == name)
{
return new IISWebsite(Server);
break;
}
}
}
return null;
}
I use IISWebsite to represent a IIS website and use static
method ISWebsite.OpenWebsite()
to get an instance of this class.
DirectoryEntry.SchemaClassName
means the prototype of the node. The prototype name of IISWebServer
is IISWebServer
. We would find the website with the specified name. However, DirectoryEntry.Name
is not the name of the website, but the name of the node. To get the name of the website, we should use DirectoryEntry.Properties["ServerComment"]
. Each property in DirectoryEntry.Properties
is represented as a collection, we should use foreach
to access members of a property. However, we know that a website must have a name, so we use Server.Properties["ServerComment"][0]
to get the real name of this website.
Create a Website
To create a website, we need to add a new node to “IIS://localhost/W3SVC”. I will list the source code and give the explanation later.
public static IISWebsite CreateWebsite
(string name, int port, string rootPath, string appPool)
{
if (System.IO.Directory.Exists(rootPath) == false)
{
throw new DirNotFoundException(rootPath);
}
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
int index = 0;
foreach (DirectoryEntry server in Services.Children)
{
if (server.SchemaClassName == "IIsWebServer")
{
if (server.Properties["ServerComment"][0].ToString() == name)
{
throw new Exception("website:" + name + " already exists.");
}
if (Convert.ToInt32(server.Name) > index)
{
index = Convert.ToInt32(server.Name);
}
}
}
index++;
DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);
Server.Properties["ServerComment"].Clear();
Server.Properties["ServerComment"].Add(name);
Server.Properties["Serverbindings"].Clear();
Server.Properties["Serverbindings"].Add(":" + port + ":");
DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
root.Properties["path"].Clear();
root.Properties["path"].Add(rootPath);
if (string.IsNullOrEmpty(appPool))
{
root.Invoke("appCreate", 0);
}
else
{
root.Invoke("appCreate3", 0, appPool, true);
}
root.Properties["AppFriendlyName"].Clear();
root.Properties["AppIsolated"].Clear();
root.Properties["AccessFlags"].Clear();
root.Properties["FrontPageWeb"].Clear();
root.Properties["AppFriendlyName"].Add(root.Name);
root.Properties["AppIsolated"].Add(2);
root.Properties["AccessFlags"].Add(513);
root.Properties["FrontPageWeb"].Add(1);
root.CommitChanges();
Server.CommitChanges();
IISWebsite website = new IISWebsite(Server);
return website;
}
It has already been clarified that the name of the node does not mean the name of the website. Actually, for a website node, name of the node is an integer. So when I'm trying to create a new website node, I should first find out the largest integer that exists and add 1 to it which makes our new website node name.
To add a node seems easy:
DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);
Then, we should set properties for this website including website name and port.
Website name:
node.Properties["ServerComment"].Add(name)
Server port:
The property name for server port is “ServerBindings
”. However, we should add “:
” to the real port at the front and rear of the port string. “:8080:
” for example. I don't know why Microsoft did this, I just do what they told me to do.
So far, we created a website. However, a website must have a root directory:
DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
root.Properties["path"].Clear();
root.Properties["path"].Add(rootPath);
if (string.IsNullOrEmpty(appPool))
{
root.Invoke("appCreate", 0);
}
else
{
root.Invoke("appCreate3", 0, appPool, true);
}
root.Properties["AppFriendlyName"].Clear();
root.Properties["AppIsolated"].Clear();
root.Properties["AccessFlags"].Clear();
root.Properties["FrontPageWeb"].Clear();
root.Properties["AppFriendlyName"].Add(root.Name);
root.Properties["AppIsolated"].Add(2);
root.Properties["AccessFlags"].Add(513);
root.Properties["FrontPageWeb"].Add(1);
A website root is a child node of the website named “ROOT” and with a SchemaClassName
of IIsWebVirtualDir
. I also created a class IISWebVirtualDir
to manipulate virtual directories. I will discuss it later. All we should know here is that we created a root virtual directory for our website.
Virtual Directory of a Website
Every website has virtual directories, at least a root virtual directory as discussed above. The other virtual directories should be sub nodes of the root virtual directory. I created the class IISWebVirtualDir
to represent virtual directories.
To access the root directory of a website, we can use IISWebsite.Root
:
public IISWebVirturalDir Root
{
get
{
foreach (DirectoryEntry entry in websiteEntry.Children)
{
if (entry.SchemaClassName == IISWebVirturalDir.IIsVirtualDir)
{
return new IISWebVirturalDir(entry);
}
}
throw new WebsiteWithoutRootException(this.Name);
}
}
With a root, we can use IISWebVirtualDir.OpenSubVirtualDir
to get sub virtual directories.
public IISWebVirturalDir OpenSubVirtualDir(string name)
{
DirectoryEntry entry = this.FindSubEntry(name);
if (entry == null)
{
return null;
}
return new IISWebVirturalDir(entry);
}
We can create a sub virtual directory using IISWebVirtualDir.CreateSubVirtualDir
:
public IISWebVirturalDir CreateSubVirtualDir(string name, string path, string appPool)
{
if (this.ExistVirtualDir(name))
{
throw new VirtualDirAlreadyExistException(this._entry, path);
}
if (System.IO.Directory.Exists(path) == false)
{
throw new DirNotFoundException(path);
}
DirectoryEntry entry = this._entry.Children.Add(name, IIsVirtualDir);
entry.Properties["path"].Clear();
entry.Properties["path"].Add(path);
if (string.IsNullOrEmpty(appPool))
{
entry.Invoke("appCreate", 0);
}
else
{
entry.Invoke("appCreate3", 0, appPool, true);
}
entry.Properties["AppFriendlyName"].Clear();
entry.Properties["AppIsolated"].Clear();
entry.Properties["AccessFlags"].Clear();
entry.Properties["FrontPageWeb"].Clear();
entry.Properties["AppFriendlyName"].Add(this._entry.Name);
entry.Properties["AppIsolated"].Add(2);
entry.Properties["AccessFlags"].Add(513);
entry.Properties["FrontPageWeb"].Add(1);
entry.CommitChanges();
return new IISWebVirturalDir(entry);
}
Application Pool
We also use DirectoryEntry
to access application pools. The root node of application pool is “IIS://localhost/W3SVC/AppPools/”.
I wrote a class IISAppPool
to manage application pools. We can use static
method IISAppPool.OpenAppPool
to open an application pool or IISAppPool.CreateAppPool
to create an application pool.
public static IISAppPool OpenAppPool(string name)
{
string connectStr = "IIS://localhost/W3SVC/AppPools/";
connectStr += name;
if (IISAppPool.Exsit(name) == false)
{
return null;
}
DirectoryEntry entry = new DirectoryEntry(connectStr);
return new IISAppPool(entry);
}
public static IISAppPool CreateAppPool(string name)
{
DirectoryEntry Service = new DirectoryEntry("IIS://localhost/W3SVC/AppPools");
foreach (DirectoryEntry entry in Service.Children)
{
if (entry.Name.Trim().ToLower() == name.Trim().ToLower())
{
return IISAppPool.OpenAppPool(name.Trim());
}
}
DirectoryEntry appPool = Service.Children.Add(name, "IIsApplicationPool");
appPool.CommitChanges();
Service.CommitChanges();
return new IISAppPool(appPool);
}
Operations on application pools are quite simple, we can Start
or Stop
an application pool:
public void Start()
{
this._entry.Invoke("Start");
}
public void Stop()
{
this._entry.Invoke("Stop");
}
Properties of DirectoryEntry
We can find references on microsoft.com about properties for each schema class. However, if we want to enumerate properties with ADS nodes, here’s one solution:
private void ListProperty(DirectoryEntry server)
{
foreach (DirectoryEntry e in server.Children)
{
ListProperty(e);
}
StringBuilder sb = new StringBuilder();
sb.AppendLine("Property for " + server.SchemaClassName);
sb.AppendLine("Name = " + server.Name);
sb.AppendLine("Path = " + server.Path);
sb.AppendLine("UserName = " + server.Username);
sb.AppendLine("====================================================================");
IEnumerator ie = server.Properties.PropertyNames.GetEnumerator();
while (ie.MoveNext())
{
try
{
string name = (string)ie.Current;
string val = "";
foreach (object obj in server.Properties[name])
{
val += obj.ToString() + ",";
}
sb.AppendLine(name + " = " + val.ToString());
}
catch (Exception)
{
}
}
System.IO.StreamWriter sw = new System.IO.StreamWriter
("PropertyList_" + server.SchemaClassName + "_" + server.Name + ".txt");
sw.Write(sb.ToString());
sw.Close();
}
}
Reference
History
- 6th August, 2010: Initial post