我们可以很方便地使用Windows提供的IIS管理器来管理IIS服务器,但有的时候,我们需要通过程序来对IIS实现自动的管理和控制。这篇文章将会介绍使用C#管理控制IIS网站的方法,并在给出一份可用的源码及其编译出来的DLL。
需要注明的是,本文中给出的方法及源码,都是针对IIS6.0及以上版本。Windows XP中的IIS版本为IIS5.1,如果使用本文提供的方法,可能会出现一些问题。
版权声明:
IISManager 由 luckzj 开发,任何人在不将其用于商业用途的前提下,都可以免费学习和使用IISManage。如果在学习使用此程序的过程中,遇到任何问题,都可以给我发email,或者直接访问本站 http://soft-bin.com 与我联系。
下载IISManager
目前IISManager只支持针对Website的操作,但在后续,我将完善IISManager,使之可以对IIS进行全面的控制。
我们的介绍将从Active Directory Service开始
Active Directory Service
我们使用C#控制IIS,需要使用Active Directory Service,关于ADS的概念我这里我不作过多讨论,网上有很多相关资料。我只介绍如何使用它。
首先我们需要添加对 System.DirectoryServices 的引用。之后需要在cs文件中引入 System.DirectoryServices 名字空间。
我们主要使用到的类是DirectoryEntry。我们可以将一个 DirectoryEntry 看作一个节点,每个节点都有其类型,被成为SchemaClass。这个节点会有其属性集合 Properties,方法以及其子节点集合 Children。 我们可以使用 DirectoryEntry 的 Properties访问节点的属性,使用Invoke 方法来调用这个节点上的方法。
DirectoryEntry 类的构造函数,可以接收一个参数,这个参数为ADS节点的名称。我们将IIS节点的名称作为参数,构造一个DirectoryEntry 的实例,就可以对IIS节点进行访问了。
使用DirectoryEntry打开IIS网站
我们可以使用以下方式构造一个DirectroyEntry的实例以使用IIS的目录服务:
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
“IIS://localhost/W3SVC” 即是IIS的目录服务的名称。在创建了这个实例后,我们就可以使用IIS目录服务。
上面的代码只是打开了IIS目录服务的根节点,各个网站就在这个根节点上,我们需要寻找到我们想要的网站。
private DirectroyEntry websiteEntry = null;
internal const string IIsWebServer = "IIsWebServer";
protected IISWebsite(DirectoryEntry Server)
{
websiteEntry = Server;
}
public static IISWebsite OpenWebsite(string name)
{
// get directory service
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
IEnumerator ie = Services.Children.GetEnumerator();
DirectoryEntry Server = null;
// find iis website
while (ie.MoveNext())
{
Server = (DirectoryEntry)ie.Current;
if (Server.SchemaClassName == IIsWebServer)
{
// "ServerComment" means name
if (Server.Properties["ServerComment"][0].ToString() == name)
{
return new IISWebsite(Server);
break;
}
}
}
return null;
}
我创建了类 IISWebsite 表示网站,用以控制IIS网站,使用其静态方法 IISWebsite.OpenWebsite()可以返回一个此类的实例。
DirectoryEntry.SchemaClassName 即代表节点的类型,我们需要查找类型为 IIsWebServer 的节点。 IIsWebServer 类型即IIS网站类型。
我们要在众多的IIS网站中查找名称为name的网站,而网站的名称保存在 DirectroyEntry.Properties["ServerComment"]中。DirectoryEntry的每一个属性又是一个集合,我们可以使用foreach语句来遍历这个属性集合,但对于网站的名称,则没有必要这么做,我们知道DirectoryEntry.Properties["ServerComment"][0]就是网站的名称。
创建网站
创建网站即是向IIS目录服务的根节点添加一个新的节点。我先列出创建新网站的源码,其后对源码进行详细讲解。
/// <summary>
/// create a new website
/// </summary>
/// <param name="name">website name</param>
/// <param name="port">website port</param>
/// <param name="rootPath">root path</param>
/// <returns></returns>
public static IISWebsite CreateWebsite(string name, int port, string rootPath, string appPool)
{
// validate root path
if (System.IO.Directory.Exists(rootPath) == false)
{
throw new DirNotFoundException(rootPath);
}
// get directory service
DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
// get server name (index)
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 exsit.");
}
if (Convert.ToInt32(server.Name) > index)
{
index = Convert.ToInt32(server.Name);
}
}
}
index++; // new index created
// create website
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 + ":");
// create ROOT for website
DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
root.Properties["path"].Clear();
root.Properties["path"].Add(rootPath);
// create application
if (string.IsNullOrEmpty(appPool))
{
root.Invoke("appCreate", 0);
}
else
{
// use application pool
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);
// commit changes
root.CommitChanges();
Server.CommitChanges();
// return the newly created website
IISWebsite website = new IISWebsite(Server);
return website;
}
目录服务中的节点有自己的名称,但这个名称并不是网站的名字,网站的名字是 Properties["ServerComment"][0]。目录服务的节点的名字是一个整型的数字,我们在创建一个新的节点时,需要查找到当前名字最大的节点,将其名字加1,成为新的节点的名字。在程序中,我使用index变量来获取已经存在的最大名称,然后将其加1,变成我的新的名称。然后调用以下语句添加新的节点。
DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);
在新的节点添加完成后,我需要对网站的属性进行设置,其中包括:
网站的名称: ServerComment
网站的端口: ServerBindings,这里需要注意的是,端口的表示为 :端口: ,即端口两端需要分别有一个冒号。
到目前为止,我们只是创建了网站,我们还需要为这个网站创建一个主目录:
// create ROOT for website
DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
root.Properties["path"].Clear();
root.Properties["path"].Add(rootPath);
// create application
if (string.IsNullOrEmpty(appPool))
{
root.Invoke("appCreate", 0);
}
else
{
// use application pool
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”。我创建了类IISWebVirtualDir用以管理虚拟目录,网站的根目录实际上也是一个虚拟目录。我将在后面介绍虚拟目录。
网站虚拟目录
网站需要包含虚拟目录,虚拟目录是网站节点的子节点。每一个网站都包含一个根虚拟目录,其节点名称叫做ROOT,这个根虚拟目录即是IIS网站的主路径。 其他的虚拟目录则是这个根虚拟目录的子节点。我使用IISWebVirturalDir来表示虚拟路径。在IISWebsite中,我使用 Root 来表示网站的根虚拟目录:
/// <summary>
/// Root path
/// </summary>
public IISWebVirturalDir Root
{
get
{
foreach (DirectoryEntry entry in websiteEntry.Children)
{
if (entry.SchemaClassName == IISWebVirturalDir.IIsVirtualDir)
{
return new IISWebVirturalDir(entry);
}
}
throw new WebsiteWithoutRootException(this.Name);
}
}
有了这个根节点,我们就可以使用IISWebVirtualDir.OpenSubVirtualDir来打开任意一个子虚拟目录。
/// <summary>
/// Open a sub virtual directory
/// </summary>
/// <param name="name">Name of directory to be opened. Case insensitive.</param>
/// <returns>A IISWebVirtualDir instance if open successfully done.Otherwise null.</returns>
public IISWebVirturalDir OpenSubVirtualDir(string name)
{
DirectoryEntry entry = this.FindSubEntry(name);
if (entry == null)
{
return null;
}
return new IISWebVirturalDir(entry);
}
我们也可以创建一个子虚拟目录,并在创建的过程中设置子虚拟目录的属性:
/// <summary>
/// Create a sub virtual directory
/// </summary>
/// <param name="name">Name of the sub virtual directory to be created.</param>
/// <param name="path">Path of the sub virtual directory.</param>
/// <param name="appPool">
/// Application pool. Application pool with this name would be created if not exist.
/// Use string.Empty or null to this parameter if you don't want to use a application pool.
/// </param>
/// <returns>A IISWebVirtualDir if created. Otherwise null.</returns>
public IISWebVirturalDir CreateSubVirtualDir(string name, string path, string appPool)
{
// already exist
if (this.ExistVirtualDir(name))
{
throw new VirtualDirAlreadyExistException(this._entry, path);
}
// validate 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);
// create application
if (string.IsNullOrEmpty(appPool))
{
entry.Invoke("appCreate", 0);
}
else
{
// use application pool
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);
}
应用程序池
应用程序池也是用目录服务来操作。只是我们需要打开的根节点,变成了 “IIS://localhost/W3SVC/AppPools/”
与上面程序一样的原理,我们可以打开,创建应用程序池:
/// <summary>
/// Open a application pool and return an IISAppPool instance
/// </summary>
/// <param name="name">application pool name</param>
/// <returns>IISAppPool object</returns>
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);
}
/// <summary>
/// create app pool
/// </summary>
/// <param name="name">the app pool to be created</param>
/// <returns>IISAppPool created if success, else null</returns>
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());
}
}
// create new app pool
DirectoryEntry appPool = Service.Children.Add(name, "IIsApplicationPool");
appPool.CommitChanges();
Service.CommitChanges();
return new IISAppPool(appPool);
}
应用程序池上的操作比较简单,我们可以启动或者停止应用程序池:
/// <summary>
/// Start application pool.
/// </summary>
public void Start()
{
this._entry.Invoke("Start");
}
/// <summary>
/// Stop application pool.
/// </summary>
public void Stop()
{
this._entry.Invoke("Stop");
}
目录节点的属性
我们可以根据目录服务节点的SchemaClass访问microsoft.com来获取节点的属性值。但如果我们想枚举属性值的话,也是可以做到的,以下为示例
代码:
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();
}
}
参考文献
IIS ADSI Provider
http://msdn.microsoft.com/en-us/library/ms524997(v=VS.90).aspx